-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
23f78fd
commit 4c67fe4
Showing
16 changed files
with
1,577 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<RootNamespace>DigitaleBriefwahl.Tally</RootNamespace> | ||
<AssemblyName>DigiTally</AssemblyName> | ||
<OutputType>Exe</OutputType> | ||
<OutputPath>../output/$(Configuration)/DigiTally</OutputPath> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Bugsnag" Version="3.1.0" /> | ||
<PackageReference Include="GitVersion.MsBuild" Version="5.12.0" PrivateAssets="true" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\DigitaleBriefwahl.ExceptionHandling\DigitaleBriefwahl.ExceptionHandling.csproj" /> | ||
<ProjectReference Include="..\DigitaleBriefwahl\DigitaleBriefwahl.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright (c) 2024 Eberhard Beilharz | ||
// This software is licensed under the GNU General Public License version 3 | ||
// (https://opensource.org/licenses/GPL-3.0) | ||
|
||
using System; | ||
using System.IO; | ||
using System.Text; | ||
using DigitaleBriefwahl.ExceptionHandling; | ||
using DigitaleBriefwahl.Model; | ||
|
||
namespace DigitaleBriefwahl.Tally | ||
{ | ||
public class Program | ||
{ | ||
public static string TallyBallots(string configName, string ballotsDirectory) | ||
{ | ||
var bldr = new StringBuilder(); | ||
var configuration = Configuration.Configure(configName); | ||
var readBallot = new ReadBallots(configName); | ||
|
||
foreach (var filename in Directory.GetFiles(ballotsDirectory)) | ||
{ | ||
readBallot.AddBallot(filename); | ||
} | ||
|
||
bldr.AppendLine(configuration.Title); | ||
bldr.AppendLine(new string('=', configuration.Title.Length)); | ||
bldr.AppendLine(); | ||
bldr.AppendLine( | ||
$"Total of {readBallot.NumberOfBallots} ballots, thereof {readBallot.NumberOfInvalidBallots} at least partially invalid."); | ||
bldr.AppendLine(); | ||
bldr.Append(readBallot.GetResultString()); | ||
return bldr.ToString(); | ||
} | ||
public static void Main(string[] args) | ||
{ | ||
ExceptionLogging.Initialize("5012aef9a281f091c1fceea40c03003b", "DigiTally", args); | ||
|
||
// Setup command line options | ||
if (args.Length <= 0 || args[0] == "--help") | ||
{ | ||
Console.WriteLine("Usage:"); | ||
Console.WriteLine("DigiTally.exe ballotsDirectory [configFile]"); | ||
Console.WriteLine("ballotsDirectory: directory that contains the decrypted ballots"); | ||
Console.WriteLine("configFile: optional name and path of the config file"); | ||
return; | ||
} | ||
var configName = args.Length > 1 ? args[1] : Configuration.ConfigName; | ||
if (!File.Exists(configName)) | ||
{ | ||
Console.WriteLine($"Can't find {configName}. Exiting."); | ||
return; | ||
} | ||
|
||
Console.WriteLine(TallyBallots(configName, args[0])); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Copyright (c) 2024 Eberhard Beilharz | ||
// This software is licensed under the GNU General Public License version 3 | ||
// (https://opensource.org/licenses/GPL-3.0) | ||
|
||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
using DigitaleBriefwahl.Model; | ||
|
||
namespace DigitaleBriefwahl.Tally | ||
{ | ||
public class ReadBallots | ||
{ | ||
private Configuration _configuration; | ||
private Dictionary<ElectionModel, Dictionary<string, CandidateResult>> _results; | ||
|
||
public ReadBallots(string configFileName) | ||
{ | ||
_results = new Dictionary<ElectionModel, Dictionary<string, CandidateResult>>(); | ||
_configuration = Configuration.Configure(configFileName); | ||
} | ||
|
||
public bool AddBallot(string ballotFileName) | ||
{ | ||
using var ballotFile = new StreamReader(ballotFileName); | ||
var title = ballotFile.ReadLine(); // The election | ||
var separator = ballotFile.ReadLine(); // ============ | ||
if (string.IsNullOrWhiteSpace(separator) || !separator.StartsWith("=")) | ||
{ | ||
return false; | ||
} | ||
SkipEmptyLines(ballotFile); | ||
var ballotIsPartiallyInvalid = false; | ||
foreach (var election in _configuration.Elections) | ||
{ | ||
var electionTitle = ballotFile.ReadLine(); // Election | ||
separator = ballotFile.ReadLine(); // -------- | ||
if (string.IsNullOrWhiteSpace(separator) || !separator.StartsWith("--") || electionTitle != election.Name) | ||
{ | ||
return false; | ||
} | ||
|
||
var prevInvalid = election.Invalid; | ||
_results[election] = election.ReadVotesFromBallot(ballotFile, _results.TryGetValue(election, out var result) ? result: null); | ||
ballotIsPartiallyInvalid |= election.Invalid > prevInvalid; | ||
} | ||
|
||
NumberOfBallots++; | ||
|
||
if (ballotIsPartiallyInvalid) | ||
NumberOfInvalidBallots++; | ||
|
||
return true; | ||
} | ||
|
||
private void SkipEmptyLines(StreamReader stream) | ||
{ | ||
int peek; | ||
while ((peek = stream.Peek()) > -1) | ||
{ | ||
if ((char)peek != '\r' && (char)peek != '\n') | ||
return; | ||
|
||
stream.ReadLine(); | ||
} | ||
} | ||
|
||
public int NumberOfBallots { get; private set; } | ||
public int NumberOfInvalidBallots { get; private set; } | ||
public Dictionary<ElectionModel, Dictionary<string, CandidateResult>> Results => _results; | ||
|
||
public string GetResultString() | ||
{ | ||
var strBuilder = new StringBuilder(); | ||
foreach (var election in _results) | ||
{ | ||
strBuilder.AppendLine($"{election.Key.Name}"); | ||
strBuilder.AppendLine(new string('-', election.Key.Name.Length)); | ||
strBuilder.AppendLine(election.Key.GetResultString(election.Value)); | ||
} | ||
return strBuilder.ToString(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// Copyright (c) 2024 Eberhard Beilharz | ||
// This software is licensed under the GNU General Public License version 3 | ||
// (https://opensource.org/licenses/GPL-3.0) | ||
|
||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using NUnit.Framework; | ||
|
||
namespace DigitaleBriefwahl.Tally.Tests | ||
{ | ||
[TestFixture] | ||
public class AcceptanceTests | ||
{ | ||
private string _configFileName; | ||
private string _ballotDirectoryName; | ||
|
||
[SetUp] | ||
public void Setup() | ||
{ | ||
_ballotDirectoryName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
Directory.CreateDirectory(_ballotDirectoryName); | ||
_configFileName = Path.GetTempFileName(); | ||
File.WriteAllText(_configFileName, @"[Wahlen] | ||
Titel=The election | ||
Wahl1=Election1 | ||
Wahl2=Election2 | ||
Email=election@example.com | ||
PublicKey=12345678.asc | ||
[Election1] | ||
Text=Some description | ||
Typ=Weighted | ||
Stimmen=2 | ||
Kandidat1=Mickey Mouse | ||
Kandidat2=Donald Duck | ||
Kandidat3=Dagobert Duck | ||
Kandidat4=Daisy Duck | ||
[Election2] | ||
Text=Some description | ||
Typ=YesNo | ||
Stimmen=2 | ||
Kandidat1=One | ||
Kandidat2=Two | ||
Kandidat3=Three | ||
Kandidat4=Four | ||
"); | ||
} | ||
|
||
[TearDown] | ||
public void Teardown() | ||
{ | ||
File.Delete(_configFileName); | ||
Directory.Delete(_ballotDirectoryName, true); | ||
} | ||
|
||
[Test] | ||
public void EndToEnd() | ||
{ | ||
var ballotFileName1 = Path.Combine(_ballotDirectoryName, Path.GetRandomFileName()); | ||
File.WriteAllText(ballotFileName1, "The election\r\n" + | ||
"============\r\n" + | ||
"\r\n" + | ||
"Election1\r\n" + | ||
"--------\r\n" + | ||
"(2 Stimmen; Wahl der Reihenfolge nach mit 1.-2. kennzeichnen)\r\n" + | ||
"2. Mickey Mouse\r\n" + | ||
" Donald Duck\r\n" + | ||
"1. Dagobert Duck\r\n" + | ||
" Daisy Duck\r\n" + | ||
"\r\n" + | ||
"Election2\r\n" + | ||
"--------\r\n" + | ||
"(J=Ja, E=Enthaltung, N=Nein)\r\n" + | ||
"1. [J] One\r\n" + | ||
"2. [N] Two\r\n" + | ||
"3. [J] Three\r\n" + | ||
"4. [E] Four\r\n" + | ||
"\r\n" + | ||
"\r\n" + | ||
"12345\r\n"); | ||
|
||
var ballotFileName2 = Path.Combine(_ballotDirectoryName, Path.GetRandomFileName()); | ||
File.WriteAllText(ballotFileName2, "The election\r\n" + | ||
"============\r\n" + | ||
"\r\n" + | ||
"Election1\r\n" + | ||
"--------\r\n" + | ||
"(2 Stimmen; Wahl der Reihenfolge nach mit 1.-2. kennzeichnen)\r\n" + | ||
" Mickey Mouse\r\n" + | ||
"1. Donald Duck\r\n" + | ||
"2. Dagobert Duck\r\n" + | ||
" Daisy Duck\r\n" + | ||
"\r\n" + | ||
"Election2\r\n" + | ||
"--------\r\n" + | ||
"(J=Ja, E=Enthaltung, N=Nein)\r\n" + | ||
"1. [N] One\r\n" + | ||
"2. [J] Two\r\n" + | ||
"3. [E] Three\r\n" + | ||
"4. [E] Four\r\n" + | ||
"\r\n" + | ||
"\r\n" + | ||
"12345\r\n"); | ||
|
||
var ballotFileName3 = Path.Combine(_ballotDirectoryName, Path.GetRandomFileName()); | ||
File.WriteAllText(ballotFileName3, "The election\r\n" + | ||
"============\r\n" + | ||
"\r\n" + | ||
"Election1\r\n" + | ||
"--------\r\n" + | ||
"(2 Stimmen; Wahl der Reihenfolge nach mit 1.-2. kennzeichnen)\r\n" + | ||
"1. Mickey Mouse\r\n" + | ||
" Donald Duck\r\n" + | ||
"2. Dagobert Duck\r\n" + | ||
" Daisy Duck\r\n" + | ||
"\r\n" + | ||
"Election2\r\n" + | ||
"--------\r\n" + | ||
"(J=Ja, E=Enthaltung, N=Nein)\r\n" + | ||
"1. [X] One\r\n" + | ||
"2. [E] Two\r\n" + | ||
"3. [N] Three\r\n" + | ||
"4. [E] Four\r\n" + | ||
"\r\n" + | ||
"\r\n" + | ||
"12345\r\n"); | ||
|
||
var output = Program.TallyBallots(_configFileName, _ballotDirectoryName); | ||
|
||
Assert.That(output, Is.EqualTo(@"The election | ||
============ | ||
Total of 3 ballots, thereof 1 at least partially invalid. | ||
Election1 | ||
--------- | ||
1. Dagobert Duck (4 points) | ||
2. Mickey Mouse (3 points) | ||
Donald Duck (2 points) | ||
Daisy Duck (0 points) | ||
(3 ballots, thereof 0 invalid) | ||
Election2 | ||
--------- | ||
One: 1 J, 1 N, 0 E | ||
Two: 1 J, 1 N, 0 E | ||
Three: 1 J, 0 N, 1 E | ||
Four: 0 J, 0 N, 2 E | ||
(3 ballots, thereof 1 invalid) | ||
")); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<RootNamespace>DigitaleBriefwahl.Tally.Tests</RootNamespace> | ||
<AssemblyTitle>DigiTallyTests</AssemblyTitle> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<OutputPath>bin/$(Configuration)</OutputPath> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<PackageReference Include="ini-parser" Version="2.5.2" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> | ||
<PackageReference Include="NUnit" Version="4.1.0" /> | ||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\DigiTally\DigiTally.csproj" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.