Skip to content

Commit 9e0046c

Browse files
feat: implement InfluenceDiagramService and related interfaces for managing influence diagrams (#434)
1 parent fb910ad commit 9e0046c

8 files changed

Lines changed: 281 additions & 230 deletions

File tree

PrismaDotnetApi/PrismaApi.Api/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public static void Main(string[] args)
125125

126126
builder.Services.AddScoped<ITableRebuildingService, TableRebuildingService>();
127127
builder.Services.AddScoped<IProjectService, ProjectService>();
128+
builder.Services.AddScoped<IInfluenceDiagramService, InfluenceDiagramService>();
128129
builder.Services.AddScoped<IIssueService, IssueService>();
129130
builder.Services.AddScoped<INodeService, NodeService>();
130131
builder.Services.AddScoped<INodeStyleService, NodeStyleService>();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using PrismaApi.Domain.Dtos;
2+
3+
namespace PrismaApi.Application.Interfaces.Services;
4+
public interface IInfluenceDiagramService
5+
{
6+
Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default);
7+
Task<InfluenceDiagramDto> GetRestrictedInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default);
8+
}

PrismaDotnetApi/PrismaApi.Application/Interfaces/Services/IProjectService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ public interface IProjectService
1111
Task<List<ProjectOutgoingDto>> GetAllAsync(UserOutgoingDto user, CancellationToken ct = default);
1212
Task<List<PopulatedProjectDto>> GetPopulatedAsync(List<Guid> ids, UserOutgoingDto user, CancellationToken ct = default);
1313
Task<List<PopulatedProjectDto>> GetAllPopulatedAsync(UserOutgoingDto user, CancellationToken ct = default);
14-
Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default);
1514
}

