Skip to content

Commit 4753f3e

Browse files
authored
Fix form submissions endpoints (#1025)
1 parent f5ada6a commit 4753f3e

File tree

10 files changed

+142
-86
lines changed

10 files changed

+142
-86
lines changed

api/src/Feature.Form.Submissions/FormSubmissionsInstaller.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public static IServiceCollection AddFormSubmissionsFeature(this IServiceCollecti
1313
SqlMapper.AddTypeHandler(typeof(BaseQuestionModel[]), new JsonToObjectConverter<BaseQuestionModel[]>());
1414
SqlMapper.AddTypeHandler(typeof(BaseAnswerModel[]), new JsonToObjectConverter<BaseAnswerModel[]>());
1515
SqlMapper.AddTypeHandler(typeof(NoteModel[]), new JsonToObjectConverter<NoteModel[]>());
16+
SqlMapper.AddTypeHandler(typeof(AggregatedSubmissionsAttachmentModel[]), new JsonToObjectConverter<AggregatedSubmissionsAttachmentModel[]>());
1617
SqlMapper.AddTypeHandler(typeof(AttachmentModel[]), new JsonToObjectConverter<AttachmentModel[]>());
1718
SqlMapper.AddTypeHandler(typeof(ObservationBreakModel[]), new JsonToObjectConverter<ObservationBreakModel[]>());
1819

api/src/Feature.Form.Submissions/GetAggregated/Endpoint.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,17 @@ @HASATTACHMENTS IS NULL
432432
.ToList();
433433

434434
var attachments = submissions
435-
.SelectMany(x => x.Attachments.Select(attachment => attachment with { SubmissionId = x.SubmissionId }));
435+
.SelectMany(submission => submission.Attachments.Select(attachment => new AggregatedSubmissionsAttachmentModel()
436+
{
437+
SubmissionId = submission.SubmissionId,
438+
QuestionId = attachment.QuestionId,
439+
TimeSubmitted = attachment.TimeSubmitted,
440+
MimeType = attachment.MimeType,
441+
MonitoringObserverId = submission.MonitoringObserverId,
442+
FilePath = attachment.FilePath,
443+
UploadedFileName = attachment.UploadedFileName,
444+
FileName = attachment.FileName,
445+
} ));
436446

437447
attachments = await Task.WhenAll(
438448
attachments.Select(async attachment =>

api/src/Feature.Form.Submissions/GetAggregated/Response.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class Response
88
{
99
public SubmissionsFilterModel SubmissionsFilter { get; set; }
1010
public FormSubmissionsAggregate SubmissionsAggregate { get; set; }
11-
public List<AttachmentModel> Attachments { get; set; }
11+
public List<AggregatedSubmissionsAttachmentModel> Attachments { get; set; }
1212
public List<NoteModel> Notes { get; set; }
1313
}
1414

api/src/Feature.Form.Submissions/GetById/Endpoint.cs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,14 @@ WITH submissions AS
3333
(SELECT psi."Id" AS "SubmissionId",
3434
'PSI' AS "FormType",
3535
'PSI' AS "FormCode",
36+
psif."Name" as "FormName",
3637
psi."PollingStationId",
3738
psi."MonitoringObserverId",
3839
psi."Answers",
39-
(SELECT "Questions"
40-
FROM "PollingStationInformationForms"
41-
WHERE "ElectionRoundId" = @electionRoundId) AS "Questions",
42-
(SELECT "DefaultLanguage"
43-
FROM "PollingStationInformationForms"
44-
WHERE "ElectionRoundId" = @electionRoundId) AS "DefaultLanguage",
45-
(SELECT "Languages"
46-
FROM "PollingStationInformationForms"
47-
WHERE "ElectionRoundId" = @electionRoundId) AS "Languages",
48-
(SELECT "Id"
49-
FROM "PollingStationInformationForms"
50-
WHERE "ElectionRoundId" = @electionRoundId) AS "FormId",
40+
psif."Questions" AS "Questions",
41+
psif."DefaultLanguage" AS "DefaultLanguage",
42+
psif."Languages" AS "Languages",
43+
psif."Id" AS "FormId",
5144
psi."FollowUpStatus" as "FollowUpStatus",
5245
'[]'::jsonb AS "Attachments",
5346
'[]'::jsonb AS "Notes",
@@ -58,12 +51,14 @@ WITH submissions AS
5851
psi."IsCompleted"
5952
FROM "PollingStationInformation" psi
6053
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = psi."MonitoringObserverId"
54+
INNER JOIN "PollingStationInformationForms" psif on psif."ElectionRoundId" = psi."ElectionRoundId"
6155
WHERE psi."Id" = @submissionId and psi."ElectionRoundId" = @electionRoundId
6256
UNION ALL
6357
SELECT
6458
fs."Id" AS "SubmissionId",
6559
f."FormType" AS "FormType",
6660
f."Code" AS "FormCode",
61+
f."Name" AS "FormName",
6762
fs."PollingStationId",
6863
fs."MonitoringObserverId",
6964
fs."Answers",
@@ -105,6 +100,7 @@ UNION ALL
105100
s."FormId",
106101
s."TimeSubmitted",
107102
s."FormCode",
103+
s."FormName",
108104
s."FormType",
109105
ps."Id" AS "PollingStationId",
110106
ps."Level1",

api/src/Feature.Form.Submissions/GetByIdV2/Endpoint.cs

Lines changed: 59 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Feature.Form.Submissions.GetByIdV2;
66
public class Endpoint(
77
IAuthorizationService authorizationService,
88
INpgsqlConnectionFactory dbConnectionFactory,
9-
IFileStorageService fileStorageService) : Endpoint<Request, Results<Ok<FormSubmissionView>, NotFound>>
9+
IFileStorageService fileStorageService) : Endpoint<Request, Results<Ok<FormSubmissionViewV2>, NotFound>>
1010
{
1111
public override void Configure()
1212
{
@@ -18,7 +18,7 @@ public override void Configure()
1818
Policies(PolicyNames.NgoAdminsOnly);
1919
}
2020

21-
public override async Task<Results<Ok<FormSubmissionView>, NotFound>> ExecuteAsync(Request req,
21+
public override async Task<Results<Ok<FormSubmissionViewV2>, NotFound>> ExecuteAsync(Request req,
2222
CancellationToken ct)
2323
{
2424
var authorizationResult =
@@ -30,70 +30,63 @@ public override async Task<Results<Ok<FormSubmissionView>, NotFound>> ExecuteAsy
3030

3131
var sql = """
3232
WITH submissions AS
33-
(SELECT psi."Id" AS "SubmissionId",
34-
'PSI' AS "FormType",
35-
'PSI' AS "FormCode",
36-
psi."PollingStationId",
37-
psi."MonitoringObserverId",
38-
psi."Answers",
39-
(SELECT "Id"
40-
FROM "PollingStationInformationForms"
41-
WHERE "ElectionRoundId" = @electionRoundId) AS "FormId",
42-
psi."FollowUpStatus" as "FollowUpStatus",
43-
'[]'::jsonb AS "Attachments",
44-
'[]'::jsonb AS "Notes",
45-
"LastUpdatedAt" AS "TimeSubmitted",
46-
psi."ArrivalTime",
47-
psi."DepartureTime",
48-
psi."Breaks",
49-
psi."IsCompleted"
50-
FROM "PollingStationInformation" psi
51-
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = psi."MonitoringObserverId"
52-
WHERE psi."Id" = @submissionId and psi."ElectionRoundId" = @electionRoundId
53-
UNION ALL
54-
SELECT
55-
fs."Id" AS "SubmissionId",
56-
f."FormType" AS "FormType",
57-
f."Code" AS "FormCode",
58-
fs."PollingStationId",
59-
fs."MonitoringObserverId",
60-
fs."Answers",
61-
f."Id" AS "FormId",
62-
fs."FollowUpStatus",
63-
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'FileName', "FileName", 'MimeType', "MimeType", 'FilePath', "FilePath", 'UploadedFileName', "UploadedFileName", 'TimeSubmitted', "LastUpdatedAt"))
64-
FROM "Attachments" a
65-
WHERE
66-
(
67-
(A."FormId" = FS."FormId" AND FS."PollingStationId" = A."PollingStationId") -- backwards compatibility
68-
OR A."SubmissionId" = FS."Id"
69-
)
70-
AND a."MonitoringObserverId" = fs."MonitoringObserverId"
71-
AND a."IsDeleted" = false
72-
AND a."IsCompleted" = true),'[]'::JSONB) AS "Attachments",
33+
(SELECT psi."Id" AS "SubmissionId",
34+
psi."PollingStationId",
35+
psi."MonitoringObserverId",
36+
psi."Answers",
37+
psif."Id" AS "FormId",
38+
psi."FollowUpStatus" as "FollowUpStatus",
39+
'[]'::jsonb AS "Attachments",
40+
'[]'::jsonb AS "Notes",
41+
"LastUpdatedAt" AS "TimeSubmitted",
42+
psi."ArrivalTime",
43+
psi."DepartureTime",
44+
psi."Breaks",
45+
psi."IsCompleted"
46+
FROM "PollingStationInformation" psi
47+
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = psi."MonitoringObserverId"
48+
INNER JOIN "PollingStationInformationForms" psif on psi."ElectionRoundId" = psif."ElectionRoundId"
49+
WHERE psi."Id" = @submissionId and psi."ElectionRoundId" = @electionRoundId
50+
UNION ALL
51+
SELECT
52+
fs."Id" AS "SubmissionId",
53+
fs."PollingStationId",
54+
fs."MonitoringObserverId",
55+
fs."Answers",
56+
f."Id" AS "FormId",
57+
fs."FollowUpStatus",
58+
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'FileName', "FileName", 'MimeType', "MimeType", 'FilePath', "FilePath", 'UploadedFileName', "UploadedFileName", 'TimeSubmitted', "LastUpdatedAt"))
59+
FROM "Attachments" a
60+
WHERE
61+
(
62+
(A."FormId" = FS."FormId" AND FS."PollingStationId" = A."PollingStationId") -- backwards compatibility
63+
OR A."SubmissionId" = FS."Id"
64+
)
65+
AND a."MonitoringObserverId" = fs."MonitoringObserverId"
66+
AND a."IsDeleted" = false
67+
AND a."IsCompleted" = true),'[]'::JSONB) AS "Attachments",
7368
74-
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'Text', "Text", 'TimeSubmitted', "LastUpdatedAt"))
75-
FROM "Notes" n
76-
WHERE
77-
(
78-
(N."FormId" = FS."FormId" AND FS."PollingStationId" = N."PollingStationId") -- backwards compatibility
79-
OR N."SubmissionId" = FS."Id"
80-
)
81-
AND n."MonitoringObserverId" = fs."MonitoringObserverId"), '[]'::JSONB) AS "Notes",
82-
83-
"LastUpdatedAt" AS "TimeSubmitted",
84-
NULL AS "ArrivalTime",
85-
NULL AS "DepartureTime",
86-
'[]'::jsonb AS "Breaks",
87-
fs."IsCompleted"
88-
FROM "FormSubmissions" fs
89-
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = FS."MonitoringObserverId"
90-
INNER JOIN "Forms" f ON f."Id" = fs."FormId"
91-
WHERE fs."Id" = @submissionId and fs."ElectionRoundId" = @electionRoundId)
69+
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'Text', "Text", 'TimeSubmitted', "LastUpdatedAt"))
70+
FROM "Notes" n
71+
WHERE
72+
(
73+
(N."FormId" = FS."FormId" AND FS."PollingStationId" = N."PollingStationId") -- backwards compatibility
74+
OR N."SubmissionId" = FS."Id"
75+
)
76+
AND n."MonitoringObserverId" = fs."MonitoringObserverId"), '[]'::JSONB) AS "Notes",
77+
78+
"LastUpdatedAt" AS "TimeSubmitted",
79+
NULL AS "ArrivalTime",
80+
NULL AS "DepartureTime",
81+
'[]'::jsonb AS "Breaks",
82+
fs."IsCompleted"
83+
FROM "FormSubmissions" fs
84+
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = FS."MonitoringObserverId"
85+
INNER JOIN "Forms" f ON f."Id" = fs."FormId"
86+
WHERE fs."Id" = @submissionId and fs."ElectionRoundId" = @electionRoundId)
9287
SELECT s."SubmissionId",
9388
s."FormId",
9489
s."TimeSubmitted",
95-
s."FormCode",
96-
s."FormType",
9790
ps."Id" AS "PollingStationId",
9891
ps."Level1",
9992
ps."Level2",
@@ -117,20 +110,20 @@ UNION ALL
117110
s."Breaks",
118111
s."IsCompleted"
119112
FROM submissions s
120-
INNER JOIN "PollingStations" ps ON ps."Id" = s."PollingStationId"
121-
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = s."MonitoringObserverId"
113+
INNER JOIN "PollingStations" ps ON ps."Id" = s."PollingStationId"
114+
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = s."MonitoringObserverId"
122115
""";
123116

124117
var queryArgs = new
125118
{
126119
electionRoundId = req.ElectionRoundId, ngoId = req.NgoId, submissionId = req.SubmissionId
127120
};
128121

129-
FormSubmissionView submission = null;
122+
FormSubmissionViewV2 submission = null;
130123

131124
using (var dbConnection = await dbConnectionFactory.GetOpenConnectionAsync(ct))
132125
{
133-
submission = await dbConnection.QueryFirstOrDefaultAsync<FormSubmissionView>(sql, queryArgs);
126+
submission = await dbConnection.QueryFirstOrDefaultAsync<FormSubmissionViewV2>(sql, queryArgs);
134127
}
135128

136129
if (submission is null)

api/src/Feature.Form.Submissions/ListEntries/Endpoint.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ WITH polling_station_submissions AS (SELECT psi."Id" AS
147147
psi."LastUpdatedAt" AS "TimeSubmitted",
148148
psi."FollowUpStatus",
149149
psif."DefaultLanguage",
150-
psif."Name",
150+
psif."Name" as "FormName",
151151
psi."IsCompleted"
152152
FROM "PollingStationInformation" psi
153153
INNER JOIN "PollingStationInformationForms" psif
@@ -202,7 +202,7 @@ form_submissions AS (SELECT fs."Id"
202202
fs."LastUpdatedAt" AS "TimeSubmitted",
203203
fs."FollowUpStatus",
204204
f."DefaultLanguage",
205-
f."Name",
205+
f."Name" as "FormName",
206206
fs."IsCompleted"
207207
FROM "FormSubmissions" fs
208208
INNER JOIN "Forms" f ON f."Id" = fs."FormId"
@@ -233,7 +233,7 @@ OR mo."PhoneNumber" ILIKE @searchText
233233
s."FormCode",
234234
s."FormType",
235235
s."DefaultLanguage",
236-
s."Name" as "FormName",
236+
s."FormName",
237237
ps."Id" AS "PollingStationId",
238238
ps."Level1",
239239
ps."Level2",

api/src/Module.Answers/Models/FormSubmissionView.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace Module.Answers.Models;
77

8+
[Obsolete("Will be removed in future version")]
89
public record FormSubmissionView
910
{
1011
public Guid SubmissionId { get; init; }
@@ -14,6 +15,7 @@ public record FormSubmissionView
1415
public string DefaultLanguage { get; init; }
1516
public string[] Languages { get; init; } = [];
1617
public FormType FormType { get; init; } = null!;
18+
public TranslatedString FormName { get; init; } = null!;
1719

1820
public SubmissionFollowUpStatus FollowUpStatus { get; init; } = null!;
1921

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Module.Forms.Models;
2+
using Vote.Monitor.Core.Models;
3+
using Vote.Monitor.Domain.Entities.FormSubmissionAggregate;
4+
5+
namespace Module.Answers.Models;
6+
7+
public record FormSubmissionViewV2
8+
{
9+
public Guid SubmissionId { get; init; }
10+
public DateTime TimeSubmitted { get; init; }
11+
public Guid FormId { get; init; }
12+
13+
public SubmissionFollowUpStatus FollowUpStatus { get; init; } = null!;
14+
15+
public Guid PollingStationId { get; init; }
16+
public string Level1 { get; init; } = null!;
17+
public string Level2 { get; init; } = null!;
18+
public string Level3 { get; init; } = null!;
19+
public string Level4 { get; init; } = null!;
20+
public string Level5 { get; init; } = null!;
21+
public string Number { get; init; } = null!;
22+
public Guid MonitoringObserverId { get; init; }
23+
public bool IsOwnObserver { get; init; }
24+
public string ObserverName { get; init; } = null!;
25+
public string Email { get; init; } = null!;
26+
public string? PhoneNumber { get; init; } = null!;
27+
public string[] Tags { get; init; } = [];
28+
public string NgoName { get; init; } = null!;
29+
public int NumberOfFlaggedAnswers { get; init; }
30+
public int NumberOfQuestionsAnswered { get; init; }
31+
32+
public BaseQuestionModel[] Questions { get; init; }
33+
public BaseAnswerModel[] Answers { get; init; } = [];
34+
public NoteModel[] Notes { get; init; } = [];
35+
public AttachmentModel[] Attachments { get; init; } = [];
36+
37+
public DateTime? ArrivalTime { get; init; }
38+
public DateTime? DepartureTime { get; init; }
39+
public ObservationBreakModel[] Breaks { get; init; } = [];
40+
public bool IsCompleted { get; init; }
41+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Vote.Monitor.Core.Models;
2+
3+
public record AggregatedSubmissionsAttachmentModel
4+
{
5+
public Guid SubmissionId { get; init; }
6+
public Guid QuestionId { get; init; }
7+
public Guid MonitoringObserverId { get; init; }
8+
public string FilePath { get; init; }
9+
public string UploadedFileName { get; init; }
10+
public string FileName { get; init; }
11+
public string MimeType { get; init; }
12+
public string PresignedUrl { get; init; }
13+
public int UrlValidityInSeconds { get; init; }
14+
15+
public DateTime TimeSubmitted { get; init; }
16+
}

0 commit comments

Comments
 (0)