Skip to content
Merged
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
Expand Up @@ -13,6 +13,7 @@ public static IServiceCollection AddFormSubmissionsFeature(this IServiceCollecti
SqlMapper.AddTypeHandler(typeof(BaseQuestionModel[]), new JsonToObjectConverter<BaseQuestionModel[]>());
SqlMapper.AddTypeHandler(typeof(BaseAnswerModel[]), new JsonToObjectConverter<BaseAnswerModel[]>());
SqlMapper.AddTypeHandler(typeof(NoteModel[]), new JsonToObjectConverter<NoteModel[]>());
SqlMapper.AddTypeHandler(typeof(AggregatedSubmissionsAttachmentModel[]), new JsonToObjectConverter<AggregatedSubmissionsAttachmentModel[]>());
SqlMapper.AddTypeHandler(typeof(AttachmentModel[]), new JsonToObjectConverter<AttachmentModel[]>());
SqlMapper.AddTypeHandler(typeof(ObservationBreakModel[]), new JsonToObjectConverter<ObservationBreakModel[]>());

Expand Down
12 changes: 11 additions & 1 deletion api/src/Feature.Form.Submissions/GetAggregated/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,17 @@ @HASATTACHMENTS IS NULL
.ToList();

var attachments = submissions
.SelectMany(x => x.Attachments.Select(attachment => attachment with { SubmissionId = x.SubmissionId }));
.SelectMany(submission => submission.Attachments.Select(attachment => new AggregatedSubmissionsAttachmentModel()
{
SubmissionId = submission.SubmissionId,
QuestionId = attachment.QuestionId,
TimeSubmitted = attachment.TimeSubmitted,
MimeType = attachment.MimeType,
MonitoringObserverId = submission.MonitoringObserverId,
FilePath = attachment.FilePath,
UploadedFileName = attachment.UploadedFileName,
FileName = attachment.FileName,
} ));

attachments = await Task.WhenAll(
attachments.Select(async attachment =>
Expand Down
2 changes: 1 addition & 1 deletion api/src/Feature.Form.Submissions/GetAggregated/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class Response
{
public SubmissionsFilterModel SubmissionsFilter { get; set; }
public FormSubmissionsAggregate SubmissionsAggregate { get; set; }
public List<AttachmentModel> Attachments { get; set; }
public List<AggregatedSubmissionsAttachmentModel> Attachments { get; set; }
public List<NoteModel> Notes { get; set; }
}

Expand Down
20 changes: 8 additions & 12 deletions api/src/Feature.Form.Submissions/GetById/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,14 @@ WITH submissions AS
(SELECT psi."Id" AS "SubmissionId",
'PSI' AS "FormType",
'PSI' AS "FormCode",
psif."Name" as "FormName",
psi."PollingStationId",
psi."MonitoringObserverId",
psi."Answers",
(SELECT "Questions"
FROM "PollingStationInformationForms"
WHERE "ElectionRoundId" = @electionRoundId) AS "Questions",
(SELECT "DefaultLanguage"
FROM "PollingStationInformationForms"
WHERE "ElectionRoundId" = @electionRoundId) AS "DefaultLanguage",
(SELECT "Languages"
FROM "PollingStationInformationForms"
WHERE "ElectionRoundId" = @electionRoundId) AS "Languages",
(SELECT "Id"
FROM "PollingStationInformationForms"
WHERE "ElectionRoundId" = @electionRoundId) AS "FormId",
psif."Questions" AS "Questions",
psif."DefaultLanguage" AS "DefaultLanguage",
psif."Languages" AS "Languages",
psif."Id" AS "FormId",
psi."FollowUpStatus" as "FollowUpStatus",
'[]'::jsonb AS "Attachments",
'[]'::jsonb AS "Notes",
Expand All @@ -58,12 +51,14 @@ WITH submissions AS
psi."IsCompleted"
FROM "PollingStationInformation" psi
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = psi."MonitoringObserverId"
INNER JOIN "PollingStationInformationForms" psif on psif."ElectionRoundId" = psi."ElectionRoundId"
WHERE psi."Id" = @submissionId and psi."ElectionRoundId" = @electionRoundId
UNION ALL
SELECT
fs."Id" AS "SubmissionId",
f."FormType" AS "FormType",
f."Code" AS "FormCode",
f."Name" AS "FormName",
fs."PollingStationId",
fs."MonitoringObserverId",
fs."Answers",
Expand Down Expand Up @@ -105,6 +100,7 @@ UNION ALL
s."FormId",
s."TimeSubmitted",
s."FormCode",
s."FormName",
s."FormType",
ps."Id" AS "PollingStationId",
ps."Level1",
Expand Down
125 changes: 59 additions & 66 deletions api/src/Feature.Form.Submissions/GetByIdV2/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Feature.Form.Submissions.GetByIdV2;
public class Endpoint(
IAuthorizationService authorizationService,
INpgsqlConnectionFactory dbConnectionFactory,
IFileStorageService fileStorageService) : Endpoint<Request, Results<Ok<FormSubmissionView>, NotFound>>
IFileStorageService fileStorageService) : Endpoint<Request, Results<Ok<FormSubmissionViewV2>, NotFound>>
{
public override void Configure()
{
Expand All @@ -18,7 +18,7 @@ public override void Configure()
Policies(PolicyNames.NgoAdminsOnly);
}

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

var sql = """
WITH submissions AS
(SELECT psi."Id" AS "SubmissionId",
'PSI' AS "FormType",
'PSI' AS "FormCode",
psi."PollingStationId",
psi."MonitoringObserverId",
psi."Answers",
(SELECT "Id"
FROM "PollingStationInformationForms"
WHERE "ElectionRoundId" = @electionRoundId) AS "FormId",
psi."FollowUpStatus" as "FollowUpStatus",
'[]'::jsonb AS "Attachments",
'[]'::jsonb AS "Notes",
"LastUpdatedAt" AS "TimeSubmitted",
psi."ArrivalTime",
psi."DepartureTime",
psi."Breaks",
psi."IsCompleted"
FROM "PollingStationInformation" psi
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = psi."MonitoringObserverId"
WHERE psi."Id" = @submissionId and psi."ElectionRoundId" = @electionRoundId
UNION ALL
SELECT
fs."Id" AS "SubmissionId",
f."FormType" AS "FormType",
f."Code" AS "FormCode",
fs."PollingStationId",
fs."MonitoringObserverId",
fs."Answers",
f."Id" AS "FormId",
fs."FollowUpStatus",
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'FileName', "FileName", 'MimeType', "MimeType", 'FilePath', "FilePath", 'UploadedFileName', "UploadedFileName", 'TimeSubmitted', "LastUpdatedAt"))
FROM "Attachments" a
WHERE
(
(A."FormId" = FS."FormId" AND FS."PollingStationId" = A."PollingStationId") -- backwards compatibility
OR A."SubmissionId" = FS."Id"
)
AND a."MonitoringObserverId" = fs."MonitoringObserverId"
AND a."IsDeleted" = false
AND a."IsCompleted" = true),'[]'::JSONB) AS "Attachments",
(SELECT psi."Id" AS "SubmissionId",
psi."PollingStationId",
psi."MonitoringObserverId",
psi."Answers",
psif."Id" AS "FormId",
psi."FollowUpStatus" as "FollowUpStatus",
'[]'::jsonb AS "Attachments",
'[]'::jsonb AS "Notes",
"LastUpdatedAt" AS "TimeSubmitted",
psi."ArrivalTime",
psi."DepartureTime",
psi."Breaks",
psi."IsCompleted"
FROM "PollingStationInformation" psi
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = psi."MonitoringObserverId"
INNER JOIN "PollingStationInformationForms" psif on psi."ElectionRoundId" = psif."ElectionRoundId"
WHERE psi."Id" = @submissionId and psi."ElectionRoundId" = @electionRoundId
UNION ALL
SELECT
fs."Id" AS "SubmissionId",
fs."PollingStationId",
fs."MonitoringObserverId",
fs."Answers",
f."Id" AS "FormId",
fs."FollowUpStatus",
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'FileName', "FileName", 'MimeType', "MimeType", 'FilePath', "FilePath", 'UploadedFileName', "UploadedFileName", 'TimeSubmitted', "LastUpdatedAt"))
FROM "Attachments" a
WHERE
(
(A."FormId" = FS."FormId" AND FS."PollingStationId" = A."PollingStationId") -- backwards compatibility
OR A."SubmissionId" = FS."Id"
)
AND a."MonitoringObserverId" = fs."MonitoringObserverId"
AND a."IsDeleted" = false
AND a."IsCompleted" = true),'[]'::JSONB) AS "Attachments",

COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'Text', "Text", 'TimeSubmitted', "LastUpdatedAt"))
FROM "Notes" n
WHERE
(
(N."FormId" = FS."FormId" AND FS."PollingStationId" = N."PollingStationId") -- backwards compatibility
OR N."SubmissionId" = FS."Id"
)
AND n."MonitoringObserverId" = fs."MonitoringObserverId"), '[]'::JSONB) AS "Notes",
"LastUpdatedAt" AS "TimeSubmitted",
NULL AS "ArrivalTime",
NULL AS "DepartureTime",
'[]'::jsonb AS "Breaks",
fs."IsCompleted"
FROM "FormSubmissions" fs
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = FS."MonitoringObserverId"
INNER JOIN "Forms" f ON f."Id" = fs."FormId"
WHERE fs."Id" = @submissionId and fs."ElectionRoundId" = @electionRoundId)
COALESCE((select jsonb_agg(jsonb_build_object('QuestionId', "QuestionId", 'Text', "Text", 'TimeSubmitted', "LastUpdatedAt"))
FROM "Notes" n
WHERE
(
(N."FormId" = FS."FormId" AND FS."PollingStationId" = N."PollingStationId") -- backwards compatibility
OR N."SubmissionId" = FS."Id"
)
AND n."MonitoringObserverId" = fs."MonitoringObserverId"), '[]'::JSONB) AS "Notes",

"LastUpdatedAt" AS "TimeSubmitted",
NULL AS "ArrivalTime",
NULL AS "DepartureTime",
'[]'::jsonb AS "Breaks",
fs."IsCompleted"
FROM "FormSubmissions" fs
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = FS."MonitoringObserverId"
INNER JOIN "Forms" f ON f."Id" = fs."FormId"
WHERE fs."Id" = @submissionId and fs."ElectionRoundId" = @electionRoundId)
SELECT s."SubmissionId",
s."FormId",
s."TimeSubmitted",
s."FormCode",
s."FormType",
ps."Id" AS "PollingStationId",
ps."Level1",
ps."Level2",
Expand All @@ -117,20 +110,20 @@ UNION ALL
s."Breaks",
s."IsCompleted"
FROM submissions s
INNER JOIN "PollingStations" ps ON ps."Id" = s."PollingStationId"
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = s."MonitoringObserverId"
INNER JOIN "PollingStations" ps ON ps."Id" = s."PollingStationId"
INNER JOIN "GetAvailableMonitoringObservers"(@electionRoundId, @ngoId, 'Coalition') AMO on AMO."MonitoringObserverId" = s."MonitoringObserverId"
""";

var queryArgs = new
{
electionRoundId = req.ElectionRoundId, ngoId = req.NgoId, submissionId = req.SubmissionId
};

FormSubmissionView submission = null;
FormSubmissionViewV2 submission = null;

using (var dbConnection = await dbConnectionFactory.GetOpenConnectionAsync(ct))
{
submission = await dbConnection.QueryFirstOrDefaultAsync<FormSubmissionView>(sql, queryArgs);
submission = await dbConnection.QueryFirstOrDefaultAsync<FormSubmissionViewV2>(sql, queryArgs);
}

if (submission is null)
Expand Down
6 changes: 3 additions & 3 deletions api/src/Feature.Form.Submissions/ListEntries/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ WITH polling_station_submissions AS (SELECT psi."Id" AS
psi."LastUpdatedAt" AS "TimeSubmitted",
psi."FollowUpStatus",
psif."DefaultLanguage",
psif."Name",
psif."Name" as "FormName",
psi."IsCompleted"
FROM "PollingStationInformation" psi
INNER JOIN "PollingStationInformationForms" psif
Expand Down Expand Up @@ -202,7 +202,7 @@ form_submissions AS (SELECT fs."Id"
fs."LastUpdatedAt" AS "TimeSubmitted",
fs."FollowUpStatus",
f."DefaultLanguage",
f."Name",
f."Name" as "FormName",
fs."IsCompleted"
FROM "FormSubmissions" fs
INNER JOIN "Forms" f ON f."Id" = fs."FormId"
Expand Down Expand Up @@ -233,7 +233,7 @@ OR mo."PhoneNumber" ILIKE @searchText
s."FormCode",
s."FormType",
s."DefaultLanguage",
s."Name" as "FormName",
s."FormName",
ps."Id" AS "PollingStationId",
ps."Level1",
ps."Level2",
Expand Down
2 changes: 2 additions & 0 deletions api/src/Module.Answers/Models/FormSubmissionView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace Module.Answers.Models;

[Obsolete("Will be removed in future version")]
public record FormSubmissionView
{
public Guid SubmissionId { get; init; }
Expand All @@ -14,6 +15,7 @@ public record FormSubmissionView
public string DefaultLanguage { get; init; }
public string[] Languages { get; init; } = [];
public FormType FormType { get; init; } = null!;
public TranslatedString FormName { get; init; } = null!;

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

Expand Down
41 changes: 41 additions & 0 deletions api/src/Module.Answers/Models/FormSubmissionViewV2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Module.Forms.Models;
using Vote.Monitor.Core.Models;
using Vote.Monitor.Domain.Entities.FormSubmissionAggregate;

namespace Module.Answers.Models;

public record FormSubmissionViewV2
{
public Guid SubmissionId { get; init; }
public DateTime TimeSubmitted { get; init; }
public Guid FormId { get; init; }

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

public Guid PollingStationId { get; init; }
public string Level1 { get; init; } = null!;
public string Level2 { get; init; } = null!;
public string Level3 { get; init; } = null!;
public string Level4 { get; init; } = null!;
public string Level5 { get; init; } = null!;
public string Number { get; init; } = null!;
public Guid MonitoringObserverId { get; init; }
public bool IsOwnObserver { get; init; }
public string ObserverName { get; init; } = null!;
public string Email { get; init; } = null!;
public string? PhoneNumber { get; init; } = null!;
public string[] Tags { get; init; } = [];
public string NgoName { get; init; } = null!;
public int NumberOfFlaggedAnswers { get; init; }
public int NumberOfQuestionsAnswered { get; init; }

public BaseQuestionModel[] Questions { get; init; }
public BaseAnswerModel[] Answers { get; init; } = [];
public NoteModel[] Notes { get; init; } = [];
public AttachmentModel[] Attachments { get; init; } = [];

public DateTime? ArrivalTime { get; init; }
public DateTime? DepartureTime { get; init; }
public ObservationBreakModel[] Breaks { get; init; } = [];
public bool IsCompleted { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Vote.Monitor.Core.Models;

public record AggregatedSubmissionsAttachmentModel
{
public Guid SubmissionId { get; init; }
public Guid QuestionId { get; init; }
public Guid MonitoringObserverId { get; init; }
public string FilePath { get; init; }

Check warning on line 8 in api/src/Vote.Monitor.Core/Models/AggregatedSubmissionsAttachmentModel.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'FilePath' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string UploadedFileName { get; init; }

Check warning on line 9 in api/src/Vote.Monitor.Core/Models/AggregatedSubmissionsAttachmentModel.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'UploadedFileName' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string FileName { get; init; }

Check warning on line 10 in api/src/Vote.Monitor.Core/Models/AggregatedSubmissionsAttachmentModel.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'FileName' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string MimeType { get; init; }

Check warning on line 11 in api/src/Vote.Monitor.Core/Models/AggregatedSubmissionsAttachmentModel.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'MimeType' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string PresignedUrl { get; init; }

Check warning on line 12 in api/src/Vote.Monitor.Core/Models/AggregatedSubmissionsAttachmentModel.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'PresignedUrl' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public int UrlValidityInSeconds { get; init; }

public DateTime TimeSubmitted { get; init; }
}
Loading
Loading