@speleotica/frcsdata 4.3.1 → 5.0.0-beta.2
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/ParseIssue.d.ts +98 -0
- package/ParseIssue.d.ts.map +1 -0
- package/ParseIssue.js +17 -0
- package/ParseIssue.js.map +1 -0
- package/SourceLoc.d.ts +67 -0
- package/SourceLoc.d.ts.map +1 -0
- package/SourceLoc.js +18 -0
- package/SourceLoc.js.map +1 -0
- package/chunksToLines.d.ts +5 -0
- package/chunksToLines.d.ts.map +1 -0
- package/chunksToLines.js +37 -0
- package/chunksToLines.js.map +1 -0
- package/cli/check-survey-correspondence.d.ts +2 -0
- package/cli/check-survey-correspondence.d.ts.map +1 -0
- package/cli/check-survey-correspondence.js +44 -0
- package/cli/check-survey-correspondence.js.map +1 -0
- package/cli/check-survey.d.ts +2 -0
- package/cli/check-survey.d.ts.map +1 -0
- package/cli/check-survey.js +27 -0
- package/cli/check-survey.js.map +1 -0
- package/cli/parse-survey.d.ts +2 -0
- package/cli/parse-survey.d.ts.map +1 -0
- package/cli/parse-survey.js +15 -0
- package/cli/parse-survey.js.map +1 -0
- package/cli/summarize-survey.d.ts +2 -0
- package/cli/summarize-survey.d.ts.map +1 -0
- package/cli/summarize-survey.js +33 -0
- package/cli/summarize-survey.js.map +1 -0
- package/cli.d.ts +2 -0
- package/cli.d.ts.map +1 -0
- package/cli.js +72 -0
- package/cli.js.map +1 -0
- package/formatFrcsTripSummaryFile.d.ts +3 -0
- package/formatFrcsTripSummaryFile.d.ts.map +1 -0
- package/formatFrcsTripSummaryFile.js +33 -0
- package/formatFrcsTripSummaryFile.js.map +1 -0
- package/formatIssues.d.ts +10 -0
- package/formatIssues.d.ts.map +1 -0
- package/formatIssues.js +52 -0
- package/formatIssues.js.map +1 -0
- package/index.d.ts +5 -7
- package/index.d.ts.map +1 -1
- package/index.js +16 -49
- package/index.js.map +1 -1
- package/node/index.d.ts +4 -2
- package/node/index.d.ts.map +1 -1
- package/node/index.js +6 -12
- package/node/index.js.map +1 -1
- package/package.json +8 -6
- package/parseFrcsPlotFile.js +112 -245
- package/parseFrcsPlotFile.js.map +1 -1
- package/parseFrcsTripSummaryFile.d.ts +3 -1
- package/parseFrcsTripSummaryFile.d.ts.map +1 -1
- package/parseFrcsTripSummaryFile.js +53 -119
- package/parseFrcsTripSummaryFile.js.map +1 -1
- package/src/ParseIssue.ts +19 -0
- package/src/SourceLoc.ts +13 -0
- package/src/chunksToLines.ts +26 -0
- package/src/cli/check-survey-correspondence.ts +49 -0
- package/src/cli/check-survey.ts +23 -0
- package/src/cli/parse-survey.ts +10 -0
- package/src/cli/summarize-survey.ts +28 -0
- package/src/cli.ts +63 -0
- package/src/formatFrcsTripSummaryFile.ts +45 -0
- package/src/formatIssues.ts +97 -0
- package/src/index.ts +9 -7
- package/src/node/index.ts +18 -5
- package/src/parseFrcsTripSummaryFile.ts +8 -2
- package/src/string/index.ts +18 -5
- package/src/survey/FrcsSurveyFile.ts +217 -0
- package/src/survey/FrcsSurveyFileJson.ts +46 -0
- package/src/survey/ZodFrcsSurveyFileJson.ts +260 -0
- package/src/survey/ZodFrcsSurveyFileToJson.ts +297 -0
- package/src/{formatFrcsShot.ts → survey/formatFrcsShot.ts} +39 -22
- package/src/{formatFrcsSurveyFile.ts → survey/formatFrcsSurveyFile.ts} +4 -5
- package/src/survey/getColumnRanges.ts +82 -0
- package/src/survey/normalizeTeamMemberName.ts +7 -0
- package/src/survey/parseFrcsSurveyFile.ts +848 -0
- package/src/survey/parsers.ts +128 -0
- package/src/survey/summarizeSurvey.ts +51 -0
- package/src/survey/validators.ts +24 -0
- package/src/underlineSource.ts +34 -0
- package/src/unwrapInvalid.ts +3 -0
- package/src/web/index.ts +27 -21
- package/string/index.d.ts +4 -2
- package/string/index.d.ts.map +1 -1
- package/string/index.js +8 -34
- package/string/index.js.map +1 -1
- package/survey/FrcsSurveyFile.d.ts +172 -0
- package/survey/FrcsSurveyFile.d.ts.map +1 -0
- package/{FrcsSurveyFile.js → survey/FrcsSurveyFile.js} +3 -1
- package/survey/FrcsSurveyFile.js.map +1 -0
- package/survey/FrcsSurveyFileJson.d.ts +15 -0
- package/survey/FrcsSurveyFileJson.d.ts.map +1 -0
- package/{FrcsTrip.js → survey/FrcsSurveyFileJson.js} +1 -1
- package/survey/FrcsSurveyFileJson.js.map +1 -0
- package/survey/ZodFrcsSurveyFileJson.d.ts +60549 -0
- package/survey/ZodFrcsSurveyFileJson.d.ts.map +1 -0
- package/survey/ZodFrcsSurveyFileJson.js +186 -0
- package/survey/ZodFrcsSurveyFileJson.js.map +1 -0
- package/survey/ZodFrcsSurveyFileToJson.d.ts +60390 -0
- package/survey/ZodFrcsSurveyFileToJson.d.ts.map +1 -0
- package/survey/ZodFrcsSurveyFileToJson.js +209 -0
- package/survey/ZodFrcsSurveyFileToJson.js.map +1 -0
- package/{formatFrcsShot.d.ts → survey/formatFrcsShot.d.ts} +2 -3
- package/survey/formatFrcsShot.d.ts.map +1 -0
- package/survey/formatFrcsShot.js +107 -0
- package/survey/formatFrcsShot.js.map +1 -0
- package/survey/formatFrcsSurveyFile.d.ts +3 -0
- package/survey/formatFrcsSurveyFile.d.ts.map +1 -0
- package/survey/formatFrcsSurveyFile.js +87 -0
- package/survey/formatFrcsSurveyFile.js.map +1 -0
- package/survey/getColumnRanges.d.ts +24 -0
- package/survey/getColumnRanges.d.ts.map +1 -0
- package/survey/getColumnRanges.js +62 -0
- package/survey/getColumnRanges.js.map +1 -0
- package/survey/normalizeTeamMemberName.d.ts +2 -0
- package/survey/normalizeTeamMemberName.d.ts.map +1 -0
- package/survey/normalizeTeamMemberName.js +12 -0
- package/survey/normalizeTeamMemberName.js.map +1 -0
- package/{parseFrcsSurveyFile.d.ts → survey/parseFrcsSurveyFile.d.ts} +2 -5
- package/survey/parseFrcsSurveyFile.d.ts.map +1 -0
- package/survey/parseFrcsSurveyFile.js +626 -0
- package/survey/parseFrcsSurveyFile.js.map +1 -0
- package/survey/parsers.d.ts +11 -0
- package/survey/parsers.d.ts.map +1 -0
- package/survey/parsers.js +119 -0
- package/survey/parsers.js.map +1 -0
- package/survey/summarizeSurvey.d.ts +6 -0
- package/survey/summarizeSurvey.d.ts.map +1 -0
- package/survey/summarizeSurvey.js +58 -0
- package/survey/summarizeSurvey.js.map +1 -0
- package/survey/validators.d.ts +7 -0
- package/survey/validators.d.ts.map +1 -0
- package/survey/validators.js +36 -0
- package/survey/validators.js.map +1 -0
- package/underlineSource.d.ts +6 -0
- package/underlineSource.d.ts.map +1 -0
- package/underlineSource.js +22 -0
- package/underlineSource.js.map +1 -0
- package/unwrapInvalid.d.ts +4 -0
- package/unwrapInvalid.d.ts.map +1 -0
- package/unwrapInvalid.js +10 -0
- package/unwrapInvalid.js.map +1 -0
- package/web/index.d.ts +6 -6
- package/web/index.d.ts.map +1 -1
- package/web/index.js +60 -118
- package/web/index.js.map +1 -1
- package/FrcsShot.d.ts +0 -57
- package/FrcsShot.d.ts.map +0 -1
- package/FrcsShot.js +0 -13
- package/FrcsShot.js.map +0 -1
- package/FrcsSurveyFile.d.ts +0 -29
- package/FrcsSurveyFile.d.ts.map +0 -1
- package/FrcsSurveyFile.js.map +0 -1
- package/FrcsTrip.d.ts +0 -23
- package/FrcsTrip.d.ts.map +0 -1
- package/FrcsTrip.js.map +0 -1
- package/formatFrcsShot.d.ts.map +0 -1
- package/formatFrcsShot.js +0 -96
- package/formatFrcsShot.js.map +0 -1
- package/formatFrcsSurveyFile.d.ts +0 -3
- package/formatFrcsSurveyFile.d.ts.map +0 -1
- package/formatFrcsSurveyFile.js +0 -165
- package/formatFrcsSurveyFile.js.map +0 -1
- package/parseFrcsSurveyFile.d.ts.map +0 -1
- package/parseFrcsSurveyFile.js +0 -812
- package/parseFrcsSurveyFile.js.map +0 -1
- package/src/FrcsShot.ts +0 -56
- package/src/FrcsSurveyFile.ts +0 -47
- package/src/FrcsTrip.ts +0 -25
- package/src/parseFrcsSurveyFile.ts +0 -788
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseFrcsTripSummaryFile.js","names":["_unitized","require","
|
|
1
|
+
{"version":3,"file":"parseFrcsTripSummaryFile.js","names":["_unitized","require","tripSummaryRegex","parseFrcsTripSummaryFile","file","lines","indexBy","errors","tripSummaries","tripStartLine","lineNumber","team","shots","line","match","exec","tripNumber","parseInt","tripIndex","length","year","date","Date","totalLength","Unitize","feet","parseFloat","numShots","name","trim","excludedLength","numExcludedShots","push","split","trimmed","module","exports","default"],"sources":["src/parseFrcsTripSummaryFile.ts"],"sourcesContent":["import { FrcsTripSummary } from './FrcsTripSummary'\nimport { FrcsTripSummaryFile } from './FrcsTripSummaryFile'\nimport { SegmentParseError } from 'parse-segment'\nimport { Unitize } from '@speleotica/unitized'\n\nconst tripSummaryRegex =\n /^\\s*(\\d+)\\s+(\\d{1,2})\\/(\\s\\d|\\d\\d)\\/(\\d{2,4})\\s+(\\d+(?:\\.\\d*)?)\\s+(\\d+)\\s+(\\S.*)EXCLUDED:\\s*(\\d+(?:\\.\\d{2})?)\\s*(\\d+)/\n/**\n * Parses data from a STAT_sum.txt file. Here is an excerpt of the format:\n<pre> 1 2/15/81 258.60 17 ENTRANCE DROPS, JOE'S \"I LOVE MY WIFE TRAVERSE\", TRICKY TRAVERSE EXCLUDED: 0.00 0\n Peter Quick Keith Ortiz\n A1 AD1-AD3 AE1 AE1 SIDE\n AE9 SIDE AE10-AE9 AE13 SIDE AE15 SIDE\n AE20-AE11\n\n 3 3/ 6/81 2371.20 61 DOUG'S DEMISE (50 FT DROP), CHRIS CROSS, CRAWL ABOVE DROP EXCLUDED: 0.00 0\n Peter Quick Chris Gerace Phil Oden Chip Hopper\n A13 SIDE B1-B5 B2 SIDE B3 SIDE\n B6-B18 B17 SIDE B19-B38 B32 SIDE\n BS1-BS5 C1-C18 </pre>\n */\nexport default async function parseFrcsTripSummaryFile(\n file: string,\n lines: AsyncIterable<string>,\n {\n indexBy = 'tripNumber',\n }: {\n indexBy?: 'tripNumber' | 'occurrence'\n } = {}\n): Promise<FrcsTripSummaryFile> {\n const errors: Array<SegmentParseError> = []\n const tripSummaries: Array<FrcsTripSummary | undefined> = []\n\n let tripStartLine = -2\n let lineNumber = 0\n\n let team: Array<string> = []\n let shots: Array<string> = []\n\n for await (const line of lines) {\n lineNumber++\n\n const match = tripSummaryRegex.exec(line)\n if (match) {\n tripStartLine = lineNumber\n const tripNumber = parseInt(match[1])\n const tripIndex =\n indexBy === 'tripNumber' ? tripNumber - 1 : tripSummaries.length\n let year = parseInt(match[4])\n if (year < 1000) year += 1900\n const date = new Date(year, parseInt(match[2]) - 1, parseInt(match[3]))\n const totalLength = Unitize.feet(parseFloat(match[5]))\n const numShots = parseInt(match[6])\n const name = match[7].trim()\n const excludedLength = Unitize.feet(parseFloat(match[8]))\n const numExcludedShots = parseInt(match[9])\n team = []\n shots = []\n tripSummaries[tripIndex] = {\n tripNumber,\n tripIndex,\n date,\n totalLength,\n numShots,\n name,\n excludedLength,\n numExcludedShots,\n team,\n shots,\n }\n continue\n }\n if (lineNumber === tripStartLine + 1) {\n team.push(...line.trim().split(/\\s\\s+|\\t+/g))\n } else {\n const trimmed = line.trim()\n if (trimmed) shots.push(...trimmed.split(/\\s\\s+|\\t+/))\n }\n }\n\n return { errors, tripSummaries }\n}\n"],"mappings":";;;;;;AAGA,IAAAA,SAAA,GAAAC,OAAA;AAEA,MAAMC,gBAAgB,GACpB,uHAAuH;AACzH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,eAAeC,wBAAwBA,CACpDC,IAAY,EACZC,KAA4B,EAC5B;EACEC,OAAO,GAAG;AAGZ,CAAC,GAAG,CAAC,CAAC,EACwB;EAC9B,MAAMC,MAAgC,GAAG,EAAE;EAC3C,MAAMC,aAAiD,GAAG,EAAE;EAE5D,IAAIC,aAAa,GAAG,CAAC,CAAC;EACtB,IAAIC,UAAU,GAAG,CAAC;EAElB,IAAIC,IAAmB,GAAG,EAAE;EAC5B,IAAIC,KAAoB,GAAG,EAAE;EAE7B,WAAW,MAAMC,IAAI,IAAIR,KAAK,EAAE;IAC9BK,UAAU,EAAE;IAEZ,MAAMI,KAAK,GAAGZ,gBAAgB,CAACa,IAAI,CAACF,IAAI,CAAC;IACzC,IAAIC,KAAK,EAAE;MACTL,aAAa,GAAGC,UAAU;MAC1B,MAAMM,UAAU,GAAGC,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC;MACrC,MAAMI,SAAS,GACbZ,OAAO,KAAK,YAAY,GAAGU,UAAU,GAAG,CAAC,GAAGR,aAAa,CAACW,MAAM;MAClE,IAAIC,IAAI,GAAGH,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC;MAC7B,IAAIM,IAAI,GAAG,IAAI,EAAEA,IAAI,IAAI,IAAI;MAC7B,MAAMC,IAAI,GAAG,IAAIC,IAAI,CAACF,IAAI,EAAEH,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAEG,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;MACvE,MAAMS,WAAW,GAAGC,iBAAO,CAACC,IAAI,CAACC,UAAU,CAACZ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;MACtD,MAAMa,QAAQ,GAAGV,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC;MACnC,MAAMc,IAAI,GAAGd,KAAK,CAAC,CAAC,CAAC,CAACe,IAAI,CAAC,CAAC;MAC5B,MAAMC,cAAc,GAAGN,iBAAO,CAACC,IAAI,CAACC,UAAU,CAACZ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;MACzD,MAAMiB,gBAAgB,GAAGd,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC;MAC3CH,IAAI,GAAG,EAAE;MACTC,KAAK,GAAG,EAAE;MACVJ,aAAa,CAACU,SAAS,CAAC,GAAG;QACzBF,UAAU;QACVE,SAAS;QACTG,IAAI;QACJE,WAAW;QACXI,QAAQ;QACRC,IAAI;QACJE,cAAc;QACdC,gBAAgB;QAChBpB,IAAI;QACJC;MACF,CAAC;MACD;IACF;IACA,IAAIF,UAAU,KAAKD,aAAa,GAAG,CAAC,EAAE;MACpCE,IAAI,CAACqB,IAAI,CAAC,GAAGnB,IAAI,CAACgB,IAAI,CAAC,CAAC,CAACI,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC,MAAM;MACL,MAAMC,OAAO,GAAGrB,IAAI,CAACgB,IAAI,CAAC,CAAC;MAC3B,IAAIK,OAAO,EAAEtB,KAAK,CAACoB,IAAI,CAAC,GAAGE,OAAO,CAACD,KAAK,CAAC,WAAW,CAAC,CAAC;IACxD;EACF;EAEA,OAAO;IAAE1B,MAAM;IAAEC;EAAc,CAAC;AAClC;AAAC2B,MAAA,CAAAC,OAAA,GAAAA,OAAA,CAAAC,OAAA","ignoreList":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SourceLoc } from './SourceLoc'
|
|
2
|
+
import z from 'zod'
|
|
3
|
+
|
|
4
|
+
export const ParseIssueSeverity = z.enum(['error', 'warning'])
|
|
5
|
+
export type ParseIssueSeverity = z.output<typeof ParseIssueSeverity>
|
|
6
|
+
|
|
7
|
+
export const ParseIssue = z.strictObject({
|
|
8
|
+
type: ParseIssueSeverity,
|
|
9
|
+
code: z.string().nonempty(),
|
|
10
|
+
message: z.string().optional(),
|
|
11
|
+
loc: SourceLoc.optional(),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export type ParseIssue = {
|
|
15
|
+
type: ParseIssueSeverity
|
|
16
|
+
code: string
|
|
17
|
+
message?: string
|
|
18
|
+
loc?: SourceLoc
|
|
19
|
+
}
|
package/src/SourceLoc.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
|
|
3
|
+
export const SourcePos = z.object({
|
|
4
|
+
line: z.number().int().nonnegative(),
|
|
5
|
+
column: z.number().int().nonnegative(),
|
|
6
|
+
index: z.number().int().nonnegative(),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export type SourcePos = z.output<typeof SourcePos>
|
|
10
|
+
|
|
11
|
+
export const SourceLoc = z.object({ start: SourcePos, end: SourcePos })
|
|
12
|
+
|
|
13
|
+
export type SourceLoc = z.output<typeof SourceLoc>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function* chunksToLines(
|
|
2
|
+
chunks: Iterable<string> | AsyncIterable<string>
|
|
3
|
+
) {
|
|
4
|
+
let chunkStartIndex = 0
|
|
5
|
+
let remainder = ''
|
|
6
|
+
let prevChunkEndedWithCarriageReturn = false
|
|
7
|
+
for await (let chunk of chunks) {
|
|
8
|
+
if (!chunk.length) continue
|
|
9
|
+
if (remainder) {
|
|
10
|
+
chunk = remainder + chunk
|
|
11
|
+
remainder = ''
|
|
12
|
+
}
|
|
13
|
+
let end = prevChunkEndedWithCarriageReturn && chunk[0] === '\n' ? 1 : 0
|
|
14
|
+
const rx = /\r\n?|\n/gm
|
|
15
|
+
rx.lastIndex = end
|
|
16
|
+
for (const match of chunk.matchAll(rx)) {
|
|
17
|
+
const line = chunk.substring(end, match.index)
|
|
18
|
+
yield { line, startIndex: chunkStartIndex + end }
|
|
19
|
+
end = match.index + match[0].length
|
|
20
|
+
}
|
|
21
|
+
if (end < chunk.length) remainder = chunk.substring(end)
|
|
22
|
+
chunkStartIndex += end
|
|
23
|
+
prevChunkEndedWithCarriageReturn = chunk[chunk.length - 1] === '\r'
|
|
24
|
+
}
|
|
25
|
+
if (remainder) yield { line: remainder, startIndex: chunkStartIndex }
|
|
26
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import { parseFrcsSurveyFile } from '../string/index'
|
|
4
|
+
import fs from 'fs/promises'
|
|
5
|
+
import { summarizeSurvey } from '../survey/summarizeSurvey'
|
|
6
|
+
import { parseFrcsTripSummaryFile } from '../node/index.js'
|
|
7
|
+
import { formatFrcsTripSummaryFile } from '../formatFrcsTripSummaryFile.js'
|
|
8
|
+
import { isDeepStrictEqual } from 'util'
|
|
9
|
+
|
|
10
|
+
export async function checkSurveyCorrespondence(
|
|
11
|
+
surveyFile: string,
|
|
12
|
+
summaryFile: string
|
|
13
|
+
) {
|
|
14
|
+
const source = await fs.readFile(surveyFile, 'utf8')
|
|
15
|
+
const parsedSurvey = await parseFrcsSurveyFile(surveyFile, source)
|
|
16
|
+
const parsedSummaries = (
|
|
17
|
+
await parseFrcsTripSummaryFile(summaryFile, { indexBy: 'occurrence' })
|
|
18
|
+
).tripSummaries.sort((a, b) => (a?.tripNumber ?? 0) - (b?.tripNumber ?? 0))
|
|
19
|
+
const convertedSummaries = summarizeSurvey(parsedSurvey, {
|
|
20
|
+
ignoreVerticalOfHShots: true,
|
|
21
|
+
}).tripSummaries.sort((a, b) => (a?.tripNumber ?? 0) - (b?.tripNumber ?? 0))
|
|
22
|
+
|
|
23
|
+
let errored = false
|
|
24
|
+
|
|
25
|
+
for (
|
|
26
|
+
let i = 0;
|
|
27
|
+
i < Math.max(parsedSummaries.length, convertedSummaries.length);
|
|
28
|
+
i++
|
|
29
|
+
) {
|
|
30
|
+
const parsed = parsedSummaries[i]
|
|
31
|
+
const converted = convertedSummaries[i]
|
|
32
|
+
|
|
33
|
+
const parsedLines = [
|
|
34
|
+
...formatFrcsTripSummaryFile({ tripSummaries: [parsed] }),
|
|
35
|
+
].slice(0, 2)
|
|
36
|
+
const convertedLines = [
|
|
37
|
+
...formatFrcsTripSummaryFile({ tripSummaries: [converted] }),
|
|
38
|
+
].slice(0, 2)
|
|
39
|
+
|
|
40
|
+
if (!isDeepStrictEqual(parsedLines, convertedLines)) {
|
|
41
|
+
errored = true
|
|
42
|
+
console.log(
|
|
43
|
+
convertedLines.map((line) => chalk.green('+' + line)).join('\n')
|
|
44
|
+
)
|
|
45
|
+
console.log(parsedLines.map((line) => chalk.red('-' + line)).join('\n'))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
process.exit(errored ? 1 : 0)
|
|
49
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import fs from 'fs/promises'
|
|
3
|
+
import parseFrcsSurveyFile from '../survey/parseFrcsSurveyFile'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { formatIssues } from '../formatIssues.js'
|
|
6
|
+
|
|
7
|
+
export async function checkSurvey(file: string) {
|
|
8
|
+
const source = await fs.readFile(file, 'utf8')
|
|
9
|
+
const parsed = await parseFrcsSurveyFile(file, [source])
|
|
10
|
+
|
|
11
|
+
const issues = parsed.issues || []
|
|
12
|
+
console.log(
|
|
13
|
+
formatIssues({
|
|
14
|
+
file,
|
|
15
|
+
source,
|
|
16
|
+
parsed,
|
|
17
|
+
errorStyle: chalk.red,
|
|
18
|
+
warningStyle: chalk.yellow,
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
process.exit(issues.some((i) => i.type === 'error') ? 1 : 0)
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { parseFrcsSurveyFile } from '../node/index'
|
|
3
|
+
import { ZodValidOrInvalidFrcsSurveyFileToJson } from '../survey/ZodFrcsSurveyFileToJson'
|
|
4
|
+
|
|
5
|
+
export async function parseSurvey(file: string) {
|
|
6
|
+
const parsed = ZodValidOrInvalidFrcsSurveyFileToJson.parse(
|
|
7
|
+
await parseFrcsSurveyFile(file)
|
|
8
|
+
)
|
|
9
|
+
console.log(JSON.stringify(parsed))
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import { formatIssues } from '../formatIssues'
|
|
4
|
+
import { parseFrcsSurveyFile } from '../string/index'
|
|
5
|
+
import fs from 'fs/promises'
|
|
6
|
+
import { formatFrcsTripSummaryFile } from '../formatFrcsTripSummaryFile'
|
|
7
|
+
import { summarizeSurvey as baseSummarizeSurvey } from '../survey/summarizeSurvey'
|
|
8
|
+
|
|
9
|
+
export async function summarizeSurvey(file: string) {
|
|
10
|
+
const source = await fs.readFile(file, 'utf8')
|
|
11
|
+
const parsed = await parseFrcsSurveyFile(file, source)
|
|
12
|
+
if ('INVALID' in parsed) {
|
|
13
|
+
console.log(
|
|
14
|
+
formatIssues({
|
|
15
|
+
file,
|
|
16
|
+
source,
|
|
17
|
+
parsed,
|
|
18
|
+
errorStyle: chalk.red,
|
|
19
|
+
warningStyle: chalk.yellow,
|
|
20
|
+
})
|
|
21
|
+
)
|
|
22
|
+
process.exit(1)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const line of formatFrcsTripSummaryFile(baseSummarizeSurvey(parsed))) {
|
|
26
|
+
console.log(line)
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import yargs from 'yargs/yargs'
|
|
2
|
+
|
|
3
|
+
void yargs(process.argv.slice(2))
|
|
4
|
+
.command({
|
|
5
|
+
command: 'check-survey <file>',
|
|
6
|
+
describe: 'check survey file for errors or warnings',
|
|
7
|
+
builder: (yargs) =>
|
|
8
|
+
yargs.positional('file', {
|
|
9
|
+
type: 'string',
|
|
10
|
+
demandOption: true,
|
|
11
|
+
}),
|
|
12
|
+
handler: async ({ file }) => {
|
|
13
|
+
const { checkSurvey } = await import('./cli/check-survey')
|
|
14
|
+
await checkSurvey(file)
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
.command({
|
|
18
|
+
command: 'parse-survey <file>',
|
|
19
|
+
describe: 'parse survey file and output JSON parse tree',
|
|
20
|
+
builder: (yargs) =>
|
|
21
|
+
yargs.positional('file', {
|
|
22
|
+
type: 'string',
|
|
23
|
+
demandOption: true,
|
|
24
|
+
}),
|
|
25
|
+
handler: async ({ file }) => {
|
|
26
|
+
const { parseSurvey } = await import('./cli/parse-survey')
|
|
27
|
+
await parseSurvey(file)
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
.command({
|
|
31
|
+
command: 'summarize-survey <file>',
|
|
32
|
+
describe: 'parse survey file and output trip summaries',
|
|
33
|
+
builder: (yargs) =>
|
|
34
|
+
yargs.positional('file', {
|
|
35
|
+
type: 'string',
|
|
36
|
+
demandOption: true,
|
|
37
|
+
}),
|
|
38
|
+
handler: async ({ file }) => {
|
|
39
|
+
const { summarizeSurvey } = await import('./cli/summarize-survey')
|
|
40
|
+
await summarizeSurvey(file)
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
.command({
|
|
44
|
+
command: 'check-survey-correspondence <surveyFile> <summaryFile>',
|
|
45
|
+
describe: 'parse survey file and output trip summaries',
|
|
46
|
+
builder: (yargs) =>
|
|
47
|
+
yargs
|
|
48
|
+
.positional('surveyFile', {
|
|
49
|
+
type: 'string',
|
|
50
|
+
demandOption: true,
|
|
51
|
+
})
|
|
52
|
+
.positional('summaryFile', {
|
|
53
|
+
type: 'string',
|
|
54
|
+
demandOption: true,
|
|
55
|
+
}),
|
|
56
|
+
handler: async ({ surveyFile, summaryFile }) => {
|
|
57
|
+
const { checkSurveyCorrespondence } = await import(
|
|
58
|
+
'./cli/check-survey-correspondence'
|
|
59
|
+
)
|
|
60
|
+
await checkSurveyCorrespondence(surveyFile, summaryFile)
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
.demandCommand().argv
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Length } from '@speleotica/unitized'
|
|
2
|
+
import { FrcsTripSummaryFile } from './FrcsTripSummaryFile.js'
|
|
3
|
+
|
|
4
|
+
const nameLength = 78
|
|
5
|
+
|
|
6
|
+
export function* formatFrcsTripSummaryFile(
|
|
7
|
+
file: FrcsTripSummaryFile
|
|
8
|
+
): Iterable<string> {
|
|
9
|
+
for (const summary of file.tripSummaries) {
|
|
10
|
+
if (!summary) continue
|
|
11
|
+
const {
|
|
12
|
+
tripNumber,
|
|
13
|
+
date,
|
|
14
|
+
totalLength,
|
|
15
|
+
numShots,
|
|
16
|
+
name,
|
|
17
|
+
excludedLength,
|
|
18
|
+
numExcludedShots,
|
|
19
|
+
team,
|
|
20
|
+
shots,
|
|
21
|
+
} = summary
|
|
22
|
+
const month = (date.getMonth() + 1).toFixed()
|
|
23
|
+
const day = date.getDate().toFixed()
|
|
24
|
+
const year = date.getFullYear().toFixed()
|
|
25
|
+
yield `${tripNumber.toFixed().padStart(3)} ${month.padStart(
|
|
26
|
+
2
|
|
27
|
+
)}/${day.padStart(2)}/${year.padStart(4)}${totalLength
|
|
28
|
+
.get(Length.feet)
|
|
29
|
+
.toFixed(2)
|
|
30
|
+
.padStart(10)}${numShots.toFixed().padStart(5)} ${name
|
|
31
|
+
.slice(0, nameLength)
|
|
32
|
+
.padEnd(nameLength)} EXCLUDED:${excludedLength
|
|
33
|
+
.get(Length.feet)
|
|
34
|
+
.toFixed(2)
|
|
35
|
+
.padStart(7)}${numExcludedShots.toFixed().padStart(3)}`
|
|
36
|
+
yield ' '.repeat(tripNumber >= 1000 ? 34 : 33) + team.join(' ')
|
|
37
|
+
for (let i = 0; i < shots.length; i += 4) {
|
|
38
|
+
yield ' '.repeat(tripNumber >= 1000 ? 36 : 35) +
|
|
39
|
+
shots
|
|
40
|
+
.slice(i, i + 4)
|
|
41
|
+
.map((s) => s.padEnd(14))
|
|
42
|
+
.join('')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { FrcsSurveyFile, InvalidFrcsSurveyFile } from './survey/FrcsSurveyFile'
|
|
2
|
+
import {
|
|
3
|
+
FrcsSurveyFileJson,
|
|
4
|
+
InvalidFrcsSurveyFileJson,
|
|
5
|
+
} from './survey/FrcsSurveyFileJson.js'
|
|
6
|
+
import { underlineSource } from './underlineSource'
|
|
7
|
+
import { unwrapInvalid } from './unwrapInvalid.js'
|
|
8
|
+
|
|
9
|
+
export function formatIssues({
|
|
10
|
+
parsed,
|
|
11
|
+
source,
|
|
12
|
+
file,
|
|
13
|
+
errorStyle = (s) => s,
|
|
14
|
+
warningStyle = (s) => s,
|
|
15
|
+
}: {
|
|
16
|
+
parsed:
|
|
17
|
+
| FrcsSurveyFile
|
|
18
|
+
| InvalidFrcsSurveyFile
|
|
19
|
+
| FrcsSurveyFileJson
|
|
20
|
+
| InvalidFrcsSurveyFileJson
|
|
21
|
+
source: string
|
|
22
|
+
file: string
|
|
23
|
+
errorStyle?: (s: string) => string
|
|
24
|
+
warningStyle?: (s: string) => string
|
|
25
|
+
}) {
|
|
26
|
+
const issues = parsed.issues || []
|
|
27
|
+
const lines: string[] = []
|
|
28
|
+
for (const issue of issues) {
|
|
29
|
+
const { type, loc, message } = issue
|
|
30
|
+
const style = type === 'error' ? errorStyle : warningStyle
|
|
31
|
+
lines.push(
|
|
32
|
+
`${style(
|
|
33
|
+
`${
|
|
34
|
+
type === 'error' ? errorStyle('✘') : warningStyle('⚠')
|
|
35
|
+
} ${`${type[0].toUpperCase()}${type.substring(1)}:`.padEnd(
|
|
36
|
+
8
|
|
37
|
+
)} ${message?.padEnd(40)}`
|
|
38
|
+
)} (${file}:${loc?.start?.line}:${loc?.start?.column})`
|
|
39
|
+
)
|
|
40
|
+
if (loc) {
|
|
41
|
+
lines.push(underlineSource(source, loc, { underlineStyle: style }))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const errorCount = issues.reduce(
|
|
46
|
+
(count, i) => (i.type === 'error' ? count + 1 : count),
|
|
47
|
+
0
|
|
48
|
+
)
|
|
49
|
+
const warningCount = issues.reduce(
|
|
50
|
+
(count, i) => (i.type === 'warning' ? count + 1 : count),
|
|
51
|
+
0
|
|
52
|
+
)
|
|
53
|
+
const trips = unwrapInvalid(parsed).trips
|
|
54
|
+
const tripCount = trips.length
|
|
55
|
+
const shotCount = trips.reduce(
|
|
56
|
+
(count, trip) => count + unwrapInvalid(trip).shots.length,
|
|
57
|
+
0
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const fmtErrorCount = `✘ ${errorCount}`
|
|
61
|
+
const fmtWarningCount = `⚠ ${warningCount}`
|
|
62
|
+
const fmtTripCount = `${tripCount}`
|
|
63
|
+
const fmtShotCount = `${shotCount}`
|
|
64
|
+
|
|
65
|
+
const countWidth = Math.max(
|
|
66
|
+
fmtErrorCount.length,
|
|
67
|
+
fmtWarningCount.length,
|
|
68
|
+
fmtTripCount.length,
|
|
69
|
+
fmtShotCount.length
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if (errorCount) {
|
|
73
|
+
lines.push(
|
|
74
|
+
errorStyle(
|
|
75
|
+
`${fmtErrorCount.padStart(countWidth)} error${
|
|
76
|
+
errorCount === 1 ? '' : 's'
|
|
77
|
+
}`
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
if (warningCount) {
|
|
82
|
+
lines.push(
|
|
83
|
+
warningStyle(
|
|
84
|
+
`${fmtWarningCount.padStart(countWidth)} warning${
|
|
85
|
+
warningCount === 1 ? '' : 's'
|
|
86
|
+
}`
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
lines.push(
|
|
91
|
+
`${fmtTripCount.padStart(countWidth)} trip${tripCount === 1 ? '' : 's'}`
|
|
92
|
+
)
|
|
93
|
+
lines.push(
|
|
94
|
+
`${fmtShotCount.padStart(countWidth)} shot${shotCount === 1 ? '' : 's'}`
|
|
95
|
+
)
|
|
96
|
+
return lines.join('\n')
|
|
97
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
FrcsShot,
|
|
3
|
+
FrcsSurveyFile,
|
|
4
|
+
FrcsTrip,
|
|
5
|
+
FrcsTripHeader,
|
|
6
|
+
} from './survey/FrcsSurveyFile'
|
|
7
|
+
import parseFrcsSurveyFile from './survey/parseFrcsSurveyFile'
|
|
5
8
|
import { FrcsPlotShot } from './FrcsPlotShot'
|
|
6
9
|
import { FrcsPlotFile } from './FrcsPlotFile'
|
|
7
10
|
import parseFrcsPlotFile from './parseFrcsPlotFile'
|
|
8
11
|
import { FrcsTripSummary } from './FrcsTripSummary'
|
|
9
12
|
import { FrcsTripSummaryFile } from './FrcsTripSummaryFile'
|
|
10
13
|
import parseFrcsTripSummaryFile from './parseFrcsTripSummaryFile'
|
|
11
|
-
import formatFrcsShot from './formatFrcsShot'
|
|
12
|
-
import { formatFrcsSurveyFile } from './formatFrcsSurveyFile'
|
|
14
|
+
import formatFrcsShot from './survey/formatFrcsShot'
|
|
15
|
+
import { formatFrcsSurveyFile } from './survey/formatFrcsSurveyFile'
|
|
13
16
|
|
|
14
17
|
export {
|
|
15
18
|
FrcsShot,
|
|
16
|
-
FrcsShotKind,
|
|
17
19
|
FrcsSurveyFile,
|
|
18
20
|
FrcsTrip,
|
|
19
21
|
FrcsTripHeader,
|
package/src/node/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import readline from 'readline'
|
|
3
|
-
import _parseFrcsSurveyFile from '../parseFrcsSurveyFile'
|
|
3
|
+
import _parseFrcsSurveyFile from '../survey/parseFrcsSurveyFile'
|
|
4
4
|
import _parseFrcsPlotFile from '../parseFrcsPlotFile'
|
|
5
5
|
import _parseFrcsTripSummaryFile from '../parseFrcsTripSummaryFile'
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const convertLineBased =
|
|
8
8
|
<T, Rest extends any[]>(
|
|
9
9
|
fn: (
|
|
10
10
|
file: string,
|
|
@@ -15,6 +15,19 @@ const convert =
|
|
|
15
15
|
(file: string, ...rest: Rest): Promise<T> =>
|
|
16
16
|
fn(file, readline.createInterface(fs.createReadStream(file)), ...rest)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const convertChunkBased =
|
|
19
|
+
<T, Rest extends any[]>(
|
|
20
|
+
fn: (
|
|
21
|
+
file: string,
|
|
22
|
+
lines: AsyncIterable<string>,
|
|
23
|
+
...rest: Rest
|
|
24
|
+
) => Promise<T>
|
|
25
|
+
) =>
|
|
26
|
+
(file: string, ...rest: Rest): Promise<T> =>
|
|
27
|
+
fn(file, fs.createReadStream(file, 'utf8'), ...rest)
|
|
28
|
+
|
|
29
|
+
export const parseFrcsSurveyFile = convertChunkBased(_parseFrcsSurveyFile)
|
|
30
|
+
export const parseFrcsPlotFile = convertLineBased(_parseFrcsPlotFile)
|
|
31
|
+
export const parseFrcsTripSummaryFile = convertLineBased(
|
|
32
|
+
_parseFrcsTripSummaryFile
|
|
33
|
+
)
|
|
@@ -21,7 +21,12 @@ const tripSummaryRegex =
|
|
|
21
21
|
*/
|
|
22
22
|
export default async function parseFrcsTripSummaryFile(
|
|
23
23
|
file: string,
|
|
24
|
-
lines: AsyncIterable<string
|
|
24
|
+
lines: AsyncIterable<string>,
|
|
25
|
+
{
|
|
26
|
+
indexBy = 'tripNumber',
|
|
27
|
+
}: {
|
|
28
|
+
indexBy?: 'tripNumber' | 'occurrence'
|
|
29
|
+
} = {}
|
|
25
30
|
): Promise<FrcsTripSummaryFile> {
|
|
26
31
|
const errors: Array<SegmentParseError> = []
|
|
27
32
|
const tripSummaries: Array<FrcsTripSummary | undefined> = []
|
|
@@ -39,7 +44,8 @@ export default async function parseFrcsTripSummaryFile(
|
|
|
39
44
|
if (match) {
|
|
40
45
|
tripStartLine = lineNumber
|
|
41
46
|
const tripNumber = parseInt(match[1])
|
|
42
|
-
const tripIndex =
|
|
47
|
+
const tripIndex =
|
|
48
|
+
indexBy === 'tripNumber' ? tripNumber - 1 : tripSummaries.length
|
|
43
49
|
let year = parseInt(match[4])
|
|
44
50
|
if (year < 1000) year += 1900
|
|
45
51
|
const date = new Date(year, parseInt(match[2]) - 1, parseInt(match[3]))
|
package/src/string/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import _parseFrcsSurveyFile from '../parseFrcsSurveyFile'
|
|
1
|
+
import _parseFrcsSurveyFile from '../survey/parseFrcsSurveyFile'
|
|
2
2
|
import _parseFrcsPlotFile from '../parseFrcsPlotFile'
|
|
3
3
|
import _parseFrcsTripSummaryFile from '../parseFrcsTripSummaryFile'
|
|
4
4
|
|
|
5
5
|
async function* linesOf(s: string): AsyncIterable<string> {
|
|
6
6
|
yield* s.split(/\r\n?|\n/gm)
|
|
7
7
|
}
|
|
8
|
-
const
|
|
8
|
+
const convertLineBased =
|
|
9
9
|
<T, Rest extends any[]>(
|
|
10
10
|
fn: (
|
|
11
11
|
file: string,
|
|
@@ -16,6 +16,19 @@ const convert =
|
|
|
16
16
|
(file: string, str: string, ...rest: Rest): Promise<T> =>
|
|
17
17
|
fn(file, linesOf(str), ...rest)
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const convertChunkBased =
|
|
20
|
+
<T, Rest extends any[]>(
|
|
21
|
+
fn: (
|
|
22
|
+
file: string,
|
|
23
|
+
lines: Iterable<string> | AsyncIterable<string>,
|
|
24
|
+
...rest: Rest
|
|
25
|
+
) => Promise<T>
|
|
26
|
+
) =>
|
|
27
|
+
(file: string, str: string, ...rest: Rest): Promise<T> =>
|
|
28
|
+
fn(file, [str], ...rest)
|
|
29
|
+
|
|
30
|
+
export const parseFrcsSurveyFile = convertChunkBased(_parseFrcsSurveyFile)
|
|
31
|
+
export const parseFrcsPlotFile = convertLineBased(_parseFrcsPlotFile)
|
|
32
|
+
export const parseFrcsTripSummaryFile = convertLineBased(
|
|
33
|
+
_parseFrcsTripSummaryFile
|
|
34
|
+
)
|