@dra2020/district-analytics 14.1.2 → 14.1.3
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/district-analytics.js +582 -58
- package/dist/district-analytics.js.map +1 -1
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/all.d.ts +15 -0
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/compactness.d.ts +3 -1
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/general.d.ts +0 -6
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/minority.d.ts +3 -3
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/partisan.d.ts +3 -3
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/population.d.ts +1 -1
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/splitting.d.ts +3 -3
- package/dist/src/_api.d.ts +1 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/legacy-types.d.ts +163 -0
- package/dist/src/score.d.ts +7 -0
- package/dist/src/settings.d.ts +0 -4
- package/dist/src/types.d.ts +1 -6
- package/dist/src/utils.d.ts +0 -2
- package/package.json +2 -2
|
@@ -46,6 +46,7 @@ exports.AnalyticsSession = void 0;
|
|
|
46
46
|
const baseclient_1 = __webpack_require__(/*! @dra2020/baseclient */ "@dra2020/baseclient");
|
|
47
47
|
// import * as DT from '@dra2020/dra-types';
|
|
48
48
|
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
|
|
49
|
+
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
49
50
|
const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
|
|
50
51
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
51
52
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
@@ -53,6 +54,7 @@ const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
|
53
54
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
54
55
|
const minority_1 = __webpack_require__(/*! ./minority */ "./src/minority.ts");
|
|
55
56
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
57
|
+
const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
|
|
56
58
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
57
59
|
// import * as S from './settings';
|
|
58
60
|
class AnalyticsSession {
|
|
@@ -108,13 +110,59 @@ class AnalyticsSession {
|
|
|
108
110
|
(0, analyze_1.doAnalyzeDistricts)(this, bLog);
|
|
109
111
|
// This does a little stuff that didn't get factored out into dra-score and then dra-analytics
|
|
110
112
|
(0, analyze_1.doAnalyzePlan)(this, bLog);
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
+
// THE MAIN ANALYTICS ARE NEXT
|
|
114
|
+
// Even though we don't save the profile (as I thought we would),
|
|
115
|
+
// we allow it to be exported, so keep generating it, and use it
|
|
116
|
+
// to gather the inputs to new analytics (as well as the legacy).
|
|
113
117
|
this._profile = (0, score_1.profilePlan)(this, bLog);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
+
let legacyScorecard = {};
|
|
119
|
+
let legacyScorecardAlt = {};
|
|
120
|
+
let newScorecard = {};
|
|
121
|
+
// LEGACY - Run legacy analytics
|
|
122
|
+
if (this.config.legacyanalytics) {
|
|
123
|
+
if (bLog)
|
|
124
|
+
console.log("Running legacy analytics ...");
|
|
125
|
+
legacyScorecard = (0, score_1.scorePlan)(this, this._profile, bLog, overridesJSON);
|
|
126
|
+
}
|
|
127
|
+
// Construct a new/alternate scorecard
|
|
128
|
+
if (this.config.newanalytics) {
|
|
129
|
+
if (bLog)
|
|
130
|
+
console.log("Running new analytics ...");
|
|
131
|
+
newScorecard = (0, score_1.computeMetrics)(this._profile, this.getGoodShapes(), bLog); // w/o ratings
|
|
132
|
+
newScorecard = (0, score_1.rateKeyDimensions)(newScorecard, this._profile, bLog); // w/ ratings
|
|
133
|
+
legacyScorecardAlt = (0, score_1.thunkScorecard)(newScorecard, bLog);
|
|
134
|
+
// Add minority notes
|
|
135
|
+
legacyScorecardAlt.minority.details['majorityMinority'] = M.getMajorityMinority(this);
|
|
136
|
+
legacyScorecardAlt.minority.details['vraPreclearance'] = M.getVRASection5(this);
|
|
137
|
+
// Compare the new & legacy scorecards
|
|
138
|
+
(0, score_1.compareScorecards)(legacyScorecardAlt, legacyScorecard, bLog);
|
|
139
|
+
}
|
|
140
|
+
// Use the new scorecard, after creating it
|
|
141
|
+
this._scorecard = (this.config.newanalytics) ? legacyScorecardAlt : legacyScorecard;
|
|
142
|
+
// Post-scorecard housekeeping - copied from scorePlan()
|
|
143
|
+
if (this.config.newanalytics) {
|
|
144
|
+
// Before returning, create a dummy population deviation test, for
|
|
145
|
+
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
146
|
+
let test = this.getTest(4 /* PopulationDeviation */);
|
|
147
|
+
// Get the raw population deviation
|
|
148
|
+
const popDev = this._scorecard.populationDeviation.raw;
|
|
149
|
+
// Populate the test entry
|
|
150
|
+
test['score'] = popDev;
|
|
151
|
+
test['details'] = { 'maxDeviation': this._scorecard.populationDeviation.notes['maxDeviation'] };
|
|
152
|
+
// Populate the N+1 summary "district" in district.statistics
|
|
153
|
+
let totalPop = this.districts.table.totalPop;
|
|
154
|
+
let popDevPct = this.districts.table.popDevPct;
|
|
155
|
+
let totalVAP = this.districts.table.totalVAP;
|
|
156
|
+
const summaryRow = this.districts.numberOfRows() - 1;
|
|
157
|
+
totalPop[summaryRow] = this._profile.population.targetSize;
|
|
158
|
+
popDevPct[summaryRow] = popDev;
|
|
159
|
+
totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / this._profile.nDistricts);
|
|
160
|
+
// Added w/ new scorecard
|
|
161
|
+
// Use 'roughly' equal population from dra-analytics
|
|
162
|
+
let test2 = this.getTest(3 /* EqualPopulation */);
|
|
163
|
+
test2['score'] = newScorecard.populationDeviation.roughlyEqual;
|
|
164
|
+
}
|
|
165
|
+
// END main analytics
|
|
118
166
|
(0, results_1.doAnalyzePostProcessing)(this, bLog);
|
|
119
167
|
}
|
|
120
168
|
catch (e) {
|
|
@@ -124,6 +172,29 @@ class AnalyticsSession {
|
|
|
124
172
|
}
|
|
125
173
|
return true;
|
|
126
174
|
}
|
|
175
|
+
getGoodShapes() {
|
|
176
|
+
const rawShapes = this.districts.getDistrictShapes();
|
|
177
|
+
// Filter the real shapes & throw everything else away
|
|
178
|
+
let goodShapes = {};
|
|
179
|
+
goodShapes['type'] = "FeatureCollection";
|
|
180
|
+
goodShapes['features'] = [];
|
|
181
|
+
for (let i = 0; i < rawShapes.features.length; i++) {
|
|
182
|
+
const shape = rawShapes.features[i];
|
|
183
|
+
if (isAShape(shape)) {
|
|
184
|
+
const d = baseclient_1.Poly.polyDescribe(shape);
|
|
185
|
+
let f = {
|
|
186
|
+
type: 'Feature',
|
|
187
|
+
properties: { districtID: `${i + 1}` },
|
|
188
|
+
geometry: {
|
|
189
|
+
type: (d.npoly > 1) ? 'MultiPolygon' : 'Polygon',
|
|
190
|
+
coordinates: shape.geometry.coordinates
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
goodShapes.features.push(f);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return goodShapes;
|
|
197
|
+
}
|
|
127
198
|
// 11-03-2020 - Added for racially polarized voting analysis
|
|
128
199
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
129
200
|
analyzeRacialPolarization(districtID, groups, bLog = false) {
|
|
@@ -196,15 +267,29 @@ class AnalyticsSession {
|
|
|
196
267
|
// Return a pointer to the the test entry for this test
|
|
197
268
|
return this.tests[testID];
|
|
198
269
|
}
|
|
270
|
+
// LEGACY - Threshold defined in dra-analytics for new analytics
|
|
199
271
|
// NOTE - Not sure why this has to be up here ...
|
|
200
272
|
populationDeviationThreshold() {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
273
|
+
let threshold;
|
|
274
|
+
if (this.config.legacyanalytics && !this.config.newanalytics) {
|
|
275
|
+
// NOTE - This assumes the plan has been profiled
|
|
276
|
+
const scorer = new Score.Scorer();
|
|
277
|
+
threshold = scorer.populationDeviationThreshold(this.legislativeDistricts); // TODO - 2020
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
threshold = dra_analytics_1.Rate.popdevThreshold(this.legislativeDistricts);
|
|
281
|
+
}
|
|
204
282
|
return threshold;
|
|
205
283
|
}
|
|
206
284
|
}
|
|
207
285
|
exports.AnalyticsSession = AnalyticsSession;
|
|
286
|
+
function isAShape(poly) {
|
|
287
|
+
if (poly == null)
|
|
288
|
+
return false;
|
|
289
|
+
if (baseclient_1.Poly.polyNull(poly))
|
|
290
|
+
return false;
|
|
291
|
+
return poly.geometry && poly.geometry.coordinates && !U.isArrayEmpty(poly.geometry.coordinates);
|
|
292
|
+
}
|
|
208
293
|
|
|
209
294
|
|
|
210
295
|
/***/ }),
|
|
@@ -1449,6 +1534,7 @@ function isAShape(poly) {
|
|
|
1449
1534
|
return poly.geometry && poly.geometry.coordinates && !U.isArrayEmpty(poly.geometry.coordinates);
|
|
1450
1535
|
}
|
|
1451
1536
|
// SCORE KIWYSI COMPACTNESS
|
|
1537
|
+
// LEGACY
|
|
1452
1538
|
function scoreKIWYSICompactness(s, bLog = false) {
|
|
1453
1539
|
const rawShapes = s.districts.getDistrictShapes();
|
|
1454
1540
|
// Filter the real shapes & throw everything else away
|
|
@@ -1528,17 +1614,20 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1528
1614
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1529
1615
|
const popDevPct = popDevTest['score'];
|
|
1530
1616
|
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1531
|
-
//
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
test
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1617
|
+
// LEGACY - Has 'roughly' equal populations is calculated in dra-analytics
|
|
1618
|
+
if (s.config.legacyanalytics && !s.config.newanalytics) {
|
|
1619
|
+
// 09-19-2020 - Added to catch edge case of only one non-empty district
|
|
1620
|
+
const p = s._profile;
|
|
1621
|
+
const totPopByDistrict = p.population.byDistrict.filter(x => x > 0);
|
|
1622
|
+
const bTwoOrMoreDistricts = (totPopByDistrict.length > 1) ? true : false;
|
|
1623
|
+
// Populate the test entry
|
|
1624
|
+
if (bTwoOrMoreDistricts && (popDevNormalized > 0)) {
|
|
1625
|
+
test['score'] = true;
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
test['score'] = false;
|
|
1629
|
+
}
|
|
1630
|
+
} // end
|
|
1542
1631
|
test['details']['deviation'] = popDevPct;
|
|
1543
1632
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1544
1633
|
// Populate the N+1 summary "district" in district.statistics
|
|
@@ -1573,7 +1662,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
1573
1662
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
1574
1663
|
};
|
|
1575
1664
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1576
|
-
exports.uncertaintyOfMembership = exports.effectiveSplits = exports.avgSVError = exports.isAntimajoritarian = exports.ratePartisanBias = exports.inferSelectedMinority = exports.fieldForFeature = exports.geoIDForFeature = void 0;
|
|
1665
|
+
exports.uncertaintyOfMembership = exports.effectiveSplits = exports.avgSVError = exports.isAntimajoritarian = exports.ratePartisanBias = exports.estSeatProbability = exports.inferSelectedMinority = exports.fieldForFeature = exports.geoIDForFeature = void 0;
|
|
1577
1666
|
__exportStar(__webpack_require__(/*! ./_api */ "./src/_api.ts"), exports);
|
|
1578
1667
|
var _data_1 = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
1579
1668
|
Object.defineProperty(exports, "geoIDForFeature", ({ enumerable: true, get: function () { return _data_1.geoIDForFeature; } }));
|
|
@@ -1584,6 +1673,7 @@ __exportStar(__webpack_require__(/*! ./types */ "./src/types.ts"), exports);
|
|
|
1584
1673
|
__exportStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"), exports);
|
|
1585
1674
|
// Re-export RPV types and COI splitting functions
|
|
1586
1675
|
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
1676
|
+
exports.estSeatProbability = dra_analytics_1.Partisan.estSeatProbability;
|
|
1587
1677
|
exports.ratePartisanBias = dra_analytics_1.Rate.ratePartisanBias;
|
|
1588
1678
|
exports.isAntimajoritarian = dra_analytics_1.Rate.isAntimajoritarian;
|
|
1589
1679
|
exports.avgSVError = dra_analytics_1.Rate.avgSVError;
|
|
@@ -2028,6 +2118,7 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
2028
2118
|
scorecard.splitting.details['countiesWithSplits'] = simpleSplits['details']['countiesWithSplits'];
|
|
2029
2119
|
// NOTE - Add split precincts in dra-client directly
|
|
2030
2120
|
// Derive secondary tests
|
|
2121
|
+
// Note - The only secondary test is 'roughly equal population (true/false)
|
|
2031
2122
|
(0, analyze_1.doDeriveSecondaryTests)(s, bLog);
|
|
2032
2123
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
2033
2124
|
s.bPostProcessingDone = true;
|
|
@@ -2067,11 +2158,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
2067
2158
|
return result;
|
|
2068
2159
|
};
|
|
2069
2160
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2070
|
-
exports.scorePlan = exports.getStatewideDemographics = exports.profilePlan = void 0;
|
|
2161
|
+
exports.compareScorecards = exports.thunkScorecard = exports.rateKeyDimensions = exports.computeMetrics = exports.scorePlan = exports.getStatewideDemographics = exports.profilePlan = void 0;
|
|
2071
2162
|
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
|
|
2072
2163
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
2073
2164
|
const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
|
|
2074
2165
|
const C = __importStar(__webpack_require__(/*! ./compact */ "./src/compact.ts"));
|
|
2166
|
+
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
2075
2167
|
// PROFILE A PLAN
|
|
2076
2168
|
const KEEP_DECIMALS = 6;
|
|
2077
2169
|
function profilePlan(s, bLog = false) {
|
|
@@ -2183,26 +2275,30 @@ function getStatewideDemographics(s, bLog = false) {
|
|
|
2183
2275
|
return demographics;
|
|
2184
2276
|
}
|
|
2185
2277
|
exports.getStatewideDemographics = getStatewideDemographics;
|
|
2186
|
-
// SCORE A PLAN
|
|
2278
|
+
// SCORE A PLAN - Legacy using dra-score
|
|
2279
|
+
// LEGACY
|
|
2187
2280
|
function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
2188
2281
|
let scorer = new Score.Scorer();
|
|
2189
2282
|
const scorecard = scorer.score(p, overridesJSON);
|
|
2190
|
-
//
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2283
|
+
// LEGACY POST-SCORECARD HOUSEKEEPING
|
|
2284
|
+
if (s.config.legacyanalytics && !s.config.newanalytics) {
|
|
2285
|
+
// Before returning, create a dummy population deviation test, for
|
|
2286
|
+
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
2287
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
2288
|
+
// Get the raw population deviation
|
|
2289
|
+
const popDev = scorecard.populationDeviation.raw;
|
|
2290
|
+
// Populate the test entry
|
|
2291
|
+
test['score'] = popDev;
|
|
2292
|
+
test['details'] = { 'maxDeviation': scorecard.populationDeviation.notes['maxDeviation'] };
|
|
2293
|
+
// Populate the N+1 summary "district" in district.statistics
|
|
2294
|
+
let totalPop = s.districts.table.totalPop;
|
|
2295
|
+
let popDevPct = s.districts.table.popDevPct;
|
|
2296
|
+
let totalVAP = s.districts.table.totalVAP;
|
|
2297
|
+
const summaryRow = s.districts.numberOfRows() - 1;
|
|
2298
|
+
totalPop[summaryRow] = p.population.targetSize;
|
|
2299
|
+
popDevPct[summaryRow] = popDev;
|
|
2300
|
+
totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / p.nDistricts);
|
|
2301
|
+
} // end
|
|
2206
2302
|
// Add minority notes
|
|
2207
2303
|
scorecard.minority.details['majorityMinority'] = M.getMajorityMinority(s);
|
|
2208
2304
|
scorecard.minority.details['vraPreclearance'] = M.getVRASection5(s);
|
|
@@ -2227,6 +2323,432 @@ function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
|
2227
2323
|
return scorecard;
|
|
2228
2324
|
}
|
|
2229
2325
|
exports.scorePlan = scorePlan;
|
|
2326
|
+
// SCORE A PLAN - New using dra-analytics
|
|
2327
|
+
function computeMetrics(p, districtShapes, bLog = false) {
|
|
2328
|
+
if (bLog)
|
|
2329
|
+
console.log("Computing metrics ...");
|
|
2330
|
+
const bLegislative = p.bStateLeg;
|
|
2331
|
+
// Calculate bias & responsiveness metrics ...
|
|
2332
|
+
const byDistrictVf = p.partisanship.byDistrict;
|
|
2333
|
+
const statewideVf = p.partisanship.statewide;
|
|
2334
|
+
let _pS = dra_analytics_1.Partisan.makePartisanScorecard(statewideVf, byDistrictVf, bLog);
|
|
2335
|
+
// Calculate minority representation metrics ...
|
|
2336
|
+
const statewideDemos = p.demographics.statewide;
|
|
2337
|
+
const byDistrictDemos = p.demographics.byDistrict;
|
|
2338
|
+
let _mS = dra_analytics_1.Minority.makeMinorityScorecard(statewideDemos, byDistrictDemos, bLog);
|
|
2339
|
+
// Calculate compactness metrics ...
|
|
2340
|
+
let _cS = dra_analytics_1.Compactness.makeCompactnessScorecard(districtShapes, bLog);
|
|
2341
|
+
// Calculate county-district splitting metrics ...
|
|
2342
|
+
const CxD = p.counties;
|
|
2343
|
+
const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
|
|
2344
|
+
// Calculate population deviation-related metrics ...
|
|
2345
|
+
const totPopByDistrict = p.population.byDistrict;
|
|
2346
|
+
const targetSize = p.population.targetSize;
|
|
2347
|
+
const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, bLog);
|
|
2348
|
+
const details = {};
|
|
2349
|
+
// Assemble the pieces into new scorecard
|
|
2350
|
+
const scorecard = {
|
|
2351
|
+
partisan: _pS,
|
|
2352
|
+
minority: _mS,
|
|
2353
|
+
compactness: _cS,
|
|
2354
|
+
splitting: _sS,
|
|
2355
|
+
populationDeviation: _pdS,
|
|
2356
|
+
details: details,
|
|
2357
|
+
scratchpad: {} // Hack to pass legacy values between processing steps
|
|
2358
|
+
};
|
|
2359
|
+
return scorecard;
|
|
2360
|
+
}
|
|
2361
|
+
exports.computeMetrics = computeMetrics;
|
|
2362
|
+
function rateKeyDimensions(scorecard, p, bLog = false) {
|
|
2363
|
+
if (bLog)
|
|
2364
|
+
console.log("Rating key dimensions ...");
|
|
2365
|
+
const bLegislative = p.bStateLeg;
|
|
2366
|
+
// Rate proportionality
|
|
2367
|
+
const statewideVf = p.partisanship.statewide;
|
|
2368
|
+
const Sf = scorecard.partisan.bias.estSf;
|
|
2369
|
+
scorecard.partisan.bias.score = dra_analytics_1.Rate.rateProportionality(scorecard.partisan.bias.deviation, statewideVf, Sf);
|
|
2370
|
+
// Rate competititveness
|
|
2371
|
+
scorecard.partisan.responsiveness.score = dra_analytics_1.Rate.rateCompetitiveness(scorecard.partisan.responsiveness.cDf);
|
|
2372
|
+
// Rate minority representation
|
|
2373
|
+
const rawOd = scorecard.minority.opportunityDistricts;
|
|
2374
|
+
const pOd = scorecard.minority.proportionalOpportunities;
|
|
2375
|
+
const rawCd = scorecard.minority.coalitionDistricts;
|
|
2376
|
+
const pCd = scorecard.minority.proportionalCoalitions;
|
|
2377
|
+
scorecard.minority.score = dra_analytics_1.Rate.rateMinorityRepresentation(rawOd, pOd, rawCd, pCd);
|
|
2378
|
+
// Rate compactness
|
|
2379
|
+
const avgReock = scorecard.compactness.avgReock;
|
|
2380
|
+
const avgPolsby = scorecard.compactness.avgPolsby;
|
|
2381
|
+
const reockRating = dra_analytics_1.Rate.rateReock(avgReock);
|
|
2382
|
+
const polsbyRating = dra_analytics_1.Rate.ratePolsby(avgPolsby);
|
|
2383
|
+
scorecard.compactness.score = dra_analytics_1.Rate.rateCompactness(reockRating, polsbyRating);
|
|
2384
|
+
// Rate county- & district-splitting
|
|
2385
|
+
const rawCountySplitting = scorecard.splitting.county;
|
|
2386
|
+
const rawDistrictSplitting = scorecard.splitting.district;
|
|
2387
|
+
const nCounties = p.nCounties;
|
|
2388
|
+
const nDistricts = p.nDistricts;
|
|
2389
|
+
const countyRating = dra_analytics_1.Rate.rateCountySplitting(rawCountySplitting, nCounties, nDistricts, bLegislative);
|
|
2390
|
+
const districtRating = dra_analytics_1.Rate.rateDistrictSplitting(rawDistrictSplitting, bLegislative);
|
|
2391
|
+
const unadjusted = dra_analytics_1.Rate.rateSplitting(countyRating, districtRating);
|
|
2392
|
+
scorecard.splitting.score = dra_analytics_1.Rate.adjustSplittingRating(unadjusted, rawCountySplitting, rawDistrictSplitting);
|
|
2393
|
+
// Rate population deviation
|
|
2394
|
+
const rawDeviation = scorecard.populationDeviation.deviation;
|
|
2395
|
+
scorecard.populationDeviation.score = dra_analytics_1.Rate.ratePopulationDeviation(rawDeviation, bLegislative);
|
|
2396
|
+
// Squirrel away normalized compactness & splitting ratings for the legacy scorecard
|
|
2397
|
+
const keep = {
|
|
2398
|
+
reockScore: reockRating,
|
|
2399
|
+
polsbyScore: polsbyRating,
|
|
2400
|
+
countyScore: countyRating,
|
|
2401
|
+
districtScore: districtRating
|
|
2402
|
+
};
|
|
2403
|
+
scorecard.scratchpad = keep;
|
|
2404
|
+
return scorecard;
|
|
2405
|
+
}
|
|
2406
|
+
exports.rateKeyDimensions = rateKeyDimensions;
|
|
2407
|
+
function thunkScorecard(newScorecard, bLog = false) {
|
|
2408
|
+
if (bLog)
|
|
2409
|
+
console.log("Thunking new scorecard into legacy structure ...");
|
|
2410
|
+
const scratchpad = newScorecard.scratchpad;
|
|
2411
|
+
// Partisan scorecard
|
|
2412
|
+
const pS = U.deepCopy(newScorecard.partisan);
|
|
2413
|
+
// Minority scorecard
|
|
2414
|
+
const mS = U.deepCopy(newScorecard.minority);
|
|
2415
|
+
// Compactness scorecard
|
|
2416
|
+
const reockM = {
|
|
2417
|
+
raw: newScorecard.compactness.avgReock,
|
|
2418
|
+
normalized: scratchpad.reockScore,
|
|
2419
|
+
notes: {}
|
|
2420
|
+
};
|
|
2421
|
+
const polsbyM = {
|
|
2422
|
+
raw: newScorecard.compactness.avgPolsby,
|
|
2423
|
+
normalized: scratchpad.polsbyScore,
|
|
2424
|
+
notes: {}
|
|
2425
|
+
};
|
|
2426
|
+
let cS = {
|
|
2427
|
+
score: newScorecard.compactness.score,
|
|
2428
|
+
reock: reockM,
|
|
2429
|
+
polsby: polsbyM,
|
|
2430
|
+
details: U.deepCopy(newScorecard.compactness.details)
|
|
2431
|
+
};
|
|
2432
|
+
// Relocate byDistrict compactness #'s
|
|
2433
|
+
cS.details.byDistrict = U.deepCopy(newScorecard.compactness.byDistrict);
|
|
2434
|
+
// Add KIWYSI compactness score
|
|
2435
|
+
cS.details['kiwysi'] = newScorecard.compactness.avgKWIWYSI;
|
|
2436
|
+
// Splitting scorecard
|
|
2437
|
+
const countyM = {
|
|
2438
|
+
raw: newScorecard.splitting.county,
|
|
2439
|
+
normalized: scratchpad.countyScore,
|
|
2440
|
+
notes: {}
|
|
2441
|
+
};
|
|
2442
|
+
const districtM = {
|
|
2443
|
+
raw: newScorecard.splitting.district,
|
|
2444
|
+
normalized: scratchpad.districtScore,
|
|
2445
|
+
notes: {}
|
|
2446
|
+
};
|
|
2447
|
+
const sS = {
|
|
2448
|
+
score: newScorecard.splitting.score,
|
|
2449
|
+
county: countyM,
|
|
2450
|
+
district: districtM,
|
|
2451
|
+
details: U.deepCopy(newScorecard.splitting.details)
|
|
2452
|
+
};
|
|
2453
|
+
// Population (equality) scorecard
|
|
2454
|
+
const pdM = {
|
|
2455
|
+
raw: newScorecard.populationDeviation.deviation,
|
|
2456
|
+
normalized: newScorecard.populationDeviation.score,
|
|
2457
|
+
notes: newScorecard.populationDeviation.notes
|
|
2458
|
+
};
|
|
2459
|
+
const pdS = pdM;
|
|
2460
|
+
const scorecard = {
|
|
2461
|
+
partisan: pS,
|
|
2462
|
+
minority: mS,
|
|
2463
|
+
compactness: cS,
|
|
2464
|
+
splitting: sS,
|
|
2465
|
+
populationDeviation: pdS,
|
|
2466
|
+
details: newScorecard.details
|
|
2467
|
+
};
|
|
2468
|
+
return scorecard;
|
|
2469
|
+
}
|
|
2470
|
+
exports.thunkScorecard = thunkScorecard;
|
|
2471
|
+
function compareScorecards(altLegacyScorecard, legacyScorecard, bLog = false) {
|
|
2472
|
+
if (bLog)
|
|
2473
|
+
console.log("Comparing new & legacy scorecards ...");
|
|
2474
|
+
// A pretty loose tolerance, because we're not trimming values in the new analytics
|
|
2475
|
+
const DECIMAL_PLACES = 2;
|
|
2476
|
+
const DEFAULT_TOLERANCE = toleranceFn(DECIMAL_PLACES);
|
|
2477
|
+
// COMPARE PARTISAN SCORECARD
|
|
2478
|
+
// Compare BIAS section
|
|
2479
|
+
matchFloats("bestS", altLegacyScorecard.partisan.bias.bestS, legacyScorecard.partisan.bias.bestS, DEFAULT_TOLERANCE);
|
|
2480
|
+
matchFloats("bestSf", altLegacyScorecard.partisan.bias.bestSf, legacyScorecard.partisan.bias.bestSf, DEFAULT_TOLERANCE);
|
|
2481
|
+
matchFloats("estS", altLegacyScorecard.partisan.bias.estS, legacyScorecard.partisan.bias.estS, DEFAULT_TOLERANCE);
|
|
2482
|
+
matchFloats("estSf", altLegacyScorecard.partisan.bias.estSf, legacyScorecard.partisan.bias.estSf, DEFAULT_TOLERANCE);
|
|
2483
|
+
matchFloats("deviation", altLegacyScorecard.partisan.bias.deviation, legacyScorecard.partisan.bias.deviation, DEFAULT_TOLERANCE);
|
|
2484
|
+
matchInts("proportionality/score", altLegacyScorecard.partisan.bias.score, legacyScorecard.partisan.bias.score);
|
|
2485
|
+
matchFloats("tOf", altLegacyScorecard.partisan.bias.tOf, legacyScorecard.partisan.bias.tOf, DEFAULT_TOLERANCE);
|
|
2486
|
+
matchFloats("fptpS", altLegacyScorecard.partisan.bias.fptpS, legacyScorecard.partisan.bias.fptpS, DEFAULT_TOLERANCE);
|
|
2487
|
+
matchFloats("bS50", altLegacyScorecard.partisan.bias.bS50, legacyScorecard.partisan.bias.bS50, DEFAULT_TOLERANCE);
|
|
2488
|
+
matchFloats("deviation", altLegacyScorecard.partisan.bias.deviation, legacyScorecard.partisan.bias.deviation, DEFAULT_TOLERANCE);
|
|
2489
|
+
matchUndefinableFloats("decl", altLegacyScorecard.partisan.bias.decl, legacyScorecard.partisan.bias.decl, toleranceFn(1));
|
|
2490
|
+
let rvPointsAlt = altLegacyScorecard.partisan.bias.rvPoints;
|
|
2491
|
+
let rvPoints = legacyScorecard.partisan.bias.rvPoints;
|
|
2492
|
+
if (matchUndefinedness("rvPoints", rvPointsAlt, rvPoints)) {
|
|
2493
|
+
rvPointsAlt = rvPointsAlt;
|
|
2494
|
+
rvPoints = rvPoints;
|
|
2495
|
+
matchFloats("rvPoints/Sb", rvPointsAlt.Sb, rvPoints.Sb, DEFAULT_TOLERANCE);
|
|
2496
|
+
matchFloats("rvPoints/Ra", rvPointsAlt.Ra, rvPoints.Ra, DEFAULT_TOLERANCE);
|
|
2497
|
+
matchFloats("rvPoints/Rb", rvPointsAlt.Rb, rvPoints.Rb, DEFAULT_TOLERANCE);
|
|
2498
|
+
matchFloats("rvPoints/Va", rvPointsAlt.Va, rvPoints.Va, DEFAULT_TOLERANCE);
|
|
2499
|
+
matchFloats("rvPoints/Vb", rvPointsAlt.Vb, rvPoints.Vb, DEFAULT_TOLERANCE);
|
|
2500
|
+
}
|
|
2501
|
+
matchFloats("gSym", altLegacyScorecard.partisan.bias.gSym, legacyScorecard.partisan.bias.gSym, DEFAULT_TOLERANCE);
|
|
2502
|
+
matchUndefinableFloats("gamma", altLegacyScorecard.partisan.bias.gamma, legacyScorecard.partisan.bias.gamma, DEFAULT_TOLERANCE);
|
|
2503
|
+
matchFloats("eG", altLegacyScorecard.partisan.bias.eG, legacyScorecard.partisan.bias.eG, DEFAULT_TOLERANCE);
|
|
2504
|
+
matchUndefinableFloats("bSV", altLegacyScorecard.partisan.bias.bSV, legacyScorecard.partisan.bias.bSV, DEFAULT_TOLERANCE);
|
|
2505
|
+
matchFloats("prop", altLegacyScorecard.partisan.bias.prop, legacyScorecard.partisan.bias.prop, DEFAULT_TOLERANCE);
|
|
2506
|
+
matchFloats("mMs", altLegacyScorecard.partisan.bias.mMs, legacyScorecard.partisan.bias.mMs, DEFAULT_TOLERANCE);
|
|
2507
|
+
matchFloats("mMd", altLegacyScorecard.partisan.bias.mMd, legacyScorecard.partisan.bias.mMd, DEFAULT_TOLERANCE);
|
|
2508
|
+
matchUndefinableFloats("lO", altLegacyScorecard.partisan.bias.lO, legacyScorecard.partisan.bias.lO, DEFAULT_TOLERANCE);
|
|
2509
|
+
// Compare Impact section
|
|
2510
|
+
matchFloats("unearnedS", altLegacyScorecard.partisan.impact.unearnedS, legacyScorecard.partisan.impact.unearnedS, DEFAULT_TOLERANCE);
|
|
2511
|
+
// Note - We don't use the impact score, so we don't compute it in the new scorecard.
|
|
2512
|
+
// matchInts("impact/score", altLegacyScorecard.partisan.impact.score as number, legacyScorecard.partisan.impact.score);
|
|
2513
|
+
// Compare Responsiveness section
|
|
2514
|
+
matchUndefinableFloats("bigR", altLegacyScorecard.partisan.responsiveness.bigR, legacyScorecard.partisan.responsiveness.bigR, DEFAULT_TOLERANCE);
|
|
2515
|
+
matchUndefinableFloats("littleR", altLegacyScorecard.partisan.responsiveness.littleR, legacyScorecard.partisan.responsiveness.littleR, DEFAULT_TOLERANCE);
|
|
2516
|
+
matchUndefinableFloats("mIR", altLegacyScorecard.partisan.responsiveness.mIR, legacyScorecard.partisan.responsiveness.mIR, DEFAULT_TOLERANCE);
|
|
2517
|
+
matchFloats("rD", altLegacyScorecard.partisan.responsiveness.rD, legacyScorecard.partisan.responsiveness.rD, DEFAULT_TOLERANCE);
|
|
2518
|
+
matchFloats("rDf", altLegacyScorecard.partisan.responsiveness.rDf, legacyScorecard.partisan.responsiveness.rDf, DEFAULT_TOLERANCE);
|
|
2519
|
+
matchFloats("cSimple", altLegacyScorecard.partisan.responsiveness.cSimple, legacyScorecard.partisan.responsiveness.cSimple, DEFAULT_TOLERANCE);
|
|
2520
|
+
matchFloats("cD", altLegacyScorecard.partisan.responsiveness.cD, legacyScorecard.partisan.responsiveness.cD, DEFAULT_TOLERANCE);
|
|
2521
|
+
matchFloats("cDf", altLegacyScorecard.partisan.responsiveness.cDf, legacyScorecard.partisan.responsiveness.cDf, DEFAULT_TOLERANCE);
|
|
2522
|
+
matchInts("competitiveness/score", altLegacyScorecard.partisan.responsiveness.score, legacyScorecard.partisan.responsiveness.score);
|
|
2523
|
+
matchPointArrays("dSVpoints", altLegacyScorecard.partisan.dSVpoints, legacyScorecard.partisan.dSVpoints, DEFAULT_TOLERANCE);
|
|
2524
|
+
matchPointArrays("rSVpoints", altLegacyScorecard.partisan.rSVpoints, legacyScorecard.partisan.rSVpoints, DEFAULT_TOLERANCE);
|
|
2525
|
+
matchUndefinableFloats("averageDVf", altLegacyScorecard.partisan.averageDVf, legacyScorecard.partisan.averageDVf, DEFAULT_TOLERANCE);
|
|
2526
|
+
matchUndefinableFloats("averageRVf", altLegacyScorecard.partisan.averageRVf, legacyScorecard.partisan.averageRVf, DEFAULT_TOLERANCE);
|
|
2527
|
+
matchDicts("partisan/details", altLegacyScorecard.partisan.details, legacyScorecard.partisan.details);
|
|
2528
|
+
// COMPARE MINORITY SCORECARD
|
|
2529
|
+
matchObjectsAndArrays("pivotByDemographic", altLegacyScorecard.minority.pivotByDemographic, legacyScorecard.minority.pivotByDemographic);
|
|
2530
|
+
matchFloats("opportunityDistricts", altLegacyScorecard.minority.opportunityDistricts, legacyScorecard.minority.opportunityDistricts, DEFAULT_TOLERANCE);
|
|
2531
|
+
matchFloats("coalitionDistricts", altLegacyScorecard.minority.coalitionDistricts, legacyScorecard.minority.coalitionDistricts, DEFAULT_TOLERANCE);
|
|
2532
|
+
matchInts("minority/score", altLegacyScorecard.minority.score, legacyScorecard.minority.score);
|
|
2533
|
+
matchDicts("minority/details", altLegacyScorecard.minority.details, legacyScorecard.minority.details);
|
|
2534
|
+
// COMPARE COMPACTNESS SCORECARD
|
|
2535
|
+
matchFloats("reock", altLegacyScorecard.compactness.reock.raw, legacyScorecard.compactness.reock.raw, DEFAULT_TOLERANCE);
|
|
2536
|
+
matchInts("reock/normalized", altLegacyScorecard.compactness.reock.normalized, legacyScorecard.compactness.reock.normalized);
|
|
2537
|
+
matchFloats("polsby", altLegacyScorecard.compactness.polsby.raw, legacyScorecard.compactness.polsby.raw, DEFAULT_TOLERANCE);
|
|
2538
|
+
matchInts("polsby/normalized", altLegacyScorecard.compactness.polsby.normalized, legacyScorecard.compactness.polsby.normalized);
|
|
2539
|
+
matchInts("compactness/score", altLegacyScorecard.compactness.score, legacyScorecard.compactness.score);
|
|
2540
|
+
// Compare 'byDistrict' results separately
|
|
2541
|
+
const _altCompactnessByDistrict = dra_analytics_1.Utils.deepCopy(altLegacyScorecard.compactness.details['byDistrict']);
|
|
2542
|
+
const _CompactnessByDistrict = dra_analytics_1.Utils.deepCopy(legacyScorecard.compactness.details['byDistrict']);
|
|
2543
|
+
const _altCompactnessDetails = dra_analytics_1.Utils.deepCopy(altLegacyScorecard.compactness.details);
|
|
2544
|
+
const _CompactnessDetails = dra_analytics_1.Utils.deepCopy(legacyScorecard.compactness.details);
|
|
2545
|
+
delete _altCompactnessDetails['byDistrict'];
|
|
2546
|
+
delete _CompactnessDetails['byDistrict'];
|
|
2547
|
+
matchDicts("compactness/details", _altCompactnessDetails, _CompactnessDetails);
|
|
2548
|
+
matchCompactnessByDistrict("compactness/byDistrict", _altCompactnessByDistrict, _CompactnessByDistrict, DEFAULT_TOLERANCE);
|
|
2549
|
+
// COMPARE SPLITTING SCORECARD
|
|
2550
|
+
matchFloats("county", altLegacyScorecard.splitting.county.raw, legacyScorecard.splitting.county.raw, DEFAULT_TOLERANCE);
|
|
2551
|
+
matchInts("county/normalized", altLegacyScorecard.splitting.county.normalized, legacyScorecard.splitting.county.normalized);
|
|
2552
|
+
matchFloats("district", altLegacyScorecard.splitting.district.raw, legacyScorecard.splitting.district.raw, DEFAULT_TOLERANCE);
|
|
2553
|
+
matchInts("district/normalized", altLegacyScorecard.splitting.district.normalized, legacyScorecard.splitting.district.normalized);
|
|
2554
|
+
matchInts("splitting/score", altLegacyScorecard.splitting.score, legacyScorecard.splitting.score);
|
|
2555
|
+
matchDicts("splitting/details", altLegacyScorecard.splitting.details, legacyScorecard.splitting.details);
|
|
2556
|
+
// COMPARE POPULATION SCORECARD
|
|
2557
|
+
matchFloats("popdev", altLegacyScorecard.populationDeviation.raw, legacyScorecard.populationDeviation.raw, DEFAULT_TOLERANCE);
|
|
2558
|
+
matchInts("popdev/score", altLegacyScorecard.populationDeviation.normalized, legacyScorecard.populationDeviation.normalized);
|
|
2559
|
+
matchDicts("popdev/notes", altLegacyScorecard.populationDeviation.notes, legacyScorecard.populationDeviation.notes);
|
|
2560
|
+
matchDicts("details", altLegacyScorecard.details, legacyScorecard.details);
|
|
2561
|
+
}
|
|
2562
|
+
exports.compareScorecards = compareScorecards;
|
|
2563
|
+
// Matching helpers
|
|
2564
|
+
function matchFloats(property, received, good, tolerance) {
|
|
2565
|
+
if (dra_analytics_1.Utils.areRoughlyEqual(received, good, tolerance)) {
|
|
2566
|
+
return true;
|
|
2567
|
+
}
|
|
2568
|
+
else {
|
|
2569
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2570
|
+
return false;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
function toleranceFn(places) {
|
|
2574
|
+
return 0.5 / Math.pow(10, places);
|
|
2575
|
+
}
|
|
2576
|
+
function matchInts(property, received, good) {
|
|
2577
|
+
if (received == good) {
|
|
2578
|
+
return true;
|
|
2579
|
+
}
|
|
2580
|
+
else {
|
|
2581
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2582
|
+
return false;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
function matchUndefinableFloats(property, received, good, tolerance) {
|
|
2586
|
+
if ((received === undefined) && (good === undefined))
|
|
2587
|
+
return true;
|
|
2588
|
+
if ((received === undefined) || (good === undefined)) {
|
|
2589
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2590
|
+
return false;
|
|
2591
|
+
}
|
|
2592
|
+
return matchFloats(property, received, good, tolerance);
|
|
2593
|
+
}
|
|
2594
|
+
// https://stackoverflow.com/questions/13142968/deep-comparison-of-objects-arrays
|
|
2595
|
+
function matchObjectsAndArrays(property, received, good) {
|
|
2596
|
+
if ((received === undefined) && (good === undefined))
|
|
2597
|
+
return true; // Both undefined
|
|
2598
|
+
if ((received === undefined) || (good === undefined)) { // One undefined but not the other
|
|
2599
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2600
|
+
return false;
|
|
2601
|
+
}
|
|
2602
|
+
if ((Object.keys(received).length === 0) && (Object.keys(good).length === 0))
|
|
2603
|
+
return true; // Both empty
|
|
2604
|
+
if (JSON.stringify(received) === JSON.stringify(good))
|
|
2605
|
+
return true; // Contents match
|
|
2606
|
+
console.log(`${property} objects or arrays do not match.`); // Contents don't match
|
|
2607
|
+
return false;
|
|
2608
|
+
}
|
|
2609
|
+
function matchDicts(property, received, good) {
|
|
2610
|
+
if ((Object.keys(received).length === 0) && (Object.keys(good).length === 0))
|
|
2611
|
+
return true; // Both empty
|
|
2612
|
+
const receivedStr = JSON.stringify(received);
|
|
2613
|
+
const goodStr = JSON.stringify(good);
|
|
2614
|
+
if (receivedStr === goodStr)
|
|
2615
|
+
return true; // Contents match
|
|
2616
|
+
console.log(`${property} does not match: ${goodStr} expected. Received ${receivedStr}.`); // Contents don't match
|
|
2617
|
+
return false;
|
|
2618
|
+
}
|
|
2619
|
+
function matchUndefinedness(property, received, good) {
|
|
2620
|
+
if ((received === undefined) && (good === undefined))
|
|
2621
|
+
return true;
|
|
2622
|
+
if ((received === undefined) || (good === undefined))
|
|
2623
|
+
return false;
|
|
2624
|
+
return true;
|
|
2625
|
+
}
|
|
2626
|
+
function matchPointArrays(property, received, good, tolerance) {
|
|
2627
|
+
if (received.length != good.length) {
|
|
2628
|
+
console.log(`${property} does not match: Different number of points.`);
|
|
2629
|
+
return false;
|
|
2630
|
+
}
|
|
2631
|
+
for (var i = 0; i < received.length; i++) {
|
|
2632
|
+
const vLabel = property + '/' + i.toString() + '/' + 'v';
|
|
2633
|
+
const sLabel = property + '/' + i.toString() + '/' + 's';
|
|
2634
|
+
if (!matchFloats(vLabel, received[i].v, good[i].v, tolerance))
|
|
2635
|
+
return false;
|
|
2636
|
+
if (!matchFloats(sLabel, received[i].s, good[i].s, tolerance))
|
|
2637
|
+
return false;
|
|
2638
|
+
}
|
|
2639
|
+
return true;
|
|
2640
|
+
}
|
|
2641
|
+
function matchCompactnessByDistrict(property, received, good, tolerance) {
|
|
2642
|
+
if (received.length != good.length) {
|
|
2643
|
+
console.log(`${property} does not match: Different number of districts.`);
|
|
2644
|
+
return false;
|
|
2645
|
+
}
|
|
2646
|
+
let bMismatched = false;
|
|
2647
|
+
for (var i = 0; i < received.length; i++) {
|
|
2648
|
+
const rawReock = property + '/' + i.toString() + '/' + 'rawReock';
|
|
2649
|
+
if (!matchFloats(rawReock, received[i].rawReock, good[i].rawReock, tolerance))
|
|
2650
|
+
bMismatched = true;
|
|
2651
|
+
const normalizedReock = property + '/' + i.toString() + '/' + 'normalizedReock';
|
|
2652
|
+
if (!matchInts(normalizedReock, received[i].normalizedReock, good[i].normalizedReock))
|
|
2653
|
+
bMismatched = true;
|
|
2654
|
+
const rawPolsby = property + '/' + i.toString() + '/' + 'rawPolsby';
|
|
2655
|
+
if (!matchFloats(rawPolsby, received[i].rawPolsby, good[i].rawPolsby, tolerance))
|
|
2656
|
+
bMismatched = true;
|
|
2657
|
+
// 09-17-21 - By-district Polsby–Popper ratings from dra-score in production are wrong!
|
|
2658
|
+
// const normalizedPolsby: string = property + '/' + i.toString() + '/' + 'normalizedPolsby';
|
|
2659
|
+
// if (!matchInts(normalizedPolsby, received[i].normalizedPolsby, good[i].normalizedPolsby)) bMismatched = true;
|
|
2660
|
+
const kiwysiScore = property + '/' + i.toString() + '/' + 'kiwysiScore';
|
|
2661
|
+
if (!matchInts(kiwysiScore, received[i].kiwysiScore, good[i].kiwysiScore))
|
|
2662
|
+
bMismatched = true;
|
|
2663
|
+
}
|
|
2664
|
+
if (bMismatched = true)
|
|
2665
|
+
return false;
|
|
2666
|
+
return true;
|
|
2667
|
+
}
|
|
2668
|
+
// Not used, after all
|
|
2669
|
+
/* Modeled after David Sielaff's Jest extension 'toBeArrayWithValuesCloseTo'
|
|
2670
|
+
|
|
2671
|
+
function matchFloatArrays(property: string, received: Array<any>, expected: Array<any>, tolerance: number): boolean
|
|
2672
|
+
{
|
|
2673
|
+
// Note - Not sure what this check was for ...
|
|
2674
|
+
// if (expected.length == 0) {
|
|
2675
|
+
// return {
|
|
2676
|
+
// message: () => `expected arrays of same size`,
|
|
2677
|
+
// pass: received.length == 0
|
|
2678
|
+
// }
|
|
2679
|
+
// }
|
|
2680
|
+
|
|
2681
|
+
if (typeof expected[0] === "number")
|
|
2682
|
+
{
|
|
2683
|
+
if (typeof received[0] === "number") return matchArrays1d(property, received, expected, tolerance);
|
|
2684
|
+
|
|
2685
|
+
console.log(`${property} does not match: array doesn't contain numbers.`);
|
|
2686
|
+
return false;
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
// Note - Ditto
|
|
2690
|
+
// if (expected[0].length == 0) {
|
|
2691
|
+
// return {
|
|
2692
|
+
// message: () => `expected arrays of same size`,
|
|
2693
|
+
// pass: received[0].length == 0
|
|
2694
|
+
// }
|
|
2695
|
+
// }
|
|
2696
|
+
|
|
2697
|
+
if (expected[0] instanceof Array && typeof expected[0][0] === "number")
|
|
2698
|
+
{
|
|
2699
|
+
if (received[0] instanceof Array && typeof received[0][0] === "number") return matchArrays2d(property, received, expected, tolerance);
|
|
2700
|
+
|
|
2701
|
+
console.log(`${property} does not match: arrays don't have the same dimensionality and content.`);
|
|
2702
|
+
return false;
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
console.log(`${property}: Expected 1d or 2d arrays.`);
|
|
2706
|
+
return false;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
function matchArrays1d(property: string, received: number[], expected: number[], tolerance: number): boolean
|
|
2710
|
+
{
|
|
2711
|
+
if (received.length != expected.length)
|
|
2712
|
+
{
|
|
2713
|
+
console.log(`${property} does not match: arrays have different lengths.`);
|
|
2714
|
+
return false;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
for (var index = 0; index < received.length; index++)
|
|
2718
|
+
{
|
|
2719
|
+
const cell: string = property + '/' + index.toString();
|
|
2720
|
+
if (!matchFloats(cell, received[index], expected[index], tolerance)) return false;
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
return true;
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
function matchArrays2d(property: string, received: number[][], expected: number[][], tolerance: number): boolean
|
|
2727
|
+
{
|
|
2728
|
+
if (received.length != expected.length)
|
|
2729
|
+
{
|
|
2730
|
+
console.log(`${property} does not match: arrays have different lengths.`);
|
|
2731
|
+
return false;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
for (var index = 0; index < received.length; index++)
|
|
2735
|
+
{
|
|
2736
|
+
if (received[index].length != expected[index].length)
|
|
2737
|
+
{
|
|
2738
|
+
console.log(`${property} does not match: arrays have different lengths.`);
|
|
2739
|
+
return false;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
for (var inner = 0; inner < received[index].length; inner++)
|
|
2743
|
+
{
|
|
2744
|
+
const cell: string = property + '/' + index.toString() + '/' + inner.toString();
|
|
2745
|
+
if (!matchFloats(cell, received[index][inner], expected[index][inner], tolerance)) return false;
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
return true;
|
|
2750
|
+
}
|
|
2751
|
+
*/
|
|
2230
2752
|
|
|
2231
2753
|
|
|
2232
2754
|
/***/ }),
|
|
@@ -2242,24 +2764,27 @@ exports.scorePlan = scorePlan;
|
|
|
2242
2764
|
// GLOBAL CONSTANTS
|
|
2243
2765
|
//
|
|
2244
2766
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2245
|
-
exports.
|
|
2767
|
+
exports.OUT_OF_STATE = exports.NUMBER_OF_ITEMS_TO_REPORT = exports.NOT_ASSIGNED = exports.PRECISION = void 0;
|
|
2246
2768
|
// Keep four decimal places for fractions [0–1], i.e.,
|
|
2247
2769
|
// keep two decimal places for %'s [0–100].
|
|
2248
2770
|
exports.PRECISION = 4;
|
|
2771
|
+
// LEGACY - Not used
|
|
2249
2772
|
// Normalized scores [0-100]
|
|
2250
|
-
|
|
2773
|
+
// export const NORMALIZED_RANGE: number = 100;
|
|
2251
2774
|
// The dummy district ID for features not assigned districts yet
|
|
2252
2775
|
exports.NOT_ASSIGNED = 0;
|
|
2253
2776
|
// # of items to report as problematic (e.g., features, districts, etc.)
|
|
2254
2777
|
exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
|
|
2255
2778
|
// The virtual geoID for "neighbors" in other states
|
|
2256
2779
|
exports.OUT_OF_STATE = "OUT_OF_STATE";
|
|
2780
|
+
// LEGACY - Not used
|
|
2257
2781
|
// "Roughly equal" = average census block size / 2
|
|
2258
|
-
const AVERAGE_BLOCK_SIZE = 30;
|
|
2259
|
-
|
|
2782
|
+
// const AVERAGE_BLOCK_SIZE = 30;
|
|
2783
|
+
// export const EQUAL_TOLERANCE: number = AVERAGE_BLOCK_SIZE / 2;
|
|
2784
|
+
// LEGACY - Not used
|
|
2260
2785
|
// County & district splitting weights
|
|
2261
|
-
|
|
2262
|
-
|
|
2786
|
+
// export const COUNTY_SPLITTING_WEIGHT = 0.8;
|
|
2787
|
+
// export const DISTRICT_SPLITTING_WEIGHT = 1.0 - COUNTY_SPLITTING_WEIGHT;
|
|
2263
2788
|
// 2020
|
|
2264
2789
|
// export const SHAPES = "2010 VTD shapes";
|
|
2265
2790
|
|
|
@@ -2270,16 +2795,13 @@ exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
|
|
|
2270
2795
|
/*!**********************!*\
|
|
2271
2796
|
!*** ./src/types.ts ***!
|
|
2272
2797
|
\**********************/
|
|
2273
|
-
/***/ ((__unused_webpack_module, exports
|
|
2798
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
2274
2799
|
|
|
2275
2800
|
|
|
2276
2801
|
//
|
|
2277
2802
|
// TYPE DEFINITIONS
|
|
2278
2803
|
//
|
|
2279
2804
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2280
|
-
exports.estSeatProbability = void 0;
|
|
2281
|
-
var dra_score_1 = __webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score");
|
|
2282
|
-
Object.defineProperty(exports, "estSeatProbability", ({ enumerable: true, get: function () { return dra_score_1.estSeatProbability; } }));
|
|
2283
2805
|
// END
|
|
2284
2806
|
|
|
2285
2807
|
|
|
@@ -2315,21 +2837,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
2315
2837
|
return result;
|
|
2316
2838
|
};
|
|
2317
2839
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2318
|
-
exports.depthof = exports.deepCopy = exports.shallowCopy = exports.countEnumValues = exports.objectContains = exports.arrayContains = exports.getSelectObjectKeys = exports.getNumericObjectKeys = exports.getObjectKeys = exports.isArrayEmpty = exports.isSetEmpty = exports.isObjectEmpty = exports.keyExists = exports.andArray = exports.initArray = exports.maxArray = exports.minArray = exports.avgArray = exports.sumArray = exports.trim = exports.isUninhabited = exports.isWaterOnly = exports.parseGeoID = exports.getDistrict =
|
|
2840
|
+
exports.depthof = exports.deepCopy = exports.shallowCopy = exports.countEnumValues = exports.objectContains = exports.arrayContains = exports.getSelectObjectKeys = exports.getNumericObjectKeys = exports.getObjectKeys = exports.isArrayEmpty = exports.isSetEmpty = exports.isObjectEmpty = exports.keyExists = exports.andArray = exports.initArray = exports.maxArray = exports.minArray = exports.avgArray = exports.sumArray = exports.trim = exports.isUninhabited = exports.isWaterOnly = exports.parseGeoID = exports.getDistrict = void 0;
|
|
2319
2841
|
const DT = __importStar(__webpack_require__(/*! @dra2020/dra-types */ "@dra2020/dra-types"));
|
|
2320
2842
|
const _data_1 = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
2321
2843
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
2322
2844
|
// PLAN HELPERS
|
|
2845
|
+
// LEGACY - Not used
|
|
2323
2846
|
// Is a "neighbor" in state?
|
|
2324
|
-
function isInState(geoID)
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2847
|
+
// export function isInState(geoID: string): boolean
|
|
2848
|
+
// {
|
|
2849
|
+
// return geoID != S.OUT_OF_STATE;
|
|
2850
|
+
// }
|
|
2851
|
+
// LEGACY - Not used
|
|
2328
2852
|
// Is a "neighbor" out of state?
|
|
2329
|
-
function isOutOfState(geoID)
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2853
|
+
// export function isOutOfState(geoID: string): boolean
|
|
2854
|
+
// {
|
|
2855
|
+
// return geoID == S.OUT_OF_STATE;
|
|
2856
|
+
// }
|
|
2333
2857
|
// Get the districtID to which a geoID is assigned
|
|
2334
2858
|
function getDistrict(plan, geoID) {
|
|
2335
2859
|
// All geoIDs in a state *should be* assigned to a district (including the
|