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
12 changes: 6 additions & 6 deletions api/src/Feature.Coalitions/GetMy/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ public override async Task<Results<Ok<CoalitionModel>, NotFound>> ExecuteAsync(R
}

var coalition = await context.Coalitions
.Include(x=>x.Memberships)
.ThenInclude(x=>x.MonitoringNgo)
.ThenInclude(x=>x.Ngo)
.Include(x=>x.Leader)
.ThenInclude(x=>x.Ngo)
.Include(x => x.Memberships)
.ThenInclude(x => x.MonitoringNgo)
.ThenInclude(x => x.Ngo)
.Include(x => x.Leader)
.ThenInclude(x => x.Ngo)
.Where(x => x.Memberships.Any(m => m.ElectionRoundId == req.ElectionRoundId
&& m.MonitoringNgo.NgoId == req.NgoId
&& m.MonitoringNgo.ElectionRoundId == req.ElectionRoundId))
Expand All @@ -42,7 +42,7 @@ public override async Task<Results<Ok<CoalitionModel>, NotFound>> ExecuteAsync(R

if (coalition is null)
{
return TypedResults.NotFound();
return TypedResults.Ok(new CoalitionModel { IsInCoalition = false });
}

return TypedResults.Ok(coalition);
Expand Down
2 changes: 2 additions & 0 deletions api/src/Feature.Coalitions/Models/CoalitionModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Feature.NgoCoalitions.Models;
public class CoalitionModel
{
public Guid Id { get; init; }
public bool IsInCoalition { get; set; }
public string Name { get; init; } = string.Empty;
public Guid LeaderId { get; set; }
public string LeaderName { get; set; } = string.Empty;
Expand All @@ -22,6 +23,7 @@ public class CoalitionModel
Name = coalition.Name,
LeaderId = coalition.Leader.NgoId,
LeaderName = coalition.Leader.Ngo.Name,
IsInCoalition = true,
Members = coalition.Memberships.Select(x => CoalitionMember.FromEntity(x.MonitoringNgo)).ToArray()
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Text.Json.Serialization;
using Ardalis.SmartEnum.SystemTextJson;
using Vote.Monitor.Domain.Entities.MonitoringObserverAggregate;
using Vote.Monitor.Domain.Entities.MonitoringObserverAggregate;

namespace Feature.MonitoringObservers;

Expand All @@ -14,8 +12,6 @@ public class MonitoringObserverModel
public string PhoneNumber { get; init; }
public string[] Tags { get; init; }
public DateTime? LatestActivityAt { get; init; }

[JsonConverter(typeof(SmartEnumNameConverter<MonitoringObserverStatus, string>))]
public MonitoringObserverStatus Status { get; init; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,35 +101,29 @@ public async Task ImportAsync(Guid electionRoundId, Guid ngoId,
existingAccount.NewInvite();
var newObserver = ObserverAggregate.Create(existingAccount);
var newMonitoringObserver = MonitoringObserverAggregate.CreateForExisting(electionRoundId,
monitoringNgo.Id, newObserver.Id, observer.Tags ?? []);
monitoringNgo.Id, newObserver.Id, observer.Tags ?? [], existingAccount.Status);
await context.Observers.AddAsync(newObserver, ct);
await context.MonitoringObservers.AddAsync(newMonitoringObserver, ct);

var endpointUri = new Uri(Path.Combine($"{_apiConfig.WebAppUrl}", "accept-invite"));
string acceptInviteUrl = QueryHelpers.AddQueryString(endpointUri.ToString(), "invitationToken",
existingAccount.InvitationToken!);

var invitationNewUserEmailProps = new InvitationNewUserEmailProps(FullName: fullName,
CdnUrl: _apiConfig.WebAppUrl,
AcceptUrl: acceptInviteUrl,
NgoName: ngoName,
ElectionRoundDetails: electionRoundName);

var email = emailFactory.GenerateNewUserInvitationEmail(invitationNewUserEmailProps);
var email = GenerateCreateAccountEmail(existingAccount.InvitationToken!, fullName, ngoName,
electionRoundName);
jobService.EnqueueSendEmail(observer.Email, email.Subject, email.Body);
}
else
{
var newMonitoringObserver = MonitoringObserverAggregate.CreateForExisting(electionRoundId,
monitoringNgo.Id,
existingObserver.Id,
observer.Tags ?? []);
observer.Tags ?? [],
existingObserver.ApplicationUser.Status);

await context.MonitoringObservers.AddAsync(newMonitoringObserver, ct);
var invitationExistingUserEmailProps = new InvitationExistingUserEmailProps(FullName: fullName,
CdnUrl: _apiConfig.WebAppUrl, NgoName: ngoName, ElectionRoundDetails: electionRoundName);

var email = emailFactory.GenerateInvitationExistingUserEmail(invitationExistingUserEmailProps);
var email = existingObserver.ApplicationUser.Status == UserStatus.Pending
? GenerateCreateAccountEmail(existingObserver.ApplicationUser.InvitationToken!,
existingObserver.ApplicationUser.DisplayName, ngoName, electionRoundName)
: GenerateNotificationEmail(fullName, ngoName, electionRoundName);

jobService.EnqueueSendEmail(observer.Email, email.Subject, email.Body);
}
}
Expand All @@ -151,25 +145,44 @@ public async Task ImportAsync(Guid electionRoundId, Guid ngoId,
newObserver.Id, observer.Tags ?? []);
await context.Observers.AddAsync(newObserver, ct);
await context.MonitoringObservers.AddAsync(newMonitoringObserver, ct);
var endpointUri = new Uri(Path.Combine($"{_apiConfig.WebAppUrl}", "accept-invite"));
string acceptInviteUrl =
QueryHelpers.AddQueryString(endpointUri.ToString(), "invitationToken", user.InvitationToken!);

