Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using JoinRpg.Helpers;

namespace JoinRpg.Dal.Impl.Repositories;

public static class CharacterGroupDictionaryBuilder
Expand Down Expand Up @@ -29,6 +31,17 @@ public static IReadOnlyDictionary<CharacterGroupIdentification, CharacterGroupIn
}
}

// Отсортировать дочерние группы
foreach (var group in project.CharacterGroups)
{
if (string.IsNullOrEmpty(group.ChildCharactersOrdering))
{
continue; // Не сохранено порядка, common case
}
var unsorted = childGroupsMap[group.CharacterGroupId];
childGroupsMap[group.CharacterGroupId] = [.. unsorted.OrderByStoredOrder(k => k.Id, group.ChildGroupsOrdering)];
}

// Кэши для рекурсивных обходов
var allChildrenCache = new Dictionary<int, List<CharacterGroupIdentification>>();
var allParentsCache = new Dictionary<int, List<CharacterGroupIdentification>>();
Expand Down Expand Up @@ -85,6 +98,8 @@ List<CharacterGroupIdentification> GetAllParentGroups(int groupId)
return list;
}

var rootGroupId = project.RootGroup.GetId();

var dict = new Dictionary<CharacterGroupIdentification, CharacterGroupInfo>();
foreach (var group in project.CharacterGroups)
{
Expand All @@ -100,6 +115,7 @@ List<CharacterGroupIdentification> GetAllParentGroups(int groupId)
IsActive: group.IsActive,
IsPublic: group.IsPublic,
IsSpecial: group.IsSpecial,
IsIntresting: !group.IsRoot && group.IsActive && (!group.IsSpecial || group.ParentCharacterGroupIds.Any(parentId => parentId != rootGroupId.Id)),
DirectChildGroupIds: childGroupsMap.GetValueOrDefault(group.CharacterGroupId, []),
DirectParentGroupIds: parentGroupsMap.GetValueOrDefault(group.CharacterGroupId, []),
AllChildGroups: allChildGroups,
Expand Down
8 changes: 1 addition & 7 deletions src/JoinRpg.Dal.Impl/Repositories/CharacterRepositoryImpl.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using JoinRpg.DataModel.Extensions;
using JoinRpg.DomainTypes.Characters;
using JoinRpg.DomainTypes.Claims;
using JoinRpg.Helpers;
using LinqKit;

namespace JoinRpg.Dal.Impl.Repositories;
Expand Down Expand Up @@ -105,12 +104,7 @@ public async Task<CharacterView> GetCharacterViewAsync(int projectId, int charac
{
IsActive = activeClaimPredicate.Invoke(claim),
}).ToListAsync(),
DirectGroups = directGroups,
AllGroups = directGroups
.SelectMany(g => g.FlatTree(group => group.ParentGroupIds._parentCharacterGroupIds.Select(id => allGroups[id])))
.Where(g => g.IsActive)
.Distinct()
.ToList()
DirectGroups = directGroups
};
return view;
}
Expand Down
2 changes: 1 addition & 1 deletion src/JoinRpg.Dal.Impl/Repositories/ClaimsRepositoryImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public async Task<Claim> GetClaimWithDetails(int projectId, int claimId)
.SingleOrDefaultAsync(e => e.ClaimId == claimId && e.ProjectId == projectId);
}

public Task<IReadOnlyCollection<Claim>> GetClaimsForGroups(int projectId, ClaimStatusSpec active, int[] characterGroupsIds)
public Task<IReadOnlyCollection<Claim>> GetClaimsForGroups(ProjectIdentification projectId, ClaimStatusSpec active, CharacterGroupIdentification[] characterGroupsIds)
{
return GetClaimsImpl(projectId, active, ClaimPredicates.GetInGroupPredicate(characterGroupsIds));
}
Expand Down
12 changes: 2 additions & 10 deletions src/JoinRpg.Dal.Impl/Repositories/PlotRepositoryImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,14 @@ await Ctx.Set<PlotFolder>()
.SingleOrDefaultAsync(pf => pf.PlotFolderId == plotFolderId.PlotFolderId && pf.ProjectId == plotFolderId.ProjectId);
}

