summaryrefslogtreecommitdiff
path: root/src/candidates-containers.adb
diff options
context:
space:
mode:
authorJed Barber <jjbarber@y7mail.com>2017-02-10 18:41:36 +1100
committerJed Barber <jjbarber@y7mail.com>2017-02-10 18:41:36 +1100
commit60b2207a469a5a1e7a7e5619a8eb1b01c67f314a (patch)
treec928299d78242f2b36798e8c1802914552626352 /src/candidates-containers.adb
parent964a28e91593c4bf1e1c132536828d87f8d12c84 (diff)
Preference data reads into Bundles properly, with packed memory and a few fixed bugs
Diffstat (limited to 'src/candidates-containers.adb')
-rw-r--r--src/candidates-containers.adb219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/candidates-containers.adb b/src/candidates-containers.adb
new file mode 100644
index 0000000..64a03ca
--- /dev/null
+++ b/src/candidates-containers.adb
@@ -0,0 +1,219 @@
+
+
+with Ada.Containers.Vectors;
+with Ada.Text_IO;
+with CSV;
+
+
+package body Candidates.Containers is
+
+
+ procedure Read_Candidates
+ (Filename : in String;
+ State : in State_Name;
+ Candidate_Data : out Candidate_Map)
+ is
+ package My_CSV is new CSV;
+ use Ada.Text_IO;
+ use type Ada.Containers.Count_Type;
+ use type SU.Unbounded_String;
+
+ Input_File : File_Type;
+ Current_Record : My_CSV.CSV_Record;
+ Current_Candidate : Candidate;
+
+ Next_ID : CandidateID := CandidateID'First;
+ begin
+ Open (Input_File, In_File, Filename);
+ Candidate_Data := Candidate_Maps.Empty_Map;
+
+ while not End_Of_File (Input_File) loop
+ Current_Record := My_CSV.Parse_Line (Get_Line (Input_File));
+
+ -- all the field numbers here correspond to how
+ -- AEC Senate candidate data is arranged in csv format
+ if Current_Record.Length = 25 and then
+ Current_Record.Element (2) = "S" and then
+ Current_Record.Element (3) = State_Name'Image (State)
+ then
+ Current_Candidate :=
+ (First_Name => Current_Record.Element (8),
+ Last_Name => Current_Record.Element (7),
+ Group => Current_Record.Element (5),
+ Group_Rank => Current_Record.Element (6),
+ Party => Current_Record.Element (9));
+ Candidate_Data.Insert (Next_ID, Current_Candidate);
+ Next_ID := Next_ID + 1;
+ end if;
+ end loop;
+
+ Close (Input_File);
+ end Read_Candidates;
+
+
+
+
+ -- these two types exist because I can't think of an easier
+ -- way to sort a Candidate_Map into the appropriate order at
+ -- the moment
+
+ type Cand_Sort_Data is record
+ Cand_ID : CandidateID;
+ Group : SU.Unbounded_String;
+ Group_Rank : SU.Unbounded_String;
+ end record;
+
+ package Cand_Sort_Data_Vectors is new Ada.Containers.Vectors
+ (Index_Type => Positive,
+ Element_Type => Cand_Sort_Data);
+
+
+
+
+ function "<"
+ (Left, Right : Cand_Sort_Data)
+ return Boolean
+ is
+ use type SU.Unbounded_String;
+ begin
+ if SU.Length (Left.Group) = SU.Length (Right.Group) then
+ if Left.Group = Right.Group then
+ return Left.Group_Rank < Right.Group_Rank;
+ else
+ return Left.Group < Right.Group;
+ end if;
+ else
+ return SU.Length (Left.Group) < SU.Length (Right.Group);
+ end if;
+ end "<";
+
+
+
+
+ function Generate_Above
+ (Candidate_Data : in Cand_Sort_Data_Vectors.Vector)
+ return Above_Line_Ballot
+ is
+ use type Ada.Containers.Count_Type;
+ use type SU.Unbounded_String;
+
+ Result : Above_Line_Ballot := CandidateID_Map_Maps.Empty_Map;
+ Working_Map : CandidateID_Maps.Map;
+ Current_Group : SU.Unbounded_String;
+ Current_Index : Positive;
+ Next_ID, Working_ID : Positive;
+ begin
+ if Candidate_Data.Length = 0 then
+ return Result;
+ end if;
+
+ Next_ID := 1;
+ Current_Index := Candidate_Data.First_Index;
+ while Current_Index <= Candidate_Data.Last_Index loop
+ Current_Group := Candidate_Data.Element (Current_Index).Group;
+
+ -- the assumption is that the "UG" group is always last
+ -- a fairly safe assumption given alphabetical group order
+ -- but will break down should there be more than... 553 grouped candidates
+ exit when Current_Group = "UG";
+
+ Working_Map := CandidateID_Maps.Empty_Map;
+ Working_ID := 1;
+ loop
+ Working_Map.Insert (Working_ID, Candidate_Data.Element (Current_Index).Cand_ID);
+ Working_ID := Working_ID + 1;
+ Current_Index := Current_Index + 1;
+ exit when Current_Index > Candidate_Data.Last_Index or else
+ Current_Group /= Candidate_Data.Element (Current_Index).Group;
+ end loop;
+ Result.Insert (Next_ID, Working_Map);
+ Next_ID := Next_ID + 1;
+ end loop;
+
+ return Result;
+ end Generate_Above;
+
+
+
+
+ function Generate_Below
+ (Candidate_Data : in Cand_Sort_Data_Vectors.Vector)
+ return Below_Line_Ballot
+ is
+ Result : Below_Line_Ballot := CandidateID_Maps.Empty_Map;
+ Next_ID : Positive := 1;
+ begin
+ for Item of Candidate_Data loop
+ Result.Insert (Next_ID, Item.Cand_ID);
+ Next_ID := Next_ID + 1;
+ end loop;
+ return Result;
+ end Generate_Below;
+
+
+
+
+ procedure Generate_Ballots
+ (Candidate_Data : in Candidate_Map;
+ Above_Ballot : out Above_Line_Ballot;
+ Below_Ballot : out Below_Line_Ballot)
+ is
+ package Sorting is new Cand_Sort_Data_Vectors.Generic_Sorting;
+
+ My_Candidate_Data : Cand_Sort_Data_Vectors.Vector;
+ Working_Candidate : Candidate;
+ begin
+ My_Candidate_Data := Cand_Sort_Data_Vectors.Empty_Vector;
+
+ for Cursor in Candidate_Data.Iterate loop
+ Working_Candidate := Candidate_Maps.Element (Cursor);
+ My_Candidate_Data.Append
+ ((Cand_ID => Candidate_Maps.Key (Cursor),
+ Group => Working_Candidate.Group,
+ Group_Rank => Working_Candidate.Group_Rank));
+ end loop;
+
+ Sorting.Sort (My_Candidate_Data);
+
+ Above_Ballot := Generate_Above (My_Candidate_Data);
+ Below_Ballot := Generate_Below (My_Candidate_Data);
+ end Generate_Ballots;
+
+
+
+
+ function To_String
+ (Above_Ballot : in Above_Line_Ballot)
+ return String
+ is
+ Result : SU.Unbounded_String := SU.To_Unbounded_String (0);
+ begin
+ for Group_Cursor in Above_Ballot.Iterate loop
+ SU.Append (Result, Integer'Image (CandidateID_Map_Maps.Key (Group_Cursor)) & ": ");
+ for Box of CandidateID_Map_Maps.Element (Group_Cursor) loop
+ SU.Append (Result, CandidateID'Image (Box) & " ");
+ end loop;
+ SU.Append (Result, ASCII.LF);
+ end loop;
+ return SU.To_String (Result);
+ end To_String;
+
+
+
+
+ function To_String
+ (Below_Ballot : in Below_Line_Ballot)
+ return String
+ is
+ Result : SU.Unbounded_String := SU.To_Unbounded_String (0);
+ begin
+ for Box of Below_Ballot loop
+ SU.Append (Result, CandidateID'Image (Box) & " ");
+ end loop;
+ return SU.To_String (Result);
+ end To_String;
+
+
+end Candidates.Containers;
+
+