@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.
@@ -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
- return (U.keyExists('useScore', this.config) && (this.config['useScore'])) ? false : true;
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 = undefined) {
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
- const scorecard = this._scorecard;
304
- return scorecard.best.populationDeviation.notes['threshold'];
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
- // const bLegacy = s._bLegacy;
932
- // TODO - Remove this mechanism. Always run all tests
933
- // Get the requested suites, and only execute those tests
934
- let requestedSuites = s.config['suites'];
935
- // Tests in the "Legal" suite, i.e., pass/ fail constraints
936
- if (requestedSuites.includes(0 /* Legal */)) {
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
- let popDevPct = popDevTest['score'];
1610
- let popDevNormalized = popDevTest['normalizedScore'];
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
- const scorecard = s._scorecard;
2240
- const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
2241
- // TODO - SCORE: Toggle
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
- // const bLegacy = s._bLegacy;
2513
- // Normalize the raw scores for all the numerics tests
2514
- let testResults = U.getNumericObjectKeys(testDefns);
2515
- for (let testID of testResults) {
2516
- if (testDefns[testID]['normalize']) {
2517
- let testResult = s.getTest(testID);
2518
- let rawScore = testResult['score'];
2519
- let normalizedScore;
2520
- normalizedScore = U.normalize(rawScore, s.testScales[testID]);
2521
- testResult['normalizedScore'] = normalizedScore;
2522
- // Add the scale used to normalize the raw score to the details
2523
- testResult['details']['scale'] = s.testScales[testID].scale;
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
- return scorer.score(p);
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