Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Latest improvements #417

Merged
merged 1 commit into from
Oct 14, 2023
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
42 changes: 21 additions & 21 deletions requests/requests.http
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ Content-Type: application/json
"uniqueId": "a channel name"
}
###

# @name adminJwtToken
POST {{url}}/api/v2/access/authorize
Content-Type: application/json
Expand All @@ -36,18 +35,6 @@ Content-Type: application/json
}
###

# @name exportForms
POST {{url}}/api/v1/export/forms
Content-Type: application/json
Authorization: Bearer {{adminJwtToken.response.body.$.access_token}}
###

# @name exportFilledInForms
POST {{url}}/api/v1/export/filled-forms
Content-Type: application/json
Authorization: Bearer {{adminJwtToken.response.body.$.access_token}}
###

# @name getCounties
GET {{url}}/api/v1/county
Content-Type: application/json
Expand Down Expand Up @@ -124,14 +111,6 @@ Authorization: Bearer {{observerJwtToken.response.body.$.access_token}}
###


# @name exportNotes
GET {{url}}/api/v2/api/v1/export/all/notes
?idNgo=1
&idObserver=1
&pollingStationNumber=1

###

# @name uploadNotes
POST {{url}}/api/v2/note
Authorization: Bearer {{observerJwtToken.response.body.$.access_token}}
Expand Down Expand Up @@ -304,6 +283,27 @@ Content-Type: text/csv
------WebKitFormBoundary7MA4YWxkTrZu0gW--
###

###
# @name addPollingStationsDetails
POST {{url}}/api/v1/polling-station
Authorization: Bearer {{observerJwtToken.response.body.$.access_token}}
Content-Type: application/json

{
"countyCode": "11",
"municipalityCode": "22",
"pollingStationNumber": 111,
"observerArrivalTime": "2023-10-13T23:35:11",
"numberOfVotersOnTheList": 77,
"numberOfCommissionMembers": 99,
"numberOfFemaleMembers": 44,
"minPresentMembers": 3,
"chairmanPresence": true,
"singlePollingStationOrCommission": false,
"adequatePollingStationSize": true
}
###

###
# @name importPollingStations
POST {{url}}/api/v1/polling-station/import
Expand Down
4 changes: 2 additions & 2 deletions src/api/VoteMonitor.Api.Core/Services/BlobService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ public async Task<UploadedFileModel> UploadFromStreamAsync(Stream sourceStream,
return new UploadedFileModel() { FileName = fileName, Path = blobUri.ToString() };
}

public Task<string> GetPreSignedUrl(string filename)
public string GetPreSignedUrl(string filename)
{
BlobClient blobClient = _blobContainerClient.GetBlobClient(filename);

var blobUri = GetBlobURI(blobClient);
return Task.FromResult(blobUri.ToString());
return blobUri.ToString();
}

private Uri GetBlobURI(BlobClient blobClient)
Expand Down
2 changes: 1 addition & 1 deletion src/api/VoteMonitor.Api.Core/Services/IFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ public interface IFileService
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
Task<string> GetPreSignedUrl(string filename);
string GetPreSignedUrl(string filename);
}
6 changes: 3 additions & 3 deletions src/api/VoteMonitor.Api.Core/Services/LocalFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public Task<UploadedFileModel> UploadFromStreamAsync(Stream sourceStream, string
});
}

public Task<string> GetPreSignedUrl(string filename)
public string GetPreSignedUrl(string filename)
{
if (_uploadedFiles.TryGetValue(filename, out var path))
{
return Task.FromResult(path);
return path;
}

return Task.FromResult(string.Empty);
return string.Empty;
}
}
4 changes: 2 additions & 2 deletions src/api/VoteMonitor.Api.Core/Services/S3Service.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public async Task<UploadedFileModel> UploadFromStreamAsync(Stream sourceStream,
return CreatePreSignedUrl(fileKey);
}

public Task<string> GetPreSignedUrl(string filename)
public string GetPreSignedUrl(string filename)
{
return Task.FromResult(CreatePreSignedUrl(filename).Path);
return CreatePreSignedUrl(filename).Path;
}

private UploadedFileModel CreatePreSignedUrl(string fileKey)
Expand Down
14 changes: 13 additions & 1 deletion src/api/VoteMonitor.Api.DataExport/FileGenerator/ExcelFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,19 @@ public ExcelFile WithSheet(string sheetName, DataTable dataTable)
IRow dataRow = sheet.CreateRow(rowIndex + 1);
for (int colIndex = 0; colIndex < dataTable.Columns.Count; colIndex++)
{
dataRow.CreateCell(colIndex).SetCellValue(dataTable.Rows[rowIndex][colIndex].ToString());
var cell = dataRow.CreateCell(colIndex);
var cellValue = dataTable.Rows[rowIndex][colIndex].ToString();
cell.SetCellValue(cellValue);

if ((cellValue?.StartsWith("https://") ?? false) || (cellValue?.StartsWith("http://") ?? false))
{
var targetLink = new XSSFHyperlink(HyperlinkType.Url)
{
Address = cellValue
};

cell.Hyperlink = targetLink;
}
}
}
#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using System.Data;
using VoteMonitor.Api.Core.Services;
using VoteMonitor.Api.DataExport.FileGenerator;
using VoteMonitor.Api.DataExport.Queries;
using VoteMonitor.Entities;
Expand All @@ -9,10 +10,12 @@

