Groups raw ballot numbers by precinct, extracts the last 4 digits of each (matching the Lansing PS analysis script), then reports min/max and any gaps in the range. Report rows are now sorted descending by precinct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>master
| @@ -105,7 +105,7 @@ Class KitController | |||||
| set Model.StidDropDown = SettingsRepository.GetStidDropDownRS() | set Model.StidDropDown = SettingsRepository.GetStidDropDownRS() | ||||
| set Model.ColorsDropDown = ColorsRepository.GetColorsDropDownRS() | set Model.ColorsDropDown = ColorsRepository.GetColorsDropDownRS() | ||||
| set Model.Precincts = PurpleEnvelopeReportHelper().SortPrecinctColorRows(InkjetRecordsRepository.GetDistinctPrecinctsByKitId(id)) | set Model.Precincts = PurpleEnvelopeReportHelper().SortPrecinctColorRows(InkjetRecordsRepository.GetDistinctPrecinctsByKitId(id)) | ||||
| set Model.PrecinctBallotRanges = PurpleEnvelopeReportHelper().SortPrecinctBallotRangeRows(InkjetRecordsRepository.GetPrecinctBallotRangesByKitId(id)) | |||||
| set Model.PrecinctBallotRanges = PurpleEnvelopeReportHelper().BuildBallotRangesWithMissing(InkjetRecordsRepository.GetBallotNumbersByKitId(id)) | |||||
| On Error Resume Next | On Error Resume Next | ||||
| Model.PurpleEnvelopeElectionLabel = PurpleEnvelopeReportHelper().FormatElectionLabel(SettingsRepository.FindByName("Electiondate")) | Model.PurpleEnvelopeElectionLabel = PurpleEnvelopeReportHelper().FormatElectionLabel(SettingsRepository.FindByName("Electiondate")) | ||||
| If Err.Number <> 0 Then | If Err.Number <> 0 Then | ||||
| @@ -340,6 +340,12 @@ Class InkjetRecordsRepository_Class | |||||
| set GetDistinctPrecinctsByKitId = rs | set GetDistinctPrecinctsByKitId = rs | ||||
| End Function | End Function | ||||
| Public Function GetBallotNumbersByKitId(KitID) | |||||
| dim sql : sql = "SELECT [PRECINCT], [BALLOT_NUMBER] FROM [InkjetRecords] WHERE [KitID] = ?" | |||||
| dim rs : set rs = DAL.Query(sql, KitID) | |||||
| set GetBallotNumbersByKitId = rs | |||||
| End Function | |||||
| Public Function GetPrecinctBallotRangesByKitId(KitID) | Public Function GetPrecinctBallotRangesByKitId(KitID) | ||||
| dim sql : sql = "SELECT [PRECINCT], MIN(VAL([BALLOT_NUMBER])) AS [LowBallotNumber], MAX(VAL([BALLOT_NUMBER])) AS [HighBallotNumber] " &_ | dim sql : sql = "SELECT [PRECINCT], MIN(VAL([BALLOT_NUMBER])) AS [LowBallotNumber], MAX(VAL([BALLOT_NUMBER])) AS [HighBallotNumber] " &_ | ||||
| "FROM [InkjetRecords] " &_ | "FROM [InkjetRecords] " &_ | ||||
| @@ -80,6 +80,101 @@ Class PurpleEnvelopeReportHelper_Class | |||||
| set SortPrecinctBallotRangeRows = list | set SortPrecinctBallotRangeRows = list | ||||
| End Function | End Function | ||||
| Public Function BuildBallotRangesWithMissing(ByVal rs) | |||||
| ' Groups raw PRECINCT/BALLOT_NUMBER rows by precinct, extracts last 4 digits of | |||||
| ' the numeric portion (matching the PowerShell InkjetRecords analysis script), | |||||
| ' computes min/max and any missing numbers in that range, then returns rows | |||||
| ' sorted by precinct in descending order. | |||||
| dim precinctNums : set precinctNums = CreateObject("Scripting.Dictionary") | |||||
| dim precinct, rawNum, digits, i, ch, last4Str | |||||
| Do Until rs.EOF | |||||
| precinct = Trim(rs("PRECINCT") & "") | |||||
| rawNum = rs("BALLOT_NUMBER") & "" | |||||
| digits = "" | |||||
| For i = 1 To Len(rawNum) | |||||
| ch = Mid(rawNum, i, 1) | |||||
| If ch >= "0" And ch <= "9" Then digits = digits & ch | |||||
| Next | |||||
| If Len(digits) >= 4 Then | |||||
| last4Str = Mid(digits, Len(digits) - 3, 4) | |||||
| If precinctNums.Exists(precinct) Then | |||||
| precinctNums(precinct) = precinctNums(precinct) & "," & last4Str | |||||
| Else | |||||
| precinctNums.Add precinct, last4Str | |||||
| End If | |||||
| End If | |||||
| rs.MoveNext | |||||
| Loop | |||||
| If precinctNums.Count = 0 Then | |||||
| set BuildBallotRangesWithMissing = new LinkedList_Class | |||||
| Exit Function | |||||
| End If | |||||
| dim keys : keys = precinctNums.Keys | |||||
| dim itemCount : itemCount = precinctNums.Count - 1 | |||||
| dim items() | |||||
| ReDim items(itemCount) | |||||
| dim k, j, n | |||||
| dim numStrs, numArr, minVal, maxVal, presenceDict, missingList, missingCount, row | |||||
| For k = 0 To itemCount | |||||
| precinct = keys(k) | |||||
| numStrs = Split(precinctNums(precinct), ",") | |||||
| ReDim numArr(UBound(numStrs)) | |||||
| For j = 0 To UBound(numStrs) | |||||
| numArr(j) = CLng(numStrs(j)) | |||||
| Next | |||||
| minVal = numArr(0) | |||||
| maxVal = numArr(0) | |||||
| For j = 1 To UBound(numArr) | |||||
| If numArr(j) < minVal Then minVal = numArr(j) | |||||
| If numArr(j) > maxVal Then maxVal = numArr(j) | |||||
| Next | |||||
| set presenceDict = CreateObject("Scripting.Dictionary") | |||||
| For j = 0 To UBound(numArr) | |||||
| If Not presenceDict.Exists(numArr(j)) Then | |||||
| presenceDict.Add numArr(j), True | |||||
| End If | |||||
| Next | |||||
| missingList = "" | |||||
| missingCount = 0 | |||||
| For n = minVal To maxVal | |||||
| If Not presenceDict.Exists(n) Then | |||||
| missingCount = missingCount + 1 | |||||
| If Len(missingList) > 0 Then missingList = missingList & ", " | |||||
| missingList = missingList & CStr(n) | |||||
| End If | |||||
| Next | |||||
| set row = new PrecinctBallotRangeRow_ViewModel_Class | |||||
| row.PRECINCT = precinct | |||||
| row.LowBallotNumber = minVal | |||||
| row.HighBallotNumber = maxVal | |||||
| row.MissingCount = missingCount | |||||
| row.MissingNumbers = missingList | |||||
| Set items(k) = row | |||||
| Next | |||||
| SortPrecinctItems items | |||||
| dim list : set list = new LinkedList_Class | |||||
| ' Iterate in reverse for descending precinct order | |||||
| For k = UBound(items) To 0 Step -1 | |||||
| list.Push items(k) | |||||
| Next | |||||
| set BuildBallotRangesWithMissing = list | |||||
| End Function | |||||
| Private Sub SortPrecinctItems(ByRef items) | Private Sub SortPrecinctItems(ByRef items) | ||||
| If Not IsArray(items) Then Exit Sub | If Not IsArray(items) Then Exit Sub | ||||
| @@ -35,6 +35,8 @@ Class PrecinctBallotRangeRow_ViewModel_Class | |||||
| Public PRECINCT | Public PRECINCT | ||||
| Public LowBallotNumber | Public LowBallotNumber | ||||
| Public HighBallotNumber | Public HighBallotNumber | ||||
| Public MissingCount | |||||
| Public MissingNumbers | |||||
| End Class | End Class | ||||
| Class SwitchBoard_PurpleEnvelopesViewModel_Class | Class SwitchBoard_PurpleEnvelopesViewModel_Class | ||||
| @@ -214,12 +214,14 @@ | |||||
| <table class="table table-striped table-bordered mb-0"> | <table class="table table-striped table-bordered mb-0"> | ||||
| <thead> | <thead> | ||||
| <tr class="print-page-spacer" aria-hidden="true"> | <tr class="print-page-spacer" aria-hidden="true"> | ||||
| <th colspan="3"></th> | |||||
| <th colspan="5"></th> | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <th>Precinct</th> | <th>Precinct</th> | ||||
| <th class="num">Low Ballot Number</th> | <th class="num">Low Ballot Number</th> | ||||
| <th class="num">High Ballot Number</th> | <th class="num">High Ballot Number</th> | ||||
| <th class="num">Missing Count</th> | |||||
| <th>Missing Numbers</th> | |||||
| </tr> | </tr> | ||||
| </thead> | </thead> | ||||
| <tbody> | <tbody> | ||||
| @@ -234,13 +236,15 @@ | |||||
| <td><%= H(ballotRangeRow.PRECINCT) %></td> | <td><%= H(ballotRangeRow.PRECINCT) %></td> | ||||
| <td class="num"><%= H(ballotRangeRow.LowBallotNumber) %></td> | <td class="num"><%= H(ballotRangeRow.LowBallotNumber) %></td> | ||||
| <td class="num"><%= H(ballotRangeRow.HighBallotNumber) %></td> | <td class="num"><%= H(ballotRangeRow.HighBallotNumber) %></td> | ||||
| <td class="num"><% If ballotRangeRow.MissingCount > 0 Then %><strong><%= H(ballotRangeRow.MissingCount) %></strong><% Else %>0<% End If %></td> | |||||
| <td><small><%= H(ballotRangeRow.MissingNumbers) %></small></td> | |||||
| </tr> | </tr> | ||||
| <% | <% | ||||
| Loop | Loop | ||||
| Else | Else | ||||
| %> | %> | ||||
| <tr> | <tr> | ||||
| <td colspan="3">No precinct ballot data found for this kit.</td> | |||||
| <td colspan="5">No precinct ballot data found for this kit.</td> | |||||
| </tr> | </tr> | ||||
| <% | <% | ||||
| End If | End If | ||||
Powered by TurnKey Linux.