private void DropAndCreateAllTagFilters(ISessionWrapper session) { var locked = GetAll(session); SVR_GroupFilter tagsdirec = locked.FirstOrDefault( a => a.FilterType == (int)(GroupFilterType.Directory | GroupFilterType.Tag)); var tagFilters = locked.Where(a => a.FilterType == (int)GroupFilterType.Tag).ToList(); using (ITransaction trans = session.BeginTransaction()) { BatchDelete(session, tagFilters); trans.Commit(); } if (tagsdirec != null) { HashSet <string> alltags = new HashSet <string>( RepoFactory.AniDB_Tag.GetAllForLocalSeries().Select(a => a.TagName.Replace('`', '\'')), StringComparer.InvariantCultureIgnoreCase); List <SVR_GroupFilter> toAdd = new List <SVR_GroupFilter>(alltags.Count); //AniDB Tags are in english so we use en-us culture TextInfo tinfo = new CultureInfo("en-US", false).TextInfo; foreach (string s in alltags) { SVR_GroupFilter yf = new SVR_GroupFilter { ParentGroupFilterID = tagsdirec.GroupFilterID, InvisibleInClients = 0, ApplyToSeries = 1, GroupFilterName = tinfo.ToTitleCase(s), BaseCondition = 1, Locked = 1, SortingCriteria = "5;1", FilterType = (int)GroupFilterType.Tag }; GroupFilterCondition gfc = new GroupFilterCondition { ConditionType = (int)GroupFilterConditionType.Tag, ConditionOperator = (int)GroupFilterOperator.In, ConditionParameter = s, GroupFilterID = yf.GroupFilterID }; yf.Conditions.Add(gfc); toAdd.Add(yf); } using (ITransaction trans = session.BeginTransaction()) { BatchInsert(session, toAdd); trans.Commit(); } } }
/// <summary> /// Creates a single <see cref="SVR_AnimeGroup"/> for each <see cref="SVR_AnimeSeries"/> in <paramref name="seriesList"/>. /// </summary> /// <remarks> /// This method assumes that there are no active transactions on the specified <paramref name="session"/>. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="seriesList">The list of <see cref="SVR_AnimeSeries"/> to create groups for.</param> /// <returns>A sequence of the created <see cref="SVR_AnimeGroup"/>s.</returns> private IEnumerable <SVR_AnimeGroup> CreateGroupPerSeries(ISessionWrapper session, IReadOnlyList <SVR_AnimeSeries> seriesList) { _log.Info("Generating AnimeGroups for {0} AnimeSeries", seriesList.Count); DateTime now = DateTime.Now; var newGroupsToSeries = new Tuple <SVR_AnimeGroup, SVR_AnimeSeries> [seriesList.Count]; // Create one group per series for (int grp = 0; grp < seriesList.Count; grp++) { SVR_AnimeGroup group = new SVR_AnimeGroup(); SVR_AnimeSeries series = seriesList[grp]; @group.Populate(series, now); newGroupsToSeries[grp] = new Tuple <SVR_AnimeGroup, SVR_AnimeSeries>(group, series); } using (ITransaction trans = session.BeginTransaction()) { _animeGroupRepo.InsertBatch(session, newGroupsToSeries.Select(gts => gts.Item1).AsReadOnlyCollection()); trans.Commit(); } // Anime groups should have IDs now they've been inserted. Now assign the group ID's to their respective series // (The caller of this method will be responsible for saving the AnimeSeries) foreach (Tuple <SVR_AnimeGroup, SVR_AnimeSeries> groupAndSeries in newGroupsToSeries) { groupAndSeries.Item2.AnimeGroupID = groupAndSeries.Item1.AnimeGroupID; } _log.Info("Generated {0} AnimeGroups", newGroupsToSeries.Length); return(newGroupsToSeries.Select(gts => gts.Item1)); }
/// <summary> /// Updates all Group Filters. This should be done as the last step. /// </summary> /// <remarks> /// Assumes that all caches are up to date. /// </remarks> private void UpdateGroupFilters(ISessionWrapper session) { _log.Info("Updating Group Filters"); _log.Info("Calculating Tag Filters"); ServerState.Instance.DatabaseBlocked = new ServerState.DatabaseBlockedInfo { Blocked = true, Status = "Calculating Tag Filters" }; _groupFilterRepo.CalculateAnimeSeriesPerTagGroupFilter(session); _log.Info("Calculating All Other Filters"); ServerState.Instance.DatabaseBlocked = new ServerState.DatabaseBlockedInfo { Blocked = true, Status = "Calculating Non-Tag Filters" }; IEnumerable <SVR_GroupFilter> grpFilters = _groupFilterRepo.GetAll(session).Where(a => a.FilterType != (int)GroupFilterType.Tag && ((GroupFilterType)a.FilterType & GroupFilterType.Directory) == 0).ToList(); // The main reason for doing this in parallel is because UpdateEntityReferenceStrings does JSON encoding // and is enough work that it can benefit from running in parallel Parallel.ForEach( grpFilters, filter => { filter.SeriesIds.Clear(); filter.CalculateGroupsAndSeries(); filter.UpdateEntityReferenceStrings(); }); using (ITransaction trans = session.BeginTransaction()) { _groupFilterRepo.BatchUpdate(session, grpFilters); trans.Commit(); } _log.Info("Group Filters updated"); }
/// <summary> /// Creates <see cref="SVR_AnimeGroup"/> that contain <see cref="SVR_AnimeSeries"/> that appear to be related. /// </summary> /// <remarks> /// This method assumes that there are no active transactions on the specified <paramref name="session"/>. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="seriesList">The list of <see cref="SVR_AnimeSeries"/> to create groups for.</param> /// <returns>A sequence of the created <see cref="SVR_AnimeGroup"/>s.</returns> private IEnumerable <SVR_AnimeGroup> AutoCreateGroupsWithRelatedSeries(ISessionWrapper session, IReadOnlyCollection <SVR_AnimeSeries> seriesList) { ServerState.Instance.DatabaseBlocked = new ServerState.DatabaseBlockedInfo { Blocked = true, Status = "Auto-generating Groups based on Relation Trees" }; _log.Info("Auto-generating AnimeGroups for {0} AnimeSeries based on aniDB relationships", seriesList.Count); DateTime now = DateTime.Now; var grpCalculator = AutoAnimeGroupCalculator.CreateFromServerSettings(session); _log.Info( "The following exclusions will be applied when generating the groups: " + grpCalculator.Exclusions); // Group all of the specified series into their respective groups (keyed by the groups main anime ID) var seriesByGroup = seriesList.ToLookup(s => grpCalculator.GetGroupAnimeId(s.AniDB_ID)); var newGroupsToSeries = new List <Tuple <SVR_AnimeGroup, IReadOnlyCollection <SVR_AnimeSeries> > >(seriesList.Count); foreach (var groupAndSeries in seriesByGroup) { int mainAnimeId = groupAndSeries.Key; SVR_AnimeSeries mainSeries = groupAndSeries.FirstOrDefault(series => series.AniDB_ID == mainAnimeId); SVR_AnimeGroup animeGroup = CreateAnimeGroup(mainSeries, mainAnimeId, now); newGroupsToSeries.Add( new Tuple <SVR_AnimeGroup, IReadOnlyCollection <SVR_AnimeSeries> >(animeGroup, groupAndSeries.AsReadOnlyCollection())); } using (ITransaction trans = session.BeginTransaction()) { _animeGroupRepo.InsertBatch(session, newGroupsToSeries.Select(gts => gts.Item1).AsReadOnlyCollection()); trans.Commit(); } // Anime groups should have IDs now they've been inserted. Now assign the group ID's to their respective series // (The caller of this method will be responsible for saving the AnimeSeries) foreach (var groupAndSeries in newGroupsToSeries) { foreach (SVR_AnimeSeries series in groupAndSeries.Item2) { series.AnimeGroupID = groupAndSeries.Item1.AnimeGroupID; } } _log.Info("Generated {0} AnimeGroups", newGroupsToSeries.Count); return(newGroupsToSeries.Select(gts => gts.Item1)); }
private void DropAllTagFilters(ISessionWrapper session) { lock (globalDBLock) { lock (Cache) { ClearCache(); } using (ITransaction trans = session.BeginTransaction()) { session.CreateQuery("DELETE FROM " + nameof(SVR_GroupFilter) + " WHERE FilterType = " + (int)GroupFilterType.Tag + ";") .ExecuteUpdate(); trans.Commit(); } } }
/// <summary> /// Re-creates all AnimeGroups based on the existing AnimeSeries. /// </summary> /// <param name="session">The NHibernate session.</param> /// <exception cref="ArgumentNullException"><paramref name="session"/> is <c>null</c>.</exception> public void RecreateAllGroups(ISessionWrapper session) { if (session == null) { throw new ArgumentNullException(nameof(session)); } bool cmdProcGeneralPaused = ShokoService.CmdProcessorGeneral.Paused; bool cmdProcHasherPaused = ShokoService.CmdProcessorHasher.Paused; bool cmdProcImagesPaused = ShokoService.CmdProcessorImages.Paused; try { // Pause queues ShokoService.CmdProcessorGeneral.Paused = true; ShokoService.CmdProcessorHasher.Paused = true; ShokoService.CmdProcessorImages.Paused = true; _log.Info("Beginning re-creation of all groups"); IReadOnlyList <SVR_AnimeSeries> animeSeries = RepoFactory.AnimeSeries.GetAll(); IReadOnlyCollection <SVR_AnimeGroup> createdGroups = null; SVR_AnimeGroup tempGroup = null; using (ITransaction trans = session.BeginTransaction()) { tempGroup = CreateTempAnimeGroup(session); ClearGroupsAndDependencies(session, tempGroup.AnimeGroupID); trans.Commit(); } if (_autoGroupSeries) { createdGroups = AutoCreateGroupsWithRelatedSeries(session, animeSeries) .AsReadOnlyCollection(); } else // Standard group re-create { createdGroups = CreateGroupPerSeries(session, animeSeries) .AsReadOnlyCollection(); } using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeSeriesContractsAndSave(session, animeSeries); session.Delete(tempGroup); // We should no longer need the temporary group we created earlier trans.Commit(); } // We need groups and series cached for updating of AnimeGroup contracts to work _animeGroupRepo.Populate(session, displayname: false); _animeSeriesRepo.Populate(session, displayname: false); using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeGroupsAndTheirContracts(session, createdGroups); trans.Commit(); } // We need to update the AnimeGroups cache again now that the contracts have been saved // (Otherwise updating Group Filters won't get the correct results) _animeGroupRepo.Populate(session, displayname: false); _animeGroupUserRepo.Populate(session, displayname: false); _groupFilterRepo.Populate(session, displayname: false); using (ITransaction trans = session.BeginTransaction()) { UpdateGroupFilters(session); trans.Commit(); } _log.Info("Successfuly completed re-creating all groups"); } catch (Exception e) { _log.Error(e, "An error occurred while re-creating all groups"); try { // If an error occurs then chances are the caches are in an inconsistent state. So re-populate them _animeSeriesRepo.Populate(); _animeGroupRepo.Populate(); _groupFilterRepo.Populate(); _animeGroupUserRepo.Populate(); } catch (Exception ie) { _log.Warn(ie, "Failed to re-populate caches"); } throw; } finally { // Un-pause queues (if they were previously running) ShokoService.CmdProcessorGeneral.Paused = cmdProcGeneralPaused; ShokoService.CmdProcessorHasher.Paused = cmdProcHasherPaused; ShokoService.CmdProcessorImages.Paused = cmdProcImagesPaused; } }
private void CreateAllTagFilters(ISessionWrapper session, SVR_GroupFilter tagsdirec, Dictionary <int, ILookup <string, int> > lookup) { if (tagsdirec == null) { return; } HashSet <string> alltags = new HashSet <string>( RepoFactory.AniDB_Tag.GetAllForLocalSeries().Select(a => a.TagName.Replace('`', '\'')), StringComparer.InvariantCultureIgnoreCase); List <SVR_GroupFilter> toAdd = new List <SVR_GroupFilter>(alltags.Count); var users = RepoFactory.JMMUser.GetAll().ToList(); //AniDB Tags are in english so we use en-us culture TextInfo tinfo = new CultureInfo("en-US", false).TextInfo; foreach (string s in alltags) { SVR_GroupFilter yf = new SVR_GroupFilter { ParentGroupFilterID = tagsdirec.GroupFilterID, InvisibleInClients = 0, ApplyToSeries = 1, GroupFilterName = tinfo.ToTitleCase(s), BaseCondition = 1, Locked = 1, SortingCriteria = "5;1", FilterType = (int)GroupFilterType.Tag, }; yf.SeriesIds[0] = lookup[0][s.ToLowerInvariant()].ToHashSet(); yf.GroupsIds[0] = yf.SeriesIds[0] .Select(id => RepoFactory.AnimeSeries.GetByID(id).TopLevelAnimeGroup?.AnimeGroupID ?? -1) .Where(id => id != -1).ToHashSet(); foreach (var user in users) { yf.SeriesIds[user.JMMUserID] = lookup[user.JMMUserID][s.ToLowerInvariant()].ToHashSet(); yf.GroupsIds[user.JMMUserID] = yf.SeriesIds[user.JMMUserID] .Select(id => RepoFactory.AnimeSeries.GetByID(id).TopLevelAnimeGroup?.AnimeGroupID ?? -1) .Where(id => id != -1).ToHashSet(); } using (var trans = session.BeginTransaction()) { // get an ID session.Insert(yf); trans.Commit(); } GroupFilterCondition gfc = new GroupFilterCondition { ConditionType = (int)GroupFilterConditionType.Tag, ConditionOperator = (int)GroupFilterOperator.In, ConditionParameter = s, GroupFilterID = yf.GroupFilterID }; yf.Conditions.Add(gfc); yf.UpdateEntityReferenceStrings(); yf.GroupConditions = Newtonsoft.Json.JsonConvert.SerializeObject(yf._conditions); yf.GroupConditionsVersion = SVR_GroupFilter.GROUPCONDITIONS_VERSION; toAdd.Add(yf); } lock (Cache) { Populate(session, false); lock (globalDBLock) { foreach (var filters in toAdd.Batch(50)) { using (ITransaction trans = session.BeginTransaction()) { BatchUpdate(session, filters); trans.Commit(); } } } } }
/// <summary> /// Creates a single <see cref="AnimeGroup"/> for each <see cref="AnimeSeries"/> in <paramref name="seriesList"/>. /// </summary> /// <remarks> /// This method assumes that there are no active transactions on the specified <paramref name="session"/>. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="seriesList">The list of <see cref="AnimeSeries"/> to create groups for.</param> /// <returns>A sequence of the created <see cref="AnimeGroup"/>s.</returns> private IEnumerable<AnimeGroup> CreateGroupPerSeries(ISessionWrapper session, IReadOnlyList<AnimeSeries> seriesList) { _log.Info("Generating AnimeGroups for {0} AnimeSeries", seriesList.Count); DateTime now = DateTime.Now; var newGroupsToSeries = new Tuple<AnimeGroup, AnimeSeries>[seriesList.Count]; // Create one group per series for (int grp = 0; grp < seriesList.Count; grp++) { AnimeGroup group = new AnimeGroup(); AnimeSeries series = seriesList[grp]; group.Populate(series, now); newGroupsToSeries[grp] = new Tuple<AnimeGroup, AnimeSeries>(group, series); } using (ITransaction trans = session.BeginTransaction()) { _animeGroupRepo.InsertBatch(session, newGroupsToSeries.Select(gts => gts.Item1).AsReadOnlyCollection()); trans.Commit(); } // Anime groups should have IDs now they've been inserted. Now assign the group ID's to their respective series // (The caller of this method will be responsible for saving the AnimeSeries) foreach (Tuple<AnimeGroup, AnimeSeries> groupAndSeries in newGroupsToSeries) { groupAndSeries.Item2.AnimeGroupID = groupAndSeries.Item1.AnimeGroupID; } _log.Info("Generated {0} AnimeGroups", newGroupsToSeries.Length); return newGroupsToSeries.Select(gts => gts.Item1); }
/// <summary> /// Creates <see cref="AnimeGroup"/> that contain <see cref="AnimeSeries"/> that appear to be related. /// </summary> /// <remarks> /// This method assumes that there are no active transactions on the specified <paramref name="session"/>. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="seriesList">The list of <see cref="AnimeSeries"/> to create groups for.</param> /// <returns>A sequence of the created <see cref="AnimeGroup"/>s.</returns> private IEnumerable<AnimeGroup> AutoCreateGroupsWithRelatedSeries(ISessionWrapper session, IReadOnlyCollection<AnimeSeries> seriesList) { _log.Info("Auto-generating AnimeGroups for {0} AnimeSeries based on aniDB relationships", seriesList.Count); DateTime now = DateTime.Now; var grpCalculator = AutoAnimeGroupCalculator.CreateFromServerSettings(session); _log.Info("The following exclusions will be applied when generating the groups: " + grpCalculator.Exclusions); // Group all of the specified series into their respective groups (keyed by the groups main anime ID) var seriesByGroup = seriesList.ToLookup(s => grpCalculator.GetGroupAnimeId(s.AniDB_ID)); var newGroupsToSeries = new List<Tuple<AnimeGroup, IReadOnlyCollection<AnimeSeries>>>(seriesList.Count); foreach (var groupAndSeries in seriesByGroup) { int mainAnimeId = groupAndSeries.Key; AnimeSeries mainSeries = groupAndSeries.FirstOrDefault(series => series.AniDB_ID == mainAnimeId); AnimeGroup animeGroup = CreateAnimeGroup(session, mainSeries, mainAnimeId, now); newGroupsToSeries.Add(new Tuple<AnimeGroup, IReadOnlyCollection<AnimeSeries>>(animeGroup, groupAndSeries.AsReadOnlyCollection())); } using (ITransaction trans = session.BeginTransaction()) { _animeGroupRepo.InsertBatch(session, newGroupsToSeries.Select(gts => gts.Item1).AsReadOnlyCollection()); trans.Commit(); } // Anime groups should have IDs now they've been inserted. Now assign the group ID's to their respective series // (The caller of this method will be responsible for saving the AnimeSeries) foreach (var groupAndSeries in newGroupsToSeries) { foreach (AnimeSeries series in groupAndSeries.Item2) { series.AnimeGroupID = groupAndSeries.Item1.AnimeGroupID; } } _log.Info("Generated {0} AnimeGroups", newGroupsToSeries.Count); return newGroupsToSeries.Select(gts => gts.Item1); }
/// <summary> /// Re-creates all AnimeGroups based on the existing AnimeSeries. /// </summary> /// <param name="session">The NHibernate session.</param> /// <exception cref="ArgumentNullException"><paramref name="session"/> is <c>null</c>.</exception> public void RecreateAllGroups(ISessionWrapper session) { if (session == null) throw new ArgumentNullException(nameof(session)); bool cmdProcGeneralPaused = JMMService.CmdProcessorGeneral.Paused; bool cmdProcHasherPaused = JMMService.CmdProcessorHasher.Paused; bool cmdProcImagesPaused = JMMService.CmdProcessorImages.Paused; try { // Pause queues JMMService.CmdProcessorGeneral.Paused = true; JMMService.CmdProcessorHasher.Paused = true; JMMService.CmdProcessorImages.Paused = true; _log.Info("Beginning re-creation of all groups"); IReadOnlyList<AnimeSeries> animeSeries = RepoFactory.AnimeSeries.GetAll(); IReadOnlyCollection<AnimeGroup> createdGroups = null; AnimeGroup tempGroup = null; using (ITransaction trans = session.BeginTransaction()) { tempGroup = CreateTempAnimeGroup(session); ClearGroupsAndDependencies(session, tempGroup.AnimeGroupID); trans.Commit(); } if (_autoGroupSeries) { createdGroups = AutoCreateGroupsWithRelatedSeries(session, animeSeries) .AsReadOnlyCollection(); } else // Standard group re-create { createdGroups = CreateGroupPerSeries(session, animeSeries) .AsReadOnlyCollection(); } using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeSeriesContractsAndSave(session, animeSeries); session.Delete(tempGroup); // We should no longer need the temporary group we created earlier trans.Commit(); } // We need groups and series cached for updating of AnimeGroup contracts to work _animeGroupRepo.Populate(session, displayname: false); _animeSeriesRepo.Populate(session, displayname: false); using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeGroupsAndTheirContracts(session, createdGroups); trans.Commit(); } // We need to update the AnimeGroups cache again now that the contracts have been saved // (Otherwise updating Group Filters won't get the correct results) _animeGroupRepo.Populate(session, displayname: false); _animeGroupUserRepo.Populate(session, displayname: false); _groupFilterRepo.Populate(session, displayname: false); using (ITransaction trans = session.BeginTransaction()) { UpdateGroupFilters(session); trans.Commit(); } _log.Info("Successfuly completed re-creating all groups"); } catch (Exception e) { _log.Error(e, "An error occurred while re-creating all groups"); try { // If an error occurs then chances are the caches are in an inconsistent state. So re-populate them _animeSeriesRepo.Populate(); _animeGroupRepo.Populate(); _groupFilterRepo.Populate(); _animeGroupUserRepo.Populate(); } catch (Exception ie) { _log.Warn(ie, "Failed to re-populate caches"); } throw; } finally { // Un-pause queues (if they were previously running) JMMService.CmdProcessorGeneral.Paused = cmdProcGeneralPaused; JMMService.CmdProcessorHasher.Paused = cmdProcHasherPaused; JMMService.CmdProcessorImages.Paused = cmdProcImagesPaused; } }