PrismaDotnetApi/PrismaApi.Application/Services/FastApiService.cs

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ namespace PrismaApi.Application.Services;
99
public class FastApiService : IFastApiService
1010
{
1111
private readonly HttpClient _httpClient;
12-
private readonly IProjectService _projectService;
12+
private readonly IInfluenceDiagramService _influenceDiagramService;
1313
private readonly IConfiguration _configuration;
14-
public FastApiService(HttpClient httpClient, IProjectService projectService, IConfiguration configuration)
14+
public FastApiService(HttpClient httpClient, IInfluenceDiagramService influenceDiagramService, IConfiguration configuration)
1515
{
1616
_httpClient = httpClient;
1717
_httpClient.Timeout = TimeSpan.FromSeconds(180);
18-
_projectService = projectService;
18+
_influenceDiagramService = influenceDiagramService;
1919
_configuration = configuration;
2020
}
2121

@@ -56,28 +56,22 @@ public async Task<ApiResponseDto> CallDownstreamFastApiPostAsync(string endpoint
5656

5757
public async Task<ApiResponseDto> SendInfluenceDiagramToFastApiAsync(Guid projectId, string endpoint, UserOutgoingDto user, CancellationToken ct = default)
5858
{
59-
var influenceDiagram = await _projectService.GetInfluenceDiagramAsync(projectId, user, ct);
60-
// make a clone to avoid mutating the chached influence diagram in the project service
61-
var influenceDiagramClone = influenceDiagram.DeepClone();
62-
influenceDiagramClone.ApplyRestrictions();
63-
var content = new StringContent(JsonSerializer.Serialize(influenceDiagramClone), Encoding.UTF8, "application/json");
59+
var influenceDiagram = await _influenceDiagramService.GetRestrictedInfluenceDiagramAsync(projectId, user, ct);
60+
var content = new StringContent(JsonSerializer.Serialize(influenceDiagram), Encoding.UTF8, "application/json");
6461
return await CallDownstreamFastApiPostAsync(endpoint, content, ct);
6562
}
6663

6764
public async Task<ApiResponseDto> SendPartialInfluenceDiagramToFastApiAsync(Guid projectId, string endpoint, List<List<Guid>> paths, UserOutgoingDto user, CancellationToken ct = default)
6865
{
69-
var influenceDiagram = await _projectService.GetInfluenceDiagramAsync(projectId, user, ct);
70-
// make a clone to avoid mutating the chached influence diagram in the project service
71-
var influenceDiagramClone = influenceDiagram.DeepClone();
72-
influenceDiagramClone.ApplyRestrictions();
66+
var influenceDiagram = await _influenceDiagramService.GetRestrictedInfluenceDiagramAsync(projectId, user, ct);
7367

7468
var payload = new
7569
{
76-
issues = influenceDiagramClone.issues,
77-
edges = influenceDiagramClone.edges,
78-
discrete_probabilities = influenceDiagramClone.discreteProbabilities,
79-
discrete_utilities = influenceDiagramClone.discreteUtilities,
80-
restriction_tables = influenceDiagramClone.restrictionTables,
70+
issues = influenceDiagram.issues,
71+
edges = influenceDiagram.edges,
72+
discrete_probabilities = influenceDiagram.discreteProbabilities,
73+
discrete_utilities = influenceDiagram.discreteUtilities,
74+
restriction_tables = influenceDiagram.restrictionTables,
8175
paths
8276
};
8377
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
@@ -86,18 +80,15 @@ public async Task<ApiResponseDto> SendPartialInfluenceDiagramToFastApiAsync(Guid
8680

8781
public async Task<ApiResponseDto> SendInfluenceDiagramWithEvidenceToFastApiAsync(Guid projectId, string endpoint, List<EvidenceRequestDto> data, UserOutgoingDto user, CancellationToken ct = default)
8882
{
89-
var influenceDiagram = await _projectService.GetInfluenceDiagramAsync(projectId, user, ct);
90-
// make a clone to avoid mutating the chached influence diagram in the project service
91-
var influenceDiagramClone = influenceDiagram.DeepClone();
92-
influenceDiagramClone.ApplyRestrictions();
83+
var influenceDiagram = await _influenceDiagramService.GetRestrictedInfluenceDiagramAsync(projectId, user, ct);
9384

9485
var payload = new
9586
{
96-
issues = influenceDiagramClone.issues,
97-
edges = influenceDiagramClone.edges,
98-
discrete_probabilities = influenceDiagramClone.discreteProbabilities,
99-
discrete_utilities = influenceDiagramClone.discreteUtilities,
100-
restriction_tables = influenceDiagramClone.restrictionTables,
87+
issues = influenceDiagram.issues,
88+
edges = influenceDiagram.edges,
89+
discrete_probabilities = influenceDiagram.discreteProbabilities,
90+
discrete_utilities = influenceDiagram.discreteUtilities,
91+
restriction_tables = influenceDiagram.restrictionTables,
10192
evidence = data,
10293
};
10394
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Linq.Expressions;
2+
using Microsoft.Extensions.Caching.Memory;
3+
using PrismaApi.Application.Interfaces.Repositories;
4+
using PrismaApi.Application.Interfaces.Services;
5+
using PrismaApi.Application.Mapping;
6+
using PrismaApi.Domain.Dtos;
7+
using PrismaApi.Domain.Entities;
8+
using PrismaApi.Domain.Extensions;
9+
using PrismaApi.Infrastructure.Caching;
10+
11+
namespace PrismaApi.Application.Services;
12+
13+
14+
15+
public class InfluenceDiagramService : IInfluenceDiagramService
16+
{
17+
private readonly IIssueRepository _issueRepository;
18+
private readonly IEdgeRepository _edgeRepository;
19+
private readonly IDiscreteProbabilityRepository _discreteProbabilityRepository;
20+
private readonly IDiscreteUtilityRepository _discreteUtilityRepository;
21+
private readonly IRestrictionTableRepository _restrictionTableRepository;
22+
private readonly IMemoryCache _cache;
23+
public InfluenceDiagramService(
24+
IIssueRepository issueRepository,
25+
IEdgeRepository edgeRepository,
26+
IDiscreteProbabilityRepository discreteProbabilityRepository,
27+
IDiscreteUtilityRepository discreteUtilityRepository,
28+
IRestrictionTableRepository restrictionTableRepository,
29+
IMemoryCache cache)
30+
{
31+
_issueRepository = issueRepository;
32+
_edgeRepository = edgeRepository;
33+
_discreteProbabilityRepository = discreteProbabilityRepository;
34+
_discreteUtilityRepository = discreteUtilityRepository;
35+
_restrictionTableRepository = restrictionTableRepository;
36+
_cache = cache;
37+
}
38+
public async Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default)
39+
{
40+
var cachedDiagram = _cache.GetCacheItemAsInfluenceDiagram(projectId, user);
41+
if (cachedDiagram != null)
42+
{
43+
return cachedDiagram;
44+
}
45+
46+
var issueEntities = await _issueRepository.GetIssuesInInfluenceDiagram(projectId, IssuesUserFilter(user), ct);
47+
var edgeEntities = await _edgeRepository.GetEdgesInInfluenceDiagram(projectId, EdgesUserFilter(user), ct);
48+
var discreteProbabilities = await _discreteProbabilityRepository.GetAllAsync(filterPredicate: e => e.Uncertainty!.Issue!.ProjectId == projectId, ct: ct);
49+
var discreteUtilities = await _discreteUtilityRepository.GetAllAsync(filterPredicate: e => e.Utility!.Issue!.ProjectId == projectId, ct: ct);
50+
var restrictionTables = await _restrictionTableRepository.GetAllAsync(filterPredicate: e => e.ProjectId == projectId, ct: ct);
51+
var diagram = new InfluenceDiagramDto
52+
{
53+
projectId = projectId,
54+
issues = issueEntities.ToOutgoingDtos(),
55+
edges = edgeEntities.ToOutgoingDtos(),
56+
discreteProbabilities = discreteProbabilities.ToDtos(),
57+
discreteUtilities = discreteUtilities.ToDtos(),
58+
restrictionTables = restrictionTables.ToOutgoingDtos(),
59+
};
60+
61+
_cache.AddCacheItem(new CacheItem { CacheKey = CacheKeys.GetInfluenceDiagramKey(projectId) }, CacheConstants.DefaultMediumQueryCacheInTimeSpan, diagram);
62+
return diagram;
63+
}
64+
65+
public async Task<InfluenceDiagramDto> GetRestrictedInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default)
66+
{
67+
var diagram = await GetInfluenceDiagramAsync(projectId, user, ct);
68+
var influenceDiagramDto = diagram.DeepClone();
69+
influenceDiagramDto.ApplyRestrictions();
70+
return influenceDiagramDto;
71+
}
72+
73+
private static Expression<Func<Issue, bool>> IssuesUserFilter(UserOutgoingDto user)
74+
=> e => e.Project!.Public || e.Project!.ProjectRoles.Any(p => p.UserId == user.Id);
75+
76+
private static Expression<Func<Edge, bool>> EdgesUserFilter(UserOutgoingDto user)
77+
=> e => (e.HeadNode!.Issue!.Project!.Public || e.HeadNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id)) && (e.TailNode!.Issue!.Project!.Public || e.TailNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id));
78+
}

PrismaDotnetApi/PrismaApi.Application/Services/ProjectService.cs

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,22 @@ public class ProjectService : IProjectService
1414
{
1515
private readonly IProjectRepository _projectRepository;
1616
private readonly IProjectRoleRepository _projectRoleRepository;
17-
private readonly IIssueRepository _issueRepository;
18-
private readonly IEdgeRepository _edgeRepository;
1917
private readonly IUserRepository _userRepository;
2018
private readonly IBoardSheetRepository _boardSheetRepository;
21-
private readonly IDiscreteProbabilityRepository _discreteProbabilityRepository;
22-
private readonly IDiscreteUtilityRepository _discreteUtilityRepository;
23-
private readonly IRestrictionTableRepository _restrictionTableRepository;
2419
private readonly IMemoryCache _cache;
2520

2621
public ProjectService(
2722
IProjectRepository projectRepository,
2823
IProjectRoleRepository projectRoleRepository,
29-
IIssueRepository issueRepository,
30-
IEdgeRepository edgeRepository,
3124
IUserRepository userRepository,
3225
IBoardSheetRepository boardSheetRepository,
33-
IDiscreteProbabilityRepository discreteProbabilityRepository,
34-
IDiscreteUtilityRepository discreteUtilityRepository,
35-
IRestrictionTableRepository restrictionTableRepository,
3626
IMemoryCache cache)
3727
{
3828
_projectRepository = projectRepository;
3929
_projectRoleRepository = projectRoleRepository;
40-
_issueRepository = issueRepository;
41-
_edgeRepository = edgeRepository;
4230
_userRepository = userRepository;
4331
_cache = cache;
4432
_boardSheetRepository = boardSheetRepository;
45-
_discreteProbabilityRepository = discreteProbabilityRepository;
46-
_discreteUtilityRepository = discreteUtilityRepository;
47-
_restrictionTableRepository = restrictionTableRepository;
4833
}
4934

5035
public async Task<List<ProjectOutgoingDto>> CreateAsync(List<ProjectCreateDto> dtos, bool createDefaultRole, UserOutgoingDto userDto, CancellationToken ct = default)
@@ -124,33 +109,6 @@ public async Task<List<PopulatedProjectDto>> GetAllPopulatedAsync(UserOutgoingDt
124109
return projects.ToPopulatedDtos();
125110
}
126111

127-
public async Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default)
128-
{
129-
var cachedDiagram = _cache.GetCacheItemAsInfluenceDiagram(projectId, user);
130-
if (cachedDiagram != null)
131-
{
132-
return cachedDiagram;
133-
}
134-
135-
var issueEntities = await _issueRepository.GetIssuesInInfluenceDiagram(projectId, IssuesUserFilter(user), ct);
136-
var edgeEntities = await _edgeRepository.GetEdgesInInfluenceDiagram(projectId, EdgesUserFilter(user), ct);
137-
var discreteProbabilities = await _discreteProbabilityRepository.GetAllAsync(filterPredicate: e => e.Uncertainty!.Issue!.ProjectId == projectId, ct: ct);
138-
var discreteUtilities = await _discreteUtilityRepository.GetAllAsync(filterPredicate: e => e.Utility!.Issue!.ProjectId == projectId, ct: ct);
139-
var restrictionTables = await _restrictionTableRepository.GetAllAsync(filterPredicate: e => e.ProjectId == projectId, ct: ct);
140-
var diagram = new InfluenceDiagramDto
141-
{
142-
projectId = projectId,
143-
issues = issueEntities.ToOutgoingDtos(),
144-
edges = edgeEntities.ToOutgoingDtos(),
145-
discreteProbabilities = discreteProbabilities.ToDtos(),
146-
discreteUtilities = discreteUtilities.ToDtos(),
147-
restrictionTables = restrictionTables.ToOutgoingDtos(),
148-
};
149-
150-
_cache.AddCacheItem(new CacheItem { CacheKey = CacheKeys.GetInfluenceDiagramKey(projectId) }, CacheConstants.DefaultMediumQueryCacheInTimeSpan, diagram);
151-
return diagram;
152-
}
153-
154112
private async Task EnsureDefaultSheetsExists(List<Guid> projectIds, UserOutgoingDto user, CancellationToken ct = default)
155113
{
156114
var existingSheets = await _boardSheetRepository.GetAllAsync(withTracking: false, filterPredicate: e => projectIds.Contains(e.ProjectId), ct: ct);
@@ -172,12 +130,6 @@ private async Task EnsureDefaultSheetsExists(List<Guid> projectIds, UserOutgoing
172130
private static Expression<Func<Project, bool>> UserFilter(UserOutgoingDto user)
173131
=> e => e.Public || e.ProjectRoles.Any(p => p.UserId == user.Id);
174132

175-
private static Expression<Func<Issue, bool>> IssuesUserFilter(UserOutgoingDto user)
176-
=> e => e.Project!.Public || e.Project!.ProjectRoles.Any(p => p.UserId == user.Id);
177-
178-
private static Expression<Func<Edge, bool>> EdgesUserFilter(UserOutgoingDto user)
179-
=> e => (e.HeadNode!.Issue!.Project!.Public || e.HeadNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id)) && (e.TailNode!.Issue!.Project!.Public || e.TailNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id));
180-
181133
private void RegisterPublicProjectsInCache(List<ProjectOutgoingDto> projects)
182134
{
183135
var publicProjectIds = projects.Where(p => p.Public).Select(p => p.Id).ToList();

0 commit comments

Comments
 (0)