var invitationNewUserEmailProps = new InvitationNewUserEmailProps(FullName: fullName,
CdnUrl: _apiConfig.WebAppUrl, AcceptUrl: acceptInviteUrl, NgoName: ngoName,
ElectionRoundDetails: electionRoundName);

var email = emailFactory.GenerateNewUserInvitationEmail(invitationNewUserEmailProps);
var email = GenerateCreateAccountEmail(user.InvitationToken!, fullName, ngoName, electionRoundName);
jobService.EnqueueSendEmail(observer.Email, email.Subject, email.Body);
}
}

await context.SaveChangesAsync(ct);
}

private EmailModel GenerateNotificationEmail(string fullName, string ngoName, string electionRoundName)
{
var invitationExistingUserEmailProps = new InvitationExistingUserEmailProps(FullName: fullName,
CdnUrl: _apiConfig.WebAppUrl, NgoName: ngoName, ElectionRoundDetails: electionRoundName);

var email = emailFactory.GenerateInvitationExistingUserEmail(invitationExistingUserEmailProps);
return email;
}

private EmailModel GenerateCreateAccountEmail(string invitationToken, string fullName, string ngoName,
string electionRoundName)
{
var endpointUri = new Uri(Path.Combine($"{_apiConfig.WebAppUrl}", "accept-invite"));
string acceptInviteUrl =
QueryHelpers.AddQueryString(endpointUri.ToString(), "invitationToken", invitationToken);

var invitationNewUserEmailProps = new InvitationNewUserEmailProps(FullName: fullName,
CdnUrl: _apiConfig.WebAppUrl,
AcceptUrl: acceptInviteUrl,
NgoName: ngoName,
ElectionRoundDetails: electionRoundName);

var email = emailFactory.GenerateNewUserInvitationEmail(invitationNewUserEmailProps);
return email;
}


private static string GetFullName(MonitoringObserverImportModel observer)
{
return observer.FirstName + " " + observer.LastName;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Dapper;
using Feature.Statistics.GetNgoAdminStatistics.Models;
using Microsoft.Extensions.Caching.Memory;
using NPOI.POIFS.NIO;
using Vote.Monitor.Domain.ConnectionFactory;

namespace Feature.Statistics.GetNgoAdminStatistics;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ public override async Task<Results<Ok<ElectionRoundModel>, Conflict<ProblemDetai
CountryIso3 = country.Iso3,
CountryName = country.Name,
CountryFullName = country.FullName,
CountryNumericCode = country.NumericCode
CountryNumericCode = country.NumericCode,
CoalitionId = null,
CoalitionName = null,
IsCoalitionLeader = false,
IsMonitoringNgoForCitizenReporting = false
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ public record ElectionRoundModel
public required string EnglishTitle { get; init; }
public required DateOnly StartDate { get; init; }

[JsonConverter(typeof(SmartEnumNameConverter<ElectionRoundStatus, string>))]
public required ElectionRoundStatus Status { get; init; }

public required DateTime CreatedOn { get; init; }
public required DateTime? LastModifiedOn { get; init; }
}

