@dra2020/district-analytics 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +149 -113
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +103 -61
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/score.d.ts +1 -1
- package/package.json +2 -2
|
@@ -143,37 +143,47 @@ class AnalyticsSession {
|
|
|
143
143
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
144
144
|
// TODO - SCORE: Toggle
|
|
145
145
|
if (this.useLegacy()) {
|
|
146
|
+
console.log("Using legacy district-analytics.");
|
|
146
147
|
// NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
147
148
|
// we could want them to passed into an analytics session as data, along with
|
|
148
149
|
// everything else. For now, this keeps branching out of the main code.
|
|
149
150
|
results_1.doConfigureScales(this);
|
|
150
151
|
}
|
|
152
|
+
else {
|
|
153
|
+
console.log("Using dra-score analytics.");
|
|
154
|
+
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
155
|
+
results_1.doConfigureScales(this);
|
|
156
|
+
}
|
|
151
157
|
}
|
|
152
158
|
processConfig(config) {
|
|
153
159
|
// NOTE - Session settings are required:
|
|
154
160
|
// - Analytics suites can be defaulted to all with [], but
|
|
155
161
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
162
|
+
// TODO - SCORE: Delete
|
|
156
163
|
config['suites'] = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
157
164
|
// Default the Census & redistricting cycle to 2010
|
|
158
165
|
if (!(U.keyExists('cycle', config)))
|
|
159
166
|
config['cycle'] = 2010;
|
|
160
167
|
return config;
|
|
161
168
|
}
|
|
169
|
+
// TODO - SCORE: Toggle = Invert this logic
|
|
162
170
|
useLegacy() {
|
|
163
|
-
|
|
171
|
+
// TODO - SCORE: Opt-out
|
|
172
|
+
return (U.keyExists('useScore', this.config) && (this.config['useScore'] != null) && (!(this.config['useScore']))) ? true : false;
|
|
173
|
+
// TODO - SCORE: Opt-in
|
|
174
|
+
// return (U.keyExists('useScore', this.config) && (this.config['useScore'])) ? false : true;
|
|
164
175
|
}
|
|
165
176
|
// Using the the data in the analytics session, calculate all the
|
|
166
177
|
// analytics & validations, saving/updating the individual test results.
|
|
167
|
-
analyzePlan(bLog = false, overridesJSON
|
|
178
|
+
analyzePlan(bLog = false, overridesJSON) {
|
|
168
179
|
try {
|
|
169
180
|
preprocess_1.doPreprocessData(this, bLog);
|
|
170
181
|
analyze_1.doAnalyzeDistricts(this, bLog);
|
|
171
|
-
// TODO - SCORE
|
|
172
182
|
analyze_1.doAnalyzePlan(this, bLog);
|
|
183
|
+
// TODO - SCORE
|
|
173
184
|
this._profile = score_1.profilePlan(this, bLog);
|
|
174
|
-
this._scorecard = score_1.scorePlan(this._profile, bLog);
|
|
185
|
+
this._scorecard = score_1.scorePlan(this, this._profile, bLog, overridesJSON);
|
|
175
186
|
results_1.doAnalyzePostProcessing(this, bLog);
|
|
176
|
-
//
|
|
177
187
|
}
|
|
178
188
|
catch (_a) {
|
|
179
189
|
console.log("Exception caught by analyzePlan()");
|
|
@@ -295,13 +305,13 @@ class AnalyticsSession {
|
|
|
295
305
|
// NOTE - Not sure why this has to be up here ...
|
|
296
306
|
populationDeviationThreshold() {
|
|
297
307
|
// TODO - SCORE: Toggle
|
|
298
|
-
// const bLegacy = this._bLegacy; DELETE
|
|
299
308
|
if (this.useLegacy()) {
|
|
300
309
|
return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
|
|
301
310
|
}
|
|
302
311
|
else {
|
|
303
|
-
|
|
304
|
-
|
|
312
|
+
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
313
|
+
// NOTE - The plan may not have been scored yet, i.e., no scorecard yet.
|
|
314
|
+
return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
|
|
305
315
|
}
|
|
306
316
|
}
|
|
307
317
|
}
|
|
@@ -478,7 +488,6 @@ class Districts {
|
|
|
478
488
|
let outerThis = this;
|
|
479
489
|
// Default the pop dev % for the dummy Unassigned district to 0%.
|
|
480
490
|
// Default the pop dev % for real (1–N) but empty districts to 100%.
|
|
481
|
-
// TODO - SCORE: Why did I mark this?
|
|
482
491
|
let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
|
|
483
492
|
// Get the geoIDs assigned to the district
|
|
484
493
|
// Guard against empty districts
|
|
@@ -928,12 +937,44 @@ exports.doAnalyzeDistricts = doAnalyzeDistricts;
|
|
|
928
937
|
// calls might make chunking for aync easier.
|
|
929
938
|
function doAnalyzePlan(s, bLog = false) {
|
|
930
939
|
// TODO - SCORE: Toggle
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
940
|
+
if (s.useLegacy()) {
|
|
941
|
+
// Disable most legacy analytics
|
|
942
|
+
// Get the requested suites, and only execute those tests
|
|
943
|
+
let requestedSuites = s.config['suites'];
|
|
944
|
+
// Tests in the "Legal" suite, i.e., pass/ fail constraints
|
|
945
|
+
if (requestedSuites.includes(0 /* Legal */)) {
|
|
946
|
+
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
947
|
+
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
948
|
+
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
949
|
+
s.tests[4 /* PopulationDeviation */] = equal_1.doPopulationDeviation(s, bLog);
|
|
950
|
+
// NOTE - I can't check whether a population deviation is legal or not, until
|
|
951
|
+
// the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
952
|
+
// the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
953
|
+
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
954
|
+
// Create an empty test entry here though ...
|
|
955
|
+
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
956
|
+
}
|
|
957
|
+
// Tests in the "Fair" suite
|
|
958
|
+
if (requestedSuites.includes(1 /* Fair */)) {
|
|
959
|
+
s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
|
|
960
|
+
s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
|
|
961
|
+
s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
|
|
962
|
+
s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
|
|
963
|
+
s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
|
|
964
|
+
s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
|
|
965
|
+
}
|
|
966
|
+
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
967
|
+
if (requestedSuites.includes(2 /* Best */)) {
|
|
968
|
+
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
969
|
+
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
970
|
+
s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
971
|
+
s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
972
|
+
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
973
|
+
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
// TODO - SCORE: Except these. Continue to do these here vs. dra-score.
|
|
937
978
|
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
938
979
|
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
939
980
|
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
@@ -944,24 +985,8 @@ function doAnalyzePlan(s, bLog = false) {
|
|
|
944
985
|
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
945
986
|
// Create an empty test entry here though ...
|
|
946
987
|
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
947
|
-
}
|
|
948
|
-
// Tests in the "Fair" suite
|
|
949
|
-
if (requestedSuites.includes(1 /* Fair */)) {
|
|
950
|
-
s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
|
|
951
|
-
s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
|
|
952
|
-
s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
|
|
953
|
-
s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
|
|
954
|
-
s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
|
|
955
|
-
s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
|
|
956
|
-
}
|
|
957
|
-
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
958
|
-
if (requestedSuites.includes(2 /* Best */)) {
|
|
959
|
-
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
960
|
-
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
961
988
|
s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
962
989
|
s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
963
|
-
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
964
|
-
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
965
990
|
}
|
|
966
991
|
// Enable a Test Log and Scorecard to be generated
|
|
967
992
|
s.bPlanAnalyzed = true;
|
|
@@ -975,8 +1000,6 @@ exports.doAnalyzePlan = doAnalyzePlan;
|
|
|
975
1000
|
// NOTE - Should this be conditionalized on the test suites requested?
|
|
976
1001
|
// Those are encapsulated in reports.ts right now, so not doing that.
|
|
977
1002
|
function doDeriveSecondaryTests(s, bLog = false) {
|
|
978
|
-
// TODO - SCORE: Toggle
|
|
979
|
-
// const bLegacy = s._bLegacy;
|
|
980
1003
|
s.tests[3 /* EqualPopulation */] = equal_1.doHasEqualPopulations(s, bLog);
|
|
981
1004
|
}
|
|
982
1005
|
exports.doDeriveSecondaryTests = doDeriveSecondaryTests;
|
|
@@ -1596,18 +1619,11 @@ exports.doPopulationDeviation = doPopulationDeviation;
|
|
|
1596
1619
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1597
1620
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1598
1621
|
function doHasEqualPopulations(s, bLog = false) {
|
|
1599
|
-
// TODO - SCORE: Toggle
|
|
1600
|
-
// const bLegacy = s._bLegacy;
|
|
1601
|
-
const scorecard = s._scorecard;
|
|
1602
|
-
const popDevNormalizedNEW = scorecard.best.populationDeviation.normalized;
|
|
1603
|
-
const bScore = (popDevNormalizedNEW > 0) ? true : false;
|
|
1604
|
-
// TODO - Get scales
|
|
1605
|
-
// TODO - Flow through to RequirementsChecklist
|
|
1606
1622
|
let test = s.getTest(3 /* EqualPopulation */);
|
|
1607
1623
|
// Get the normalized population deviation %
|
|
1608
1624
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1609
|
-
|
|
1610
|
-
|
|
1625
|
+
const popDevPct = popDevTest['score'];
|
|
1626
|
+
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1611
1627
|
// Populate the test entry
|
|
1612
1628
|
if (popDevNormalized > 0) {
|
|
1613
1629
|
test['score'] = true;
|
|
@@ -1617,7 +1633,6 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1617
1633
|
}
|
|
1618
1634
|
test['details']['deviation'] = popDevPct;
|
|
1619
1635
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1620
|
-
console.log("Equal population =", test['score'], bScore);
|
|
1621
1636
|
// Populate the N+1 summary "district" in district.statistics
|
|
1622
1637
|
let bEqualPop = s.districts.statistics[D.DistrictField.bEqualPop];
|
|
1623
1638
|
let summaryRow = s.districts.numberOfRows() - 1;
|
|
@@ -2236,12 +2251,14 @@ function prepareRequirementsChecklist(s, bLog = false) {
|
|
|
2236
2251
|
const emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
2237
2252
|
const discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
2238
2253
|
const embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
2239
|
-
|
|
2240
|
-
const
|
|
2241
|
-
//
|
|
2254
|
+
// TODO - SCORE: DELETE - This code is hooked correctly to use dra-score
|
|
2255
|
+
// const scorecard = s._scorecard as Score.Scorecard;
|
|
2256
|
+
// const populationDeviation = scorecard.best.populationDeviation.raw;
|
|
2242
2257
|
const populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
2258
|
+
// console.log("Population deviations =", populationDeviationDetail, populationDeviation);
|
|
2259
|
+
// const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
|
|
2243
2260
|
const deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
2244
|
-
console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
|
|
2261
|
+
// console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
|
|
2245
2262
|
const xx = s.state.xx;
|
|
2246
2263
|
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
2247
2264
|
const stateReqsDict = state_reqs_json_1.default;
|
|
@@ -2509,20 +2526,27 @@ exports.doConfigureScales = doConfigureScales;
|
|
|
2509
2526
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
2510
2527
|
function doAnalyzePostProcessing(s, bLog = false) {
|
|
2511
2528
|
// TODO - SCORE: Toggle
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2529
|
+
if (s.useLegacy()) {
|
|
2530
|
+
// Normalize the raw scores for all the numerics tests
|
|
2531
|
+
let testResults = U.getNumericObjectKeys(testDefns);
|
|
2532
|
+
for (let testID of testResults) {
|
|
2533
|
+
if (testDefns[testID]['normalize']) {
|
|
2534
|
+
let testResult = s.getTest(testID);
|
|
2535
|
+
let rawScore = testResult['score'];
|
|
2536
|
+
let normalizedScore;
|
|
2537
|
+
normalizedScore = U.normalize(rawScore, s.testScales[testID]);
|
|
2538
|
+
testResult['normalizedScore'] = normalizedScore;
|
|
2539
|
+
// Add the scale used to normalize the raw score to the details
|
|
2540
|
+
testResult['details']['scale'] = s.testScales[testID].scale;
|
|
2541
|
+
}
|
|
2524
2542
|
}
|
|
2525
2543
|
}
|
|
2544
|
+
else {
|
|
2545
|
+
// Just populate the normalized population deviation score in the test
|
|
2546
|
+
const scorecard = s._scorecard;
|
|
2547
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
2548
|
+
test['normalizedScore'] = scorecard.best.populationDeviation.normalized;
|
|
2549
|
+
}
|
|
2526
2550
|
// Derive secondary tests
|
|
2527
2551
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
2528
2552
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
@@ -2643,9 +2667,27 @@ function makeArrayOfDemographics(s, bLog = false) {
|
|
|
2643
2667
|
return demographicsArray;
|
|
2644
2668
|
}
|
|
2645
2669
|
// SCORE A PLAN
|
|
2646
|
-
function scorePlan(p, bLog = false) {
|
|
2670
|
+
function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
2647
2671
|
let scorer = new Score.Scorer();
|
|
2648
|
-
|
|
2672
|
+
const scorecard = scorer.score(p, overridesJSON);
|
|
2673
|
+
// TODO - SCORE: Toggle: Before returning, create a dummy population deviation
|
|
2674
|
+
// test, for doHasEqualPopulations() to use later. This is preserving the old
|
|
2675
|
+
// calling sequence.
|
|
2676
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
2677
|
+
// TODO - SCORE: U.trim(popDev)???
|
|
2678
|
+
// Get the raw population deviation
|
|
2679
|
+
const popDev = scorecard.best.populationDeviation.raw;
|
|
2680
|
+
// Populate the test entry
|
|
2681
|
+
test['score'] = popDev;
|
|
2682
|
+
test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
|
|
2683
|
+
// Populate the N+1 summary "district" in district.statistics
|
|
2684
|
+
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
2685
|
+
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|
|
2686
|
+
let summaryRow = s.districts.numberOfRows() - 1;
|
|
2687
|
+
totalPop[summaryRow] = p.populationProfile.targetSize;
|
|
2688
|
+
popDevPct[summaryRow] = popDev;
|
|
2689
|
+
//
|
|
2690
|
+
return scorecard;
|
|
2649
2691
|
}
|
|
2650
2692
|
exports.scorePlan = scorePlan;
|
|
2651
2693
|
|