@dra2020/district-analytics 1.0.4 → 1.0.7

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.
Files changed (114) hide show
  1. package/.prettierrc +5 -0
  2. package/dist/_api.d.ts +27 -0
  3. package/dist/_data.d.ts +130 -0
  4. package/dist/analyze.d.ts +4 -0
  5. package/dist/cli.js +12091 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/cohesive.d.ts +4 -0
  8. package/dist/compact.d.ts +5 -0
  9. package/dist/constants.d.ts +6 -0
  10. package/dist/district-analytics.js +102 -52
  11. package/dist/district-analytics.js.map +1 -0
  12. package/dist/equal.d.ts +4 -0
  13. package/dist/geofeature.d.ts +3 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/minority.d.ts +3 -0
  16. package/dist/political.d.ts +8 -0
  17. package/dist/preprocess.d.ts +2 -0
  18. package/dist/report.d.ts +15 -0
  19. package/dist/settings.d.ts +5 -0
  20. package/dist/test/_cli.d.ts +5 -0
  21. package/dist/types.d.ts +110 -0
  22. package/dist/utils.d.ts +28 -0
  23. package/dist/valid.d.ts +8 -0
  24. package/jestconfig.json +14 -0
  25. package/lib/HelloWorld.d.ts +3 -0
  26. package/lib/HelloWorld.js +11 -0
  27. package/lib/_api.js +91 -0
  28. package/lib/_api.js.map +1 -0
  29. package/lib/_cli.js +434 -0
  30. package/lib/_cli.js.map +1 -0
  31. package/lib/_data.js +425 -0
  32. package/lib/_data.js.map +1 -0
  33. package/lib/analyze.d.ts +3 -0
  34. package/lib/analyze.js +69 -0
  35. package/lib/analyze.js.map +1 -0
  36. package/lib/api.d.ts +34 -0
  37. package/lib/api.js +117 -0
  38. package/lib/api.js.map +1 -0
  39. package/lib/cli.d.ts +1 -0
  40. package/lib/cli.js +386 -0
  41. package/lib/cli.js.map +1 -0
  42. package/lib/cohesive.d.ts +4 -0
  43. package/lib/cohesive.js +132 -0
  44. package/lib/cohesive.js.map +1 -0
  45. package/lib/compact.d.ts +4 -0
  46. package/lib/compact.js +183 -0
  47. package/lib/compact.js.map +1 -0
  48. package/lib/constants.js +367 -0
  49. package/lib/constants.js.map +1 -0
  50. package/lib/data.js +188 -0
  51. package/lib/data.js.map +1 -0
  52. package/lib/equal.d.ts +4 -0
  53. package/lib/equal.js +59 -0
  54. package/lib/equal.js.map +1 -0
  55. package/lib/features.js +19 -0
  56. package/lib/features.js.map +1 -0
  57. package/lib/geofeature.js +112 -0
  58. package/lib/geofeature.js.map +1 -0
  59. package/lib/index.d.ts +5 -0
  60. package/lib/index.js +11 -0
  61. package/lib/index.js.map +1 -0
  62. package/lib/minority.d.ts +3 -0
  63. package/lib/minority.js +61 -0
  64. package/lib/minority.js.map +1 -0
  65. package/lib/political.d.ts +7 -0
  66. package/lib/political.js +88 -0
  67. package/lib/political.js.map +1 -0
  68. package/lib/preprocess.d.ts +4 -0
  69. package/lib/preprocess.js +101 -0
  70. package/lib/preprocess.js.map +1 -0
  71. package/lib/report.d.ts +14 -0
  72. package/lib/report.js +817 -0
  73. package/lib/report.js.map +1 -0
  74. package/lib/sample.d.ts +1 -0
  75. package/lib/sample.js +32 -0
  76. package/lib/sample.js.map +1 -0
  77. package/lib/scorecard.d.ts +4 -0
  78. package/lib/scorecard.js +237 -0
  79. package/lib/scorecard.js.map +1 -0
  80. package/lib/settings.d.ts +5 -0
  81. package/lib/settings.js +18 -0
  82. package/lib/settings.js.map +1 -0
  83. package/lib/types.d.ts +125 -0
  84. package/lib/types.js +7 -0
  85. package/lib/types.js.map +1 -0
  86. package/lib/utils.d.ts +20 -0
  87. package/lib/utils.js +223 -0
  88. package/lib/utils.js.map +1 -0
  89. package/lib/valid.d.ts +5 -0
  90. package/lib/valid.js +230 -0
  91. package/lib/valid.js.map +1 -0
  92. package/main.js +4 -0
  93. package/package.json +2 -7
  94. package/src/_api.ts +121 -0
  95. package/src/_data.ts +595 -0
  96. package/src/analyze.ts +92 -0
  97. package/src/cohesive.ts +156 -0
  98. package/src/compact.ts +208 -0
  99. package/src/constants.ts +371 -0
  100. package/src/equal.ts +75 -0
  101. package/src/geofeature.ts +138 -0
  102. package/src/index.ts +6 -0
  103. package/src/minority.ts +70 -0
  104. package/src/political.ts +114 -0
  105. package/src/preprocess.ts +132 -0
  106. package/src/report.ts +1022 -0
  107. package/src/settings.ts +20 -0
  108. package/src/types.ts +185 -0
  109. package/src/utils.ts +245 -0
  110. package/src/valid.ts +275 -0
  111. package/tsconfig.json +25 -0
  112. package/tslint.json +3 -0
  113. package/types/polygon-clipping/index.d.ts +1 -0
  114. package/webpack.config.js +73 -0