public class GetExcelDbCommandHandler : IRequestHandler<GetExcelDbCommand, byte[]>
{
private readonly IFileService _fileService;
private readonly VoteMonitorContext _context;

public GetExcelDbCommandHandler(VoteMonitorContext context)
public GetExcelDbCommandHandler(IFileService fileService, VoteMonitorContext context)
{
_fileService = fileService;
_context = context;
}

Expand Down Expand Up @@ -73,6 +76,8 @@
})
.ToListAsync(cancellationToken: cancellationToken);

var pollingStationObservations = await GetPollingStationsObservations(cancellationToken);

var aggregatedForms = await GetForms(cancellationToken);
var filledInForms = await GetFilledInForms(cancellationToken);
var notes = await GetNotesForExport(cancellationToken);
Expand All @@ -85,7 +90,8 @@
.WithSheet("counties", counties)
.WithSheet("municipalities", municipalities)
.WithSheet("polling-stations", pollingStations)
.WithSheet("forms", aggregatedForms);
.WithSheet("forms", aggregatedForms)
.WithSheet("polling-stations-observations", pollingStationObservations);

filledInForms.ForEach(f =>
{
Expand All @@ -97,6 +103,68 @@
return excelBuilder.Write();
}

private async Task<DataTable> GetPollingStationsObservations(CancellationToken cancellationToken)
{
var data = await _context.PollingStationInfos
.AsNoTracking()
.Include(x => x.Observer)
.Include(x => x.PollingStation)
.ThenInclude(x => x.Municipality)
.Select(x => new
{
ObserverId = x.Observer.Id,
ObserverName = x.Observer.Name,
ObserverPhone = x.Observer.Phone,
PollingStation = x.PollingStation.Municipality.Code + ":" + x.PollingStation.Number,
LastModified = x.LastModified,
ArrivalTime = x.ObserverArrivalTime,
NumberOfVotersOnTheList = x.NumberOfVotersOnTheList,
NumberOfCommissionMembers = x.NumberOfCommissionMembers,
NumberOfFemaleMembers = x.NumberOfFemaleMembers,
MinPresentMembers = x.MinPresentMembers,
ChairmanPresence = x.ChairmanPresence,
SinglePollingStationOrCommission = x.SinglePollingStationOrCommission,
AdequatePollingStationSize = x.AdequatePollingStationSize
})
.ToListAsync(cancellationToken);

var dataTable = new DataTable();

dataTable.Columns.Add("Observer Id", typeof(int));
dataTable.Columns.Add("Observer Phone", typeof(string));
dataTable.Columns.Add("Observer Name", typeof(string));
dataTable.Columns.Add("Polling Station (municipalityCode:number)", typeof(string));
dataTable.Columns.Add("Last Modified (UTC)", typeof(string));
dataTable.Columns.Add("Observer Arrival Time (UTC)", typeof(string));
dataTable.Columns.Add("NumberOfVotersOnTheList", typeof(string));
dataTable.Columns.Add("NumberOfCommissionMembers", typeof(string));
dataTable.Columns.Add("NumberOfFemaleMembers", typeof(string));
dataTable.Columns.Add("MinPresentMembers", typeof(string));
dataTable.Columns.Add("ChairmanPresence", typeof(bool));
dataTable.Columns.Add("SinglePollingStationOrCommission", typeof(bool));
dataTable.Columns.Add("AdequatePollingStationSize", typeof(bool));

foreach (var row in data)
{
object?[] rowValues =

Check warning on line 149 in src/api/VoteMonitor.Api.DataExport/Handlers/GetExcelDbCommandHandler.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
row.ObserverId, row.ObserverPhone,
row.ObserverName,
row.PollingStation,
row.LastModified.ToString("s"),
row.ArrivalTime?.ToString("s") ?? "",
row.NumberOfVotersOnTheList,
row.NumberOfCommissionMembers,
row.NumberOfFemaleMembers,
row.MinPresentMembers,
row.ChairmanPresence,
};
dataTable.Rows.Add(rowValues);
}

return dataTable;
}

private async Task<DataTable> GetForms(CancellationToken cancellationToken)
{
var forms = await _context.Forms
Expand Down Expand Up @@ -151,7 +219,7 @@

foreach (var question in questions)
{
object?[] rowValues = new List<object?>

Check warning on line 222 in src/api/VoteMonitor.Api.DataExport/Handlers/GetExcelDbCommandHandler.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 222 in src/api/VoteMonitor.Api.DataExport/Handlers/GetExcelDbCommandHandler.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
question.FormCode,
question.FormSectionCode,
Expand Down Expand Up @@ -188,7 +256,7 @@
ObserverId = note.Observer.Id,
LastModified = note.LastModified,
note.Text,
Attachments = note.Attachments.Select(attachment => new { attachment.Path, attachment.FileName }).ToList()
Attachments = note.Attachments.Select(attachment => new { attachment.FileName }).ToList()
})
.OrderBy(x => x.ObserverId)
.ThenBy(x => x.LastModified)
Expand All @@ -211,7 +279,7 @@