public required bool IsMonitoringNgoForCitizenReporting { get; init; }
public required bool IsCoalitionLeader { get; init; }

public required Guid? CoalitionId { get; init; }
public required string? CoalitionName { get; init; }
}
107 changes: 102 additions & 5 deletions api/src/Vote.Monitor.Api.Feature.ElectionRound/Get/Endpoint.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
namespace Vote.Monitor.Api.Feature.ElectionRound.Get;
using Microsoft.EntityFrameworkCore;
using Vote.Monitor.Core.Services.Security;
using Vote.Monitor.Domain;

public class Endpoint(IReadRepository<ElectionRoundAggregate> repository)
namespace Vote.Monitor.Api.Feature.ElectionRound.Get;

public class Endpoint(
VoteMonitorContext context,
ICurrentUserProvider userProvider,
ICurrentUserRoleProvider roleProvider)
: Endpoint<Request, Results<Ok<ElectionRoundModel>, NotFound>>
{
public override void Configure()
Expand All @@ -9,10 +16,100 @@ public override void Configure()
Policies(PolicyNames.AdminsOnly);
}

public override async Task<Results<Ok<ElectionRoundModel>, NotFound>> ExecuteAsync(Request req, CancellationToken ct)
public override async Task<Results<Ok<ElectionRoundModel>, NotFound>> ExecuteAsync(Request req,
CancellationToken ct)
{
if (roleProvider.IsPlatformAdmin())
{
return await GetElectionRoundAsPlatformAdmin(req, ct);
}

return await GetElectionRoundAsNgoAdmin(req, ct);
}

private async Task<Results<Ok<ElectionRoundModel>, NotFound>> GetElectionRoundAsPlatformAdmin(Request req,
CancellationToken ct)
{
var electionRound = await context.ElectionRounds.Where(x => x.Id == req.Id)
.Include(x => x.MonitoringNgos)
.ThenInclude(x => x.Ngo)
.Include(x => x.MonitoringNgos)
.ThenInclude(x => x.MonitoringObservers)
.Include(x => x.Country)
.AsSplitQuery()
.Select(electionRound => new ElectionRoundModel
{
Id = electionRound.Id,
CountryId = electionRound.CountryId,
CountryIso2 = electionRound.Country.Iso2,
CountryIso3 = electionRound.Country.Iso3,
CountryName = electionRound.Country.Name,
CountryFullName = electionRound.Country.FullName,
CountryNumericCode = electionRound.Country.NumericCode,
Title = electionRound.Title,
EnglishTitle = electionRound.EnglishTitle,
Status = electionRound.Status,
StartDate = electionRound.StartDate,
LastModifiedOn = electionRound.LastModifiedOn,
CreatedOn = electionRound.CreatedOn,
CoalitionId = null,
CoalitionName = null,
IsCoalitionLeader = false,
IsMonitoringNgoForCitizenReporting = false,
})
.FirstOrDefaultAsync(ct);

if (electionRound is null)
{
return TypedResults.NotFound();
}

return TypedResults.Ok(electionRound);
}