package/lib/report.js ADDED
@@ -0,0 +1,817 @@
1
+ "use strict";
2
+ //
3
+ // GENERATE REPORTS
4
+ // - A test log: a simple enumeration of all analytics & validations w/ raw results
5
+ // - A scorecard: a structured subset of analytics & validations w/ normalized
6
+ // results, cateories, and an overall score
7
+ //
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const U = require("./utils");
10
+ const S = require("./settings");
11
+ const D = require("./_data");
12
+ const analyze_1 = require("./analyze");
13
+ // TEST META-DATA
14
+ var TestType;
15
+ (function (TestType) {
16
+ TestType[TestType["PassFail"] = 0] = "PassFail";
17
+ TestType[TestType["Percentage"] = 1] = "Percentage";
18
+ TestType[TestType["Number"] = 2] = "Number";
19
+ })(TestType || (TestType = {}));
20
+ const completeDefn = {
21
+ ID: 0 /* Complete */,
22
+ name: "Complete",
23
+ normalize: false,
24
+ externalType: TestType.PassFail,
25
+ detailsFn: doPrepareCompleteDetails,
26
+ suites: [0 /* Legal */]
27
+ };
28
+ const contiguousDefn = {
29
+ ID: 1 /* Contiguous */,
30
+ name: "Contiguous",
31
+ normalize: false,
32
+ externalType: TestType.PassFail,
33
+ detailsFn: doPrepareContiguousDetails,
34
+ suites: [0 /* Legal */]
35
+ };
36
+ const freeOfHolesDefn = {
37
+ ID: 2 /* FreeOfHoles */,
38
+ name: "Free of Holes",
39
+ normalize: false,
40
+ externalType: TestType.PassFail,
41
+ detailsFn: doPrepareFreeOfHolesDetails,
42
+ suites: [0 /* Legal */]
43
+ };
44
+ const equalPopulationDefn = {
45
+ ID: 3 /* EqualPopulation */,
46
+ name: "Equal Population",
47
+ normalize: false,
48
+ externalType: TestType.PassFail,
49
+ detailsFn: doPrepareEqualPopulationDetails,
50
+ suites: [0 /* Legal */]
51
+ };
52
+ const populationDeviationDefn = {
53
+ ID: 4 /* PopulationDeviation */,
54
+ name: "Population Deviation",
55
+ normalize: true,
56
+ externalType: TestType.Percentage,
57
+ detailsFn: doPreparePopulationDeviationDetails,
58
+ suites: [0 /* Legal */, 2 /* Best */] // Both so EqualPopulation can be assessed
59
+ };
60
+ const reockDefn = {
61
+ ID: 5 /* Reock */,
62
+ name: "Reock",
63
+ normalize: true,
64
+ externalType: TestType.Number,
65
+ detailsFn: doPrepareReockDetails,
66
+ suites: [2 /* Best */]
67
+ };
68
+ const polsbyPopperDefn = {
69
+ ID: 6 /* PolsbyPopper */,
70
+ name: "Polsby-Popper",
71
+ normalize: true,
72
+ externalType: TestType.Number,
73
+ detailsFn: doPreparePolsbyPopperDetails,
74
+ suites: [2 /* Best */]
75
+ };
76
+ const countySplitsDefn = {
77
+ ID: 7 /* CountySplits */,
78
+ name: "County splits",
79
+ trailer: "of the population had their county split unexpectedly.",
80
+ normalize: true,
81
+ externalType: TestType.Percentage,
82
+ detailsFn: doPrepareCountySplitDetails,
83
+ suites: [2 /* Best */]
84
+ };
85
+ const efficiencyGapDefn = {
86
+ ID: 13 /* EfficiencyGap */,
87
+ name: "Efficiency Gap",
88
+ normalize: false,
89
+ externalType: TestType.Percentage,
90
+ detailsFn: doPrepareEfficiencyGapDetails,
91
+ suites: [1 /* Fair */]
92
+ };
93
+ // All the tests that have been defined (can be reported on)
94
+ const testDefns = {
95
+ [0 /* Complete */]: completeDefn,
96
+ [1 /* Contiguous */]: contiguousDefn,
97
+ [2 /* FreeOfHoles */]: freeOfHolesDefn,
98
+ [3 /* EqualPopulation */]: equalPopulationDefn,
99
+ [4 /* PopulationDeviation */]: populationDeviationDefn,
100
+ [5 /* Reock */]: reockDefn,
101
+ [6 /* PolsbyPopper */]: polsbyPopperDefn,
102
+ [7 /* CountySplits */]: countySplitsDefn,
103
+ [13 /* EfficiencyGap */]: efficiencyGapDefn
104
+ /* TODO - More tests ... */
105
+ };
106
+ // Scorecard category definitions
107
+ const validCategory = {
108
+ catName: "Valid",
109
+ catTests: [
110
+ { testID: 0 /* Complete */ },
111
+ { testID: 1 /* Contiguous */ },
112
+ { testID: 2 /* FreeOfHoles */ },
113
+ { testID: 3 /* EqualPopulation */ }
114
+ ],
115
+ catNumeric: false,
116
+ catWeight: undefined,
117
+ catPrepareFn: doPrepareValidSection
118
+ };
119
+ const fairCategory = {
120
+ catName: "Fair",
121
+ catTests: [
122
+ {
123
+ testID: 13 /* EfficiencyGap */,
124
+ testWeight: 100
125
+ }
126
+ ],
127
+ catNumeric: true,
128
+ catWeight: undefined,
129
+ catPrepareFn: doPrepareFairSection
130
+ };
131
+ // TODO - Decide on the relative weights of these tests!
132
+ // NOTE: 'testWeights' are simply relative, i.e., each normalized score is
133
+ // multiplied by the associated 'testWeight', and the sum of those is divided
134
+ // by the total weight. Weights don't have to add to 100.
135
+ const bestCategory = {
136
+ catName: "Best",
137
+ catTests: [
138
+ {
139
+ testID: 4 /* PopulationDeviation */,
140
+ testWeight: 10
141
+ },
142
+ {
143
+ testID: 5 /* Reock */,
144
+ testWeight: 25
145
+ },
146
+ {
147
+ testID: 6 /* PolsbyPopper */,
148
+ testWeight: 25
149
+ },
150
+ {
151
+ testID: 7 /* CountySplits */,
152
+ testWeight: 50
153
+ }
154
+ ],
155
+ catNumeric: true,
156
+ catWeight: undefined,
157
+ catPrepareFn: doPrepareBestSection
158
+ };
159
+ // The overall scorecard definition
160
+ const scorecardDefn = {
161
+ [0 /* Legal */]: validCategory,
162
+ // TODO - NIY
163
+ // [T.Suite.Fair]: fairCategory,
164
+ [2 /* Best */]: bestCategory
165
+ };
166
+ // NORMALIZE RAW ANALYTICS
167
+ // Raw numeric analytics, such as population deviation, compactness, etc. are
168
+ // normalized as part of creating a scorecard, so the code to normalize results
169
+ // is encapsulated here.
170
+ // Configure scale parameters for normalizing each raw test result
171
+ // This needs to be separate from the scorecard configuration info above,
172
+ // because some scales need access to the analytics session object.
173
+ function doConfigureScales(s) {
174
+ // Scale defn for PopulationDeviation
175
+ const CDLimit = 0.75 / 100; // Deviation threshold for CD's
176
+ const LDLimit = 10.00 / 100; // Deviation threshold for LD's
177
+ const CDGoodEnough = 0.20 / 100;
178
+ const LDGoodEnough = (CDGoodEnough / CDLimit) * LDLimit;
179
+ const scale = (s.legislativeDistricts) ? [1.0 - LDLimit, 1.0 - LDGoodEnough] : [1.0 - CDLimit, 1.0 - CDGoodEnough];
180
+ // const scale = [1.0 - CDLimit, 1.0 - CDGoodEnough];
181
+ s.testScales[4 /* PopulationDeviation */] = { testScale: scale, testInvertp: true };
182
+ s.testScales[5 /* Reock */] = { testScale: [0.25, 0.50], testInvertp: false };
183
+ s.testScales[6 /* PolsbyPopper */] = { testScale: [0.10, 0.50], testInvertp: false };
184
+ const SPLITLimit = 0.1; // TODO - Just a placeholder default maximum (10%)
185
+ s.testScales[7 /* CountySplits */] = { testScale: [1.0 - SPLITLimit, 1.0], testInvertp: true };
186
+ // TODO - More analytics ...
187
+ }
188
+ exports.doConfigureScales = doConfigureScales;
189
+ // Postprocess analytics - Normalize numeric results and derive secondary tests.
190
+ // Do this after analytics have been run and before preparing a test log or scorecard.
191
+ function doAnalyzePostProcessing(s) {
192
+ // Normalize the raw scores for all the numerics tests
193
+ let testResults = U.getNumericObjectKeys(testDefns);
194
+ for (let testID of testResults) {
195
+ if (testDefns[testID]['normalize']) {
196
+ let testResult = s.getTest(testID);
197
+ let rawScore = testResult['score'];
198
+ let normalizedScore;
199
+ let { testScale, testInvertp } = s.testScales[testID];
200
+ normalizedScore = U.normalize(rawScore, testScale, testInvertp);
201
+ testResult['normalizedScore'] = normalizedScore;
202
+ // Add the scale used to normalize the raw score to the details
203
+ testResult['details']['scale'] = testScale;
204
+ }
205
+ }
206
+ // Derive secondary tests
207
+ analyze_1.doDeriveSecondaryTests(s);
208
+ // Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
209
+ s.bPostProcessingDone = true;
210
+ }
211
+ exports.doAnalyzePostProcessing = doAnalyzePostProcessing;
212
+ // Prepare a structured but unformatted scorecard, from the test results
213
+ function doGenerateScorecard(s) {
214
+ if (!(s.bPostProcessingDone)) {
215
+ doAnalyzePostProcessing(s);
216
+ }
217
+ // Create a new scorecard
218
+ let scorecard = {};
219
+ // Filter the defined scorecard categories by the requested test suites
220
+ let categories = U.getNumericObjectKeys(scorecardDefn);
221
+ let suitesRequested = s.config['suites'];
222
+ categories = categories.filter(x => suitesRequested.includes(x));
223
+ // ... and initialize each one in the new scorecard
224
+ for (let c of categories) {
225
+ scorecard[c] = {};
226
+ scorecard[c]['catName'] = scorecardDefn[c]['catName'];
227
+ scorecard[c]['catTests'] = {};
228
+ // scorecard[c]['catScore'] = undefined;
229
+ }
230
+ // For each scorecard category
231
+ for (let c of categories) {
232
+ // Grab the scorecard category definition
233
+ let { catName, catTests, catNumeric } = scorecardDefn[c];
234
+ let numericCategoryScore = 0;
235
+ let totalWeight = 0;
236
+ let booleanCategoryScore = true;
237
+ // Process the results for each test result in the category
238
+ for (let testDefn of catTests) {
239
+ // Get the config info for the test
240
+ let testID = testDefn['testID'];
241
+ // ... and the actual test result
242
+ let testResult = s.getTest(testID);
243
+ // Create a new test entry for the scorecard
244
+ let testReport = U.deepCopy(testResult);
245
+ // Add the name
246
+ testReport['name'] = testDefns[testID]['name'];
247
+ if (catNumeric) {
248
+ // Normalize raw numeric scores ... moved to FIRST PASS above
249
+ // Accumulate a category score
250
+ let normalizedScore = testReport['normalizedScore'];
251
+ numericCategoryScore += normalizedScore * testDefn['testWeight'];
252
+ totalWeight += testDefn['testWeight'];
253
+ }
254
+ else {
255
+ // AND together pass/fail tests into a category score
256
+ if (!testReport['score']) {
257
+ booleanCategoryScore = false;
258
+ }
259
+ }
260
+ scorecard[c]['catTests'][testID] = testReport;
261
+ }
262
+ // Set the category score
263
+ if (catNumeric) {
264
+ scorecard[c]['catScore'] = Math.round(numericCategoryScore / totalWeight);
265
+ }
266
+ else {
267
+ scorecard[c]['catScore'] = booleanCategoryScore;
268
+ }
269
+ }
270
+ // TODO - Compute an overall score from the category weights
271
+ return scorecard;
272
+ }
273
+ // Prepare a formatted scorecard suitable for rendering
274
+ function doPrepareScorecard(s) {
275
+ // Initialize the output format
276
+ let text = { data: [] };
277
+ let blocks = text.data;
278
+ // If the plan as already been analyzed, prepare a scorecard
279
+ if (s.bPlanAnalyzed) {
280
+ // Create and cache a new, unformatted scorecard
281
+ s.scorecard = doGenerateScorecard(s);
282
+ // Create a scorecard header
283
+ blocks.push({ variant: 'h4', text: `Analysis` });
284
+ // Report district statistics
285
+ blocks.push({ variant: 'h5', text: `Individual Districts` });
286
+ let districtStatisticsText = doPrepareDistrictStatistics(s);
287
+ blocks.push(...districtStatisticsText);
288
+ // Prepare each scorecard category
289
+ blocks.push({ variant: 'h5', text: `Overall Plan` });
290
+ let categories = U.getNumericObjectKeys(s.scorecard);
291
+ for (let c of categories) {
292
+ let sectionPrepareFn = scorecardDefn[c]['catPrepareFn'];
293
+ let sectionText = sectionPrepareFn(s, c);
294
+ blocks.push(...sectionText);
295
+ }
296
+ // Report what datasets were used
297
+ let c = s.config['datasets']["CENSUS" /* CENSUS */];
298
+ let v = s.config['datasets']["VAP" /* VAP */];
299
+ let e = s.config['datasets']["ELECTION" /* ELECTION */];
300
+ blocks.push({ variant: 'body1', text: `Using datasets:` });
301
+ blocks.push({ variant: 'body1', text: `* ${c}: ${D.DatasetDescriptions[c]}` });
302
+ blocks.push({ variant: 'body1', text: `* ${v}: ${D.DatasetDescriptions[v]}` });
303
+ blocks.push({ variant: 'body1', text: `* ${e}: ${D.DatasetDescriptions[e]}` });
304
+ }
305
+ // Otherwise, return a blank scorecard
306
+ // TODO - What dra-client returns from renderAnalyzeCore()
307
+ // return <STV.StaticTextView text={ text } />;
308
+ return text;
309
+ }
310
+ exports.doPrepareScorecard = doPrepareScorecard;
311
+ function doPrepareDistrictStatistics(s) {
312
+ let text = { data: [] };
313
+ let blocks = text.data;
314
+ blocks.push({ variant: 'beginTable' });
315
+ blocks.push({ variant: 'row', cells: ['ID', 'Total', 'Δ%', 'OK?', '*', 'Dem', 'Rep', 'White', 'Minority', 'Black', 'Hispanic', 'Pacific', 'Asian', 'Native'] });
316
+ for (let d = 0; d < s.districts.numberOfRows(); d++) {
317
+ let tot = s.districts.statistics[D.DistrictField.TotalPop][d];
318
+ if (tot == 0)
319
+ blocks.push({ variant: 'row', cells: [String(d), '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-'] });
320
+ else {
321
+ tot = Math.round(tot);
322
+ let dev = fractionToPercentage(s.districts.statistics[D.DistrictField.PopDevPct][d]);
323
+ let bEq = true; // TODO - Set based on population threshold
324
+ let bC = s.districts.statistics[D.DistrictField.bContiguous][d]
325
+ && s.districts.statistics[D.DistrictField.bNotEmbedded][d];
326
+ let dPct = fractionToPercentage(s.districts.statistics[D.DistrictField.DemPct][d]);
327
+ let rPct = fractionToPercentage(s.districts.statistics[D.DistrictField.RepPct][d]);
328
+ let wPct = fractionToPercentage(s.districts.statistics[D.DistrictField.WhitePct][d]);
329
+ let mPct = fractionToPercentage(s.districts.statistics[D.DistrictField.MinorityPct][d]);
330
+ let bPct = fractionToPercentage(s.districts.statistics[D.DistrictField.BlackPct][d]);
331
+ let hPct = fractionToPercentage(s.districts.statistics[D.DistrictField.HispanicPct][d]);
332
+ let pPct = fractionToPercentage(s.districts.statistics[D.DistrictField.PacificPct][d]);
333
+ let aPct = fractionToPercentage(s.districts.statistics[D.DistrictField.AsianPct][d]);
334
+ let nPct = fractionToPercentage(s.districts.statistics[D.DistrictField.NativePct][d]);
335
+ let id;
336
+ if (d == 0)
337
+ id = "??";
338
+ else if (d == (s.districts.numberOfRows() - 1))
339
+ id = " ";
340
+ else
341
+ id = String(d);
342
+ blocks.push({
343
+ variant: 'row',
344
+ cells: [
345
+ `${id}`,
346
+ `${formatInteger(tot)}`,
347
+ `${formatPercentage(dev)}%`,
348
+ `${pfBoolToString(bEq)}`,
349
+ `${pfBoolToString(bC)}`,
350
+ `${formatPercentage(dPct)}%`,
351
+ `${formatPercentage(rPct)}%`,
352
+ `${formatPercentage(wPct)}%`,
353
+ `${formatPercentage(mPct)}%`,
354
+ `${formatPercentage(bPct)}%`,
355
+ `${formatPercentage(hPct)}%`,
356
+ `${formatPercentage(pPct)}%`,
357
+ `${formatPercentage(aPct)}%`,
358
+ `${formatPercentage(nPct)}%`
359
+ ]
360
+ });
361
+ }
362
+ }
363
+ blocks.push({ variant: 'endTable' });
364
+ return blocks;
365
+ }
366
+ // TEST LOG
367
+ // Prepare formatted test results for rendering
368
+ function doPrepareTestLog(s) {
369
+ // Initialize the output format
370
+ let text = { data: [] };
371
+ let blocks = text.data;
372
+ // If the plan as already been analyzed, prepare a test log
373
+ if (s.bPlanAnalyzed) {
374
+ if (!(s.bPostProcessingDone)) {
375
+ doAnalyzePostProcessing(s);
376
+ }
377
+ // Create a test log header
378
+ blocks.push({ variant: 'h4', text: `Test Log` });
379
+ let testResults = U.getNumericObjectKeys(testDefns);
380
+ let suitesRequested = new Set(s.config['suites']);
381
+ for (let testID of testResults) {
382
+ // Filter the defined tests by the requested test suites
383
+ let inSuites = testDefns[testID]['suites'];
384
+ if (!(U.isArrayEmpty(inSuites.filter(x => suitesRequested.has(x))))) {
385
+ // Get the test result
386
+ let testResult = s.getTest(testID);
387
+ // Prepare the text for it, and append it to the output
388
+ let testText = prepareTestEntry(testID, testResult);
389
+ blocks.push(...testText);
390
+ }
391
+ }
392
+ }
393
+ // Otherwise, return a blank test log
394
+ // TODO - What dra-client returns from renderAnalyzeCore()
395
+ // return <STV.StaticTextView text={ text } />;
396
+ return text;
397
+ }
398
+ exports.doPrepareTestLog = doPrepareTestLog;
399
+ function prepareTestEntry(testID, testResult) {
400
+ let text = { data: [] };
401
+ let blocks = text.data;
402
+ let testName = testDefns[testID]['name'];
403
+ let testNameTrailer = "";
404
+ if (U.keyExists('trailer', testDefns[testID])) {
405
+ testNameTrailer = testDefns[testID]['trailer'];
406
+ }
407
+ let testType = testDefns[testID]['externalType'];
408
+ let bNormalize = testDefns[testID]['normalize'];
409
+ let detailsFn = testDefns[testID]['detailsFn'];
410
+ let detailsText = detailsFn(testResult);
411
+ let score; // NOTE - Won't be undefined here
412
+ let normalizedScore;
413
+ let scoreText;
414
+ // Get the score ...
415
+ score = testResult['score'];
416
+ // ... and format it for rendering
417
+ switch (testType) {
418
+ case TestType.PassFail: {
419
+ scoreText = pfBoolToString(score);
420
+ blocks.push({ variant: 'body1', text: `${testName}: ${scoreText} ${testNameTrailer}` });
421
+ break;
422
+ }
423
+ case TestType.Percentage: {
424
+ score = fractionToPercentage(score);
425
+ if (bNormalize) {
426
+ normalizedScore = testResult['normalizedScore'];
427
+ blocks.push({ variant: 'body1', text: `${testName}: ${normalizedScore} / 100 : ${formatPercentage(score)}% ${testNameTrailer}` });
428
+ }
429
+ else {
430
+ blocks.push({ variant: 'body1', text: `${testName}: ${formatPercentage(score)}% ${testNameTrailer}` });
431
+ }
432
+ break;
433
+ }
434
+ case TestType.Number: {
435
+ if (bNormalize) {
436
+ normalizedScore = testResult['normalizedScore'];
437
+ blocks.push({ variant: 'body1', text: `${testName}: ${normalizedScore} / 100 : ${formatNumber(score)} ${testNameTrailer}` });
438
+ }
439
+ else {
440
+ blocks.push({ variant: 'body1', text: `${testName}: ${formatNumber(score)} ${testNameTrailer}` });
441
+ }
442
+ break;
443
+ }
444
+ default: {
445
+ // Unknown test type
446
+ throw new RangeError();
447
+ }
448
+ }
449
+ // Add the details text
450
+ blocks.push(...detailsText);
451
+ return blocks;
452
+ }
453
+ // FORMATTERS FOR TEST DETAILS
454
+ function doPrepareCompleteDetails(testResult) {
455
+ let text = { data: [] };
456
+ let blocks = text.data;
457
+ if (!U.isObjectEmpty(testResult['details'])) {
458
+ let unassignedText = "";
459
+ let emptyText = "";
460
+ let missingText = "";
461
+ if (U.keyExists('unassignedFeatures', testResult['details'])) {
462
+ let unassignedFeatures = testResult['details']['unassignedFeatures'];
463
+ let unassignedList = prepareListItems(unassignedFeatures);
464
+ let unassignedTextTemplates = [
465
+ `GEOID ${unassignedList} is not assigned to a district.`,
466
+ `GEOIDs ${unassignedList} are not assigned to districts.`,
467
+ `Several GEOIDs are not assigned to districts, including ${unassignedList}.`
468
+ ];
469
+ unassignedText = prepareListText(unassignedFeatures, unassignedTextTemplates);
470
+ }
471
+ if (U.keyExists('emptyDistricts', testResult['details'])) {
472
+ let emptyDistricts = testResult['details']['emptyDistricts'];
473
+ let emptyList = prepareListItems(emptyDistricts);
474
+ let emptyTextTemplates = [
475
+ `District ${emptyList} is empty.`,
476
+ `Districts ${emptyList} are empty.`,
477
+ `Several districts are empty, including ${emptyList}.`
478
+ ];
479
+ emptyText = prepareListText(emptyDistricts, emptyTextTemplates);
480
+ }
481
+ if (U.keyExists('missingDistricts', testResult['details'])) {
482
+ missingText = `Not enough districts have been defined. `;
483
+ }
484
+ let detailsText = " " + unassignedText + emptyText + missingText;
485
+ blocks.push({ variant: 'body1', text: detailsText });
486
+ }
487
+ return blocks;
488
+ }
489
+ function doPrepareContiguousDetails(testResult) {
490
+ let text = { data: [] };
491
+ let blocks = text.data;
492
+ if (!U.isObjectEmpty(testResult['details'])) {
493
+ let discontiguousDistricts = testResult['details']['discontiguousDistricts'];
494
+ let discontiguousList = prepareListItems(discontiguousDistricts);
495
+ let discontiguousTextTemplates = [
496
+ `District ${discontiguousList} is not contiguous.`,
497
+ `Districts ${discontiguousList} are not contiguous.`,
498
+ `Several districts are not contiguous, including ${discontiguousList}.`
499
+ ];
500
+ let detailsText = prepareListText(discontiguousDistricts, discontiguousTextTemplates);
501
+ blocks.push({ variant: 'body1', text: detailsText });
502
+ }
503
+ return blocks;
504
+ }
505
+ function doPrepareFreeOfHolesDetails(testResult) {
506
+ let text = { data: [] };
507
+ let blocks = text.data;
508
+ if (!U.isObjectEmpty(testResult['details'])) {
509
+ let embeddedDistricts = testResult['details']['embeddedDistricts'];
510
+ let embeddedList = prepareListItems(embeddedDistricts);
511
+ let embeddedTextTemplates = [
512
+ `District ${embeddedList} is fully embedded within another district.`,
513
+ `Both districts ${embeddedList} are fully embedded within other districts.`,
514
+ `Several districts are fully embedded within other districts, including ${embeddedList}.`
515
+ ];
516
+ let detailsText = prepareListText(embeddedDistricts, embeddedTextTemplates);
517
+ blocks.push({ variant: 'body1', text: detailsText });
518
+ }
519
+ return blocks;
520
+ }
521
+ function doPrepareEqualPopulationDetails(testResult) {
522
+ let text = { data: [] };
523
+ let blocks = text.data;
524
+ if (!U.isObjectEmpty(testResult['details'])) {
525
+ let popDevPct = fractionToPercentage(testResult['details']['deviation']);
526
+ let thresholdPct = fractionToPercentage(1.0 - testResult['details']['thresholds'][0]);
527
+ let detailsText = '';
528
+ if (!(testResult['score'])) {
529
+ detailsText = `The ${formatPercentage(popDevPct)}% population deviation is greater than the ${formatPercentage(thresholdPct)}% threshold tolerated by courts.`;
530
+ }
531
+ blocks.push({ variant: 'body1', text: detailsText });
532
+ }
533
+ return blocks;
534
+ }
535
+ function doPreparePopulationDeviationDetails(testResult) {
536
+ let text = { data: [] };
537
+ let blocks = text.data;
538
+ let n = Math.round(testResult['details']['maxDeviation']);
539
+ let term = "people";
540
+ if (n == 1) {
541
+ term = "person";
542
+ }
543
+ blocks.push({ variant: 'body1', text: `The maximum population deviation between districts is ${formatInteger(n)} ${term}.` });
544
+ return blocks;
545
+ }
546
+ function doPrepareReockDetails(testResult) {
547
+ let text = { data: [] };
548
+ let blocks = text.data;
549
+ // TODO - No details implemented yet
550
+ return blocks;
551
+ }
552
+ function doPreparePolsbyPopperDetails(testResult) {
553
+ let text = { data: [] };
554
+ let blocks = text.data;
555
+ // TODO - No details implemented yet
556
+ return blocks;
557
+ }
558
+ function doPrepareEfficiencyGapDetails(testResult) {
559
+ let text = { data: [] };
560
+ let blocks = text.data;
561
+ // TODO - Not yet implemented
562
+ return blocks;
563
+ }
564
+ function doPrepareCountySplitDetails(testResult) {
565
+ let text = { data: [] };
566
+ let blocks = text.data;
567
+ let unexpectedAffected = fractionToPercentage(testResult['details']['unexpectedAffected']);
568
+ let affectedText = `affecting ${formatPercentage(unexpectedAffected)}% of the total population.`;
569
+ let countiesSplitUnexpectedly = testResult['details']['countiesSplitUnexpectedly'];
570
+ let nCountiesSplitUnexpectedly = countiesSplitUnexpectedly.length;
571
+ let nUnexpectedSplits = testResult['details']['unexpectedSplits'];
572
+ let splitList = prepareListItems(countiesSplitUnexpectedly);
573
+ let splitTextTemplates = [
574
+ `${splitList} county is split unexpectedly, `,
575
+ `${splitList} counties are split unexpectedly, `,
576
+ // `These ${formatInteger(nCountiesSplitUnexpectedly)} counties are split unexpectedly--${splitList}--`
577
+ `${splitList} counties are split unexpectedly ${formatInteger(nUnexpectedSplits)} times, `
578
+ ];
579
+ let detailsText = prepareListText(countiesSplitUnexpectedly, splitTextTemplates) + affectedText;
580
+ blocks.push({ variant: 'body1', text: detailsText });
581
+ return blocks;
582
+ }
583
+ // FORMATTERS FOR CATEGORIES
584
+ // This function parses & formats the 'Valid' section of a scorecard.
585
+ function doPrepareValidSection(s, c) {
586
+ // Get the category meta data
587
+ let { catName, catScore, catTests } = s.scorecard[c];
588
+ let testReport;
589
+ // Initialize the section text
590
+ let text = { data: [] };
591
+ let blocks = text.data;
592
+ let testText;
593
+ // Section header
594
+ let stringScore = pfBoolToString(catScore);
595
+ blocks.push({ variant: 'h6', text: `${catName}: ${stringScore}` });
596
+ // Complete
597
+ testReport = catTests[0 /* Complete */];
598
+ testText = prepareTestEntry(0 /* Complete */, testReport);
599
+ blocks.push(...testText);
600
+ // Contiguous
601
+ testReport = catTests[1 /* Contiguous */];
602
+ testText = prepareTestEntry(1 /* Contiguous */, testReport);
603
+ blocks.push(...testText);
604
+ // Free of holes (no embedded districts)
605
+ testReport = catTests[2 /* FreeOfHoles */];
606
+ testText = prepareTestEntry(2 /* FreeOfHoles */, testReport);
607
+ blocks.push(...testText);
608
+ // Equal population (w/in the appropriate legal threshold)
609
+ testReport = catTests[3 /* EqualPopulation */];
610
+ testText = prepareTestEntry(3 /* EqualPopulation */, testReport);
611
+ blocks.push(...testText);
612
+ return blocks;
613
+ }
614
+ // TODO - NIY
615
+ // This function parses & formats the 'Fair' section of a scorecard.
616
+ function doPrepareFairSection(s, c) {
617
+ // Get the category meta data
618
+ let { catName, catScore, catTests } = s.scorecard[c];
619
+ let testReport;
620
+ // Initialize the section text
621
+ let text = { data: [] };
622
+ let blocks = text.data;
623
+ let testText;
624
+ // Section header
625
+ blocks.push({ variant: 'h6', text: `${catName}: ${catScore}` });
626
+ // TODO - Flesh this out
627
+ // There's only one test: Population Deviation.
628
+ // testReport = catTests[T.Test.PopulationDeviation];
629
+ // testText = prepareTestEntry(T.Test.PopulationDeviation, testReport);
630
+ // blocks.push(...testText);
631
+ return blocks;
632
+ }
633
+ // This function parses & formats the 'Best' section of a scorecard.
634
+ function doPrepareBestSection(s, c) {
635
+ // Get the category meta data
636
+ let { catName, catScore, catTests } = s.scorecard[c];
637
+ let testReport;
638
+ // Initialize the section text
639
+ let text = { data: [] };
640
+ let blocks = text.data;
641
+ let testText;
642
+ // Section header
643
+ blocks.push({ variant: 'h6', text: `${catName}: ${catScore}` });
644
+ // Population deviation
645
+ testReport = catTests[4 /* PopulationDeviation */];
646
+ testText = prepareTestEntry(4 /* PopulationDeviation */, testReport);
647
+ blocks.push(...testText);
648
+ // Compactness
649
+ testReport = catTests[5 /* Reock */];
650
+ let normalizedReock = testReport['normalizedScore'];
651
+ let reockTestText = prepareTestEntry(5 /* Reock */, testReport);
652
+ testReport = catTests[6 /* PolsbyPopper */];
653
+ let normalizedPolsbyPopper = testReport['normalizedScore'];
654
+ let polsbyPopperTestText = prepareTestEntry(6 /* PolsbyPopper */, testReport);
655
+ let compactnessScore = (normalizedReock + normalizedPolsbyPopper) / 2;
656
+ blocks.push({ variant: 'body1', text: `Compactness: ${compactnessScore} / 100` });
657
+ blocks.push(...reockTestText);
658
+ blocks.push(...polsbyPopperTestText);
659
+ // County splits
660
+ testReport = catTests[7 /* CountySplits */];
661
+ testText = prepareTestEntry(7 /* CountySplits */, testReport);
662
+ blocks.push(...testText);
663
+ return blocks;
664
+ }
665
+ // FORMATTING HELPERS
666
+ // Convert a boolean representing Pass/Fail to a string
667
+ function pfBoolToString(score) {
668
+ if (score) {
669
+ return "Yes";
670
+ }
671
+ else {
672
+ return "No";
673
+ }
674
+ }
675
+ function fractionToPercentage(f) {
676
+ return f * 100;
677
+ }
678
+ function formatNumber(n) {
679
+ let p = S.PRECISION;
680
+ return n.toLocaleString('en-US', { minimumFractionDigits: p, maximumFractionDigits: p });
681
+ }
682
+ function formatPercentage(n) {
683
+ let p = (S.PRECISION / 2);
684
+ return n.toLocaleString('en-US', { minimumFractionDigits: p, maximumFractionDigits: p });
685
+ }
686
+ function formatInteger(i) {
687
+ return new Intl.NumberFormat().format(i);
688
+ }
689
+ // Prepare the items in a list for rendering
690
+ function prepareListItems(list) {
691
+ let nItems = list.length;
692
+ let listStr;
693
+ switch (nItems) {
694
+ case 1: {
695
+ listStr = list[0];
696
+ break;
697
+ }
698
+ case 2: {
699
+ listStr = list[0] + " and " + list[1];
700
+ break;
701
+ }
702
+ default: {
703
+ let listWithCommas = list.join(', ');
704
+ let lastCommaIndex = listWithCommas.length - ((list[list.length - 1].length) + 1);
705
+ let beforeAnd = listWithCommas.substr(0, lastCommaIndex);
706
+ let afterAnd = listWithCommas.substr(lastCommaIndex + 1);
707
+ listStr = beforeAnd + " and " + afterAnd;
708
+ break;
709
+ }
710
+ }
711
+ return listStr;
712
+ }
713
+ // Pick the rendering text for the appropriate list length
714
+ function prepareListText(list, listTemplates) {
715
+ let nItems = list.length;
716
+ switch (nItems) {
717
+ case 1: {
718
+ return listTemplates[0];
719
+ break;
720
+ }
721
+ case 2: {
722
+ return listTemplates[1];
723
+ break;
724
+ }
725
+ default: {
726
+ return listTemplates[2];
727
+ break;
728
+ }
729
+ }
730
+ }
731
+ /* COMMAND-LINE TESTS
732
+
733
+ node main.js scorecard -v -x NC -n 13 -p ~/src/district-analytics/data/SAMPLE-BG-map.csv -d ~/src/district-analytics/data/SAMPLE-BG-data2.json -s ~/src/district-analytics/data/SAMPLE-BG-shapes.geojson -g ~/src/district-analytics/data/SAMPLE-BG-graph.json -c ~/src/district-analytics/data/SAMPLE-COUNTY.geojson
734
+ node --inspect --inspect-brk main.js scorecard -v -x NC -n 13 -p ~/src/district-analytics/data/SAMPLE-BG-map.csv -d ~/src/district-analytics/data/SAMPLE-BG-data2.json -s ~/src/district-analytics/data/SAMPLE-BG-shapes.geojson -g ~/src/district-analytics/data/SAMPLE-BG-graph.json -c ~/src/district-analytics/data/SAMPLE-COUNTY.geojson
735
+
736
+ -or-
737
+
738
+ ./main.js scorecard -v -x NC -n 13 -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
739
+ ./main.js --inspect --inspect-brk scorecard -v -x NC -n 13 -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
740
+
741
+ These calls work at the project directory, using samples in the data/ directory:
742
+
743
+ ./main.js testlog -v -x NC -n 13 -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
744
+
745
+ TODO - Fix this
746
+ ./main.js testlog -v -x NC -n 13 -p ../data/SAMPLE-BG-map-missing.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
747
+ ./main.js scorecard -v -x NC-n 13 -p ../data/SAMPLE-BG-map-missing.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
748
+
749
+ ./main.js testlog -v -x NC -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
750
+ ./main.js scorecard -v -x NC -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
751
+
752
+ TODO - HERE
753
+
754
+ ./main.js -v -x NC testlog -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson --empty
755
+ ./main.js -v -x NC scorecard -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson --empty
756
+
757
+ TODO
758
+
759
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
760
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
761
+
762
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
763
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
764
+
765
+
766
+ node --inspect --inspect-brk main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
767
+ node main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
768
+
769
+
770
+ These calls test NC block-level data stored in my ~/data/redistricting-data/2010/compact/sample directory:
771
+
772
+ node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
773
+ node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
774
+
775
+ node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
776
+ node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
777
+
778
+
779
+
780
+ */
781
+ /* TODO - DELETE
782
+
783
+ These calls work at the project directory, using samples in the data/ directory:
784
+
785
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
786
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
787
+
788
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-missing.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
789
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-missing.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
790
+
791
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
792
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
793
+
794
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv --empty
795
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv --empty
796
+
797
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
798
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
799
+
800
+ ./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
801
+ ./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
802
+
803
+
804
+ node --inspect --inspect-brk main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
805
+ node main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
806
+
807
+
808
+ These calls test NC block-level data stored in my ~/data/redistricting-data/2010/compact/sample directory:
809
+
810
+ node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
811
+ node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
812
+
813
+ node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
814
+ node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
815
+
816
+ */
817
+ //# sourceMappingURL=report.js.map