@chronicle.app/moves-app 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Extractor, Record } from '@chronicle.app/etl';
|
|
1
|
+
import { Extractor, Record, type KindSurvey, type Survey, type SurveyOptions } from '@chronicle.app/etl';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import MovesTransformer from './MovesTransformer.js';
|
|
4
4
|
export declare class MovesExtractor extends Extractor<typeof MovesExtractor> {
|
|
@@ -28,6 +28,15 @@ export declare class MovesExtractor extends Extractor<typeof MovesExtractor> {
|
|
|
28
28
|
}>;
|
|
29
29
|
extract(): AsyncGenerator<Record>;
|
|
30
30
|
determineCount(): Promise<number | null>;
|
|
31
|
+
/**
|
|
32
|
+
* The places and moves a run reads, and when they started, from every day
|
|
33
|
+
* in range: the export has no index, so each day is read. A segment that
|
|
34
|
+
* spans midnight is in both days' files, and counts once, as a run reads it.
|
|
35
|
+
*/
|
|
36
|
+
survey(options?: SurveyOptions): Promise<{
|
|
37
|
+
places: KindSurvey;
|
|
38
|
+
moves: KindSurvey;
|
|
39
|
+
} & Survey>;
|
|
31
40
|
private selectFrom;
|
|
32
41
|
private readDays;
|
|
33
42
|
private resolveInput;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Extractor, selfAgent } from '@chronicle.app/etl';
|
|
1
|
+
import { Extractor, countMonth, selfAgent, } from '@chronicle.app/etl';
|
|
2
2
|
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -68,30 +68,53 @@ export class MovesExtractor extends Extractor {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
async determineCount() {
|
|
71
|
-
const config = this.config;
|
|
72
71
|
try {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
for (const segment of day.segments ?? []) {
|
|
76
|
-
for (const item of this.selectFrom(segment)) {
|
|
77
|
-
const start = parseMovesTime(item.data.startTime);
|
|
78
|
-
if (!start)
|
|
79
|
-
continue;
|
|
80
|
-
if (config.since && start < config.since)
|
|
81
|
-
continue;
|
|
82
|
-
if (config.until && start > config.until)
|
|
83
|
-
continue;
|
|
84
|
-
seen.add(`${item.recordType}:${start.toISOString()}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
72
|
+
const { places, moves } = await this.survey();
|
|
73
|
+
const total = places.count + moves.count;
|
|
88
74
|
const limit = this.getEffectiveLimit();
|
|
89
|
-
return limit === null ?
|
|
75
|
+
return limit === null ? total : Math.min(total, limit);
|
|
90
76
|
}
|
|
91
77
|
catch {
|
|
92
78
|
return null;
|
|
93
79
|
}
|
|
94
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* The places and moves a run reads, and when they started, from every day
|
|
83
|
+
* in range: the export has no index, so each day is read. A segment that
|
|
84
|
+
* spans midnight is in both days' files, and counts once, as a run reads it.
|
|
85
|
+
*/
|
|
86
|
+
async survey(options = {}) {
|
|
87
|
+
const config = this.config;
|
|
88
|
+
const kind = () => ({ count: 0, ...(options.byMonth && { byMonth: {} }) });
|
|
89
|
+
const kinds = { places: kind(), moves: kind() };
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for await (const day of this.readDays(config.input)) {
|
|
92
|
+
for (const segment of day.segments ?? []) {
|
|
93
|
+
for (const item of this.selectFrom(segment)) {
|
|
94
|
+
const start = parseMovesTime(item.data.startTime);
|
|
95
|
+
if (!start)
|
|
96
|
+
continue;
|
|
97
|
+
if (config.since && start < config.since)
|
|
98
|
+
continue;
|
|
99
|
+
if (config.until && start > config.until)
|
|
100
|
+
continue;
|
|
101
|
+
const marker = `${item.recordType}:${start.toISOString()}`;
|
|
102
|
+
if (seen.has(marker))
|
|
103
|
+
continue;
|
|
104
|
+
seen.add(marker);
|
|
105
|
+
const counted = kinds[item.recordType];
|
|
106
|
+
counted.count++;
|
|
107
|
+
if (!counted.earliest || start < counted.earliest)
|
|
108
|
+
counted.earliest = start;
|
|
109
|
+
if (!counted.latest || start > counted.latest)
|
|
110
|
+
counted.latest = start;
|
|
111
|
+
if (counted.byMonth)
|
|
112
|
+
countMonth(counted.byMonth, start);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return kinds;
|
|
117
|
+
}
|
|
95
118
|
// The records a segment yields. A stay is one record. Movement splits into
|
|
96
119
|
// its legs — each carries a single mode of travel, its own distance, and its
|
|
97
120
|
// own track, which is what a Journey holds. A leg recorded while at a place
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chronicle.app/moves-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"description": "Chronicle plugin for the location storyline from a Moves data export",
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@chronicle.app/etl": "0.
|
|
26
|
-
"@chronicle.app/schema": "0.
|
|
27
|
-
"@chronicle.app/tsconfig": "0.
|
|
25
|
+
"@chronicle.app/etl": "0.5.0",
|
|
26
|
+
"@chronicle.app/schema": "0.5.0",
|
|
27
|
+
"@chronicle.app/tsconfig": "0.5.0",
|
|
28
28
|
"@types/node": "^22.10.1",
|
|
29
29
|
"typescript": "^5.7.2"
|
|
30
30
|
},
|