public async Task<IReadOnlyCollection<PlotElement>> GetPlotsForCharacter(Character character)
public async Task<IReadOnlyCollection<PlotElement>> GetDirectPlotsForCharacter(CharacterIdentification character)
{
var ids =
character.Groups.SelectMany(group => group.FlatTree(g => g.ParentGroups))
.Select(g => g.CharacterGroupId)
.Distinct()
.ToList(); //ToList required here so all lazy loads are finished before we are starting making condition below.
return
await Ctx.Set<PlotElement>()
.Include(e => e.Texts)
.Include(e => e.TargetCharacters)
.Include(e => e.TargetGroups)
.Where(
e =>
e.TargetCharacters.Any(ch => ch.CharacterId == character.CharacterId) ||
e.TargetGroups.Any(g => ids.Contains(g.CharacterGroupId)))
.Where(e => e.TargetCharacters.Any(ch => ch.CharacterId == character.CharacterId))
.ToListAsync();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ internal static ProjectInfo CreateInfoFromProject(Project project, ProjectIdenti

ProjectLifecycleStatus status = ProjectLoaderCommon.CreateStatus(project.Active, project.IsAcceptingClaims);



var groups = CharacterGroupDictionaryBuilder.Build(project, projectId);

return new ProjectInfo(
Expand Down
3 changes: 2 additions & 1 deletion src/JoinRpg.Data.Interfaces/Claims/IClaimsRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public interface IClaimsRepository : IDisposable
Task<Claim?> GetClaim(ClaimIdentification claimId);
[Obsolete]
Task<Claim> GetClaimWithDetails(int projectId, int claimId);
Task<IReadOnlyCollection<Claim>> GetClaimsForGroups(int projectId, ClaimStatusSpec active, int[] characterGroupsIds);

Task<IReadOnlyCollection<Claim>> GetClaimsForGroups(ProjectIdentification projectId, ClaimStatusSpec active, CharacterGroupIdentification[] characterGroupsIds);

Task<IReadOnlyCollection<ClaimWithPlayer>> GetClaimHeadersWithPlayer(IReadOnlyCollection<CharacterGroupIdentification> characterGroupsIds, ClaimStatusSpec spec);
Task<IReadOnlyCollection<Claim>> GetClaimsForPlayer(int projectId, ClaimStatusSpec claimStatusSpec, int userId);
Expand Down
1 change: 0 additions & 1 deletion src/JoinRpg.Data.Interfaces/ICharacterRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public class CharacterView : IFieldContainter
public Claim? ApprovedClaim { get; set; }
public required IReadOnlyCollection<ClaimHeader> Claims { get; set; }
public required IReadOnlyCollection<GroupHeader> DirectGroups { get; set; }
public required IReadOnlyCollection<GroupHeader> AllGroups { get; set; }
public required string JsonData { get; set; }
public required string Name { get; set; }
public required string Description { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/JoinRpg.Data.Interfaces/IPlotRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public interface IPlotRepository : IDisposable

Task<IReadOnlyList<PlotFolder>> GetPlots(ProjectIdentification projectId);
Task<PlotFolder?> GetPlotFolderAsync(PlotFolderIdentification plotFolderId);
Task<IReadOnlyCollection<PlotElement>> GetPlotsForCharacter(Character character);
Task<IReadOnlyCollection<PlotElement>> GetDirectPlotsForCharacter(CharacterIdentification characterId);
Task<IReadOnlyCollection<PlotFolder>> GetPlotsWithTargetAndText(int projectid);

[Obsolete]
Expand Down
3 changes: 1 addition & 2 deletions src/JoinRpg.DataModel/Character.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ public int[] ParentCharacterGroupIds

public virtual Project Project { get; set; }

IEnumerable<CharacterGroup> IWorldObject.ParentGroups => Groups;

[Obsolete("Use groups from ProjectInfo")]
public IEnumerable<CharacterGroup> Groups => Project.CharacterGroups.Where(c => ParentCharacterGroupIds.Contains(c.CharacterGroupId));

public string CharacterName { get; set; }
Expand Down
1 change: 0 additions & 1 deletion src/JoinRpg.DataModel/Interfaces/IWorldObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace JoinRpg.DataModel;

public interface IWorldObject : IProjectEntity, ILinkable
{
IEnumerable<CharacterGroup> ParentGroups { get; }
string Name { get; }
bool IsPublic { get; }

Expand Down
9 changes: 3 additions & 6 deletions src/JoinRpg.Domain.Test/FieldSaveHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public void SaveOnAddTest()
.ShouldBe(_original.Character.JsonData,
"Adding claim should not modify any character fields");

mock.Character.Groups.Select(g => g.CharacterGroupId).ShouldBe(
mock.Character.Groups.Select(g => g.CharacterGroupId),
mock.Character.ParentCharacterGroupIds.ShouldBe(
mock.Character.ParentCharacterGroupIds,
"Adding claim should not modify any character groups");

claim.JsonData.ShouldBe($"{{\"{mock.CharacterFieldInfo.Id.ProjectFieldId}\":\"test\"}}");
Expand Down Expand Up @@ -212,10 +212,7 @@ public void DisableUnapprovedClaimToChangeCharacterTest()
ShouldBeTestExtensions.ShouldBe(mock.Character.JsonData,
_original.Character.JsonData,
"Adding claim should not modify any character fields");
mock.Character.Groups.Select(g => g.CharacterGroupId).ToList().ShouldBe(
(IEnumerable<int>)_original.Character.Groups.Select(g => g.CharacterGroupId)
.ToList(),
"Adding claim should not modify any character groups");
mock.Character.ParentCharacterGroupIds.ToList().ShouldBe(_original.Character.ParentCharacterGroupIds.ToList(), "Adding claim should not modify any character groups");
ShouldBeTestExtensions.ShouldBe(claim.JsonData,
$"{{\"{mock.CharacterFieldInfo.Id.ProjectFieldId}\":\"test\"}}");
}
Expand Down
23 changes: 2 additions & 21 deletions src/JoinRpg.Domain/CharacterBulkLoader.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,19 @@

using JoinRpg.Helpers;

namespace JoinRpg.Domain;

public class CharacterBulkLoader
{
private readonly Dictionary<CharacterGroupIdentification, CharacterGroupIdentification[]> parentGroupsCache = [];
private readonly Dictionary<int, CharacterItem> characterCache = [];

public CharacterItem LoadCharacter(Character character)
public CharacterItem LoadCharacter(Character character, ProjectInfo projectInfo)
{
if (characterCache.TryGetValue(character.CharacterId, out var item))
{
return item;
}
var directParents = character.ParentCharacterGroupIds.Select(c => new CharacterGroupIdentification(character.ProjectId, c)).ToList();
var allParents = directParents.SelectMany(g => ResolveGroupsToTop(character.Project, g)).ToList();
var result = new CharacterItem(character, allParents);
var result = new CharacterItem(character, [.. character.GetParentGroupIdsToTop(projectInfo)]);
characterCache.Add(character.CharacterId, result);
return result;
}

private CharacterGroupIdentification[] ResolveGroupsToTop(Project project, CharacterGroupIdentification groupId)
{
if (parentGroupsCache.TryGetValue(groupId, out var groups))
{
return groups;
}
var entity = project.CharacterGroups.Single(g => g.CharacterGroupId == groupId.CharacterGroupId);
var all = entity.FlatTree(e => e.ParentGroups);
var value = all.Select(e => new CharacterGroupIdentification(e.ProjectId, e.CharacterGroupId)).ToArray();
parentGroupsCache.Add(groupId, value);
return value;
}
}

public record class CharacterItem(Character Character, IReadOnlyCollection<CharacterGroupIdentification> ParentGroups);
16 changes: 16 additions & 0 deletions src/JoinRpg.Domain/CharacterParentGroupExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace JoinRpg.Domain;

public static class CharacterParentGroupExtensions
{
public static IEnumerable<CharacterGroupIdentification> GetParentGroupIdsToTop(this Character target, ProjectInfo projectInfo)
=> projectInfo.GetParentGroupIdsIncludingThis(target.GetDirectGroupIds());

public static IEnumerable<CharacterGroupIdentification> GetDirectGroupIds(this Character target)
=> CharacterGroupIdentification.FromList(target.ParentCharacterGroupIds, new(target.ProjectId));

public static IEnumerable<CharacterGroupInfo> GetParentGroupsToTop(this Character target, ProjectInfo projectInfo)
=> projectInfo.GetParentGroupsIncludingThis(target.GetDirectGroupIds());

public static IEnumerable<CharacterGroupInfo> GetIntrestingGroupsForDisplayToTop(this Character character, ProjectInfo projectInfo)
=> character.GetParentGroupsToTop(projectInfo).Where(g => g.IsIntresting);
}
14 changes: 0 additions & 14 deletions src/JoinRpg.Domain/OrderingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,9 @@ public static IReadOnlyList<CharacterGroup> GetOrderedChildGroups(this Character
public static VirtualOrderContainer<CharacterGroup> GetCharacterGroupsContainer(this CharacterGroup characterGroup)
=> VirtualOrderContainerFacade.Create(characterGroup.ChildGroups, characterGroup.ChildGroupsOrdering);

public static IReadOnlyList<PlotElement> GetOrderedPlots(this Character character, IReadOnlyCollection<PlotElement> elements)
=> character.GetCharacterPlotContainer(elements).OrderedItems;

public static IReadOnlyList<PlotElementTexts> GetOrderedPlots(this Character character, IReadOnlyCollection<PlotElementTexts> elements)
=> character.GetCharacterPlotContainer(elements).OrderedItems;

public static VirtualOrderContainer<PlotElement> GetCharacterPlotContainer(this Character character,
IReadOnlyCollection<PlotElement> plots) => VirtualOrderContainerFacade.Create(plots.OrderBy(pe => pe.PlotFolderId), character.PlotElementOrderData, preserveOrder: true);

public static VirtualOrderContainer<PlotElementTexts> GetCharacterPlotContainer(this Character character,
IReadOnlyCollection<PlotElementTexts> plots) => VirtualOrderContainerFacade.Create(plots, character.PlotElementOrderData, preserveOrder: true);

public static IReadOnlyList<ProjectFieldDropdownValue> GetOrderedValues(this ProjectField field)
=> field.GetFieldValuesContainer().OrderedItems;

Expand All @@ -43,9 +34,4 @@ public static VirtualOrderContainer<ProjectField> GetFieldsContainer(

public static VirtualOrderContainer<PlotFolder> GetPlotFoldersContainer(this Project field)
=> VirtualOrderContainerFacade.Create(field.PlotFolders, field.Details.PlotFoldersOrdering);

public static IReadOnlyList<PlotFolder> GetOrderedPlotFolders(this Project field) => field.GetPlotFoldersContainer().OrderedItems;

public static VirtualOrderContainer<PlotElement> GetPlotElementsContainer(this PlotFolder field)
=> VirtualOrderContainerFacade.Create(field.Elements, field.ElementsOrdering);
}
50 changes: 13 additions & 37 deletions src/JoinRpg.Domain/ParentGroupCalculateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,29 @@ namespace JoinRpg.Domain;

public static class ParentGroupCalculateExtensions
{
public static IEnumerable<CharacterGroup> GetParentGroupsToTop(this IWorldObject? target)
[Obsolete("Pass ProjectInfo")]
public static IEnumerable<CharacterGroup> GetParentGroupsToTop(this CharacterGroup? target)
{
return target?.ParentGroups.SelectMany(g => g.FlatTree(gr => gr.ParentGroups))
.OrderBy(g => g.CharacterGroupId)
.Distinct() ?? [];
}

public static IReadOnlyCollection<CharacterGroupIdentification> GetParentGroupIdsToTop(this IWorldObject? target)
[Obsolete("Pass ProjectInfo")]
public static IEnumerable<CharacterGroup> GetParentGroupsToTop(this Character? target)
{
return target?.Groups.SelectMany(g => g.FlatTree(gr => gr.ParentGroups))
.OrderBy(g => g.CharacterGroupId)
.Distinct() ?? [];
}

[Obsolete("Pass ProjectInfo")]
public static IReadOnlyCollection<CharacterGroupIdentification> GetParentGroupIdsToTop(this Character? target)
{
if (target == null)
{
return [];
}
return [.. target.ParentGroups.SelectMany(g => g.FlatTree(gr => gr.ParentGroups)).Select(x => x.GetId()).Order().Distinct()];
}

public static IEnumerable<CharacterGroup> GetIntrestingGroupsForDisplayToTop(this Character character)
=> character.GetParentGroupsToTop().Where(g => !g.IsRoot && g.IsActive && (!g.IsSpecial || g.ParentGroups.All(g => !g.IsRoot)));

public static IEnumerable<CharacterGroup> GetChildrenGroupsRecursive(
this CharacterGroup target)
{
ArgumentNullException.ThrowIfNull(target);

return target.ChildGroups.SelectMany(g => g.FlatTree(gr => gr.ChildGroups)).Distinct();
}

[Obsolete]
public static int[] GetChildrenGroupsIdRecursiveIncludingThis(this CharacterGroup target)
{
ArgumentNullException.ThrowIfNull(target);

return [.. target.GetChildrenGroupsRecursive().Select(g => g.CharacterGroupId), target.CharacterGroupId];
}

public static IEnumerable<CharacterGroupIdentification> GetChildrenGroupsIdentificationRecursiveIncludingThis(this CharacterGroup target)
{
ArgumentNullException.ThrowIfNull(target);

return [.. target.GetChildrenGroupsRecursive().Select(g => g.GetId()), target.GetId()];
}

public static IEnumerable<CharacterGroup> GetOrderedChildrenGroupsRecursive(
this CharacterGroup target)
{
ArgumentNullException.ThrowIfNull(target);

return target.GetOrderedChildGroups().SelectMany(g => g.FlatTree(gr => gr.GetOrderedChildGroups())).Distinct();
return [.. target.Groups.SelectMany(g => g.FlatTree(gr => gr.ParentGroups)).Select(x => x.GetId()).Order().Distinct()];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ internal class BrokenCharactersFilter : IProblemFilter<Character>
{
public IEnumerable<ClaimProblem> GetProblems(Character character, ProjectInfo projectInfo)
{
var groups = character.GetParentGroupsToTop().Where(g => g.IsActive && !g.IsSpecial).ToArray();
var groups = character.GetParentGroupsToTop(projectInfo).Where(g => g.IsActive && !g.IsSpecial).ToArray();
if (!groups.Any())
{
yield return new ClaimProblem(ClaimProblemType.NoParentGroup, ProblemSeverity.Fatal);
Expand All @@ -15,15 +15,15 @@ public IEnumerable<ClaimProblem> GetProblems(Character character, ProjectInfo pr
}
}

private IEnumerable<ClaimProblem> GetProblemFroGroup(CharacterGroup group)
private IEnumerable<ClaimProblem> GetProblemFroGroup(CharacterGroupInfo group)
{
if (group.IsRoot)
{
yield break;
}
if (!group.ParentCharacterGroupIds.Any() || group.ParentCharacterGroupIds.Any(id => id == group.CharacterGroupId))
if (!group.DirectParentGroupIds.Any() || group.DirectParentGroupIds.Any(id => id == group.Id))
{
yield return new ClaimProblem(ClaimProblemType.GroupIsBroken, ProblemSeverity.Fatal, group.CharacterGroupName);
yield return new ClaimProblem(ClaimProblemType.GroupIsBroken, ProblemSeverity.Fatal, group.Name);
}
}
}
8 changes: 4 additions & 4 deletions src/JoinRpg.Domain/Problems/ProblemValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public IEnumerable<FieldRelatedProblem> ValidateFieldsOnly(TObject obj, ProjectI

FieldWithValue[] fieldWithValues = GetFields(obj, projectInfo).Where(f => fields.Contains(f.Field.Id)).ToArray();

return ValidateFieldsInternal(obj, fieldWithValues);
return ValidateFieldsInternal(obj, fieldWithValues, projectInfo);
}

public IEnumerable<FieldRelatedProblem> ValidateFieldsOnly(TObject obj, ProjectInfo projectInfo)
Expand All @@ -36,12 +36,12 @@ public IEnumerable<FieldRelatedProblem> ValidateFieldsOnly(TObject obj, ProjectI
ArgumentNullException.ThrowIfNull(projectInfo);

FieldWithValue[] fieldWithValues = GetFields(obj, projectInfo);
return ValidateFieldsInternal(obj, fieldWithValues);
return ValidateFieldsInternal(obj, fieldWithValues, projectInfo);
}

private IEnumerable<FieldRelatedProblem> ValidateFieldsInternal(TObject obj, FieldWithValue[] fieldWithValues)
private IEnumerable<FieldRelatedProblem> ValidateFieldsInternal(TObject obj, FieldWithValue[] fieldWithValues, ProjectInfo projectInfo)
{
var target = characterBulkLoader.LoadCharacter(GetClaimSource(obj));
var target = characterBulkLoader.LoadCharacter(GetClaimSource(obj), projectInfo);

foreach (var fieldWithValue in fieldWithValues)
{
Expand Down
12 changes: 0 additions & 12 deletions src/JoinRpg.Domain/SubscribeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ namespace JoinRpg.Domain;

public static class SubscribeExtensions
{
public static IEnumerable<User> GetSubscriptions(
this ForumThread forumThread,
IEnumerable<User?>? extraRecipients,
bool isVisibleToPlayer)
{
return
forumThread.Subscriptions //get subscriptions on forum
.Select(u => u.User) //Select users
.Union(extraRecipients ?? Enumerable.Empty<User>()) //add extra recipients
.VerifySubscriptions(isVisibleToPlayer, forumThread);
}

public static IEnumerable<User> GetSubscriptions(
this Character character,
Func<UserSubscription, bool> predicate,
Expand Down
Loading
Loading