private async Task<Results<Ok<ElectionRoundModel>, NotFound>> GetElectionRoundAsNgoAdmin(Request req,
CancellationToken ct)
{
var specification = new GetElectionRoundByIdSpecification(req.Id);
var electionRound = await repository.SingleOrDefaultAsync(specification, ct);
var ngoId = userProvider.GetNgoId()!.Value;

var electionRound = await context.MonitoringNgos
.Include(x => x.ElectionRound)
.ThenInclude(x => x.MonitoringNgoForCitizenReporting)
.Where(x => x.NgoId == ngoId)
.Where(x => x.ElectionRoundId == req.Id)
.OrderBy(x => x.ElectionRound.StartDate)
.Select(x => new ElectionRoundModel
{
Id = x.ElectionRound.Id,
CountryId = x.ElectionRound.CountryId,
CountryIso2 = x.ElectionRound.Country.Iso2,
CountryIso3 = x.ElectionRound.Country.Iso3,
CountryName = x.ElectionRound.Country.Name,
CountryFullName = x.ElectionRound.Country.FullName,
CountryNumericCode = x.ElectionRound.Country.NumericCode,
Title = x.ElectionRound.Title,
EnglishTitle = x.ElectionRound.EnglishTitle,
Status = x.ElectionRound.Status,
StartDate = x.ElectionRound.StartDate,
LastModifiedOn = x.ElectionRound.LastModifiedOn,
CreatedOn = x.ElectionRound.CreatedOn,
IsMonitoringNgoForCitizenReporting = x.ElectionRound.CitizenReportingEnabled &&
x.ElectionRound.MonitoringNgoForCitizenReporting.NgoId ==
ngoId,
IsCoalitionLeader =
context.Coalitions.Any(c => c.Leader.NgoId == ngoId && c.ElectionRoundId == x.ElectionRoundId),
CoalitionName = context.Coalitions
.Where(c =>
c.Memberships.Any(m => m.MonitoringNgoId == x.Id) && c.ElectionRoundId == x.ElectionRoundId)
.Select(c => c.Name)
.FirstOrDefault(),
CoalitionId = context.Coalitions
.Where(c =>
c.Memberships.Any(m => m.MonitoringNgoId == x.Id) && c.ElectionRoundId == x.ElectionRoundId)
.Select(c => c.Id)
.FirstOrDefault()
})
.FirstOrDefaultAsync(ct);

if (electionRound is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,29 @@ public override async Task<Ok<Result>> ExecuteAsync(Request req, CancellationTok
var electionRounds = await context.MonitoringNgos
.Include(x => x.ElectionRound)
.ThenInclude(x => x.MonitoringNgoForCitizenReporting)
.Include(x=>x.ElectionRound.Country)
.Where(x => x.NgoId == req.NgoId)
.OrderBy(x => x.ElectionRound.StartDate)
.Select(x => new NgoElectionRoundView
.Select(x => new ElectionRoundModel
{
MonitoringNgoId = x.Id,
ElectionRoundId = x.ElectionRoundId,
Id = x.ElectionRound.Id,
CountryId = x.ElectionRound.CountryId,
CountryIso2 = x.ElectionRound.Country.Iso2,
CountryIso3 = x.ElectionRound.Country.Iso3,
CountryName = x.ElectionRound.Country.Name,
CountryFullName = x.ElectionRound.Country.FullName,
CountryNumericCode = x.ElectionRound.Country.NumericCode,
Title = x.ElectionRound.Title,
EnglishTitle = x.ElectionRound.EnglishTitle,
Status = x.ElectionRound.Status,
StartDate = x.ElectionRound.StartDate,
Country = x.ElectionRound.Country.FullName,
CountryId = x.ElectionRound.CountryId,
LastModifiedOn = x.ElectionRound.LastModifiedOn,
CreatedOn = x.ElectionRound.CreatedOn,
IsMonitoringNgoForCitizenReporting = x.ElectionRound.CitizenReportingEnabled &&
x.ElectionRound.MonitoringNgoForCitizenReporting.NgoId ==
req.NgoId,
IsCoalitionLeader =
context.Coalitions.Any(c => c.Leader.NgoId == req.NgoId && c.ElectionRoundId == x.ElectionRoundId),
Status = x.ElectionRound.Status,
CoalitionName = context.Coalitions
.Where(c =>
c.Memberships.Any(m => m.MonitoringNgoId == x.Id) && c.ElectionRoundId == x.ElectionRoundId)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

public class Result
{
public List<NgoElectionRoundView> ElectionRounds { get; set; }
public List<ElectionRoundModel> ElectionRounds { get; set; }
}
Loading