foreach (var note in notes)
{
object?[] rowValues = new List<object?>

Check warning on line 282 in src/api/VoteMonitor.Api.DataExport/Handlers/GetExcelDbCommandHandler.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
note.ObserverId,
note.ObserverPhone,
Expand All @@ -220,7 +288,7 @@
note.LastModified.ToString("s"),
note.Text
}
.Union(note.Attachments.Select(x => x.Path))
.Union(note.Attachments.Select(x => _fileService.GetPreSignedUrl(x.FileName)))
.ToArray();

dataTable.Rows.Add(rowValues);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public async Task<int> Handle(RegisterPollingStationCommand message, Cancellatio

if (pollingStation == null)
{
throw new ArgumentException($"polling station not found for {message.CountyCode} {message.MunicipalityCode} {message.PollingStationNumber}");
return await SaveToPollingStationInfoCorruptedData(message);
}

var pollingStationInfo = await _context.PollingStationInfos
Expand Down Expand Up @@ -75,7 +75,7 @@ public async Task<int> Handle(RegisterPollingStationCommand message, Cancellatio
pollingStationInfo.AdequatePollingStationSize = message.AdequatePollingStationSize;
}

return await _context.SaveChangesAsync();
return await _context.SaveChangesAsync(cancellationToken);
}
catch (Exception ex)
{
Expand All @@ -84,4 +84,51 @@ public async Task<int> Handle(RegisterPollingStationCommand message, Cancellatio

return -1;
}

private async Task<int> SaveToPollingStationInfoCorruptedData(RegisterPollingStationCommand message)
{
var pollingStationInfo = await _context.PollingStationInfosCorrupted
.FirstOrDefaultAsync(a =>
a.IdObserver == message.IdObserver &&
a.MunicipalityCode == message.MunicipalityCode &&
a.CountyCode == message.CountyCode);

if (pollingStationInfo == null)
{
pollingStationInfo = new PollingStationInfoCorrupted
{
IdObserver = message.IdObserver,
CountyCode = message.CountyCode,
MunicipalityCode = message.MunicipalityCode,

LastModified = DateTime.UtcNow,
ObserverArrivalTime = message.ObserverArrivalTime.AsUtc(),
ObserverLeaveTime = message.ObserverLeaveTime.AsUtc(),
NumberOfVotersOnTheList = message.NumberOfVotersOnTheList,
NumberOfCommissionMembers = message.NumberOfCommissionMembers,
NumberOfFemaleMembers = message.NumberOfFemaleMembers,
MinPresentMembers = message.MinPresentMembers,
ChairmanPresence = message.ChairmanPresence,
SinglePollingStationOrCommission = message.SinglePollingStationOrCommission,
AdequatePollingStationSize = message.AdequatePollingStationSize,
};

_context.Add(pollingStationInfo);
}
else
{
pollingStationInfo.LastModified = DateTime.UtcNow;
pollingStationInfo.ObserverArrivalTime = message.ObserverArrivalTime.AsUtc();
pollingStationInfo.ObserverLeaveTime = message.ObserverLeaveTime.AsUtc();
pollingStationInfo.NumberOfVotersOnTheList = message.NumberOfVotersOnTheList;
pollingStationInfo.NumberOfCommissionMembers = message.NumberOfCommissionMembers;
pollingStationInfo.NumberOfFemaleMembers = message.NumberOfFemaleMembers;
pollingStationInfo.MinPresentMembers = message.MinPresentMembers;
pollingStationInfo.ChairmanPresence = message.ChairmanPresence;
pollingStationInfo.SinglePollingStationOrCommission = message.SinglePollingStationOrCommission;
pollingStationInfo.AdequatePollingStationSize = message.AdequatePollingStationSize;
}

return await _context.SaveChangesAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using MediatR;
using VoteMonitor.Api.Core.Models;

namespace VoteMonitor.Api.Note.Commands;

public record AddNoteToUnknownPollingStation : IRequest<int>
{
public int ObserverId { get; }
public string CountyCode { get; }
public string MunicipalityCode { get; }
public int? QuestionId { get; }
public string Text { get; }
public UploadedFileModel[] Attachments { get; }

public AddNoteToUnknownPollingStation(int observerId, string countyCode, string municipalityCode, int? questionId, string text, UploadedFileModel[] attachments = null)
{
ObserverId = observerId;
CountyCode = countyCode;
MunicipalityCode = municipalityCode;
QuestionId = questionId;
Text = text;
Attachments = attachments ?? Array.Empty<UploadedFileModel>();
}
}
Loading
Loading