├── .gitattributes ├── .github ├── CODEOWNERS └── workflows │ ├── build-image-api.yml │ ├── build-image-migrator.yml │ ├── build-image-seed.yml │ ├── codeql-analysis.yml │ └── dotnetcore.yml ├── .gitignore ├── LICENSE ├── README.md ├── bombardier ├── 1-polling-stations-details-flow.js ├── 2-forms-versions.js ├── 3-answers-flow.js ├── 4-notes-flow.js ├── attachments │ ├── image1.jpg │ ├── image2.jpg │ ├── image3.jpg │ ├── image4.jpg │ ├── image5.jpg │ ├── image6.jpg │ ├── image7.jpg │ ├── image8.jpg │ └── image9.jpg ├── polling-stations.csv ├── users.csv └── utils.js ├── docs ├── README.md ├── dummy-db-data │ ├── counties.csv │ ├── form-sections.csv │ ├── forms.csv │ ├── ngo-admin.csv │ ├── ngos.csv │ ├── observers.csv │ ├── options-to-questions.csv │ ├── options.csv │ ├── polling-stations.csv │ └── questions.csv └── images │ ├── data_model.png │ ├── sequence_diagrams │ ├── sequence_diagram_mobile_answers.png │ ├── sequence_diagram_mobile_forms.png │ ├── sequence_diagram_mobile_login.png │ ├── sequence_diagram_mobile_login_v2.png │ ├── sequence_diagram_mobile_manual_sync.png │ ├── sequence_diagram_mobile_notes.png │ └── sequence_diagram_mobile_pick_section.jpeg │ └── vote_monitor_diagram.png ├── forms-parser ├── README.md ├── form-parser.js ├── forms-2023-10-05.xlsx ├── forms-2023-10-10-en.xlsx ├── forms-2023-10-10-pl.xlsx ├── forms-2023-10-10.xlsx ├── forms-2023-10-11.xlsx ├── output │ ├── EN-A.json │ ├── EN-B.json │ ├── EN-C.json │ ├── PL-A.json │ ├── PL-B.json │ └── PL-C.json ├── package.json └── pnpm-lock.yaml ├── requests ├── EN-A.json ├── EN-B.json ├── EN-C.json ├── PL-A.json ├── PL-B.json ├── PL-C.json ├── image1.jpg ├── observers.csv ├── polish-data-2023-10-10.xlsx ├── polish_counties_2023.csv ├── polish_municipalities_2023.csv ├── polish_polling_stations_2023.csv ├── polish_provinces_2023.csv └── requests.http ├── src ├── .dockerignore ├── .editorconfig ├── .env.example ├── Directory.Build.targets ├── VotingIrregularities.sln ├── VotingIrregularities.sln.DotSettings ├── api │ ├── VoteMonitor.Api.Answer │ │ ├── Commands │ │ │ ├── BulkAnswers.cs │ │ │ └── FillInAnswerCommand.cs │ │ ├── Controllers │ │ │ └── AnswersController.cs │ │ ├── Handlers │ │ │ ├── AnswerQueryHandler.cs │ │ │ ├── AnswersQueryHandler.cs │ │ │ └── FillInAnswerQueryHandler.cs │ │ ├── Models │ │ │ ├── AnswerDTO.cs │ │ │ ├── AnswerQueryDTO.cs │ │ │ ├── BulkAnswerDto.cs │ │ │ ├── BulkAnswersRequest.cs │ │ │ ├── CorruptedAnswerDto.cs │ │ │ ├── FilledInAnswerDTO.cs │ │ │ ├── ObserverAnswersRequest.cs │ │ │ ├── PollingStationInfoDto.cs │ │ │ ├── QuestionDTO.cs │ │ │ ├── SectionAnswersRequest.cs │ │ │ └── SelectedOptionDto.cs │ │ ├── Queries │ │ │ ├── AnswersQuery.cs │ │ │ ├── FilledInAnswersQuery.cs │ │ │ └── FormAnswersQuery.cs │ │ └── VoteMonitor.Api.Answer.csproj │ ├── VoteMonitor.Api.Auth │ │ ├── Commands │ │ │ └── RegisterDeviceIdRequest.cs │ │ ├── Controllers │ │ │ ├── AuthorizationControllerBase.cs │ │ │ ├── AuthorizationV1Controller.cs │ │ │ └── AuthorizationV2Controller.cs │ │ ├── Handlers │ │ │ ├── AdminQueryHandler.cs │ │ │ ├── ObserverAuthenticationQueryHandler.cs │ │ │ └── RegisterDeviceIdRequestHandler.cs │ │ ├── Models │ │ │ ├── AuthenticateUserRequest.cs │ │ │ ├── AuthenticateUserRequestV2.cs │ │ │ ├── AuthenticationResponseModel.cs │ │ │ ├── RegisteredObserverModel.cs │ │ │ └── UserInfo.cs │ │ ├── Queries │ │ │ ├── NgoAdminApplicationUser.cs │ │ │ └── ObserverApplicationUser.cs │ │ └── VoteMonitor.Api.Auth.csproj │ ├── VoteMonitor.Api.Core │ │ ├── ApiListResponse.cs │ │ ├── ApiResponse.cs │ │ ├── Attributes │ │ │ └── AllowedExtensions.cs │ │ ├── ClaimsHelper.cs │ │ ├── Commands │ │ │ ├── ClearCache.cs │ │ │ ├── NotificationRegistrationDataCommand.cs │ │ │ ├── UploadFileCommand.cs │ │ │ └── UploadFileCommandV2.cs │ │ ├── ControllerExtensions.cs │ │ ├── Controllers │ │ │ └── CacheController.cs │ │ ├── Extensions │ │ │ ├── ClaimsExtensions.cs │ │ │ ├── DateTimeExtensions.cs │ │ │ ├── OptionsExtensions.cs │ │ │ └── QueryExtension.cs │ │ ├── Handlers │ │ │ ├── CacheHandler.cs │ │ │ └── UploadFileHandler.cs │ │ ├── ListExtensions.cs │ │ ├── Models │ │ │ ├── CountyPollingStationLimit.cs │ │ │ ├── JwtIssuerOptions.cs │ │ │ ├── UploadedFileResult.cs │ │ │ └── UserType.cs │ │ ├── Options │ │ │ ├── ApplicationCacheOptions.cs │ │ │ ├── BlobStorageOptions.cs │ │ │ ├── DefaultNgoOptions.cs │ │ │ ├── FileStorageType.cs │ │ │ ├── FirebaseServiceOptions.cs │ │ │ ├── HashOptions.cs │ │ │ ├── LocalFileStorageOptions.cs │ │ │ ├── MobileSecurityOptions.cs │ │ │ ├── PollingStationsOptions.cs │ │ │ └── S3StorageOptions.cs │ │ ├── PagingDefaultsConstants.cs │ │ ├── PagingModel.cs │ │ ├── PagingResponseModel.cs │ │ ├── Services │ │ │ ├── BlobService.cs │ │ │ ├── CacheService.cs │ │ │ ├── ClearTextService.cs │ │ │ ├── FirebaseService.cs │ │ │ ├── ICacheService.cs │ │ │ ├── IFileService.cs │ │ │ ├── IFirebaseService.cs │ │ │ ├── IHashService.cs │ │ │ ├── IPollingStationService.cs │ │ │ ├── LocalFileService.cs │ │ │ ├── NoCacheService.cs │ │ │ ├── S3Service.cs │ │ │ └── SHA256HashService.cs │ │ ├── UploadType.cs │ │ └── VoteMonitor.Api.Core.csproj │ ├── VoteMonitor.Api.County │ │ ├── Commands │ │ │ ├── CreateOrUpdateCounties.cs │ │ │ ├── CreateOrUpdateMunicipalities.cs │ │ │ ├── CreateOrUpdateProvinces.cs │ │ │ ├── PrefillCache.cs │ │ │ ├── UpdateCounty.cs │ │ │ ├── UpdateMunicipality.cs │ │ │ └── UpdateProvince.cs │ │ ├── Controllers │ │ │ ├── CacheController.cs │ │ │ ├── CountyController.cs │ │ │ ├── MunicipalityController.cs │ │ │ └── ProvinceController.cs │ │ ├── Handlers │ │ │ ├── CacheHandler.cs │ │ │ ├── CountiesCommandHandler.cs │ │ │ ├── MunicipalityCommandHandler.cs │ │ │ └── ProvincesCommandHandler.cs │ │ ├── Models │ │ │ ├── CountiesUploadRequest.cs │ │ │ ├── CountyCsvModel.cs │ │ │ ├── CountyModel.cs │ │ │ ├── ErrorModel.cs │ │ │ ├── MunicipalitiesUploadRequest.cs │ │ │ ├── MunicipalityCsvModel.cs │ │ │ ├── MunicipalityModel.cs │ │ │ ├── MunicipalityModelV2.cs │ │ │ ├── ProvinceCsvModel.cs │ │ │ ├── ProvinceModel.cs │ │ │ ├── ProvincesUploadRequest.cs │ │ │ ├── UpdateCountyRequest.cs │ │ │ ├── UpdateMunicipalityRequest.cs │ │ │ └── UpdateProvinceRequest.cs │ │ ├── Queries │ │ │ ├── GetAllCounties.cs │ │ │ ├── GetAllCountiesByProvinceCode.cs │ │ │ ├── GetAllMunicipalities.cs │ │ │ ├── GetAllMunicipalitiesByCountyCode.cs │ │ │ ├── GetAllProvinces.cs │ │ │ ├── GetCountiesForExport.cs │ │ │ ├── GetCountyById.cs │ │ │ ├── GetMunicipalitiesForExport.cs │ │ │ ├── GetMunicipalityById.cs │ │ │ ├── GetProvinceById.cs │ │ │ └── GetProvincesForExport.cs │ │ └── VoteMonitor.Api.County.csproj │ ├── VoteMonitor.Api.DataExport │ │ ├── Controllers │ │ │ ├── DataExportController.cs │ │ │ └── DataExportV2Controller.cs │ │ ├── FileGenerator │ │ │ ├── CsvGenerator.cs │ │ │ ├── CsvUtility.cs │ │ │ ├── ExcelFile.cs │ │ │ ├── ExcelUtility.cs │ │ │ └── ICsvGenerator.cs │ │ ├── Handlers │ │ │ ├── CsvGeneratorQueryHandler.cs │ │ │ ├── DataExportQueryHandler.cs │ │ │ └── GetExcelDbCommandHandler.cs │ │ ├── Models │ │ │ ├── ExportModelDto.cs │ │ │ └── NotesExportModel.cs │ │ ├── Queries │ │ │ ├── GenerateCSVFile.cs │ │ │ ├── GenerateNotesCSVFile.cs │ │ │ ├── GetDataForExport.cs │ │ │ ├── GetExcelDbCommand.cs │ │ │ └── GetNotesForExport.cs │ │ └── VoteMonitor.Api.DataExport.csproj │ ├── VoteMonitor.Api.Form │ │ ├── CommandHandlers │ │ │ ├── AddFormCommandHandler.cs │ │ │ ├── AddOptionCommandHandler.cs │ │ │ ├── DeleteFormCommandHandler.cs │ │ │ ├── UpdateFormCommandHandler.cs │ │ │ └── UpdateOptionCommandHandler.cs │ │ ├── Commands │ │ │ ├── AddFormCommand.cs │ │ │ ├── AddOptionCommand.cs │ │ │ ├── DeleteFormCommand.cs │ │ │ ├── DeleteQuestionCommand.cs │ │ │ ├── DeleteSectionCommand.cs │ │ │ ├── UpdateFormCommand.cs │ │ │ └── UpdateOptionCommand.cs │ │ ├── Controllers │ │ │ ├── ExportController.cs │ │ │ ├── FormController.cs │ │ │ └── OptionController.cs │ │ ├── DependencyInjectionExtensions.cs │ │ ├── Handlers │ │ │ ├── DeleteQuestionHandler.cs │ │ │ └── DeleteSectionHandler.cs │ │ ├── Mappers │ │ │ ├── HierarchicalMapper.cs │ │ │ ├── IEntityMapper.cs │ │ │ ├── IUpdateOrCreateEntityMapper.cs │ │ │ └── UpdateOrCreateEntityMapper.cs │ │ ├── Models │ │ │ ├── FilledFormResponseModel.cs │ │ │ ├── FormDTO.cs │ │ │ ├── FormDetailsModel.cs │ │ │ ├── FormResponseModel.cs │ │ │ ├── FormSectionDTO.cs │ │ │ ├── FormVersionsModel.cs │ │ │ ├── OptionToQuestionDTO.cs │ │ │ ├── Options │ │ │ │ ├── CreateOptionModel.cs │ │ │ │ ├── OptionDTO.cs │ │ │ │ └── OptionModel.cs │ │ │ └── QuestionDTO.cs │ │ ├── Profiles │ │ │ ├── FormProfile.cs │ │ │ ├── FormSectionProfile.cs │ │ │ ├── OptionProfile.cs │ │ │ ├── OptionToQuestionProfile.cs │ │ │ └── QuestionProfile.cs │ │ ├── Queries │ │ │ ├── ExistsFormByCodeOrIdQuery.cs │ │ │ ├── ExistsFormByIdQuery.cs │ │ │ ├── FetchAllOptionsQuery.cs │ │ │ ├── FormQuestionQuery.cs │ │ │ ├── FormVersionQuery.cs │ │ │ ├── GetFilledFormsQuery.cs │ │ │ ├── GetFormsQuery.cs │ │ │ └── GetOptionByIdCommand.cs │ │ ├── QueryHandlers │ │ │ ├── FetchAllOptionsQueryHandler.cs │ │ │ ├── FormQuestionQueryHandler.cs │ │ │ ├── FormVersionQueryHandler.cs │ │ │ ├── GetFilledFormsQueryHandler.cs │ │ │ ├── GetFormExistsByCodeOrIdQueryHandler.cs │ │ │ ├── GetFormExistsByIdQueryHandler.cs │ │ │ ├── GetFormsQueryHandler.cs │ │ │ └── GetOptionByIdQueryHandler.cs │ │ └── VoteMonitor.Api.Form.csproj │ ├── VoteMonitor.Api.Location │ │ ├── Commands │ │ │ ├── ImportPollingStationsCommand.cs │ │ │ ├── PrefillCache.cs │ │ │ ├── RegisterPollingStationCommand.cs │ │ │ └── UpdatePollingSectionCommand.cs │ │ ├── Controllers │ │ │ ├── CacheController.cs │ │ │ └── PollingStationController.cs │ │ ├── Exceptions │ │ │ └── PollingStationImportException.cs │ │ ├── Handlers │ │ │ ├── PollingStationAssignmentHandler.cs │ │ │ ├── PollingStationHandler.cs │ │ │ ├── PollingStationQueryHandler.cs │ │ │ ├── PrefillCacheHandler.cs │ │ │ ├── RegisterPollingSectionHandler.cs │ │ │ └── UpdatePollingStationHandler.cs │ │ ├── Models │ │ │ ├── AddPollingStationInfo.cs │ │ │ ├── PollingStationCsvModel.cs │ │ │ ├── PollingStationsUploadRequest.cs │ │ │ ├── ResultValues │ │ │ │ ├── PollingStationImportErrorCode.cs │ │ │ │ └── PollingStationImportResultValue.cs │ │ │ └── UpdatePollingStationInfo.cs │ │ ├── Queries │ │ │ ├── GetPollingStationId.cs │ │ │ └── PollingStationsAssignmentQuery.cs │ │ ├── Services │ │ │ └── PollingStationService.cs │ │ └── VoteMonitor.Api.Location.csproj │ ├── VoteMonitor.Api.Ngo │ │ ├── Commands │ │ │ ├── CreateNgo.cs │ │ │ ├── CreateNgoAdmin.cs │ │ │ ├── DeleteNgo.cs │ │ │ ├── DeleteNgoAdmin.cs │ │ │ ├── SetNgoOrganizerFlag.cs │ │ │ ├── SetNgoStatusFlag.cs │ │ │ ├── UpdateNgo.cs │ │ │ └── UpdateNgoAdmin.cs │ │ ├── Controllers │ │ │ ├── NgoAdminController.cs │ │ │ └── NgoController.cs │ │ ├── Handlers │ │ │ ├── NgoAdminCommandsHandler.cs │ │ │ ├── NgoAdminQueryHandler.cs │ │ │ ├── NgoCommandsHandler.cs │ │ │ └── NgoQueryHandler.cs │ │ ├── Models │ │ │ ├── CreateUpdateNgoAdminModel.cs │ │ │ ├── CreateUpdateNgoModel.cs │ │ │ ├── NgoAdminModel.cs │ │ │ ├── NgoModel.cs │ │ │ ├── PatchNgoOrganizerModel.cs │ │ │ └── PatchNgoStatusModel.cs │ │ ├── Queries │ │ │ ├── GetAllNgoAdmins.cs │ │ │ ├── GetAllNgos.cs │ │ │ ├── GetNgoAdminDetails.cs │ │ │ └── GetNgoDetails.cs │ │ └── VoteMonitor.Api.Ngo.csproj │ ├── VoteMonitor.Api.Note │ │ ├── Commands │ │ │ ├── AddNoteCommand.cs │ │ │ ├── AddNoteCommandV2.cs │ │ │ └── AddNoteToUnknownPollingStation.cs │ │ ├── Controllers │ │ │ └── NoteController.cs │ │ ├── Handlers │ │ │ └── NoteQueriesHandler.cs │ │ ├── Models │ │ │ ├── NoteFilterModel.cs │ │ │ ├── NoteModel.cs │ │ │ ├── UploadNoteModel.cs │ │ │ ├── UploadNoteModelV2.cs │ │ │ ├── UploadNoteResult.cs │ │ │ └── UploadNoteResultV2.cs │ │ ├── Queries │ │ │ └── NoteQuery.cs │ │ └── VoteMonitor.Api.Note.csproj │ ├── VoteMonitor.Api.Notification │ │ ├── Commands │ │ │ ├── NotificationListCommand.cs │ │ │ ├── SendNotificationCommand.cs │ │ │ └── SendNotificationToAllCommand.cs │ │ ├── Controllers │ │ │ └── NotificationController.cs │ │ ├── Handlers │ │ │ ├── NotificationListQueryHandler.cs │ │ │ └── NotificationRegistrationDataHandler.cs │ │ ├── Models │ │ │ ├── NotificationModel.cs │ │ │ ├── NotificationRegistrationDataModel.cs │ │ │ ├── SendNotificationModel.cs │ │ │ └── SendNotificationToAllModel.cs │ │ ├── Queries │ │ │ └── NotificationListQuery.cs │ │ └── VoteMonitor.Api.Notification.csproj │ ├── VoteMonitor.Api.Observer │ │ ├── Commands │ │ │ ├── DeleteObserverCommand.cs │ │ │ ├── EditObserverCommand.cs │ │ │ ├── ImportObserversRequest.cs │ │ │ ├── NewObserverCommand.cs │ │ │ ├── ObserverCountCommand.cs │ │ │ ├── ObserverGenerateCommand.cs │ │ │ ├── ObserverListCommand.cs │ │ │ ├── RemoveDeviceIdCommand.cs │ │ │ ├── ResetDeviceCommand.cs │ │ │ └── ResetPasswordCommand.cs │ │ ├── Controllers │ │ │ └── ObserverController.cs │ │ ├── Handlers │ │ │ ├── ActiveObserversQueryHandler.cs │ │ │ ├── CheckObserverExistsHandler.cs │ │ │ ├── GenerateObserversHandler.cs │ │ │ ├── ObserverCountQueryHandler.cs │ │ │ ├── ObserverListQueryHandler.cs │ │ │ ├── ObserverRequestsHandler.cs │ │ │ ├── RemoveDeviceIdHandler.cs │ │ │ ├── ResetDeviceHandler.cs │ │ │ └── ResetPasswordHandler.cs │ │ ├── Models │ │ │ ├── ActiveObserverFilter.cs │ │ │ ├── EditObserverModel.cs │ │ │ ├── GeneratedObserver.cs │ │ │ ├── NewObserverModel.cs │ │ │ ├── ObserverModel.cs │ │ │ ├── ObserversImportModel.cs │ │ │ ├── RemoveDeviceIdModel.cs │ │ │ └── ResetModel.cs │ │ ├── Queries │ │ │ ├── ActiveObserversQuery.cs │ │ │ ├── GetObserverDetails.cs │ │ │ └── ObserverListQuery.cs │ │ ├── Utils │ │ │ ├── RandomNumberGenerator.cs │ │ │ └── RandomObserverBuilder.cs │ │ └── VoteMonitor.Api.Observer.csproj │ ├── VoteMonitor.Api.PollingStation │ │ ├── Commands │ │ │ └── ClearAllPollingStationsCommand.cs │ │ ├── Controllers │ │ │ ├── PollingStationController.cs │ │ │ └── PollingStationInfoController.cs │ │ ├── Handlers │ │ │ ├── CheckPollingStationExistsHandler.cs │ │ │ ├── ClearAllPollingStationHandler.cs │ │ │ ├── CreatePollingStationInfoHandler.cs │ │ │ ├── GetPollingStationByIdHandler.cs │ │ │ ├── GetPollingStationsHadler.cs │ │ │ ├── UpdatePollingStationInfoHandler.cs │ │ │ └── UpdatePollingStationsHandler.cs │ │ ├── Models │ │ │ ├── CreatePollingStationInfoModel.cs │ │ │ ├── GetPollingStationModel.cs │ │ │ ├── PollingStationsFilterModel.cs │ │ │ ├── UpdatePollingStationInfo.cs │ │ │ └── UpdatePollingStationModel.cs │ │ ├── Queries │ │ │ ├── CheckPollingStationExists.cs │ │ │ ├── CreatePollingStationInfo.cs │ │ │ ├── GetPollingStationById.cs │ │ │ ├── GetPollingStations.cs │ │ │ ├── UpdatePollingStation.cs │ │ │ └── UpdatePollingStationInfo.cs │ │ └── VoteMonitor.Api.PollingStation.csproj │ ├── VoteMonitor.Api.Statistics │ │ ├── Controllers │ │ │ └── StatisticsController.cs │ │ ├── Handlers │ │ │ ├── MiniStatisticsQueryHandler.cs │ │ │ ├── StatisticsQueryBuilder.cs │ │ │ └── StatisticsQueryHandler.cs │ │ ├── Models │ │ │ ├── FilterStatisticsModel.cs │ │ │ ├── OptionStatisticsModel.cs │ │ │ ├── OptionsFilterModel.cs │ │ │ ├── SimpleStatisticsModel.cs │ │ │ ├── StatisticsGroupingTypes.cs │ │ │ └── StatisticsOptionsModel.cs │ │ ├── Queries │ │ │ ├── AnswersQuery.cs │ │ │ ├── CountiesVisitedQuery.cs │ │ │ ├── FlaggedAnswersQuery.cs │ │ │ ├── LoggedInObserversQuery.cs │ │ │ ├── NotesUploadedQuery.cs │ │ │ ├── StationsVisitedQuery.cs │ │ │ ├── StatisticsObserverNumberQuery.cs │ │ │ ├── StatisticsOptionsQuery.cs │ │ │ ├── StatisticsPaginatedQuery.cs │ │ │ ├── StatisticsQuery.cs │ │ │ └── StatisticsTopIrregularitiesQuery.cs │ │ └── VoteMonitor.Api.Statistics.csproj │ ├── VoteMonitor.Api │ │ ├── Dockerfile │ │ ├── Extensions │ │ │ ├── ExceptionHandler.cs │ │ │ ├── HealthChecks │ │ │ │ ├── AzureBlobStorageHealthChecksExtensions.cs │ │ │ │ ├── ConditionalHealthCheck.cs │ │ │ │ ├── FirebaseHealthChecksExtensions.cs │ │ │ │ └── S3HealthChecksExtensions.cs │ │ │ ├── HealthChecksConfiguration.cs │ │ │ ├── JwtConfigurationExtensions.cs │ │ │ ├── LoggingConfiguration.cs │ │ │ ├── ServicesExtensions.cs │ │ │ └── SwaggerConfiguration.cs │ │ ├── ImportHelper.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── VoteMonitor.Api.csproj │ │ └── appsettings.json │ ├── VoteMonitor.Entities │ │ ├── Answer.cs │ │ ├── AnswerCorrupted.cs │ │ ├── AnswerQueryInfo.cs │ │ ├── County.cs │ │ ├── EfBuilderExtensions.cs │ │ ├── ExportModel.cs │ │ ├── Form.cs │ │ ├── FormSection.cs │ │ ├── IHierarchicalEntity.cs │ │ ├── IIdentifiableEntity.cs │ │ ├── MobileUniqueIdType.cs │ │ ├── Municipality.cs │ │ ├── Ngo.cs │ │ ├── NgoAdmin.cs │ │ ├── Note.cs │ │ ├── NoteCorrupted.cs │ │ ├── NotesAttachmentCorrupted.cs │ │ ├── NotesAttachments.cs │ │ ├── Notification.cs │ │ ├── NotificationRecipient.cs │ │ ├── NotificationRegistrationData.cs │ │ ├── Observer.cs │ │ ├── Option.cs │ │ ├── OptionToQuestion.cs │ │ ├── PollingStation.cs │ │ ├── PollingStationInfo.cs │ │ ├── PollingStationInfoCorrupted.cs │ │ ├── Province.cs │ │ ├── Question.cs │ │ ├── QuestionType.cs │ │ ├── Statistici.cs │ │ ├── VoteMonitor.Entities.csproj │ │ └── VotingContext.cs │ ├── VotingIrregularities.Domain.Migrator │ │ ├── ConfigurationHelper.cs │ │ ├── ContextFactory.cs │ │ ├── Dockerfile │ │ ├── Migrations │ │ │ ├── 20230818075147_InitialPostgresMigration.Designer.cs │ │ │ ├── 20230818075147_InitialPostgresMigration.cs │ │ │ ├── 20230829162705_CleanupPollingStations.Designer.cs │ │ │ ├── 20230829162705_CleanupPollingStations.cs │ │ │ ├── 20230906055404_PollingStationsRefactor.Designer.cs │ │ │ ├── 20230906055404_PollingStationsRefactor.cs │ │ │ ├── 20230907120427_ChangeLimits.Designer.cs │ │ │ ├── 20230907120427_ChangeLimits.cs │ │ │ ├── 20230914091458_RenameCommunity.Designer.cs │ │ │ ├── 20230914091458_RenameCommunity.cs │ │ │ ├── 20230922074009_AddAttachmentFileName.Designer.cs │ │ │ ├── 20230922074009_AddAttachmentFileName.cs │ │ │ ├── 20230922190024_ProvincesTable.Designer.cs │ │ │ ├── 20230922190024_ProvincesTable.cs │ │ │ ├── 20230926071737_NewPollingStationsInfo.Designer.cs │ │ │ ├── 20230926071737_NewPollingStationsInfo.cs │ │ │ ├── 20231005064956_AddFileNameColumn.Designer.cs │ │ │ ├── 20231005064956_AddFileNameColumn.cs │ │ │ ├── 20231006085620_ChangeAttachmentsLimits.Designer.cs │ │ │ ├── 20231006085620_ChangeAttachmentsLimits.cs │ │ │ ├── 20231010171443_ChangeCodeSizeLimit.Designer.cs │ │ │ ├── 20231010171443_ChangeCodeSizeLimit.cs │ │ │ ├── 20231014093815_AddCorruptedRecoveryTables.Designer.cs │ │ │ ├── 20231014093815_AddCorruptedRecoveryTables.cs │ │ │ ├── 20231014104127_AddMissingColumn.Designer.cs │ │ │ ├── 20231014104127_AddMissingColumn.cs │ │ │ ├── 20231015061038_AddCorruptedAnswers.Designer.cs │ │ │ ├── 20231015061038_AddCorruptedAnswers.cs │ │ │ └── VoteMonitorContextModelSnapshot.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── VotingIrregularities.Domain.Migrator.csproj │ │ └── appsettings.json │ └── VotingIrregularities.Domain.Seed │ │ ├── ConfigurationHelper.cs │ │ ├── Dockerfile │ │ ├── Options │ │ ├── AdminSeed.cs │ │ ├── HashServiceType.cs │ │ ├── NgoSeed.cs │ │ ├── ObserverSeed.cs │ │ └── SeedOption.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Services │ │ ├── ClearTextService.cs │ │ ├── IHashService.cs │ │ └── SHA256HashService.cs │ │ ├── VotingContextSeeder.cs │ │ ├── VotingIrregularities.Domain.Seed.csproj │ │ ├── appsettings.Development.json │ │ └── appsettings.json ├── docker-compose.yml └── test │ ├── VoteMonitor.Api.PollingStation.Tests │ ├── Handlers │ │ ├── CheckPollingStationExistsHandlerTests.cs │ │ ├── CreatePollingStationInfoHandlerTests.cs │ │ ├── GetPollingStationsHandlerTests.cs │ │ └── UpdatePollingStationsHandlerTests.cs │ ├── PollingStationBuilder.cs │ └── VoteMonitor.Api.PollingStation.Tests.csproj │ ├── VoteMonitor.Api.Tests │ ├── Extensions │ │ └── ServiceExtensions_ConditionalChecksRegistrationsTests.cs │ └── VoteMonitor.Api.Tests.csproj │ └── VotingIrregularities.Tests │ ├── AnswerHandlerTests.cs │ ├── AnswersApi │ └── AnswerQueryHandlerShould.cs │ ├── CountyApi │ └── CountiesCommandHandlerTests.cs │ ├── DateTimeTest.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── VotingIrregularities.Tests.csproj │ └── appsettings.json └── terraform ├── .gitignore ├── .terraform.lock.hcl ├── acm.tf ├── data.tf ├── database.tf ├── ec2_bastion.tf ├── ecs.tf ├── iam.tf ├── lb.tf ├── locals.tf ├── modules ├── ecs-cluster │ ├── assets │ │ └── user_data.sh │ ├── cloudwatch.tf │ ├── data.tf │ ├── iam.tf │ ├── locals.tf │ ├── main.tf │ ├── outputs.tf │ ├── service_discovery.tf │ └── variables.tf ├── ecs-service │ ├── autoscaling.tf │ ├── data.tf │ ├── ecs_task_definition.tf │ ├── iam.tf │ ├── lb.tf │ ├── locals.tf │ ├── main.tf │ ├── outputs.tf │ ├── route53.tf │ ├── service_discovery.tf │ └── variables.tf └── s3 │ ├── main.tf │ ├── outputs.tf │ ├── random.tf │ └── variables.tf ├── networking_eips.tf ├── networking_gateways.tf ├── networking_routing.tf ├── networking_subnets.tf ├── networking_vpc.tf ├── outputs.tf ├── providers.tf ├── secrets.tf ├── service_api.tf ├── service_migrator.tf ├── service_seed.tf └── variables.tf /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # they will be requested for review when someone 4 | # opens a pull request. 5 | * @aniri @idormenco 6 | 7 | # More details on creating a codeowners file: 8 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 9 | -------------------------------------------------------------------------------- /.github/workflows/dotnetcore.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET Core 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: '7.0.x' 20 | 21 | - name: Build with dotnet 22 | run: dotnet build ./src/VotingIrregularities.sln --configuration Release 23 | 24 | - name: Test with dotnet 25 | run: dotnet test ./src/VotingIrregularities.sln --configuration Release 26 | -------------------------------------------------------------------------------- /bombardier/attachments/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image1.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image2.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image3.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image4.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image5.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image6.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image7.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image8.jpg -------------------------------------------------------------------------------- /bombardier/attachments/image9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/bombardier/attachments/image9.jpg -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | Please see more info and docs about the MV apps [in the wiki](https://github.com/code4romania/monitorizare-vot/wiki). 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/dummy-db-data/counties.csv: -------------------------------------------------------------------------------- 1 | Id,Code,Name,NumberOfPollingStations,Diaspora,Order 2 | 1,AA,County AA,2,0,1 3 | 2,BB,County BB,1,0,2 4 | -------------------------------------------------------------------------------- /docs/dummy-db-data/form-sections.csv: -------------------------------------------------------------------------------- 1 | Id,Code,Description,IdForm,OrderNumber 2 | 1,A,A Form Section,1,1 3 | 2,B,Other Form Section,1,2 4 | 3,C,Some Form Section,2,1 5 | 4,D,Another Form Section,3,1 6 | -------------------------------------------------------------------------------- /docs/dummy-db-data/forms.csv: -------------------------------------------------------------------------------- 1 | Id,Code,Description,CurrentVersion,Diaspora,Draft,Order 2 | 1,A,First Form,1,0,0,1 3 | 2,B,Second form,2,0,0,2 4 | 3,C,Draft form,1,0,1,3 5 | -------------------------------------------------------------------------------- /docs/dummy-db-data/ngo-admin.csv: -------------------------------------------------------------------------------- 1 | Id,IdNgo,Account,Password 2 | 1,1,master_admin,password 3 | 2,2,regular_admin,password 4 | -------------------------------------------------------------------------------- /docs/dummy-db-data/ngos.csv: -------------------------------------------------------------------------------- 1 | Id,ShortName,Name,Organizer,IsActive 2 | 1,Organizer,Organizer NGO,1,1 3 | 2,Regular,Regular NGO,0,1 4 | -------------------------------------------------------------------------------- /docs/dummy-db-data/observers.csv: -------------------------------------------------------------------------------- 1 | Id,FromTeam,IdNgo,Phone,Name,Pin,MobileDeviceId,DeviceRegisterDate,IsTestObserver,MobileDeviceIdType 2 | 1,0,1,0123456789,Observer 1,1234,none,2020-10-21 17:35:44.830,0,0 3 | 2,0,2,9876543210,Observer 2,1234,none,2020-10-21 17:35:44.830,0,0 4 | 3,0,1,12345678,Test Observer,1234,none,2020-10-21 17:35:44.830,1,0 5 | -------------------------------------------------------------------------------- /docs/dummy-db-data/options-to-questions.csv: -------------------------------------------------------------------------------- 1 | Id,IdQuestion,IdOption,Flagged 2 | 1,1,1,0 3 | 2,1,2,0 4 | 3,2,1,0 5 | 4,2,2,0 6 | 5,3,1,0 7 | 6,3,2,0 8 | 7,3,3,1 9 | 8,4,1,0 10 | 9,4,2,0 11 | 10,4,3,1 12 | 11,5,1,0 13 | 12,5,2,0 14 | 13,6,1,0 15 | 14,6,2,0 16 | -------------------------------------------------------------------------------- /docs/dummy-db-data/options.csv: -------------------------------------------------------------------------------- 1 | Id,IsFreeText,Text,Hint,OrderNumber 2 | 1,0,Option one,NULL,1 3 | 2,0,Option two,NULL,2 4 | 3,1,Other,NULL,3 5 | -------------------------------------------------------------------------------- /docs/dummy-db-data/polling-stations.csv: -------------------------------------------------------------------------------- 1 | Id,Address,Coordinates,AdministrativeTerritoryCode,IdCounty,TerritoryCode,Number 2 | 1,"Some Address 1",NULL,111,1,123,1 3 | 2,"Some Address 2",NULL,222,1,234,2 4 | 3,"Some Address 3",NULL,333,2,345,1 5 | -------------------------------------------------------------------------------- /docs/dummy-db-data/questions.csv: -------------------------------------------------------------------------------- 1 | Id,Code,IdSection,QuestionType,Text,Hint,OrderNumber 2 | 1,A1,1,0,"Multiple choice question?",NULL,1 3 | 2,A2,1,1,"Single choice question?",NULL,2 4 | 3,B1,2,2,"Single choice question with free text option?",NULL,1 5 | 4,B2,2,3,"Multiple choice question with free text option?",NULL,2 6 | 5,C1,3,1,"Single choice question?",NULL,1 7 | 6,C2,4,0,"Multiple choice question?",NULL,1 8 | -------------------------------------------------------------------------------- /docs/images/data_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/data_model.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_answers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_answers.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_forms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_forms.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_login.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_login_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_login_v2.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_manual_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_manual_sync.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_notes.png -------------------------------------------------------------------------------- /docs/images/sequence_diagrams/sequence_diagram_mobile_pick_section.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/sequence_diagrams/sequence_diagram_mobile_pick_section.jpeg -------------------------------------------------------------------------------- /docs/images/vote_monitor_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/docs/images/vote_monitor_diagram.png -------------------------------------------------------------------------------- /forms-parser/README.md: -------------------------------------------------------------------------------- 1 | Install dependencies 2 | ```js 3 | pnpm install 4 | ``` 5 | Running the app 6 | ```js 7 | node form-parser.js -f "forms-2023-10-11.xlsx" -s 1 -o "output" 8 | ``` -------------------------------------------------------------------------------- /forms-parser/forms-2023-10-05.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/forms-parser/forms-2023-10-05.xlsx -------------------------------------------------------------------------------- /forms-parser/forms-2023-10-10-en.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/forms-parser/forms-2023-10-10-en.xlsx -------------------------------------------------------------------------------- /forms-parser/forms-2023-10-10-pl.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/forms-parser/forms-2023-10-10-pl.xlsx -------------------------------------------------------------------------------- /forms-parser/forms-2023-10-10.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/forms-parser/forms-2023-10-10.xlsx -------------------------------------------------------------------------------- /forms-parser/forms-2023-10-11.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/forms-parser/forms-2023-10-11.xlsx -------------------------------------------------------------------------------- /forms-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forms-parser", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "form-parser.js", 6 | "type": "module", 7 | "scripts": { 8 | "form-parser": " node form-parser.js -f forms-2023-10-11.xlsx -s 1 -o output" 9 | }, 10 | "keywords": [], 11 | "author": "Ion Dormenco", 12 | "license": "MIT", 13 | "dependencies": { 14 | "lodash-es": "^4.17.21", 15 | "nanoid": "^4.0.2", 16 | "read-excel-file": "^5.6.1", 17 | "yargs": "^17.7.2" 18 | } 19 | } -------------------------------------------------------------------------------- /requests/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/requests/image1.jpg -------------------------------------------------------------------------------- /requests/polish-data-2023-10-10.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/requests/polish-data-2023-10-10.xlsx -------------------------------------------------------------------------------- /requests/polish_provinces_2023.csv: -------------------------------------------------------------------------------- 1 | "Id","Code","Name","Order" 2 | 1,"02","dolnośląskie",1 3 | 2,"04","kujawsko-pomorskie",2 4 | 3,"06","lubelskie",3 5 | 4,"08","lubuskie",4 6 | 5,"10","łódzkie",5 7 | 6,"12","małopolskie",6 8 | 7,"14","mazowieckie",7 9 | 8,"16","opolskie",8 10 | 9,"18","podkarpackie",9 11 | 10,"20","podlaskie",10 12 | 11,"22","pomorskie",11 13 | 12,"24","śląskie",12 14 | 13,"26","świętokrzyskie",13 15 | 14,"28","warmińsko-mazurskie",14 16 | 15,"30","wielkopolskie",15 17 | 16,"32","zachodniopomorskie",16 18 | 17,"zagranica","zagranica",17 19 | 18,"statki","statki",18 20 | -------------------------------------------------------------------------------- /src/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.vs 6 | **/.vscode 7 | **/*.*proj.user 8 | **/azds.yaml 9 | **/charts 10 | **/bin 11 | **/obj 12 | **/Dockerfile 13 | **/Dockerfile.develop 14 | **/docker-compose.yml 15 | **/docker-compose.*.yml 16 | **/*.dbmdl 17 | **/*.jfm 18 | **/secrets.dev.yaml 19 | **/values.dev.yaml 20 | **/.toolstarget -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 3 | 4 | 1701;1702;1591;1573 5 | $(SolutionDir)\api-docs\$(MSBuildProjectName).xml 6 | 7 | -------------------------------------------------------------------------------- /src/VotingIrregularities.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DTO 3 | NG -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Commands/BulkAnswers.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Answer.Models; 3 | 4 | namespace VoteMonitor.Api.Answer.Commands; 5 | 6 | public record BulkAnswers : IRequest 7 | { 8 | public BulkAnswers(int observerId, IEnumerable answers) 9 | { 10 | ObserverId = observerId; 11 | Answers = answers.ToList().AsReadOnly(); 12 | } 13 | 14 | public int ObserverId { get; } 15 | 16 | public IReadOnlyCollection Answers { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Commands/FillInAnswerCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Answer.Models; 3 | 4 | namespace VoteMonitor.Api.Answer.Commands; 5 | 6 | public record FillInAnswerCommand : IRequest 7 | { 8 | public FillInAnswerCommand(int observerId, IEnumerable answers, IEnumerable corruptedAnswers) 9 | { 10 | ObserverId = observerId; 11 | Answers = answers.ToList().AsReadOnly(); 12 | CorruptedAnswers = corruptedAnswers.ToList().AsReadOnly(); 13 | } 14 | 15 | public int ObserverId { get; } 16 | public IReadOnlyCollection Answers { get; } 17 | public IReadOnlyCollection CorruptedAnswers { get; } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/AnswerDTO.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class AnswerDto 4 | { 5 | public int QuestionId { get; set; } 6 | public int PollingStationId { get; set; } 7 | public string CountyCode { get; set; } 8 | public int PollingStationNumber { get; set; } 9 | public List Options { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/AnswerQueryDTO.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class AnswerQueryDto 4 | { 5 | public int ObserverId { get; set; } 6 | public int PollingStationId { get; set; } 7 | public string ObserverName { get; set; } 8 | public string ObserverPhoneNumber { get; set; } 9 | public string PollingStationName { get; set; } 10 | public DateTime LastModified { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/BulkAnswerDto.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Answer.Models; 4 | 5 | public class BulkAnswerDto 6 | { 7 | [Required] 8 | public int QuestionId { get; set; } 9 | 10 | [Required(AllowEmptyStrings = false)] 11 | public string CountyCode { get; set; } 12 | 13 | [Required(AllowEmptyStrings = false)] 14 | public string MunicipalityCode { get; set; } 15 | 16 | [Required(AllowEmptyStrings = false)] 17 | public int PollingStationNumber { get; set; } 18 | 19 | public List Options { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/BulkAnswersRequest.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class BulkAnswersRequest 4 | { 5 | public BulkAnswerDto[] Answers { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/CorruptedAnswerDto.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class CorruptedAnswerDto 4 | { 5 | public int QuestionId { get; set; } 6 | public string CountyCode { get; set; } 7 | public string MunicipalityCode { get; set; } 8 | public int PollingStationNumber { get; set; } 9 | public List Options { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/FilledInAnswerDTO.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class FilledInAnswerDto 4 | { 5 | public int OptionId { get; set; } 6 | public string Text { get; set; } 7 | public bool IsFreeText { get; set; } 8 | public string Value { get; set; } 9 | public bool IsFlagged { get; set; } 10 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/ObserverAnswersRequest.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class ObserverAnswersRequest 4 | { 5 | public int PollingStationId { get; set; } 6 | public int ObserverId { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/PollingStationInfoDto.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class PollingStationInfoDto 4 | { 5 | public DateTime LastModified { get; set; } 6 | public DateTime? ObserverArrivalTime { get; set; } 7 | public DateTime? ObserverLeaveTime { get; set; } 8 | 9 | public int NumberOfVotersOnTheList { get; set; } 10 | 11 | public int NumberOfCommissionMembers { get; set; } 12 | 13 | public int NumberOfFemaleMembers { get; set; } 14 | 15 | public int MinPresentMembers { get; set; } 16 | 17 | public bool ChairmanPresence { get; set; } 18 | 19 | public bool SinglePollingStationOrCommission { get; set; } 20 | 21 | public bool AdequatePollingStationSize { get; set; } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/QuestionDTO.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class QuestionDto 4 | where T : class 5 | { 6 | public int Id { get; set; } 7 | public string Text { get; set; } 8 | public int QuestionTypeId { get; set; } 9 | public string QuestionId { get; set; } 10 | public string FormCode { get; set; } 11 | 12 | public IList Answers { get; set; } 13 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/SectionAnswersRequest.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core; 2 | 3 | namespace VoteMonitor.Api.Answer.Models; 4 | 5 | public class SectionAnswersRequest : PagingModel 6 | { 7 | public bool IsUrgent { get; set; } 8 | public string County { get; set; } 9 | public int PollingStationNumber { get; set; } 10 | public int ObserverId { get; set; } 11 | public string ObserverPhoneNumber { get; set; } 12 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Models/SelectedOptionDto.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Answer.Models; 2 | 3 | public class SelectedOptionDto 4 | { 5 | public int OptionId { get; set; } 6 | public string Value { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Queries/AnswersQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Answer.Models; 3 | using VoteMonitor.Api.Core; 4 | 5 | namespace VoteMonitor.Api.Answer.Queries; 6 | 7 | public class AnswersQuery : PagingModel, IRequest> 8 | { 9 | public int NgoId { get; set; } 10 | public bool Urgent { get; set; } 11 | public bool Organizer { get; set; } 12 | public string County { get; set; } 13 | public int PollingStationNumber { get; set; } 14 | public int ObserverId { get; set; } 15 | public string ObserverPhoneNumber { get; set; } 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Queries/FilledInAnswersQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Answer.Models; 3 | 4 | namespace VoteMonitor.Api.Answer.Queries; 5 | 6 | public record FilledInAnswersQuery(int ObserverId, int PollingStationId) : IRequest>>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/Queries/FormAnswersQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Answer.Models; 3 | 4 | namespace VoteMonitor.Api.Answer.Queries; 5 | 6 | public record FormAnswersQuery(int ObserverId, int PollingStationId) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Answer/VoteMonitor.Api.Answer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Commands/RegisterDeviceIdRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Entities; 3 | 4 | namespace VoteMonitor.Api.Auth.Commands; 5 | 6 | public record RegisterDeviceId(string MobileDeviceId, MobileDeviceIdType MobileDeviceIdType, int ObserverId) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Models/AuthenticateUserRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Auth.Models; 4 | 5 | public class AuthenticateUserRequest 6 | { 7 | [Required] 8 | public string User { get; set; } 9 | [Required] 10 | public string Password { get; set; } 11 | public string UniqueId { get; set; } 12 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Models/AuthenticateUserRequestV2.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Auth.Models; 4 | 5 | public class AuthenticateUserRequestV2 6 | { 7 | [Required] 8 | public string User { get; set; } 9 | [Required] 10 | public string Password { get; set; } 11 | public string ChannelName { get; set; } 12 | public string FcmToken { get; set; } 13 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Models/AuthenticationResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Auth.Models; 2 | 3 | public class AuthenticationResponseModel 4 | { 5 | public string access_token { get; set; } 6 | public int expires_in { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Models/RegisteredObserverModel.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Entities; 2 | 3 | namespace VoteMonitor.Api.Auth.Models; 4 | 5 | public class RegisteredObserverModel 6 | { 7 | public bool IsAuthenticated { get; set; } 8 | 9 | public int ObserverId { get; set; } 10 | /// 11 | /// The is registered if the lock device feature is on and the user is logging for the first time, 12 | /// or the user is logging for the first time with instead of . 13 | /// 14 | public bool ShouldRegisterMobileDeviceId { get; set; } 15 | public int IdNgo { get; set; } 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Models/UserInfo.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Auth.Models; 2 | 3 | public class UserInfo 4 | { 5 | public int IdNgo { get; set; } 6 | public int NgoAdminId { get; set; } 7 | public bool Organizer { get; set; } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/Queries/NgoAdminApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using System.ComponentModel.DataAnnotations; 3 | using VoteMonitor.Api.Auth.Models; 4 | using VoteMonitor.Api.Core.Models; 5 | 6 | namespace VoteMonitor.Api.Auth.Queries; 7 | 8 | public class NgoAdminApplicationUser : IRequest 9 | { 10 | [Required(AllowEmptyStrings = false)] 11 | public string UserName { get; set; } 12 | 13 | [Required(AllowEmptyStrings = false)] 14 | public string Password { get; set; } 15 | 16 | public UserType UserType { get; set; } 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Auth/VoteMonitor.Api.Auth.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/ApiListResponse.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public class ApiListResponse : PagingResponseModel 4 | { 5 | public List Data { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/ApiResponse.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public class ApiResponse 4 | where T : class 5 | { 6 | public T Data { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/ClaimsHelper.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public static class ClaimsHelper 4 | { 5 | public const string ObserverIdProperty = "ObserverId"; 6 | public const string NgoAdminIdProperty = "NgoAdminId"; 7 | public const string GenericIdProvider = "Token"; 8 | public const string IdNgo = "IdNgo"; 9 | public const string Organizer = "Organizer"; 10 | public const string UserType = "UserType"; 11 | public static readonly string TOKEN_VALUE = "Token"; 12 | public static readonly string AUTH_HEADER_VALUE = "Authorization"; 13 | 14 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Commands/ClearCache.cs: -------------------------------------------------------------------------------- 1 | 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.Core.Commands; 5 | 6 | public record ClearCache: IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Commands/NotificationRegistrationDataCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Core.Commands; 4 | 5 | public record NotificationRegistrationDataCommand(int ObserverId, string ChannelName, string Token) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Commands/UploadFileCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Http; 3 | using VoteMonitor.Api.Core.Models; 4 | 5 | namespace VoteMonitor.Api.Core.Commands; 6 | 7 | [Obsolete("Will be removed when ui will use multiple files upload")] 8 | public record UploadFileCommand(IFormFile File, UploadType UploadType) : IRequest; 9 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Commands/UploadFileCommandV2.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Http; 3 | using VoteMonitor.Api.Core.Models; 4 | 5 | namespace VoteMonitor.Api.Core.Commands; 6 | 7 | public class UploadFileCommandV2 : IRequest 8 | { 9 | public UploadFileCommandV2(List files, UploadType uploadType) 10 | { 11 | Files = files.ToList().AsReadOnly(); 12 | UploadType = uploadType; 13 | } 14 | 15 | public IReadOnlyCollection Files { get; } 16 | public UploadType UploadType { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Controllers/CacheController.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using VoteMonitor.Api.Core.Commands; 5 | 6 | namespace VoteMonitor.Api.Core.Controllers; 7 | 8 | [ApiController] 9 | [Route("api/v1/cache")] 10 | public class CacheController: Controller 11 | { 12 | private readonly IMediator _mediator; 13 | 14 | public CacheController(IMediator mediator) 15 | { 16 | _mediator = mediator; 17 | } 18 | 19 | 20 | [Authorize("Organizer")] 21 | [HttpPost("clear")] 22 | public async Task ClearCacheAsync() 23 | { 24 | await _mediator.Send(new ClearCache()); 25 | return Ok(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Extensions/ClaimsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace VoteMonitor.Api.Core.Extensions; 4 | 5 | public static class ClaimsExtensions 6 | { 7 | public static int? GetObserverId(this IEnumerable claims) 8 | { 9 | return int.TryParse(claims.FirstOrDefault(c => c.Type == ClaimsHelper.ObserverIdProperty)?.Value, out var observerId) ? observerId : (int?)null; 10 | } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Extensions/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Extensions; 2 | 3 | public static class DateTimeExtensions 4 | { 5 | /// Date converted to seconds since Unix epoch (Jan 1, 1970, midnight UTC). 6 | public static long ToUnixEpochDate(this DateTime date) 7 | => (long)Math.Round((date.ToUniversalTime() - 8 | new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)) 9 | .TotalSeconds); 10 | 11 | public static DateTime? AsUtc(this DateTime? date) => 12 | date == null ? null : DateTime.SpecifyKind( 13 | date.Value, 14 | DateTimeKind.Utc); 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Extensions/QueryExtension.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Extensions; 2 | 3 | public class QueryExtension 4 | { 5 | public IQueryable GetPagedQuery(IQueryable entities, int page, int pageSize) 6 | where T: class // could be more restrictive if all entities implemented the same interface or the same base class 7 | { 8 | if (pageSize > 0) 9 | { 10 | return entities 11 | .Skip(pageSize * (page - 1)) 12 | .Take(pageSize); 13 | } 14 | 15 | return entities; 16 | } 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Handlers/CacheHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core.Commands; 3 | using VoteMonitor.Api.Core.Services; 4 | 5 | namespace VoteMonitor.Api.Core.Handlers; 6 | 7 | public class CacheHandler : IRequestHandler 8 | { 9 | private readonly ICacheService _cacheService; 10 | 11 | public CacheHandler(ICacheService cacheService) 12 | { 13 | _cacheService = cacheService; 14 | } 15 | public async Task Handle(ClearCache request, CancellationToken cancellationToken) 16 | { 17 | await _cacheService.ClearAllValuesAsync(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public static class ListExtensions 4 | { 5 | public static List Paginate(this List unPagedList, int page, int pageSize) 6 | { 7 | return unPagedList 8 | .Skip((page - 1) * pageSize) 9 | .Take(pageSize) 10 | .ToList(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Models/CountyPollingStationLimit.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Models; 2 | 3 | public class CountyPollingStationLimit 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Code { get; set; } 8 | public int Limit { get; set; } 9 | 10 | public bool Diaspora { get; set; } 11 | public int Order { get; set; } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Models/UploadedFileResult.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Models; 2 | 3 | public record UploadedFileModel 4 | { 5 | public string FileName { get; init; } 6 | public string Path { get; init; } 7 | } 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Models/UserType.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Models; 2 | 3 | public enum UserType 4 | { 5 | Observer, 6 | NgoAdmin 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/ApplicationCacheOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class ApplicationCacheOptions 4 | { 5 | public int Hours { get; set; } 6 | 7 | public int Minutes { get; set; } = 30; 8 | 9 | public int Seconds { get; set; } 10 | 11 | public ApplicationCacheImplementationType Implementation { get; set; } = ApplicationCacheImplementationType.NoCache; 12 | } 13 | 14 | public enum ApplicationCacheImplementationType 15 | { 16 | NoCache, 17 | MemoryDistributedCache, 18 | RedisCache 19 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/BlobStorageOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | /// 4 | /// Manages the details about the blob storage being used 5 | /// 6 | public class BlobStorageOptions 7 | { 8 | public string ConnectionString { get; set; } 9 | 10 | /// 11 | /// The name of the blob container. 12 | /// 13 | public string ContainerName { get; set; } 14 | 15 | /// 16 | /// Gets or sets blob availability in minutes 17 | /// 18 | public int SASBlobAvailabilityInMinutes { get; set; } 19 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/DefaultNgoOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class DefaultNgoOptions 4 | { 5 | public int DefaultNgoId { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/FileStorageType.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public enum FileStorageType 4 | { 5 | LocalFileService, 6 | BlobService, 7 | S3Service, 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/FirebaseServiceOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class FirebaseServiceOptions 4 | { 5 | public string ServerKey { get; set; } 6 | public string ServerPath{ get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/HashOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class HashOptions 4 | { 5 | public string Salt { get; set; } 6 | 7 | /// 8 | /// Can be set to `Hash` or `ClearText` 9 | /// `Hash` will use the HashService (that needs the Salt setting) to generate hashes for the password 10 | /// :warning: `ClearText` will allow your development environment to create and store clear text passwords in the database. Please only use this in development to speed up things. 11 | /// 12 | public HashServiceType HashServiceType { get; set; } = HashServiceType.ClearText; 13 | } 14 | public enum HashServiceType 15 | { 16 | Hash, 17 | ClearText 18 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/LocalFileStorageOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | /// 4 | /// Options for defining the FileService implementation 5 | /// 6 | public class LocalFileStorageOptions 7 | { 8 | /// 9 | /// Only relevant when `Type`=`LocalFileService`. 10 | /// This will be a relative path (`\notes`). 11 | /// Make sure you configure your container persistent storage on this path 12 | /// 13 | public Dictionary StoragePaths { get; set; } 14 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/MobileSecurityOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class MobileSecurityOptions 4 | { 5 | public bool LockDevice { get; set; } 6 | public string InvalidCredentialsErrorMessage { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/PollingStationsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class PollingStationsOptions 4 | { 5 | public bool OverrideDefaultSorting { get; set; } 6 | public string CodeOfFirstToDisplayCounty { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Options/S3StorageOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Options; 2 | 3 | public class S3StorageOptions 4 | { 5 | public string BucketName { get; set; } 6 | public int PresignedUrlExpirationInMinutes { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/PagingDefaultsConstants.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public static class PagingDefaultsConstants 4 | { 5 | public const int DEFAULT_PAGE_SIZE = 20; 6 | public const int DEFAULT_PAGE = 1; 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/PagingModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace VoteMonitor.Api.Core; 4 | 5 | public class PagingModel 6 | { 7 | protected int _page; 8 | protected int _pageSize; 9 | 10 | [FromQuery] 11 | public int Page 12 | { 13 | get => _page < 1 ? PagingDefaultsConstants.DEFAULT_PAGE : _page; 14 | set => _page = value < 1 ? PagingDefaultsConstants.DEFAULT_PAGE : value; 15 | } 16 | 17 | 18 | [FromQuery] 19 | public int PageSize 20 | { 21 | get => _pageSize < 1 ? PagingDefaultsConstants.DEFAULT_PAGE_SIZE : _pageSize; 22 | set => _pageSize = value < 1 ? PagingDefaultsConstants.DEFAULT_PAGE_SIZE : value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/PagingResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public class PagingResponseModel : PagingModel 4 | { 5 | protected int _totalItems; 6 | 7 | public int TotalItems 8 | { 9 | get { return _totalItems; } 10 | set { _totalItems = value; } 11 | } 12 | 13 | public int TotalPages 14 | { 15 | get { return 1 + (_totalItems - 1) / _pageSize; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/ClearTextService.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Services; 2 | 3 | public class ClearTextService : IHashService 4 | { 5 | public string GetHash(string clearString) => clearString; 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/ICacheService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Distributed; 2 | 3 | namespace VoteMonitor.Api.Core.Services; 4 | 5 | /// 6 | /// Interface for the caching service to be used. 7 | /// 8 | public interface ICacheService 9 | { 10 | Task GetOrSaveDataInCacheAsync(string name, Func> source, DistributedCacheEntryOptions options = null); 11 | Task GetObjectSafeAsync(string name); 12 | Task SaveObjectSafeAsync(string name, T value, DistributedCacheEntryOptions options = null); 13 | Task ClearAllValuesAsync(); 14 | 15 | Task RemoveValueAsync(string cacheKey); 16 | } 17 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/IFileService.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core.Models; 2 | 3 | namespace VoteMonitor.Api.Core.Services; 4 | 5 | /// 6 | /// Interface for the file service to be used 7 | /// 8 | public interface IFileService 9 | { 10 | /// 11 | /// Upload stream into file storage 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// the reference to the resource just uploaded 17 | Task UploadFromStreamAsync(Stream sourceStream, string contentType, string extension, UploadType uploadType); 18 | 19 | /// 20 | /// Gets pre-signed url for a given file 21 | /// 22 | /// 23 | /// 24 | string GetPreSignedUrl(string filename); 25 | } 26 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/IFirebaseService.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Services; 2 | 3 | public interface IFirebaseService 4 | { 5 | int Send(string from, string title, string message, List recipients); 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/IHashService.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core.Services; 2 | 3 | public interface IHashService 4 | { 5 | string GetHash(string clearString); 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/IPollingStationService.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core.Models; 2 | 3 | namespace VoteMonitor.Api.Core.Services; 4 | 5 | public interface IPollingStationService 6 | { 7 | Task GetPollingStationId(string countyCode, string municipalityCode, int pollingStationNumber); 8 | Task GetPollingStationByMunicipalityId(int municipalityId, int pollingStationNumber); 9 | Task> GetPollingStationsAssignmentsForAllCounties(bool? diaspora); 10 | } 11 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/NoCacheService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Distributed; 2 | 3 | namespace VoteMonitor.Api.Core.Services; 4 | 5 | public class NoCacheService : ICacheService 6 | { 7 | public async Task GetOrSaveDataInCacheAsync(string name, Func> source, 8 | DistributedCacheEntryOptions options = null) 9 | { 10 | return await source(); 11 | } 12 | 13 | public Task GetObjectSafeAsync(string name) => throw new NotImplementedException(); 14 | 15 | public Task SaveObjectSafeAsync(string name, T value, 16 | DistributedCacheEntryOptions options = null) => throw new NotImplementedException(); 17 | 18 | public Task RemoveValueAsync(string cacheKey) => Task.CompletedTask; 19 | 20 | public Task ClearAllValuesAsync() => Task.CompletedTask; 21 | } 22 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/Services/SHA256HashService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | using VoteMonitor.Api.Core.Options; 5 | 6 | namespace VoteMonitor.Api.Core.Services; 7 | 8 | /// 9 | public class SHA256HashService : IHashService 10 | { 11 | private readonly string _salt; 12 | 13 | public SHA256HashService(IOptions options) 14 | { 15 | _salt = options.Value.Salt; 16 | } 17 | 18 | public string GetHash(string clearString) 19 | { 20 | // SHA512 is disposable by inheritance. 21 | using (var sha256 = SHA256.Create()) 22 | { 23 | // Send a sample text to hash. 24 | var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(clearString + _salt)); 25 | // Get the hashed string. 26 | return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/UploadType.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Core; 2 | 3 | public enum UploadType 4 | { 5 | Notes, 6 | Observers 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Core/VoteMonitor.Api.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/CreateOrUpdateCounties.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace VoteMonitor.Api.County.Commands; 6 | 7 | public record CreateOrUpdateCounties(IFormFile File) : IRequest; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/CreateOrUpdateMunicipalities.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace VoteMonitor.Api.County.Commands; 6 | 7 | public record CreateOrUpdateMunicipalities(IFormFile File) : IRequest; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/CreateOrUpdateProvinces.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace VoteMonitor.Api.County.Commands; 6 | 7 | public record CreateOrUpdateProvinces(IFormFile File) : IRequest; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/PrefillCache.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.County.Commands; 4 | 5 | public record PrefillCache : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/UpdateCounty.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Commands; 6 | 7 | public record UpdateCounty : IRequest 8 | { 9 | public int CountyId { get; } 10 | public string Code { get; } 11 | public string Name { get; } 12 | public bool Diaspora { get; } 13 | public int Order { get; } 14 | 15 | public UpdateCounty(int countyId, UpdateCountyRequest county) 16 | { 17 | CountyId = countyId; 18 | Name = county.Name; 19 | Code = county.Code; 20 | Diaspora = county.Diaspora; 21 | Order = county.Order; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/UpdateMunicipality.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Commands; 6 | 7 | public record UpdateMunicipality : IRequest 8 | { 9 | public int MunicipalityId { get; } 10 | public string Code { get; } 11 | public string Name { get; } 12 | public int Order { get; } 13 | 14 | public UpdateMunicipality(int municipalityId, UpdateMunicipalityRequest county) 15 | { 16 | MunicipalityId = municipalityId; 17 | Name = county.Name; 18 | Code = county.Code; 19 | Order = county.Order; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Commands/UpdateProvince.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Commands; 6 | 7 | public record UpdateProvince: IRequest 8 | { 9 | public int ProvinceId { get; } 10 | public string Code { get; } 11 | public string Name { get; } 12 | public bool Diaspora { get; } 13 | public int Order { get; } 14 | 15 | public UpdateProvince(int provinceId, UpdateProvinceRequest county) 16 | { 17 | ProvinceId = provinceId; 18 | Name = county.Name; 19 | Code = county.Code; 20 | Order = county.Order; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Controllers/CacheController.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | using VoteMonitor.Api.County.Commands; 6 | 7 | namespace VoteMonitor.Api.County.Controllers; 8 | 9 | 10 | [ApiController] 11 | [Route("api/v1/locations-cache")] 12 | public class CacheController: ControllerBase 13 | { 14 | private readonly IMediator _mediator; 15 | 16 | public CacheController(IMediator mediator) 17 | { 18 | _mediator = mediator; 19 | } 20 | 21 | [Authorize("Organizer")] 22 | [HttpGet("prefill")] 23 | [ProducesResponseType(StatusCodes.Status204NoContent)] 24 | [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] 25 | public async Task PrefillCache() 26 | { 27 | await _mediator.Send(new PrefillCache()); 28 | return NoContent(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/CountiesUploadRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Microsoft.AspNetCore.Http; 3 | using VoteMonitor.Api.Core.Attributes; 4 | 5 | namespace VoteMonitor.Api.County.Models; 6 | 7 | public class CountiesUploadRequest 8 | { 9 | [Required(ErrorMessage = "Please select a file.")] 10 | [DataType(DataType.Upload)] 11 | [AllowedExtensions(new[] { ".csv" })] 12 | public IFormFile CsvFile { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/CountyCsvModel.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper.Configuration.Attributes; 2 | 3 | namespace VoteMonitor.Api.County.Models; 4 | 5 | public class CountyCsvModel 6 | { 7 | [Index(0)] 8 | public int Id { get; set; } 9 | 10 | [Index(1)] 11 | public string Code { set; get; } 12 | 13 | [Index(2)] 14 | public string Name { get; set; } 15 | 16 | [Index(3)] 17 | public string ProvinceCode { get; set; } 18 | 19 | [Index(4)] 20 | public bool Diaspora { get; set; } 21 | 22 | [Index(5)] 23 | public int Order { get; set; } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/CountyModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.County.Models; 2 | 3 | public class CountyModel 4 | { 5 | public int Id { get; set; } 6 | public string Code { get; set; } 7 | public string ProvinceCode { get; set; } 8 | public int NumberOfPollingStations { get; set; } 9 | public string Name { get; set; } 10 | public bool Diaspora { get; set; } 11 | public int Order { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/ErrorModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.County.Models; 2 | 3 | public class ErrorModel 4 | { 5 | public string Message { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/MunicipalitiesUploadRequest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System.ComponentModel.DataAnnotations; 3 | using VoteMonitor.Api.Core.Attributes; 4 | 5 | namespace VoteMonitor.Api.County.Models; 6 | 7 | public class MunicipalitiesUploadRequest 8 | { 9 | [Required(ErrorMessage = "Please select a file.")] 10 | [DataType(DataType.Upload)] 11 | [AllowedExtensions(new[] { ".csv" })] 12 | public IFormFile CsvFile { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/MunicipalityCsvModel.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper.Configuration.Attributes; 2 | 3 | namespace VoteMonitor.Api.County.Models; 4 | 5 | public class MunicipalityCsvModel 6 | { 7 | [Index(0)] 8 | public int Id { get; set; } 9 | 10 | [Index(1)] 11 | public string CountyCode { set; get; } 12 | 13 | [Index(2)] 14 | public string Code { set; get; } 15 | 16 | [Index(3)] 17 | public string Name { get; set; } 18 | 19 | [Index(4)] 20 | public int Order { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/MunicipalityModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.County.Models; 2 | 3 | public class MunicipalityModel 4 | { 5 | public int Id { get; set; } 6 | public string CountyCode { get; set; } 7 | public string Code { get; set; } 8 | public string Name { get; set; } 9 | public int NumberOfPollingStations { get; set; } 10 | public int Order { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/MunicipalityModelV2.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.County.Models; 2 | 3 | public class MunicipalityModelV2 4 | { 5 | public int Id { get; set; } 6 | 7 | public string Code { get; set; } 8 | public string Name { get; set; } 9 | public int NumberOfPollingStations { get; set; } 10 | public int Order { get; set; } 11 | 12 | public int CountyId { get; set; } 13 | public string CountyCode { get; set; } 14 | public string CountyName { get; set; } 15 | public bool Diaspora { get; set; } 16 | public int CountyOrder { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/ProvinceCsvModel.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper.Configuration.Attributes; 2 | 3 | namespace VoteMonitor.Api.County.Models; 4 | 5 | public class ProvinceCsvModel 6 | { 7 | [Index(0)] 8 | public int Id { get; set; } 9 | 10 | [Index(1)] 11 | public string Code { set; get; } 12 | 13 | [Index(2)] 14 | public string Name { get; set; } 15 | 16 | [Index(3)] 17 | public int Order { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/ProvinceModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.County.Models; 2 | 3 | public class ProvinceModel 4 | { 5 | public int Id { get; set; } 6 | public string Code { get; set; } 7 | public string Name { get; set; } 8 | public int Order { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/ProvincesUploadRequest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System.ComponentModel.DataAnnotations; 3 | using VoteMonitor.Api.Core.Attributes; 4 | 5 | namespace VoteMonitor.Api.County.Models; 6 | 7 | public class ProvincesUploadRequest 8 | { 9 | [Required(ErrorMessage = "Please select a file.")] 10 | [DataType(DataType.Upload)] 11 | [AllowedExtensions(new[] { ".csv" })] 12 | public IFormFile CsvFile { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/UpdateCountyRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.County.Models; 4 | 5 | public class UpdateCountyRequest 6 | { 7 | [Required] 8 | [StringLength(100)] 9 | public string Name { get; set; } 10 | 11 | [Required] 12 | [StringLength(20)] 13 | public string Code { get; set; } 14 | 15 | public bool Diaspora { get; set; } 16 | public int Order { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/UpdateMunicipalityRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.County.Models; 4 | 5 | public class UpdateMunicipalityRequest 6 | { 7 | [Required] 8 | [StringLength(100)] 9 | public string Name { get; set; } 10 | 11 | [Required] 12 | [StringLength(20)] 13 | public string Code { get; set; } 14 | 15 | public int Order { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Models/UpdateProvinceRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.County.Models; 4 | 5 | public class UpdateProvinceRequest 6 | { 7 | [Required] 8 | [StringLength(100)] 9 | public string Name { get; set; } 10 | 11 | [Required] 12 | [StringLength(20)] 13 | public string Code { get; set; } 14 | 15 | public int Order { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetAllCounties.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetAllCounties : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetAllCountiesByProvinceCode.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetAllCountiesByProvinceCode(string ProvinceCode) : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetAllMunicipalities.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetAllMunicipalities : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetAllMunicipalitiesByCountyCode.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetAllMunicipalitiesByCountyCode(string CountyCode) : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetAllProvinces.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetAllProvinces : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetCountiesForExport.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetCountiesForExport : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetCountyById.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetCountyById(int CountyId) : IRequest>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetMunicipalitiesForExport.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetMunicipalitiesForExport : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetMunicipalityById.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetMunicipalityById(int MunicipalityId) : IRequest>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetProvinceById.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetProvinceById(int ProvinceId) : IRequest>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/Queries/GetProvincesForExport.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.County.Models; 4 | 5 | namespace VoteMonitor.Api.County.Queries; 6 | 7 | public record GetProvincesForExport : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.County/VoteMonitor.Api.County.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Controllers/DataExportV2Controller.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using VoteMonitor.Api.DataExport.FileGenerator; 5 | using VoteMonitor.Api.DataExport.Queries; 6 | 7 | namespace VoteMonitor.Api.DataExport.Controllers; 8 | 9 | [Route("api/v2/export")] 10 | public class DataExportV2Controller : Controller 11 | { 12 | private readonly IMediator _mediator; 13 | 14 | public DataExportV2Controller(IMediator mediator) 15 | { 16 | _mediator = mediator; 17 | } 18 | 19 | /// 20 | /// Exports all data in excel file 21 | /// 22 | /// 23 | [HttpGet("all")] 24 | [Authorize("Organizer")] 25 | public async Task GetAllData() 26 | { 27 | 28 | var data = await _mediator.Send(new GetExcelDbCommand()); 29 | 30 | 31 | return File( 32 | fileContents: data, 33 | contentType: ExcelUtility.EXCEL_MEDIA_TYPE, 34 | fileDownloadName: "data.xlsx" 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/FileGenerator/CsvGenerator.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper; 2 | using System.Globalization; 3 | 4 | namespace VoteMonitor.Api.DataExport.FileGenerator; 5 | 6 | public class CsvGenerator : ICsvGenerator 7 | { 8 | public byte[] Export(IEnumerable exportData, string fileName) 9 | { 10 | using (var mem = new MemoryStream()) 11 | using (var writer = new StreamWriter(mem)) 12 | using (var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture)) 13 | { 14 | csvWriter.WriteRecords(exportData); 15 | 16 | writer.Flush(); 17 | return mem.ToArray(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/FileGenerator/CsvUtility.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.DataExport.FileGenerator; 2 | 3 | public class CsvUtility 4 | { 5 | public const string CSV_MEDIA_TYPE = "text/csv"; // ref: https://stackoverflow.com/questions/7076042/what-mime-type-should-i-use-for-csv > https://tools.ietf.org/html/rfc7111 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/FileGenerator/ExcelUtility.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.DataExport.FileGenerator; 2 | 3 | public class ExcelUtility 4 | { 5 | public const string DATETIME_FORMAT = "dd/MM/yyyy hh:mm:ss"; 6 | public const string EXCEL_MEDIA_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 7 | 8 | 9 | #region DataType available for Excel Export 10 | public const string STRING = "string"; 11 | public const string INT32 = "int32"; 12 | public const string DOUBLE = "double"; 13 | public const string DATETIME = "datetime"; 14 | public const string BOOLEAN = "boolean"; 15 | #endregion 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/FileGenerator/ICsvGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.DataExport.FileGenerator; 2 | 3 | public interface ICsvGenerator 4 | { 5 | byte[] Export(IEnumerable exportData, string fileName); 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Handlers/CsvGeneratorQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.DataExport.FileGenerator; 3 | using VoteMonitor.Api.DataExport.Queries; 4 | 5 | namespace VoteMonitor.Api.DataExport.Handlers; 6 | 7 | public class CsvGeneratorQueryHandler : IRequestHandler, 8 | IRequestHandler 9 | { 10 | private readonly ICsvGenerator _csvGenerator; 11 | 12 | public CsvGeneratorQueryHandler() 13 | { 14 | _csvGenerator = new CsvGenerator(); 15 | } 16 | 17 | public Task Handle(GenerateCSVFile request, CancellationToken cancellationToken) 18 | { 19 | var fileContents = _csvGenerator.Export(request.Data, "myData"); 20 | 21 | return Task.FromResult(fileContents); 22 | } 23 | 24 | public Task Handle(GenerateNotesCSVFile request, CancellationToken cancellationToken) 25 | { 26 | var fileContents = _csvGenerator.Export(request.Data, "notes"); 27 | 28 | return Task.FromResult(fileContents); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Models/ExportModelDto.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.DataExport.Models; 2 | 3 | /// 4 | /// data transfer object for ExportModel on DataExportQueryHandler 5 | /// Based on ExportModel entity, hide not necessary Id field 6 | /// 7 | public class ExportModelDto 8 | { 9 | public string ObserverPhone { get; set; } 10 | public int IdNgo { get; set; } 11 | public string FormCode { get; set; } 12 | public string QuestionText { get; set; } 13 | public string OptionText { get; set; } 14 | public string AnswerFreeText { get; set; } 15 | public DateTime LastModified { get; set; } 16 | public string CountyCode { get; set; } 17 | public int PollingStationNumber { get; set; } 18 | public bool HasNotes => NumberOfNotes > 0; 19 | public int NumberOfNotes { get; set; } 20 | public bool HasAttachments => NumberOfAttachments > 0; 21 | public int NumberOfAttachments { get; set; } 22 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Models/NotesExportModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.DataExport.Models; 2 | 3 | public class NotesExportModel 4 | { 5 | public string ObserverPhone { get; set; } 6 | public int IdNgo { get; set; } 7 | public string FormCode { get; set; } 8 | public string QuestionText { get; set; } 9 | public string OptionText { get; set; } 10 | public string AnswerFreeText { get; set; } 11 | public string NoteText { get; set; } 12 | public string NoteAttachmentPath { get; set; } 13 | public DateTime? LastModified { get; set; } 14 | public string CountyCode { get; set; } 15 | public int PollingStationNumber { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Queries/GenerateCSVFile.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.DataExport.Models; 3 | 4 | namespace VoteMonitor.Api.DataExport.Queries; 5 | 6 | public class GenerateCSVFile : IRequest 7 | { 8 | public IReadOnlyCollection Data { get; } 9 | 10 | public GenerateCSVFile(IEnumerable data) 11 | { 12 | Data = data.ToList().AsReadOnly(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Queries/GenerateNotesCSVFile.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.DataExport.Models; 3 | 4 | namespace VoteMonitor.Api.DataExport.Queries; 5 | 6 | public class GenerateNotesCSVFile : IRequest 7 | { 8 | public IReadOnlyCollection Data { get; } 9 | 10 | public GenerateNotesCSVFile(IEnumerable data) 11 | { 12 | Data = data.ToList().AsReadOnly(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Queries/GetDataForExport.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.DataExport.Models; 3 | 4 | namespace VoteMonitor.Api.DataExport.Queries; 5 | 6 | public class GetDataForExport : IRequest> 7 | { 8 | public int? NgoId { get; set; } 9 | public int? ObserverId { get; set; } 10 | public int? PollingStationNumber { get; set; } 11 | public string County { get; set; } 12 | public DateTime? From { get; set; } 13 | public DateTime? To { get; set; } 14 | 15 | public bool ApplyFilters => NgoId.HasValue || ObserverId.HasValue || PollingStationNumber.HasValue || 16 | From.HasValue || To.HasValue || !string.IsNullOrEmpty(County); 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Queries/GetExcelDbCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.DataExport.Queries; 4 | 5 | public record GetExcelDbCommand : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/Queries/GetNotesForExport.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.DataExport.Models; 3 | 4 | namespace VoteMonitor.Api.DataExport.Queries; 5 | 6 | public class GetNotesForExport : IRequest> 7 | { 8 | public int? NgoId { get; set; } 9 | public int? ObserverId { get; set; } 10 | public int? PollingStationNumber { get; set; } 11 | public string County { get; set; } 12 | public DateTime? From { get; set; } 13 | public DateTime? To { get; set; } 14 | 15 | public bool ApplyFilters => NgoId.HasValue || ObserverId.HasValue || PollingStationNumber.HasValue || 16 | From.HasValue || To.HasValue || !string.IsNullOrEmpty(County); 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.DataExport/VoteMonitor.Api.DataExport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/CommandHandlers/AddFormCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Commands; 3 | using VoteMonitor.Api.Form.Mappers; 4 | using VoteMonitor.Api.Form.Models; 5 | using VoteMonitor.Entities; 6 | 7 | namespace VoteMonitor.Api.Form.CommandHandlers; 8 | 9 | public class AddFormCommandHandler : IRequestHandler 10 | { 11 | private readonly VoteMonitorContext _context; 12 | private readonly IEntityMapper _entityMapper; 13 | 14 | public AddFormCommandHandler(VoteMonitorContext context, IEntityMapper entityMapper) 15 | { 16 | _context = context; 17 | _entityMapper = entityMapper; 18 | } 19 | 20 | public async Task Handle(AddFormCommand message, CancellationToken cancellationToken) 21 | { 22 | Entities.Form form = null; 23 | _entityMapper.Map(ref form, message.Form); 24 | 25 | _context.Forms.Add(form); 26 | 27 | await _context.SaveChangesAsync(); 28 | message.Form.Id = form.Id; 29 | return message.Form; 30 | } 31 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/CommandHandlers/AddOptionCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Commands; 3 | using VoteMonitor.Api.Form.Models.Options; 4 | using VoteMonitor.Entities; 5 | 6 | namespace VoteMonitor.Api.Form.CommandHandlers; 7 | 8 | public class AddOptionCommandHandler : IRequestHandler 9 | { 10 | private readonly VoteMonitorContext _context; 11 | 12 | public AddOptionCommandHandler(VoteMonitorContext context) 13 | { 14 | _context = context; 15 | } 16 | 17 | public async Task Handle(AddOptionCommand request, CancellationToken cancellationToken) 18 | { 19 | var optionEntity = new Option { Text = request.Text, Hint = request.Hint, IsFreeText = request.IsFreeText, }; 20 | 21 | _context.Options.Add(optionEntity); 22 | await _context.SaveChangesAsync(cancellationToken); 23 | 24 | return new OptionDTO 25 | { 26 | Id = optionEntity.Id, 27 | Hint = optionEntity.Hint, 28 | IsFreeText = optionEntity.IsFreeText, 29 | Text = optionEntity.Text 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/AddFormCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Commands; 5 | 6 | public record AddFormCommand(FormDTO Form) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/AddOptionCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models.Options; 3 | 4 | namespace VoteMonitor.Api.Form.Commands; 5 | 6 | public record AddOptionCommand(string Text, string Hint, bool IsFreeText) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/DeleteFormCommand.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.Form.Commands; 5 | 6 | public enum DeleteFormErrorType 7 | { 8 | FormNotFound, 9 | FormHasAnswers, 10 | FormNotDraft, 11 | ErrorOccurred 12 | } 13 | 14 | public record DeleteFormCommand(int FormId) : IRequest>; 15 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/DeleteQuestionCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Form.Commands; 4 | 5 | public record DeleteQuestionCommand(int SectionId, int QuestionId) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/DeleteSectionCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Form.Commands; 4 | 5 | public record DeleteSectionCommand(int SectionId) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/UpdateFormCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Commands; 5 | 6 | public record UpdateFormCommand(FormDTO Form, int Id) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Commands/UpdateOptionCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Form.Commands; 4 | 5 | public record UpdateOptionCommand(int OptionId, string Text, string Hint, bool IsFreeText) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Controllers/ExportController.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using VoteMonitor.Api.Form.Models; 5 | using VoteMonitor.Api.Form.Queries; 6 | 7 | namespace VoteMonitor.Api.Form.Controllers; 8 | 9 | [Route("api/v1/export")] 10 | public class ExportController : Controller 11 | { 12 | private readonly IMediator _mediator; 13 | 14 | public ExportController(IMediator mediator) 15 | { 16 | _mediator = mediator; 17 | } 18 | 19 | [HttpPost("forms")] 20 | [Authorize("Organizer")] 21 | public async Task> ExportFormsAsync() 22 | { 23 | var filledInForms = await _mediator.Send(new GetFormsQuery()); 24 | return filledInForms; 25 | } 26 | 27 | [HttpPost("filled-forms")] 28 | [Authorize("Organizer")] 29 | public async Task> ExportFilledFormsAsync() 30 | { 31 | var filledInForms = await _mediator.Send(new GetFilledFormsQuery()); 32 | return filledInForms; 33 | } 34 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Mappers/IEntityMapper.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Form.Mappers; 2 | 3 | public interface IEntityMapper 4 | { 5 | void Map(ref TEntity entity, TDto dto); 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Mappers/IUpdateOrCreateEntityMapper.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Form.Mappers; 2 | 3 | public interface IUpdateOrCreateEntityMapper : IEntityMapper 4 | { 5 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Mappers/UpdateOrCreateEntityMapper.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace VoteMonitor.Api.Form.Mappers; 4 | 5 | public class UpdateOrCreateEntityMapper : IUpdateOrCreateEntityMapper 6 | { 7 | private readonly IMapper _mapper; 8 | 9 | public UpdateOrCreateEntityMapper(IMapper mapper) 10 | { 11 | _mapper = mapper; 12 | } 13 | 14 | public virtual void Map(ref TEntity entity, TDto dto) 15 | { 16 | // If the entity is new in the dto (entity is null), we just map it 17 | // Otherwise, we update all properties except the hierarchy part(Excluded from profile). 18 | entity = entity == null 19 | ? _mapper.Map(dto) 20 | : _mapper.Map(dto, entity); 21 | } 22 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/FilledFormResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Form.Models; 2 | 3 | public class FilledFormResponseModel 4 | { 5 | public int FormId { get; set; } 6 | public int ObserverId { get; set; } 7 | public IReadOnlyList FilledInQuestions { get; set; } 8 | /// 9 | /// Last date when an answer from a form was modified. 10 | /// 11 | public DateTime LastModified { get; set; } 12 | 13 | public int PollingStationId { get; set; } 14 | } 15 | 16 | public class AnswerResponseModel 17 | { 18 | public int QuestionId { get; set; } 19 | public int FormSectionId { get; set; } 20 | public IReadOnlyList SelectedOptions { get; set; } 21 | } 22 | 23 | public class SelectedOptionResponseModel 24 | { 25 | public int Id { get; set; } 26 | public int OptionId { get; set; } 27 | public string FreeTextValue { get; set; } 28 | public DateTime LasModified { get; set; } 29 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/FormDTO.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using VoteMonitor.Entities; 3 | 4 | namespace VoteMonitor.Api.Form.Models; 5 | 6 | public class FormDTO : IHierarchicalEntity 7 | { 8 | public int Id { get; set; } 9 | public string Code { get; set; } 10 | public int CurrentVersion { get; set; } 11 | public string Description { get; set; } 12 | public ICollection FormSections { get; set; } 13 | public bool Diaspora { get; set; } 14 | public bool Draft { get; set; } 15 | public int Order { get; set; } 16 | 17 | [JsonIgnore] 18 | [Newtonsoft.Json.JsonIgnore] 19 | public ICollection Children { get => FormSections; set => FormSections = value; } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/FormDetailsModel.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace VoteMonitor.Api.Form.Models; 4 | 5 | public class FormDetailsModel 6 | { 7 | [JsonPropertyName("id")] 8 | public int Id { get; set; } 9 | 10 | [JsonPropertyName("code")] 11 | public string Code { get; set; } 12 | 13 | [JsonPropertyName("description")] 14 | public string Description { get; set; } 15 | 16 | [JsonPropertyName("currentVersion")] 17 | public int CurrentVersion { get; set; } 18 | 19 | // quick and dirty fix to sync iOS and Android 20 | [JsonPropertyName("ver")] 21 | public int CurrentVersionOld { get; set; } 22 | 23 | [JsonPropertyName("diaspora")] 24 | public bool Diaspora { get; set; } 25 | 26 | [JsonPropertyName("order")] 27 | public int Order { get; set; } 28 | 29 | [JsonPropertyName("draft")] 30 | public bool Draft { get; set; } 31 | } 32 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/FormSectionDTO.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using VoteMonitor.Entities; 3 | 4 | namespace VoteMonitor.Api.Form.Models; 5 | 6 | public class FormSectionDTO : IHierarchicalEntity, IIdentifiableEntity 7 | { 8 | public FormSectionDTO() 9 | { 10 | Questions = new List(); 11 | } 12 | public string UniqueId { get; set; } 13 | public int Id { get; set; } 14 | public string Code { get; set; } 15 | public string Description { get; set; } 16 | public int OrderNumber { get; set; } 17 | public ICollection Questions { get; set; } 18 | 19 | [JsonIgnore] 20 | [Newtonsoft.Json.JsonIgnore] 21 | ICollection IHierarchicalEntity.Children { get => Questions; set => Questions = value; } 22 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/FormVersionsModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Form.Models; 2 | 3 | public class FormVersionsModel 4 | { 5 | public List FormVersions { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/OptionToQuestionDTO.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Entities; 2 | 3 | namespace VoteMonitor.Api.Form.Models; 4 | 5 | public class OptionToQuestionDTO : IIdentifiableEntity 6 | { 7 | public int Id { get; set; } 8 | public int OptionId { get; set; } 9 | public string Text { get; set; } 10 | public bool IsFreeText { get; set; } 11 | public bool Flagged { get; set; } 12 | public int OrderNumber { get; set; } 13 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/Options/CreateOptionModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Form.Models.Options; 4 | 5 | public class CreateOptionModel 6 | { 7 | [Required] 8 | public bool IsFreeText { get; set; } 9 | 10 | [Required(AllowEmptyStrings = false), MaxLength(1000)] 11 | public string Text { get; set; } 12 | 13 | public string Hint { get; set; } 14 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/Options/OptionDTO.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Form.Models.Options; 2 | 3 | public class OptionDTO 4 | { 5 | public int Id { get; set; } 6 | public bool IsFreeText { get; set; } 7 | public string Text { get; set; } 8 | public string Hint { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/Options/OptionModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Form.Models.Options; 4 | 5 | public class OptionModel 6 | { 7 | [Required] 8 | public int Id { get; set; } 9 | 10 | [Required] 11 | public bool IsFreeText { get; set; } 12 | 13 | [Required(AllowEmptyStrings = false), MaxLength(1000)] 14 | public string Text { get; set; } 15 | 16 | public string Hint { get; set; } 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Models/QuestionDTO.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using VoteMonitor.Entities; 3 | 4 | namespace VoteMonitor.Api.Form.Models; 5 | 6 | public class QuestionDTO : IHierarchicalEntity, IIdentifiableEntity 7 | { 8 | public QuestionDTO() 9 | { 10 | OptionsToQuestions = new List(); 11 | } 12 | 13 | public int Id { get; set; } 14 | public string FormCode { get; set; } 15 | public string Code { get; set; } 16 | public int IdSection { get; set; } 17 | public QuestionType QuestionType { get; set; } 18 | public string Text { get; set; } 19 | public string Hint { get; set; } 20 | public int OrderNumber { get; set; } 21 | 22 | public ICollection OptionsToQuestions { get; set; } 23 | 24 | [JsonIgnore] 25 | [Newtonsoft.Json.JsonIgnore] 26 | public ICollection Children { get => OptionsToQuestions; set => OptionsToQuestions = value; } 27 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Profiles/FormProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Profiles; 5 | 6 | public class FormProfile : Profile 7 | { 8 | public FormProfile() 9 | { 10 | CreateMap() 11 | .ForMember(dest => dest.FormSections, c => c.Ignore()) 12 | .ForMember(dest => dest.Id, c => c.Ignore()); 13 | } 14 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Profiles/FormSectionProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VoteMonitor.Api.Form.Models; 3 | using VoteMonitor.Entities; 4 | 5 | namespace VoteMonitor.Api.Form.Profiles; 6 | 7 | public class FormSectionProfile : Profile 8 | { 9 | public FormSectionProfile() 10 | { 11 | CreateMap() 12 | .ForMember(dest => dest.Questions, c => c.Ignore()) 13 | .ForMember(dest => dest.Id, c => c.Ignore()) 14 | .ForMember(dest => dest.IdForm, c => c.Ignore()); 15 | } 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Profiles/OptionProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VoteMonitor.Api.Form.Models.Options; 3 | using VoteMonitor.Entities; 4 | 5 | namespace VoteMonitor.Api.Form.Profiles; 6 | 7 | public class OptionProfile : Profile 8 | { 9 | public OptionProfile() 10 | { 11 | CreateMap(MemberList.Source); 12 | CreateMap(MemberList.Source); 13 | 14 | CreateMap(MemberList.Source); 15 | 16 | CreateMap().ForMember(o => o.OptionsToQuestions, c => c.Ignore()); 17 | 18 | CreateMap() 19 | .ForMember(x => x.Id, c => c.Ignore()); 20 | } 21 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Profiles/QuestionProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VoteMonitor.Api.Form.Models; 3 | using VoteMonitor.Entities; 4 | 5 | namespace VoteMonitor.Api.Form.Profiles; 6 | 7 | public class QuestionProfile : Profile 8 | { 9 | public QuestionProfile() 10 | { 11 | CreateMap() 12 | .ForMember(dest => dest.OptionsToQuestions, c => c.MapFrom(src => src.OptionsToQuestions)); 13 | 14 | CreateMap() 15 | .ForMember(dest => dest.OptionsToQuestions, c => c.Ignore()) 16 | .ForMember(dest => dest.Id, c => c.Ignore()) 17 | .ForMember(dest => dest.IdSection, c => c.Ignore()); 18 | } 19 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/ExistsFormByCodeOrIdQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Form.Queries; 4 | 5 | public record ExistsFormByCodeOrIdQuery(int Id, string Code) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/ExistsFormByIdQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Form.Queries; 4 | 5 | public record GetFormExistsByIdQuery(int Id) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/FetchAllOptionsQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models.Options; 3 | 4 | namespace VoteMonitor.Api.Form.Queries; 5 | 6 | public record FetchAllOptionsQuery : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/FormQuestionQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Queries; 5 | 6 | public record FormQuestionQuery(int FormId, int CacheHours, int CacheMinutes, int CacheSeconds) : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/FormVersionQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Queries; 5 | 6 | public record FormVersionQuery(bool? Diaspora, bool? Draft) : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/GetFilledFormsQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Queries; 5 | 6 | public record GetFilledFormsQuery: IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/GetFormsQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models; 3 | 4 | namespace VoteMonitor.Api.Form.Queries; 5 | 6 | public record GetFormsQuery: IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/Queries/GetOptionByIdCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models.Options; 3 | 4 | namespace VoteMonitor.Api.Form.Queries; 5 | 6 | public record GetOptionByIdQuery(int OptionId) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/QueryHandlers/FetchAllOptionsQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.EntityFrameworkCore; 3 | using VoteMonitor.Api.Form.Models.Options; 4 | using VoteMonitor.Api.Form.Queries; 5 | using VoteMonitor.Entities; 6 | 7 | namespace VoteMonitor.Api.Form.QueryHandlers; 8 | 9 | public class FetchAllOptionsQueryHandler : IRequestHandler> 10 | { 11 | private readonly VoteMonitorContext _context; 12 | 13 | public FetchAllOptionsQueryHandler(VoteMonitorContext context) 14 | { 15 | _context = context; 16 | } 17 | 18 | public async Task> Handle(FetchAllOptionsQuery request, CancellationToken cancellationToken) 19 | { 20 | return await _context.Options.Select(x => new OptionDTO 21 | { 22 | Hint = x.Hint, 23 | Id = x.Id, 24 | IsFreeText = x.IsFreeText, 25 | Text = x.Text, 26 | }).ToListAsync(cancellationToken); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/QueryHandlers/GetFormExistsByCodeOrIdQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.EntityFrameworkCore; 3 | using VoteMonitor.Api.Form.Queries; 4 | using VoteMonitor.Entities; 5 | 6 | namespace VoteMonitor.Api.Form.QueryHandlers; 7 | 8 | public class GetFormExistsByCodeOrIdQueryHandler : IRequestHandler 9 | { 10 | private readonly VoteMonitorContext _context; 11 | public GetFormExistsByCodeOrIdQueryHandler(VoteMonitorContext voteMonitorContext) 12 | { 13 | _context = voteMonitorContext; 14 | } 15 | 16 | public async Task Handle(ExistsFormByCodeOrIdQuery request, CancellationToken cancellationToken) 17 | { 18 | return await _context.Forms.AnyAsync(form => form.Code == request.Code || form.Id == request.Id); 19 | } 20 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/QueryHandlers/GetFormExistsByIdQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.EntityFrameworkCore; 3 | using VoteMonitor.Api.Form.Queries; 4 | using VoteMonitor.Entities; 5 | 6 | namespace VoteMonitor.Api.Form.QueryHandlers; 7 | 8 | public class GetFormExistsByIdQueryHandler : IRequestHandler 9 | { 10 | private readonly VoteMonitorContext _context; 11 | 12 | public GetFormExistsByIdQueryHandler(VoteMonitorContext voteMonitorContext) 13 | { 14 | _context = voteMonitorContext; 15 | } 16 | 17 | public Task Handle(GetFormExistsByIdQuery request, CancellationToken cancellationToken) 18 | { 19 | return _context.Forms.AnyAsync(form => form.Id == request.Id, cancellationToken); 20 | } 21 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/QueryHandlers/GetOptionByIdQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Form.Models.Options; 3 | using VoteMonitor.Api.Form.Queries; 4 | using VoteMonitor.Entities; 5 | 6 | namespace VoteMonitor.Api.Form.QueryHandlers; 7 | 8 | public class GetOptionByIdQueryHandler : IRequestHandler 9 | 10 | { 11 | private readonly VoteMonitorContext _context; 12 | 13 | public GetOptionByIdQueryHandler(VoteMonitorContext context) 14 | { 15 | _context = context; 16 | } 17 | 18 | public Task Handle(GetOptionByIdQuery request, CancellationToken cancellationToken) 19 | { 20 | var option = _context.Options.FirstOrDefault(c => c.Id == request.OptionId); 21 | 22 | var optionDto = new OptionDTO 23 | { 24 | Id = option.Id, Hint = option.Hint, IsFreeText = option.IsFreeText, Text = option.Text 25 | }; 26 | 27 | return Task.FromResult(optionDto); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Form/VoteMonitor.Api.Form.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Commands/ImportPollingStationsCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Http; 3 | using VoteMonitor.Api.Location.Models.ResultValues; 4 | 5 | namespace VoteMonitor.Api.Location.Commands; 6 | 7 | public record ImportPollingStationsCommand(IFormFile File) : IRequest; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Commands/PrefillCache.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Location.Commands; 4 | 5 | public record PrefillCache : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Commands/RegisterPollingStationCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Location.Commands; 4 | 5 | public class RegisterPollingStationCommand : IRequest 6 | { 7 | public int IdObserver { get; set; } 8 | public int PollingStationNumber { get; set; } 9 | public string CountyCode { get; set; } 10 | public string MunicipalityCode { get; set; } 11 | public DateTime? ObserverArrivalTime { get; set; } 12 | public DateTime? ObserverLeaveTime { get; set; } 13 | public int NumberOfVotersOnTheList { get; set; } 14 | public int NumberOfCommissionMembers { get; set; } 15 | public int NumberOfFemaleMembers { get; set; } 16 | public int MinPresentMembers { get; set; } 17 | public bool ChairmanPresence { get; set; } 18 | public bool SinglePollingStationOrCommission { get; set; } 19 | public bool AdequatePollingStationSize { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Commands/UpdatePollingSectionCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Location.Commands; 4 | 5 | public class UpdatePollingSectionCommand : IRequest 6 | { 7 | public int ObserverId { get; set; } 8 | public int PollingStationId { get; set; } 9 | public DateTime ObserverLeaveTime { get; set; } 10 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Controllers/CacheController.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using VoteMonitor.Api.Location.Commands; 5 | 6 | namespace VoteMonitor.Api.Location.Controllers; 7 | 8 | [ApiController] 9 | [Route("api/v1/polling-stations-cache")] 10 | public class CacheController : ControllerBase 11 | { 12 | private readonly IMediator _mediator; 13 | 14 | public CacheController(IMediator mediator) 15 | { 16 | _mediator = mediator; 17 | } 18 | 19 | [HttpGet] 20 | [Route("prefill")] 21 | [Authorize("Organizer")] 22 | public async Task PrefillCache() 23 | { 24 | await _mediator.Send(new PrefillCache()); 25 | return NoContent(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Exceptions/PollingStationImportException.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Location.Exceptions; 2 | 3 | public class PollingStationImportException : Exception 4 | { 5 | public PollingStationImportException(string message) : base(message) 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Handlers/PollingStationQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core.Services; 3 | using VoteMonitor.Api.Location.Queries; 4 | 5 | namespace VoteMonitor.Api.Location.Handlers; 6 | 7 | public class PollingStationQueryHandler : IRequestHandler 8 | { 9 | private readonly IPollingStationService _pollingStationService; 10 | 11 | public PollingStationQueryHandler(IPollingStationService pollingStationService) 12 | { 13 | _pollingStationService = pollingStationService; 14 | } 15 | 16 | public async Task Handle(GetPollingStationId message, CancellationToken cancellationToken) 17 | { 18 | return await _pollingStationService.GetPollingStationId(message.CountyCode, message.MunicipalityCode, message.PollingStationNumber); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Models/PollingStationCsvModel.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper.Configuration.Attributes; 2 | 3 | namespace VoteMonitor.Api.Location.Models; 4 | 5 | public class PollingStationCsvModel 6 | { 7 | [Index(0)] 8 | public string MunicipalityCode { get; set; } 9 | 10 | [Index(1)] 11 | public string Address { get; set; } 12 | 13 | [Index(2)] 14 | public int Number { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Models/PollingStationsUploadRequest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System.ComponentModel.DataAnnotations; 3 | using VoteMonitor.Api.Core.Attributes; 4 | 5 | namespace VoteMonitor.Api.Location.Models; 6 | 7 | public class PollingStationsUploadRequest 8 | { 9 | [Required(ErrorMessage = "Please select a file.")] 10 | [DataType(DataType.Upload)] 11 | [AllowedExtensions(new string[] { ".csv" })] 12 | public IFormFile CsvFile { get; set; } 13 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Models/ResultValues/PollingStationImportErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Location.Models.ResultValues; 2 | 3 | public enum PollingStationImportErrorCode 4 | { 5 | GenericError = -1, 6 | CountyNotFound = -2 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Models/UpdatePollingStationInfo.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Location.Models; 4 | 5 | public class UpdatePollingStationInfo 6 | { 7 | [Required(AllowEmptyStrings = false)] 8 | public string CountyCode { get; set; } 9 | 10 | [Required(AllowEmptyStrings = false)] 11 | public string MunicipalityCode { get; set; } 12 | 13 | [Required(AllowEmptyStrings = false)] 14 | public int PollingStationNumber { get; set; } 15 | 16 | [Required(AllowEmptyStrings = false)] 17 | public DateTime? ObserverLeaveTime { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Queries/GetPollingStationId.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Location.Queries; 4 | 5 | public record GetPollingStationId(string CountyCode, string MunicipalityCode, int PollingStationNumber) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/Queries/PollingStationsAssignmentQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core.Models; 3 | 4 | namespace VoteMonitor.Api.Location.Queries; 5 | 6 | public record PollingStationsAssignmentQuery(bool? Diaspora) : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Location/VoteMonitor.Api.Location.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/CreateNgo.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Commands; 6 | 7 | public class CreateNgo : IRequest 8 | { 9 | public NgoModel Ngo { get; } 10 | 11 | public CreateNgo(CreateUpdateNgoModel ngo) 12 | { 13 | Ngo = new NgoModel 14 | { 15 | Name = ngo.Name, 16 | IsActive = ngo.IsActive, 17 | Organizer = ngo.Organizer, 18 | ShortName = ngo.ShortName 19 | }; 20 | } 21 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/CreateNgoAdmin.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Commands; 6 | 7 | public class CreateNgoAdmin : IRequest 8 | { 9 | public NgoAdminModel NgoAdmin { get; } 10 | public CreateNgoAdmin(int idNgo, CreateUpdateNgoAdminModel admin) 11 | { 12 | NgoAdmin = new NgoAdminModel 13 | { 14 | Account = admin.Account, 15 | IdNgo = idNgo, 16 | Password = admin.Password 17 | }; 18 | } 19 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/DeleteNgo.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.Ngo.Commands; 5 | 6 | public record DeleteNgo(int Id) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/DeleteNgoAdmin.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.Ngo.Commands; 5 | 6 | public record DeleteNgoAdmin(int IdNgo, int IdNgoAdmin) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/SetNgoOrganizerFlag.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.Ngo.Commands; 5 | 6 | public record SetNgoOrganizerFlag(int Id, bool IsOrganizer) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/SetNgoStatusFlag.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.Ngo.Commands; 5 | 6 | public record SetNgoStatusFlag(int Id, bool IsActive) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/UpdateNgo.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Commands; 6 | 7 | public class UpdateNgo : IRequest 8 | { 9 | public NgoModel Ngo { get; } 10 | public UpdateNgo(int id, CreateUpdateNgoModel ngo) 11 | { 12 | Ngo = new NgoModel 13 | { 14 | Id = id, 15 | Name = ngo.Name, 16 | IsActive = ngo.IsActive, 17 | Organizer = ngo.Organizer, 18 | ShortName = ngo.ShortName 19 | }; 20 | } 21 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Commands/UpdateNgoAdmin.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Commands; 6 | 7 | public class UpdateNgoAdmin : IRequest 8 | { 9 | public NgoAdminModel NgoAdmin { get; } 10 | 11 | public UpdateNgoAdmin(int ngoId, int ngoAdminId, CreateUpdateNgoAdminModel admin) 12 | { 13 | NgoAdmin = new NgoAdminModel 14 | { 15 | Id = ngoAdminId, 16 | IdNgo = ngoId, 17 | Account = admin.Account, 18 | Password = admin.Password 19 | }; 20 | } 21 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Models/CreateUpdateNgoAdminModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Ngo.Models; 4 | 5 | public class CreateUpdateNgoAdminModel 6 | { 7 | [Required] [StringLength(50)] public string Account { get; set; } 8 | [Required] [StringLength(100)] public string Password { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Models/CreateUpdateNgoModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Ngo.Models; 4 | 5 | public class CreateUpdateNgoModel 6 | { 7 | [Required] [StringLength(10)] public string ShortName { get; set; } 8 | [Required] [StringLength(200)] public string Name { get; set; } 9 | [Required] public bool Organizer { get; set; } 10 | [Required] public bool IsActive { get; set; } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Models/NgoAdminModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Ngo.Models; 2 | 3 | public class NgoAdminModel 4 | { 5 | public int Id { get; set; } 6 | public int IdNgo { get; set; } 7 | public string Account { get; set; } 8 | public string Password { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Models/NgoModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Ngo.Models; 2 | 3 | public class NgoModel 4 | { 5 | public int Id { get; set; } 6 | public string ShortName { get; set; } 7 | public string Name { get; set; } 8 | public bool Organizer { get; set; } 9 | public bool IsActive { get; set; } 10 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Models/PatchNgoOrganizerModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Ngo.Models; 2 | 3 | public class PatchNgoOrganizerModel 4 | { 5 | public bool IsOrganizer { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Models/PatchNgoStatusModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Ngo.Models; 2 | 3 | public class PatchNgoStatusModel 4 | { 5 | public bool IsActive { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Queries/GetAllNgoAdmins.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Queries; 6 | 7 | public record GetAllNgoAdmins(int IdNgo) : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Queries/GetAllNgos.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Queries; 6 | 7 | public record GetAllNgos : IRequest>>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Queries/GetNgoAdminDetails.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Queries; 6 | 7 | public record GetNgoAdminDetails(int NgoId, int AdminId) : IRequest>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/Queries/GetNgoDetails.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | using VoteMonitor.Api.Ngo.Models; 4 | 5 | namespace VoteMonitor.Api.Ngo.Queries; 6 | 7 | public record GetNgoDetails(int NgoId) :IRequest>; 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Ngo/VoteMonitor.Api.Ngo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Commands/AddNoteCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core.Models; 3 | 4 | namespace VoteMonitor.Api.Note.Commands; 5 | 6 | [Obsolete("Will be removed when ui will use multiple files upload")] 7 | public class AddNoteCommand : IRequest 8 | { 9 | public int IdObserver { get; set; } 10 | public int IdPollingStation { get; set; } 11 | public int? IdQuestion { get; set; } 12 | public string Text { get; set; } 13 | public UploadedFileModel Attachement { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Commands/AddNoteCommandV2.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core.Models; 3 | 4 | namespace VoteMonitor.Api.Note.Commands; 5 | 6 | public record AddNoteCommandV2 : IRequest 7 | { 8 | public int ObserverId { get; } 9 | public int PollingStationId { get; } 10 | public int? QuestionId { get; } 11 | public string Text { get; } 12 | public UploadedFileModel[] Attachments { get; } 13 | 14 | public AddNoteCommandV2(int observerId, int pollingStationId, int? questionId, string text, UploadedFileModel[] attachments = null) 15 | { 16 | ObserverId = observerId; 17 | PollingStationId = pollingStationId; 18 | QuestionId = questionId; 19 | Text = text; 20 | Attachments = attachments ?? Array.Empty(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Commands/AddNoteToUnknownPollingStation.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core.Models; 3 | 4 | namespace VoteMonitor.Api.Note.Commands; 5 | 6 | public record AddNoteToUnknownPollingStation : IRequest 7 | { 8 | public int ObserverId { get; } 9 | public string CountyCode { get; } 10 | public string MunicipalityCode { get; } 11 | public int PollingStationNumber { get; } 12 | public int? QuestionId { get; } 13 | public string Text { get; } 14 | public UploadedFileModel[] Attachments { get; } 15 | 16 | public AddNoteToUnknownPollingStation(int observerId, string countyCode, string municipalityCode, int pollingStationNumber, int? questionId, string text, UploadedFileModel[] attachments = null) 17 | { 18 | ObserverId = observerId; 19 | CountyCode = countyCode; 20 | MunicipalityCode = municipalityCode; 21 | PollingStationNumber = pollingStationNumber; 22 | QuestionId = questionId; 23 | Text = text; 24 | Attachments = attachments ?? Array.Empty(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Models/NoteFilterModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Note.Models; 2 | 3 | public class NoteFilterModel 4 | { 5 | public int IdPollingStaion { get; set; } 6 | public int IdObserver { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Models/NoteModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Note.Models; 4 | 5 | public class NoteModel 6 | { 7 | [Required(AllowEmptyStrings = false)] 8 | public string CountyCode { get; set; } 9 | 10 | [Required(AllowEmptyStrings = false)] 11 | public string MunicipalityCode { get; set; } 12 | 13 | [Required] 14 | public int PollingStationNumber { get; set; } 15 | public int? QuestionId { get; set; } 16 | public string Text { get; set; } 17 | public string[] AttachmentsPaths { get; set; } 18 | public string FormCode { get; set; } 19 | public int FormId { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Models/UploadNoteModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace VoteMonitor.Api.Note.Models; 5 | 6 | [Obsolete("Will be removed when ui will use multiple files upload")] 7 | public class UploadNoteModel 8 | { 9 | [Required(AllowEmptyStrings = false)] 10 | public string CountyCode { get; set; } 11 | 12 | [Required(AllowEmptyStrings = false)] 13 | public string MunicipalityCode { get; set; } 14 | 15 | [Required] 16 | public int PollingStationNumber { get; set; } 17 | 18 | public int? QuestionId { get; set; } 19 | 20 | public string Text { get; set; } 21 | 22 | [DataType(DataType.Upload)] 23 | public IFormFile File { get; set; } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Models/UploadNoteModelV2.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace VoteMonitor.Api.Note.Models; 5 | 6 | public class UploadNoteModelV2 7 | { 8 | [Required(AllowEmptyStrings = false)] 9 | public string CountyCode { get; set; } 10 | 11 | [Required(AllowEmptyStrings = false)] 12 | public string MunicipalityCode { get; set; } 13 | 14 | [Required] 15 | public int PollingStationNumber { get; set; } 16 | 17 | public int? QuestionId { get; set; } 18 | 19 | public string Text { get; set; } 20 | 21 | [DataType(DataType.Upload)] 22 | public List Files { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Models/UploadNoteResult.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Note.Models; 2 | 3 | [Obsolete("Will be removed when ui will use multiple files upload")] 4 | public class UploadNoteResult 5 | { 6 | public string FileAddress { get; set; } 7 | public UploadNoteModel Note { get; set; } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Models/UploadNoteResultV2.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Note.Models; 2 | 3 | public class UploadNoteResultV2 4 | { 5 | public string[] FilesAddress { get; set; } 6 | public UploadNoteModelV2 Note { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/Queries/NoteQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Note.Models; 3 | 4 | namespace VoteMonitor.Api.Note.Queries; 5 | 6 | public class NoteQuery : IRequest> 7 | { 8 | public int? PollingStationId { get; set; } 9 | public int? ObserverId { get; set; } 10 | public int? IdQuestion { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Note/VoteMonitor.Api.Note.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Commands/NotificationListCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core; 3 | using VoteMonitor.Api.Notification.Models; 4 | 5 | namespace VoteMonitor.Api.Notification.Commands; 6 | 7 | public class NotificationListCommand : IRequest> 8 | { 9 | public bool IsOrganizer { get; set; } 10 | public int? NgoId { get; set; } 11 | public int Page { get; set; } 12 | public int PageSize { get; set; } 13 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Commands/SendNotificationCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Notification.Commands; 4 | 5 | public record SendNotificationCommand(List Recipients, int SenderAdminId, string Channel, string From, string Title, string Message) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Commands/SendNotificationToAllCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Notification.Commands; 4 | 5 | public record SendNotificationToAllCommand(int SenderAdminId, string Channel, string From, string Title, string Message) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Models/NotificationModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Notification.Models; 2 | 3 | public class NotificationModel 4 | { 5 | public int Id { get; set; } 6 | public string Title { get; set; } 7 | public string Channel { get; set; } 8 | public string Body { get; set; } 9 | public DateTime InsertedAt { get; set; } 10 | // 11 | public int SenderId { get; set; } 12 | public int SenderIdNgo { get; set; } 13 | public string SenderNgoName { get; set; } 14 | public string SenderAccount { get; set; } 15 | // 16 | public ICollection SentObserverIds { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Models/NotificationRegistrationDataModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Notification.Models; 4 | 5 | public class NotificationRegistrationDataModel 6 | { 7 | public int? ObserverId { get; set; } 8 | 9 | [Required] 10 | [MaxLength(256)] 11 | public string ChannelName { get; set; } 12 | 13 | [Required] 14 | [MaxLength(512)] 15 | public string Token { get; set; } 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Models/SendNotificationModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Notification.Models; 4 | 5 | public class SendNotificationModel 6 | { 7 | [Required] 8 | public string Channel { get; set; } 9 | [Required] 10 | public string From { get; set; } 11 | [Required] 12 | public string Title { get; set; } 13 | [Required] 14 | public string Message { get; set; } 15 | public List Recipients { get; set; } 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Models/SendNotificationToAllModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Notification.Models; 4 | 5 | public class SendNotificationToAllModel 6 | { 7 | [Required] 8 | public string Channel { get; set; } 9 | [Required] 10 | public string From { get; set; } 11 | [Required] 12 | public string Title { get; set; } 13 | [Required] 14 | public string Message { get; set; } 15 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/Queries/NotificationListQuery.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core; 2 | 3 | namespace VoteMonitor.Api.Notification.Queries; 4 | 5 | public class NotificationListQuery : PagingModel 6 | { 7 | public bool IsOrganizer { get; set; } 8 | public int? NgoId { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Notification/VoteMonitor.Api.Notification.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/DeleteObserverCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public record DeleteObserverCommand(int ObserverId) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/EditObserverCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public class EditObserverCommand : IRequest 6 | { 7 | public int ObserverId { get; set; } 8 | public string Phone { get; set; } 9 | public string Name { get; set; } 10 | public string Pin { get; set; } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/ImportObserversRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace VoteMonitor.Api.Observer.Commands; 5 | 6 | public record ImportObserversRequest(int NgoId, IFormFile File) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/NewObserverCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public class NewObserverCommand : IRequest 6 | { 7 | public int NgoId { get; set; } 8 | public string Phone { get; set; } 9 | public string Pin { get; set; } 10 | public string Name { get; set; } 11 | public bool SendSMS { get; set; } 12 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/ObserverCountCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public record ObserverCountCommand(int NgoId, bool IsCallerOrganizer) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/ObserverGenerateCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Observer.Models; 3 | 4 | namespace VoteMonitor.Api.Observer.Commands; 5 | 6 | public record ObserverGenerateCommand(int NumberOfObservers, int NgoId) : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/ObserverListCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core; 3 | using VoteMonitor.Api.Observer.Models; 4 | 5 | namespace VoteMonitor.Api.Observer.Commands; 6 | 7 | public class ObserverListCommand : IRequest> 8 | { 9 | public int NgoId { get; set; } 10 | public string Number { get; set; } 11 | public string Name { get; set; } 12 | public int Page { get; set; } 13 | public int PageSize { get; set; } 14 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/RemoveDeviceIdCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public record RemoveDeviceIdCommand(int ObserverId) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/ResetDeviceCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public class ResetDeviceCommand : IRequest 6 | { 7 | public int NgoId { get; set; } 8 | public string PhoneNumber { get; set; } 9 | 10 | public bool Organizer { get; set; } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Commands/ResetPasswordCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.Observer.Commands; 4 | 5 | public class ResetPasswordCommand : IRequest 6 | { 7 | public int NgoId { get; set; } 8 | public string PhoneNumber { get; set; } 9 | public string Pin { get; set; } 10 | public bool IsOrganizer { get; set; } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/ActiveObserverFilter.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Observer.Models; 2 | 3 | public class ActiveObserverFilter 4 | { 5 | /// 6 | /// A list of counties to filter the observers 7 | /// [ "B", "AB", "CT" ] 8 | /// 9 | public string[] CountyCodes { get; set; } 10 | 11 | /// 12 | /// Get only the observers that have not left the polling stations selected in the interval 13 | /// 14 | public bool CurrentlyCheckedIn { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/EditObserverModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Observer.Models; 4 | 5 | public class EditObserverModel 6 | { 7 | [Required] 8 | public int? ObserverId { get; set; } 9 | public string Phone { get; set; } 10 | public string Name { get; set; } 11 | public string Pin { get; set; } 12 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/GeneratedObserver.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Observer.Models; 2 | 3 | public class GeneratedObserver 4 | { 5 | public string Id { get; set; } 6 | public string PhoneNumber { get; set; } 7 | public string Pin { get; set; } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/NewObserverModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Observer.Models; 4 | 5 | public class NewObserverModel 6 | { 7 | [Required] 8 | public string Phone { get; set; } 9 | 10 | [Required] 11 | public string Pin { get; set; } 12 | 13 | [Required] 14 | public string Name { get; set; } 15 | public bool SendSMS { get; set; } 16 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/ObserverModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Observer.Models; 2 | 3 | public class ObserverModel 4 | { 5 | public int Id { get; set; } 6 | public string Ngo { get; set; } 7 | public string Phone { get; set; } 8 | public string Name { get; set; } 9 | public DateTime? DeviceRegisterDate { get; set; } 10 | public DateTime? LastSeen { get; set; } 11 | public int NumberOfNotes { get; set; } 12 | public int NumberOfPollingStations { get; set; } 13 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/ObserversImportModel.cs: -------------------------------------------------------------------------------- 1 | using CsvHelper.Configuration.Attributes; 2 | 3 | namespace VoteMonitor.Api.Observer.Models; 4 | 5 | internal class ObserversImportModel 6 | { 7 | [Index(0)] 8 | public string Phone { get; set; } 9 | 10 | [Index(1)] 11 | public string Pin { get; set; } 12 | 13 | [Index(2)] 14 | public string Name { get; set; } 15 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/RemoveDeviceIdModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Observer.Models; 2 | 3 | public class RemoveDeviceIdModel 4 | { 5 | public int Id { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Models/ResetModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Observer.Models; 2 | 3 | public class ResetModel 4 | { 5 | public string Action { get; set; } 6 | public string PhoneNumber { get; set; } 7 | public string Pin { get; set; } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Queries/ActiveObserversQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Observer.Models; 3 | 4 | namespace VoteMonitor.Api.Observer.Queries; 5 | 6 | public record ActiveObserversQuery(int NgoId, string[] CountyCodes, bool CurrentlyCheckedIn) : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Queries/GetObserverDetails.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Observer.Models; 3 | 4 | namespace VoteMonitor.Api.Observer.Queries; 5 | 6 | public record GetObserverDetails(int NgoId, int ObserverId) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Queries/ObserverListQuery.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using VoteMonitor.Api.Core; 3 | 4 | namespace VoteMonitor.Api.Observer.Queries; 5 | 6 | public class ObserverListQuery : PagingModel 7 | { 8 | [FromQuery] public string Number { get; set; } 9 | [FromQuery] public string Name { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/Utils/RandomNumberGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Observer.Utils; 2 | 3 | public class RandomNumberGenerator 4 | { 5 | public static string Generate(int digits) 6 | { 7 | var random = new Random(); 8 | var number = ""; 9 | for (var i = 1; i < digits + 1; i++) 10 | { 11 | number += random.Next(0, 9).ToString(); 12 | } 13 | return number; 14 | } 15 | 16 | public static string GenerateWithPadding(int digits, string prefix) 17 | { 18 | var random = new Random(); 19 | var number = prefix; 20 | for (var i = 1 + prefix.Length; i < digits + 1; i++) 21 | { 22 | number += random.Next(0, 9).ToString(); 23 | } 24 | return number; 25 | } 26 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Observer/VoteMonitor.Api.Observer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Commands/ClearAllPollingStationsCommand.cs: -------------------------------------------------------------------------------- 1 | using CSharpFunctionalExtensions; 2 | using MediatR; 3 | 4 | namespace VoteMonitor.Api.PollingStation.Commands; 5 | 6 | public record ClearAllPollingStationsCommand(bool IncludeRelatedData) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Models/CreatePollingStationInfoModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Models; 4 | 5 | public class CreatePollingStationInfoModel 6 | { 7 | [Required(AllowEmptyStrings = false)] 8 | public int PollingStationId { get; set; } 9 | 10 | [Required(AllowEmptyStrings = false)] 11 | public string CountyCode { get; set; } 12 | 13 | public DateTime? ObserverLeaveTime { get; set; } 14 | public DateTime? ObserverArrivalTime { get; set; } 15 | public bool? IsPollingStationPresidentFemale { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Models/GetPollingStationModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.PollingStation.Models; 2 | 3 | public class GetPollingStationModel 4 | { 5 | public int Id { get; set; } 6 | public int MunicipalityId { get; set; } 7 | public string Address { get; set; } 8 | public int Number { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Models/PollingStationsFilterModel.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Models; 4 | 5 | public class PollingStationsFilterModel : PagingModel 6 | { 7 | public int CountyId { get; set; } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Models/UpdatePollingStationInfo.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Models; 4 | 5 | public class EditPollingStationInfo 6 | { 7 | 8 | [Required(AllowEmptyStrings = false)] 9 | public DateTime? ObserverLeaveTime { get; set; } 10 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Models/UpdatePollingStationModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.PollingStation.Models; 2 | 3 | public class UpdatePollingStationModel 4 | { 5 | public string Address { get; set; } 6 | public int Number { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Queries/CheckPollingStationExists.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Queries; 4 | 5 | public record CheckPollingStationExists(int PollingStationId) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Queries/CreatePollingStationInfo.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Queries; 4 | 5 | public record CreatePollingStationInfo(int ObserverId, int PollingStationId, string CountyCode, DateTime? ObserverLeaveTime, DateTime? ObserverArrivalTime, bool? IsPollingStationPresidentFemale) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Queries/GetPollingStationById.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.PollingStation.Models; 3 | 4 | namespace VoteMonitor.Api.PollingStation.Queries; 5 | 6 | public record GetPollingStationById(int PollingStationId) : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Queries/GetPollingStations.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.PollingStation.Models; 3 | 4 | namespace VoteMonitor.Api.PollingStation.Queries; 5 | 6 | public record GetPollingStations(int CountyId, int Page, int PageSize) : IRequest>; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Queries/UpdatePollingStation.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Queries; 4 | 5 | public record UpdatePollingStation(int PollingStationId, string Address, int Number) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/Queries/UpdatePollingStationInfo.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace VoteMonitor.Api.PollingStation.Queries; 4 | 5 | public record UpdatePollingStationInfo(int ObserverId, int PollingStationId, DateTime ObserverLeaveTime) : IRequest; 6 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.PollingStation/VoteMonitor.Api.PollingStation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Models/FilterStatisticsModel.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core; 2 | 3 | namespace VoteMonitor.Api.Statistics.Models; 4 | 5 | public class FilterStatisticsModel : PagingModel 6 | { 7 | public string FormCode { get; set; } 8 | public StatisticsGroupingTypes GroupingType { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Models/OptionStatisticsModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Statistics.Models; 2 | 3 | public class OptionStatisticsModel : SimpleStatisticsModel 4 | { 5 | public int OptionId { get; set; } 6 | public bool IsFlagged { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Models/OptionsFilterModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace VoteMonitor.Api.Statistics.Models; 4 | 5 | public class OptionsFilterModel 6 | { 7 | [Required] 8 | public int QuestionId { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Models/SimpleStatisticsModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Statistics.Models; 2 | 3 | public class SimpleStatisticsModel 4 | { 5 | public string Label { get; set; } 6 | public string Value { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Models/StatisticsGroupingTypes.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Statistics.Models; 2 | 3 | public enum StatisticsGroupingTypes 4 | { 5 | County, 6 | PollingStation 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Models/StatisticsOptionsModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Statistics.Models; 2 | 3 | public class StatisticsOptionsModel 4 | { 5 | public int QuestionId { get; set; } 6 | public IList Options { get; set; } 7 | public int Total { get; set; } 8 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/AnswersQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record AnswersQuery : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/CountiesVisitedQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record CountiesVisitedQuery : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/FlaggedAnswersQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record FlaggedAnswersQuery : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/LoggedInObserversQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record LoggedInObserversQuery : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/NotesUploadedQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record NotesUploadedQuery : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/StationsVisitedQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record StationsVisitedQuery : IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/StatisticsObserverNumberQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core; 3 | using VoteMonitor.Api.Statistics.Models; 4 | 5 | namespace VoteMonitor.Api.Statistics.Queries; 6 | 7 | public class StatisticsObserverNumberQuery : StatisticsPaginatedQuery, IRequest> 8 | { 9 | } 10 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/StatisticsOptionsQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Statistics.Models; 3 | 4 | namespace VoteMonitor.Api.Statistics.Queries; 5 | 6 | public record StatisticsOptionsQuery(int QuestionId, int NgoId, bool IsOrganizer, int CacheHours, int CacheMinutes, int CacheSeconds) : StatisticsQuery(NgoId, IsOrganizer, CacheHours, CacheMinutes, CacheSeconds), IRequest; 7 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/StatisticsPaginatedQuery.cs: -------------------------------------------------------------------------------- 1 | using VoteMonitor.Api.Core; 2 | 3 | namespace VoteMonitor.Api.Statistics.Queries; 4 | 5 | public class StatisticsPaginatedQuery : PagingModel 6 | { 7 | public int NgoId { get; set; } 8 | public bool IsOrganizer { get; set; } 9 | public int CacheHours { get; set; } 10 | public int CacheMinutes { get; set; } 11 | public int CacheSeconds { get; set; } 12 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/StatisticsQuery.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Api.Statistics.Queries; 2 | 3 | public record StatisticsQuery(int NgoId, bool IsOrganizer, int CacheHours, int CacheMinutes, int CacheSeconds); 4 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/Queries/StatisticsTopIrregularitiesQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using VoteMonitor.Api.Core; 3 | using VoteMonitor.Api.Statistics.Models; 4 | 5 | namespace VoteMonitor.Api.Statistics.Queries; 6 | 7 | public class StatisticsTopIrregularitiesQuery : StatisticsPaginatedQuery, IRequest> 8 | { 9 | public string FormCode { get; set; } 10 | public StatisticsGroupingTypes GroupType { get; set; } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api.Statistics/VoteMonitor.Api.Statistics.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "VoteMonitor.Api": { 5 | "commandName": "Project", 6 | "launchBrowser": true, 7 | "launchUrl": "swagger", 8 | "environmentVariables": { 9 | "ASPNETCORE_ENVIRONMENT": "Development" 10 | }, 11 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 12 | }, 13 | "VoteMonitor.Api HealthChecks": { 14 | "commandName": "Project", 15 | "launchBrowser": true, 16 | "launchUrl": "health", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | }, 20 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Answer.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class Answer 4 | { 5 | public int IdObserver { get; set; } 6 | public int IdOptionToQuestion { get; set; } 7 | public int IdPollingStation { get; set; } 8 | public DateTime LastModified { get; set; } 9 | public string Value { get; set; } 10 | public string CountyCode { get; set; } 11 | public int PollingStationNumber { get; set; } 12 | 13 | public virtual Observer Observer { get; set; } 14 | public virtual OptionToQuestion OptionAnswered { get; set; } 15 | public virtual PollingStation PollingStation { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/AnswerCorrupted.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class AnswerCorrupted 4 | { 5 | public int IdObserver { get; set; } 6 | public int IdOptionToQuestion { get; set; } 7 | public DateTime LastModified { get; set; } 8 | public string Value { get; set; } 9 | public string CountyCode { get; set; } 10 | public string MunicipalityCode { get; set; } 11 | public int PollingStationNumber { get; set; } 12 | 13 | public virtual Observer Observer { get; set; } 14 | public virtual OptionToQuestion OptionAnswered { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/AnswerQueryInfo.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class AnswerQueryInfo 4 | { 5 | public int IdPollingStation { get; set; } 6 | public int IdObserver { get; set; } 7 | public string ObserverPhoneNumber { get; set; } 8 | public string ObserverName { get; set; } 9 | public string PollingStation { get; set; } 10 | public DateTime LastModified { get; set; } 11 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/County.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class County : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public string Code { get; set; } 7 | public string Name { get; set; } 8 | public bool Diaspora { get; set; } 9 | public int Order { get; set; } 10 | public int ProvinceId { get; set; } 11 | public Province Province { get; set; } 12 | public virtual ICollection Municipalities { get; set; } = new HashSet(); 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/ExportModel.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class ExportModel 4 | { 5 | public Guid Id { get; set; } 6 | public string ObserverPhone { get; set; } 7 | public int IdNgo { get; set; } 8 | public string FormCode { get; set; } 9 | public string QuestionText { get; set; } 10 | public string OptionText { get; set; } 11 | public string AnswerFreeText { get; set; } 12 | public string NoteText { get; set; } 13 | public string NoteAttachmentPath { get; set; } 14 | public DateTime LastModified { get; set; } 15 | public string CountyCode { get; set; } 16 | public int PollingStationNumber { get; set; } 17 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Form.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class Form : IHierarchicalEntity, IIdentifiableEntity 7 | { 8 | [Key] 9 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 10 | public int Id { get; set; } 11 | public string Code { get; set; } 12 | 13 | [Required] 14 | public string Description { get; set; } 15 | 16 | public int CurrentVersion { get; set; } 17 | 18 | public bool Diaspora { get; set; } 19 | 20 | public bool Draft { get; set; } 21 | 22 | public int Order { get; set; } 23 | 24 | public virtual ICollection FormSections { get; set; } 25 | 26 | ICollection IHierarchicalEntity.Children { get => FormSections; set => FormSections = value; } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/FormSection.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class FormSection : IHierarchicalEntity, IIdentifiableEntity 7 | { 8 | [Key] 9 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 10 | public int Id { get; set; } 11 | public string Code { get; set; } 12 | public string Description { get; set; } 13 | public int IdForm { get; set; } 14 | 15 | public int OrderNumber { get; set; } 16 | 17 | public Form Form { get; set; } 18 | 19 | public ICollection Questions { get; set; } = new HashSet(); 20 | 21 | ICollection IHierarchicalEntity.Children { get => Questions; set => Questions = value; } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/IHierarchicalEntity.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public interface IHierarchicalEntity 4 | { 5 | ICollection Children { get; set; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/IIdentifiableEntity.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public interface IIdentifiableEntity 4 | { 5 | int Id { get; } 6 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/MobileUniqueIdType.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public enum MobileDeviceIdType 4 | { 5 | UserGeneratedGuid, 6 | FcmToken 7 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Municipality.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class Municipality : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public string Code { get; set; } 7 | public string Name { get; set; } 8 | public int Order { get; set; } 9 | public int CountyId { get; set; } 10 | public County County { get; set; } 11 | 12 | public virtual ICollection PollingStations { get; set; } = new HashSet(); 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Ngo.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class Ngo : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public string ShortName { get; set; } 7 | public string Name { get; set; } 8 | public bool Organizer { get; set; } 9 | public bool IsActive { get; set; } 10 | 11 | public ICollection NgoAdmins { get; set; } = new HashSet(); 12 | public ICollection Observers { get; set; } = new HashSet(); 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/NgoAdmin.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class NgoAdmin : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public int IdNgo { get; set; } 7 | public string Account { get; set; } 8 | public string Password { get; set; } 9 | 10 | public virtual Ngo Ngo { get; set; } 11 | public virtual ICollection NotificationsSent { get; set; } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Note.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class Note : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public DateTime LastModified { get; set; } 7 | public int? IdQuestion { get; set; } 8 | public int IdObserver { get; set; } 9 | public int IdPollingStation { get; set; } 10 | public string Text { get; set; } 11 | 12 | public virtual Question Question { get; set; } 13 | public virtual Observer Observer { get; set; } 14 | public virtual PollingStation PollingStation { get; set; } 15 | public virtual ICollection Attachments { get; set; } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/NoteCorrupted.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class NoteCorrupted : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public DateTime LastModified { get; set; } 7 | public int? IdQuestion { get; set; } 8 | public int IdObserver { get; set; } 9 | public string CountyCode { get; set; } 10 | public string MunicipalityCode { get; set; } 11 | public int PollingStationNumber { get; set; } 12 | public string Text { get; set; } 13 | 14 | public virtual Question Question { get; set; } 15 | public virtual Observer Observer { get; set; } 16 | public virtual ICollection Attachments { get; set; } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/NotesAttachmentCorrupted.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class NotesAttachmentCorrupted : IIdentifiableEntity 7 | { 8 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 9 | [Key] 10 | public int Id { get; set; } 11 | public int NoteId { get; set; } 12 | public virtual NoteCorrupted Note { get; set; } 13 | public string Path { get; set; } 14 | public string FileName { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/NotesAttachments.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class NotesAttachments : IIdentifiableEntity 7 | { 8 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 9 | [Key] 10 | public int Id { get; set; } 11 | public int NoteId { get; set; } 12 | public virtual Note Note { get; set; } 13 | public string Path { get; set; } 14 | public string FileName { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Notification.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class Notification : IIdentifiableEntity 7 | { 8 | [Key] 9 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 10 | public int Id { get; set; } 11 | public string Title { get; set; } 12 | public string Channel { get; set; } 13 | public string Body { get; set; } 14 | public DateTime InsertedAt { get; set; } 15 | public int SenderAdminId { get; set; } 16 | public NgoAdmin SenderAdmin { get; set; } 17 | public virtual ICollection NotificationRecipients { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/NotificationRecipient.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class NotificationRecipient 7 | { 8 | [Key, Column(Order = 1)] 9 | public int ObserverId { get; set; } 10 | [Key, Column(Order = 2)] 11 | public int NotificationId { get; set; } 12 | public Observer Observer { get; set; } 13 | public Notification Notification { get; set; } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/NotificationRegistrationData.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class NotificationRegistrationData 4 | { 5 | public int ObserverId { get; set; } 6 | public string ChannelName { get; set; } 7 | public string Token { get; set; } 8 | 9 | public virtual Observer Observer { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Option.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class Option : IIdentifiableEntity 7 | { 8 | [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 9 | public int Id { get; set; } 10 | public bool IsFreeText { get; set; } 11 | [Required, MaxLength(1000)] 12 | public string Text { get; set; } 13 | public string Hint { get; set; } 14 | public int OrderNumber { get; set; } 15 | 16 | public virtual ICollection OptionsToQuestions { get; set; } = new HashSet(); 17 | } 18 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/OptionToQuestion.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace VoteMonitor.Entities; 5 | 6 | public class OptionToQuestion : IIdentifiableEntity 7 | { 8 | [Key] 9 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 10 | public int Id { get; set; } 11 | public int IdQuestion { get; set; } 12 | public int IdOption { get; set; } 13 | public bool Flagged { get; set; } 14 | 15 | public virtual ICollection Answers { get; } = new HashSet(); 16 | public virtual ICollection CorruptedAnswers { get; } = new HashSet(); 17 | public virtual Question Question { get; set; } 18 | public virtual Option Option { get; set; } 19 | } 20 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/PollingStation.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class PollingStation : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | public int Number { get; set; } 7 | public string Address { get; set; } 8 | 9 | public ICollection Notes { get; set; } = new HashSet(); 10 | public ICollection Answers { get; set; } = new HashSet(); 11 | public ICollection PollingStationInfos { get; set; } = new HashSet(); 12 | public int MunicipalityId { get; set; } 13 | public Municipality Municipality { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/PollingStationInfo.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class PollingStationInfo 4 | { 5 | public int IdObserver { get; set; } 6 | public int IdPollingStation { get; set; } 7 | public DateTime LastModified { get; set; } 8 | public DateTime? ObserverArrivalTime { get; set; } 9 | public DateTime? ObserverLeaveTime { get; set; } 10 | 11 | public int NumberOfVotersOnTheList { get; set; } 12 | 13 | public int NumberOfCommissionMembers { get; set; } 14 | 15 | public int NumberOfFemaleMembers { get; set; } 16 | 17 | public int MinPresentMembers { get; set; } 18 | 19 | public bool ChairmanPresence { get; set; } 20 | 21 | public bool SinglePollingStationOrCommission { get; set; } 22 | 23 | public bool AdequatePollingStationSize { get; set; } 24 | 25 | public virtual Observer Observer { get; set; } 26 | public virtual PollingStation PollingStation { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/PollingStationInfoCorrupted.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class PollingStationInfoCorrupted 4 | { 5 | public int IdObserver { get; set; } 6 | public DateTime LastModified { get; set; } 7 | public DateTime? ObserverArrivalTime { get; set; } 8 | public DateTime? ObserverLeaveTime { get; set; } 9 | 10 | public int NumberOfVotersOnTheList { get; set; } 11 | 12 | public int NumberOfCommissionMembers { get; set; } 13 | 14 | public int NumberOfFemaleMembers { get; set; } 15 | 16 | public int MinPresentMembers { get; set; } 17 | 18 | public bool ChairmanPresence { get; set; } 19 | 20 | public bool SinglePollingStationOrCommission { get; set; } 21 | 22 | public bool AdequatePollingStationSize { get; set; } 23 | 24 | public virtual Observer Observer { get; set; } 25 | public string CountyCode { get; set; } 26 | public string MunicipalityCode { get; set; } 27 | public int PollingStationNumber { get; set; } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Province.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public class Province : IIdentifiableEntity 4 | { 5 | public int Id { get; set; } 6 | 7 | public string Code { get; set; } 8 | public string Name { get; set; } 9 | public int Order { get; set; } 10 | 11 | public virtual ICollection Counties { get; set; } = new HashSet(); 12 | } 13 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/QuestionType.cs: -------------------------------------------------------------------------------- 1 | namespace VoteMonitor.Entities; 2 | 3 | public enum QuestionType 4 | { 5 | MultipleOption = 0, 6 | SingleOption = 1, 7 | SingleOptionWithText = 2, 8 | MultipleOptionWithText = 3 9 | } -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/Statistici.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace VoteMonitor.Entities; 3 | 4 | public class SimpleStatistics 5 | { 6 | public string Label { get; set; } 7 | public int Value { get; set; } 8 | } 9 | 10 | public class ComposedStatistics 11 | { 12 | public string Label { get; set; } 13 | public int Code { get; set; } 14 | public int Value { get; set; } 15 | } 16 | 17 | public class OptionsStatistics 18 | { 19 | public string Label { get; set; } 20 | public int Value { get; set; } 21 | public int Code { get; set; } 22 | public bool Flagged { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /src/api/VoteMonitor.Entities/VoteMonitor.Entities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net7.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Migrator/ConfigurationHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace VotingIrregularities.Domain.Migrator; 4 | 5 | public class ConfigurationHelper 6 | { 7 | public static IConfigurationRoot GetConfiguration() 8 | { 9 | return new ConfigurationBuilder() 10 | .SetBasePath(Directory.GetCurrentDirectory()) 11 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 12 | .AddJsonFile("appsettings.development.json", optional: true) 13 | .AddEnvironmentVariables() 14 | .Build(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Migrator/ContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using Microsoft.Extensions.Configuration; 4 | using VoteMonitor.Entities; 5 | 6 | namespace VotingIrregularities.Domain.Migrator; 7 | 8 | public class ContextFactory : IDesignTimeDbContextFactory 9 | { 10 | public VoteMonitorContext CreateDbContext(string[] args) 11 | { 12 | var configuration = ConfigurationHelper.GetConfiguration(); 13 | var connectionString = configuration.GetConnectionString("DefaultConnection")!; 14 | 15 | var optionsBuilder = new DbContextOptionsBuilder(); 16 | 17 | optionsBuilder.UseNpgsql(connectionString, x => x.MigrationsAssembly("VotingIrregularities.Domain.Migrator")); 18 | 19 | return new VoteMonitorContext(optionsBuilder.Options); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Migrator/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 4 | WORKDIR /src 5 | COPY ["api/VotingIrregularities.Domain.Migrator/VotingIrregularities.Domain.Migrator.csproj", "api/VotingIrregularities.Domain.Migrator/"] 6 | COPY ["api/VoteMonitor.Entities/VoteMonitor.Entities.csproj", "api/VoteMonitor.Entities/"] 7 | RUN dotnet restore "api/VotingIrregularities.Domain.Migrator/VotingIrregularities.Domain.Migrator.csproj" 8 | COPY . . 9 | WORKDIR "/src/api/VotingIrregularities.Domain.Migrator" 10 | RUN dotnet build "VotingIrregularities.Domain.Migrator.csproj" -c Release -o /app/build 11 | 12 | FROM build AS publish 13 | WORKDIR "/src/api/VotingIrregularities.Domain.Migrator" 14 | RUN dotnet publish "VotingIrregularities.Domain.Migrator.csproj" -c Release -o /app/publish 15 | 16 | FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine 17 | WORKDIR /app 18 | COPY --from=publish /app/publish . 19 | ENTRYPOINT ["dotnet", "VotingIrregularities.Domain.Migrator.dll"] -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Migrator/Migrations/20230922074009_AddAttachmentFileName.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace VotingIrregularities.Domain.Seed.Migrations 6 | { 7 | /// 8 | public partial class AddAttachmentFileName : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "FileName", 15 | table: "NotesAttachments", 16 | type: "text", 17 | nullable: true); 18 | } 19 | 20 | /// 21 | protected override void Down(MigrationBuilder migrationBuilder) 22 | { 23 | migrationBuilder.DropColumn( 24 | name: "FileName", 25 | table: "NotesAttachments"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Migrator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VotingIrregularities.Domain.Seed": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Migrator/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=localhost;Port=5432;Database=vote-monitor-db;User Id=docker;Password=docker;" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Debug", 8 | "System": "Information", 9 | "Microsoft": "Information" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/ConfigurationHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace VotingIrregularities.Domain.Seed; 4 | 5 | public class ConfigurationHelper 6 | { 7 | public static IConfigurationRoot GetConfiguration() 8 | { 9 | return new ConfigurationBuilder() 10 | .SetBasePath(Directory.GetCurrentDirectory()) 11 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 12 | .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true) 13 | .AddEnvironmentVariables() 14 | .Build(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 4 | WORKDIR /src 5 | COPY ["api/VotingIrregularities.Domain.Seed/VotingIrregularities.Domain.Seed.csproj", "api/VotingIrregularities.Domain.Seed/"] 6 | COPY ["api/VoteMonitor.Entities/VoteMonitor.Entities.csproj", "api/VoteMonitor.Entities/"] 7 | RUN dotnet restore "api/VotingIrregularities.Domain.Seed/VotingIrregularities.Domain.Seed.csproj" 8 | COPY . . 9 | WORKDIR "/src/api/VotingIrregularities.Domain.Seed" 10 | RUN dotnet build "VotingIrregularities.Domain.Seed.csproj" -c Release -o /app/build 11 | 12 | FROM build AS publish 13 | WORKDIR "/src/api/VotingIrregularities.Domain.Seed" 14 | RUN dotnet publish "VotingIrregularities.Domain.Seed.csproj" -c Release -o /app/publish 15 | 16 | FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine 17 | WORKDIR /app 18 | COPY --from=publish /app/publish . 19 | ENTRYPOINT ["dotnet", "VotingIrregularities.Domain.Seed.dll"] -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Options/AdminSeed.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Options; 2 | 3 | public class AdminSeed 4 | { 5 | public string Account { get; set; } 6 | public string Password { get; set; } 7 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Options/HashServiceType.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Options; 2 | 3 | public enum HashServiceType 4 | { 5 | Hash, 6 | ClearText 7 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Options/NgoSeed.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Options; 2 | 3 | public class NgoSeed 4 | { 5 | public string ShortName { get; set; } 6 | public string Name { get; set; } 7 | public bool IsOrganizer { get; set; } 8 | 9 | /// 10 | /// A dictionary containing admins to be seeded. Dictionary key is the admin id 11 | /// 12 | public Dictionary Admins { get; set; } = new Dictionary(); 13 | 14 | /// 15 | /// A dictionary containing observers to be seeded. Dictionary key is the observer id 16 | /// 17 | public Dictionary Observers { get; set; } = new Dictionary(); 18 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Options/ObserverSeed.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Options; 2 | 3 | public class ObserverSeed 4 | { 5 | public string Phone { get; set; } 6 | public bool FromTeam { get; set; } 7 | public string Name { get; set; } 8 | public string Pin { get; set; } 9 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Options/SeedOption.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Options; 2 | 3 | public class SeedOption 4 | { 5 | public const string SectionKey = "SeedOptions"; 6 | 7 | /// 8 | /// A flag to specify if Seed app should override existing data in db 9 | /// 10 | public bool OverrideExistingData { get; set; } 11 | 12 | public string PasswordSalt { get; set; } 13 | 14 | /// 15 | /// Can be set to `Hash` or `ClearText` 16 | /// `Hash` will use the HashService (that needs the Salt setting) to generate hashes for the password 17 | /// :warning: `ClearText` will allow your development environment to create and store clear text passwords in the database. Please only use this in development to speed up things. 18 | /// 19 | public HashServiceType HashServiceType { get; set; } = HashServiceType.ClearText; 20 | 21 | 22 | /// 23 | /// A dictionary containing NGOs to be seeded. Dictionary key is the ngo id 24 | /// 25 | public Dictionary Ngos { get; set; } = new Dictionary(); 26 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VotingIrregularities.Domain.Seed": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "ASPNETCORE_ENVIRONMENT": "Development" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Services/ClearTextService.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Services; 2 | 3 | public class ClearTextService : IHashService 4 | { 5 | public string GetHash(string clearString) => clearString; 6 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Services/IHashService.cs: -------------------------------------------------------------------------------- 1 | namespace VotingIrregularities.Domain.Seed.Services; 2 | 3 | public interface IHashService 4 | { 5 | string GetHash(string clearString); 6 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/Services/SHA256HashService.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace VotingIrregularities.Domain.Seed.Services; 5 | 6 | public class SHA256HashService : IHashService 7 | { 8 | private readonly string _salt; 9 | 10 | public SHA256HashService(string salt) 11 | { 12 | _salt = salt; 13 | } 14 | 15 | public string GetHash(string clearString) 16 | { 17 | // SHA512 is disposable by inheritance. 18 | using (var sha256 = SHA256.Create()) 19 | { 20 | // Send a sample text to hash. 21 | var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(clearString + _salt)); 22 | // Get the hashed string. 23 | return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/api/VotingIrregularities.Domain.Seed/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=localhost;Port=5432;Database=vote-monitor-db;User Id=docker;Password=docker;" 4 | }, 5 | "SeedOptions": { 6 | "OverrideExistingData": true, 7 | "HashServiceType": "Hash", 8 | "PasswordSalt": "PasswordSaltOfDoom" 9 | }, 10 | "Logging": { 11 | "LogLevel": { 12 | "Default": "Debug", 13 | "System": "Information", 14 | "Microsoft": "Information" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/VoteMonitor.Api.Tests/VoteMonitor.Api.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/VotingIrregularities.Tests/DateTimeTest.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Xunit; 3 | 4 | namespace VotingIrregularities.Tests; 5 | 6 | public class DateTimeTest 7 | { 8 | [Theory] 9 | [MemberData(nameof(DateTimeComparisonTestData))] 10 | public void SanityCheck(DateTime? first, DateTime? second, DateTime? expected) 11 | { 12 | var result = (first?? DateTime.MinValue) > (second ?? DateTime.MinValue) ? first : second; 13 | 14 | result.ShouldBe(expected); 15 | } 16 | 17 | private static DateTime EarliestDate = new(2020, 11, 12); 18 | private static DateTime LatestDate = new(2020, 11, 13); 19 | public static IEnumerable DateTimeComparisonTestData => 20 | new List 21 | { 22 | new object[] {EarliestDate,EarliestDate,EarliestDate }, 23 | new object[] {EarliestDate,LatestDate , LatestDate}, 24 | new object[] { LatestDate, EarliestDate, LatestDate}, 25 | new object[] { null, LatestDate, LatestDate}, 26 | new object[] { LatestDate, null, LatestDate}, 27 | new object[] { null, null, null}, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/test/VotingIrregularities.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("VotingIrregularities.Tests")] 10 | [assembly: AssemblyTrademark("")] 11 | 12 | // Setting ComVisible to false makes the types in this assembly not visible 13 | // to COM components. If you need to access a type in this assembly from 14 | // COM, set the ComVisible attribute to true on that type. 15 | [assembly: ComVisible(false)] 16 | 17 | // The following GUID is for the ID of the typelib if this project is exposed to COM 18 | [assembly: Guid("41fe87ae-428c-4cd5-88f1-a7a713334f2e")] 19 | -------------------------------------------------------------------------------- /src/test/VotingIrregularities.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "SecretKey": "TBR", 3 | "HashOptions": { 4 | "Salt": "TBR" 5 | } 6 | } -------------------------------------------------------------------------------- /terraform/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 13 | # .tfvars files are managed as part of configuration and so should be included in 14 | # version control. 15 | *.tfvars 16 | *.tfvars.json 17 | 18 | # Ignore override files as they are usually used to override resources locally and so 19 | # are not checked in 20 | override.tf 21 | override.tf.json 22 | *_override.tf 23 | *_override.tf.json 24 | 25 | # Include override files you do wish to add to version control using negated pattern 26 | # 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc 35 | 36 | .infracost 37 | -------------------------------------------------------------------------------- /terraform/acm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_acm_certificate" "main" { 2 | validation_method = "DNS" 3 | domain_name = var.domain_name 4 | 5 | lifecycle { 6 | create_before_destroy = true 7 | } 8 | } 9 | 10 | resource "aws_route53_record" "acm_validation" { 11 | for_each = { 12 | for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => { 13 | name = dvo.resource_record_name 14 | record = dvo.resource_record_value 15 | type = dvo.resource_record_type 16 | } 17 | } 18 | 19 | allow_overwrite = true 20 | name = each.value.name 21 | records = [each.value.record] 22 | ttl = 60 23 | type = each.value.type 24 | zone_id = data.aws_route53_zone.main.zone_id 25 | } 26 | -------------------------------------------------------------------------------- /terraform/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_availability_zones" "current" {} 2 | 3 | data "aws_region" "current" {} 4 | 5 | data "aws_route53_zone" "main" { 6 | zone_id = var.route_53_zone_id 7 | } 8 | -------------------------------------------------------------------------------- /terraform/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "ecs_task" { 2 | statement { 3 | actions = [ 4 | "s3:ListBucket", 5 | "s3:HeadBucket" 6 | ] 7 | 8 | resources = ["*"] 9 | } 10 | 11 | statement { 12 | actions = [ 13 | "s3:ListBucket", 14 | "s3:GetObject", 15 | "s3:DeleteObject", 16 | "s3:GetObjectAcl", 17 | "s3:PutObjectAcl", 18 | "s3:PutObject" 19 | ] 20 | 21 | resources = [ 22 | module.s3_private.arn, 23 | "${module.s3_private.arn}/*" 24 | ] 25 | } 26 | } 27 | 28 | data "aws_iam_policy_document" "ecs_task_assume" { 29 | statement { 30 | actions = ["sts:AssumeRole"] 31 | 32 | principals { 33 | type = "Service" 34 | identifiers = ["ecs-tasks.amazonaws.com"] 35 | } 36 | } 37 | } 38 | 39 | resource "aws_iam_role" "ecs_task_role" { 40 | name = "${local.namespace}-ecs-task-role" 41 | assume_role_policy = data.aws_iam_policy_document.ecs_task_assume.json 42 | 43 | 44 | inline_policy { 45 | name = "EcsTaskPolicy" 46 | policy = data.aws_iam_policy_document.ecs_task.json 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /terraform/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | namespace = "votemonitor-${var.slug}-${var.env}" 3 | availability_zone = data.aws_availability_zones.current.names[0] 4 | 5 | images = { 6 | api = { 7 | image = "code4romania/monitorizare-vot-api" 8 | tag = var.docker_tag 9 | } 10 | 11 | migrator = { 12 | image = "code4romania/monitorizare-vot-migrator" 13 | tag = var.docker_tag 14 | } 15 | 16 | seed = { 17 | image = "code4romania/monitorizare-vot-seed" 18 | tag = var.docker_tag 19 | } 20 | } 21 | 22 | ecs = { 23 | instance_types = { 24 | "t3a.medium" = "" 25 | } 26 | } 27 | 28 | db = { 29 | name = "votemonitor" 30 | instance_class = "db.t4g.micro" 31 | # instance_class = "db.t4g.medium" 32 | } 33 | 34 | networking = { 35 | cidr_block = "10.0.0.0/16" 36 | 37 | public_subnets = [ 38 | "10.0.1.0/24", 39 | "10.0.2.0/24", 40 | "10.0.3.0/24" 41 | ] 42 | 43 | private_subnets = [ 44 | "10.0.4.0/24", 45 | "10.0.5.0/24", 46 | "10.0.6.0/24" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /terraform/modules/ecs-cluster/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | ### CloudWatch Log group for container logs 2 | resource "aws_cloudwatch_log_group" "ecs" { 3 | name = "${var.name}-container-logs" 4 | retention_in_days = var.ecs_cloudwatch_log_retention 5 | 6 | tags = var.tags 7 | } 8 | 9 | resource "aws_cloudwatch_log_group" "userdata" { 10 | name = "${var.name}-ecs-nodes-userdata" 11 | retention_in_days = var.userdata_cloudwatch_log_retention 12 | 13 | tags = var.tags 14 | } 15 | -------------------------------------------------------------------------------- /terraform/modules/ecs-cluster/data.tf: -------------------------------------------------------------------------------- 1 | ### Latest Amazon Linux ECS Optimized AMI 2 | data "aws_ami" "this" { 3 | most_recent = true 4 | 5 | owners = ["amazon"] 6 | 7 | filter { 8 | name = "name" 9 | values = ["amzn2-ami-ecs-kernel-5.10-hvm-*-ebs"] 10 | } 11 | 12 | filter { 13 | name = "owner-alias" 14 | values = ["amazon"] 15 | } 16 | 17 | filter { 18 | name = "architecture" 19 | values = ["x86_64"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /terraform/modules/ecs-cluster/iam.tf: -------------------------------------------------------------------------------- 1 | ### IAM Resources 2 | data "aws_iam_policy_document" "ecs" { 3 | statement { 4 | actions = ["sts:AssumeRole"] 5 | 6 | principals { 7 | type = "Service" 8 | identifiers = ["ec2.amazonaws.com"] 9 | } 10 | } 11 | } 12 | 13 | resource "aws_iam_role" "ecs" { 14 | name = "${var.name}-ecs-instance" 15 | path = var.iam_path 16 | assume_role_policy = data.aws_iam_policy_document.ecs.json 17 | managed_policy_arns = [ 18 | "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role", 19 | "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", 20 | "arn:aws:iam::aws:policy/CloudWatchAgentAdminPolicy" 21 | ] 22 | 23 | tags = var.tags 24 | } 25 | 26 | resource "aws_iam_instance_profile" "ecs" { 27 | name = "${var.name}-ecs-instance" 28 | role = aws_iam_role.ecs.name 29 | } 30 | -------------------------------------------------------------------------------- /terraform/modules/ecs-cluster/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | tags = concat( 3 | [ 4 | { 5 | key = "Name" 6 | value = "${var.name}-ecs-node" 7 | propagate_at_launch = true 8 | }, 9 | { 10 | key = "AmazonECSManaged" 11 | value = "true" 12 | propagate_at_launch = true 13 | } 14 | ], 15 | var.asg_tags, 16 | local.tags_asg_format, 17 | ) 18 | 19 | tags_asg_format = null_resource.tags_as_list_of_maps.*.triggers 20 | } 21 | 22 | resource "null_resource" "tags_as_list_of_maps" { 23 | count = length(keys(var.tags)) 24 | 25 | triggers = { 26 | "key" = keys(var.tags)[count.index] 27 | "value" = values(var.tags)[count.index] 28 | "propagate_at_launch" = "true" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /terraform/modules/ecs-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_id" { 2 | value = aws_ecs_cluster.ecs.id 3 | } 4 | 5 | output "cluster_name" { 6 | value = aws_ecs_cluster.ecs.name 7 | } 8 | 9 | output "log_group_name" { 10 | value = aws_cloudwatch_log_group.ecs.name 11 | } 12 | 13 | output "asg_arn" { 14 | value = aws_autoscaling_group.ecs.arn 15 | } 16 | 17 | output "asg_name" { 18 | value = aws_autoscaling_group.ecs.name 19 | } 20 | 21 | output "service_discovery_namespace_id" { 22 | value = aws_service_discovery_private_dns_namespace.ecs.id 23 | } 24 | -------------------------------------------------------------------------------- /terraform/modules/ecs-cluster/service_discovery.tf: -------------------------------------------------------------------------------- 1 | resource "aws_service_discovery_private_dns_namespace" "ecs" { 2 | name = var.service_discovery_domain 3 | vpc = var.vpc_id 4 | } 5 | -------------------------------------------------------------------------------- /terraform/modules/ecs-service/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" {} 2 | 3 | data "aws_ecs_cluster" "this" { 4 | cluster_name = var.cluster_name 5 | } 6 | -------------------------------------------------------------------------------- /terraform/modules/ecs-service/lb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lb_target_group" "this" { 2 | count = var.use_load_balancer ? 1 : 0 3 | 4 | name = var.name 5 | port = var.container_port 6 | protocol = "HTTP" 7 | vpc_id = var.lb_vpc_id 8 | target_type = "ip" 9 | 10 | health_check { 11 | enabled = var.lb_health_check_enabled 12 | healthy_threshold = var.lb_healthy_threshold 13 | interval = var.lb_interval 14 | protocol = var.lb_protocol 15 | matcher = var.lb_matcher 16 | timeout = var.lb_timeout 17 | path = var.lb_path 18 | unhealthy_threshold = var.lb_unhealthy_threshold 19 | } 20 | } 21 | 22 | resource "aws_lb_listener_rule" "routing" { 23 | count = var.use_load_balancer ? 1 : 0 24 | 25 | listener_arn = var.lb_listener_arn 26 | 27 | action { 28 | type = "forward" 29 | target_group_arn = aws_lb_target_group.this.0.arn 30 | } 31 | 32 | condition { 33 | host_header { 34 | values = var.lb_hosts 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /terraform/modules/ecs-service/outputs.tf: -------------------------------------------------------------------------------- 1 | output "task_arn" { 2 | value = aws_ecs_task_definition.this.arn 3 | } 4 | 5 | output "service_name" { 6 | value = aws_ecs_service.this.name 7 | } 8 | 9 | output "service_arn" { 10 | value = aws_ecs_service.this.id 11 | } 12 | -------------------------------------------------------------------------------- /terraform/modules/ecs-service/route53.tf: -------------------------------------------------------------------------------- 1 | # A record 2 | resource "aws_route53_record" "ipv4" { 3 | count = length(var.lb_hosts) 4 | 5 | zone_id = var.lb_domain_zone_id 6 | name = var.lb_hosts[count.index] 7 | type = "A" 8 | 9 | alias { 10 | name = var.lb_dns_name 11 | zone_id = var.lb_zone_id 12 | evaluate_target_health = true 13 | } 14 | } 15 | 16 | # AAAA record 17 | resource "aws_route53_record" "ipv6" { 18 | count = length(var.lb_hosts) 19 | 20 | zone_id = var.lb_domain_zone_id 21 | name = var.lb_hosts[count.index] 22 | type = "AAAA" 23 | 24 | alias { 25 | name = var.lb_dns_name 26 | zone_id = var.lb_zone_id 27 | evaluate_target_health = true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /terraform/modules/ecs-service/service_discovery.tf: -------------------------------------------------------------------------------- 1 | resource "aws_service_discovery_service" "this" { 2 | name = var.name 3 | 4 | dynamic "dns_config" { 5 | for_each = var.service_discovery_namespace_id == null ? [] : [1] 6 | 7 | content { 8 | namespace_id = var.service_discovery_namespace_id 9 | routing_policy = "MULTIVALUE" 10 | 11 | dns_records { 12 | ttl = 10 13 | type = "A" 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /terraform/modules/s3/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "this" { 2 | bucket = "${var.name}-${random_string.suffix.result}" 3 | } 4 | 5 | resource "aws_s3_bucket_ownership_controls" "this" { 6 | bucket = aws_s3_bucket.this.id 7 | 8 | rule { 9 | object_ownership = "BucketOwnerPreferred" 10 | } 11 | } 12 | 13 | resource "aws_s3_bucket_public_access_block" "this" { 14 | bucket = aws_s3_bucket.this.id 15 | 16 | block_public_acls = var.block_public_acls 17 | block_public_policy = var.block_public_policy 18 | ignore_public_acls = var.ignore_public_acls 19 | restrict_public_buckets = var.restrict_public_buckets 20 | } 21 | 22 | resource "aws_s3_bucket_server_side_encryption_configuration" "this" { 23 | bucket = aws_s3_bucket.this.id 24 | 25 | rule { 26 | apply_server_side_encryption_by_default { 27 | sse_algorithm = "AES256" 28 | } 29 | } 30 | } 31 | 32 | resource "aws_s3_bucket_versioning" "this" { 33 | count = var.enable_versioning ? 1 : 0 34 | bucket = aws_s3_bucket.this.id 35 | 36 | versioning_configuration { 37 | status = var.enable_versioning ? "Enabled" : "Suspended" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /terraform/modules/s3/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bucket" { 2 | value = aws_s3_bucket.this.bucket 3 | } 4 | 5 | output "arn" { 6 | value = aws_s3_bucket.this.arn 7 | } 8 | -------------------------------------------------------------------------------- /terraform/modules/s3/random.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "suffix" { 2 | length = 4 3 | special = false 4 | upper = false 5 | numeric = false 6 | } 7 | -------------------------------------------------------------------------------- /terraform/modules/s3/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to be used throughout the resources" 3 | type = string 4 | } 5 | 6 | variable "enable_versioning" { 7 | description = "Whether Amazon S3 should enable versioning for this bucket." 8 | type = bool 9 | default = false 10 | } 11 | 12 | variable "block_public_acls" { 13 | description = "Whether Amazon S3 should block public ACLs for this bucket." 14 | type = bool 15 | default = true 16 | } 17 | 18 | variable "block_public_policy" { 19 | description = "Whether Amazon S3 should block public bucket policies for this bucket." 20 | type = bool 21 | default = true 22 | } 23 | 24 | variable "ignore_public_acls" { 25 | description = "Whether Amazon S3 should ignore public ACLs for this bucket." 26 | type = bool 27 | default = true 28 | } 29 | 30 | variable "restrict_public_buckets" { 31 | description = "Whether Amazon S3 should restrict public bucket policies for this bucket." 32 | type = bool 33 | default = true 34 | } 35 | -------------------------------------------------------------------------------- /terraform/networking_eips.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "aws_eip" "nat_gateway" { 3 | domain = "vpc" 4 | tags = { 5 | Name = "${local.namespace}-nat-gateway" 6 | } 7 | } 8 | 9 | resource "aws_eip" "bastion" { 10 | instance = aws_instance.bastion.id 11 | domain = "vpc" 12 | tags = { 13 | Name = "${local.namespace}-bastion" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /terraform/networking_gateways.tf: -------------------------------------------------------------------------------- 1 | resource "aws_internet_gateway" "main" { 2 | vpc_id = aws_vpc.main.id 3 | 4 | tags = { 5 | Name = "${local.namespace}-internet-gateway" 6 | } 7 | } 8 | 9 | resource "aws_nat_gateway" "nat_gateway" { 10 | allocation_id = aws_eip.nat_gateway.id 11 | subnet_id = aws_subnet.public.0.id 12 | 13 | tags = { 14 | Name = "${local.namespace}-nat-gateway" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /terraform/networking_routing.tf: -------------------------------------------------------------------------------- 1 | # Public 2 | resource "aws_route_table" "public" { 3 | vpc_id = aws_vpc.main.id 4 | 5 | route { 6 | cidr_block = "0.0.0.0/0" 7 | gateway_id = aws_internet_gateway.main.id 8 | } 9 | 10 | tags = { 11 | Name = "${local.namespace}-public" 12 | } 13 | } 14 | 15 | resource "aws_route_table_association" "public" { 16 | count = length(data.aws_availability_zones.current.names) 17 | subnet_id = element(aws_subnet.public.*.id, count.index) 18 | route_table_id = aws_route_table.public.id 19 | } 20 | 21 | # Private 22 | resource "aws_route_table" "private" { 23 | vpc_id = aws_vpc.main.id 24 | 25 | route { 26 | cidr_block = "0.0.0.0/0" 27 | nat_gateway_id = aws_nat_gateway.nat_gateway.id 28 | } 29 | 30 | tags = { 31 | Name = "${local.namespace}-private" 32 | } 33 | } 34 | 35 | resource "aws_route_table_association" "private" { 36 | count = length(data.aws_availability_zones.current.names) 37 | subnet_id = element(aws_subnet.private.*.id, count.index) 38 | route_table_id = aws_route_table.private.id 39 | } 40 | -------------------------------------------------------------------------------- /terraform/networking_vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "main" { 2 | cidr_block = local.networking.cidr_block 3 | enable_dns_hostnames = true 4 | enable_dns_support = true 5 | 6 | tags = { 7 | Name = "${local.namespace}-vpc" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /terraform/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4romania/monitorizare-vot/b575f81e3d741a8879bfeba59d49756b0075e26b/terraform/outputs.tf -------------------------------------------------------------------------------- /terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.16" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | bucket = "code4-terraform-state" 13 | key = "votemonitor/terraform.tfstate" 14 | region = "eu-west-1" 15 | } 16 | } 17 | 18 | provider "aws" { 19 | region = var.region 20 | 21 | default_tags { 22 | tags = { 23 | app = "votemonitor" 24 | env = var.env 25 | } 26 | } 27 | } 28 | --------------------------------------------------------------------------------