From 60b2207a469a5a1e7a7e5619a8eb1b01c67f314a Mon Sep 17 00:00:00 2001 From: Jed Barber Date: Fri, 10 Feb 2017 18:41:36 +1100 Subject: Preference data reads into Bundles properly, with packed memory and a few fixed bugs --- src/candidates-containers.adb | 219 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 src/candidates-containers.adb (limited to 'src/candidates-containers.adb') 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; + + -- cgit