Skip to content

Commit cb5ba4a

Browse files
committed
logging code, better tree-manip utils, and folder reorg
1 parent 3c1d13f commit cb5ba4a

15 files changed

+268
-61
lines changed

daemonist/dynalist_custom_style.css

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* If using Dynalist Pro, paste this in to hide stat */
2+
3+
a[href*="㊙️"] {
4+
display: none;
5+
}

daemonist/src/api/api-model.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Representations of Dynalist API calls. */
22

3-
import { DynalistModel } from "../dynalist-model";
3+
import { DynalistModel } from "../dynalist/dynalist-model";
44

55
export namespace API {
66
type FailCode =

daemonist/src/api/dynalist-client.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { API } from "./api-model";
22
import { APIDispatcher } from "./dispatcher";
3-
import { DynalistModel } from "../dynalist-model";
3+
import { DynalistModel } from "../dynalist/dynalist-model";
44
import * as _ from "lodash";
5+
import { MutablePotentialNodeTree } from "../dynalist/tree-util";
56

67
interface FetchRequest {
78
url: string;
@@ -18,6 +19,12 @@ export class DynalistClient {
1819
this.dispatcher = new APIDispatcher();
1920
}
2021

22+
public async getMutableNodeTreeByKey(
23+
key: DynalistModel.NodeKey
24+
): Promise<MutablePotentialNodeTree> {
25+
return new MutablePotentialNodeTree(await this.getNodeTree(key));
26+
}
27+
2128
public applyChanges = async (newTrees: DynalistModel.PotentialNodeTree[]) => {
2229
console.log("applyChanges called...");
2330
for (const newTree of newTrees) {
@@ -54,7 +61,6 @@ export class DynalistClient {
5461
}
5562
};
5663
bfs(newTree, 0);
57-
console.log(flat);
5864
for (const child of flat.slice(1)) {
5965
const { content, note, checked, checkbox, heading, color } = child;
6066
rootAndAdditions.push({

daemonist/src/daemon.ts

-27
This file was deleted.
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { MutablePotentialNodeTree } from "../dynalist/tree-util";
2+
import { INVOCATION, STATE } from "./daemonist";
3+
import { DynalistModel } from "../dynalist/dynalist-model";
4+
5+
export interface DaemonState {
6+
displayNote?: string;
7+
}
8+
9+
export class MutableDaemonistNodeTree<
10+
State extends DaemonState = DaemonState
11+
> extends MutablePotentialNodeTree {
12+
constructor(tree: DynalistModel.PotentialNodeTree, defaultState: State) {
13+
super(tree);
14+
if (!this.hasState && defaultState) {
15+
this.setState(defaultState);
16+
}
17+
}
18+
get state(): State | null {
19+
try {
20+
return {
21+
...JSON.parse(STATE.exec(this.note)[1])
22+
};
23+
} catch {
24+
return null;
25+
}
26+
}
27+
get encodedState() {
28+
return STATE.exec(this.note)[0];
29+
}
30+
get isActivated() {
31+
return INVOCATION.exec(this.note) !== null;
32+
}
33+
get invocationString() {
34+
return INVOCATION.exec(this.note)[0];
35+
}
36+
get invocationParameters() {
37+
return INVOCATION.exec(this.note)[1];
38+
}
39+
get hasState() {
40+
return !!this.state;
41+
}
42+
43+
private encodeState = (state: State) => `[${JSON.stringify(state)}](㊙️)`;
44+
45+
setState(state: State) {
46+
this.note = `${this.invocationString}${this.encodeState(state)}${(this
47+
.state &&
48+
this.state.displayNote &&
49+
`\n${this.state.displayNote}`) ||
50+
""}`;
51+
}
52+
53+
updateState(updates: Partial<State>) {
54+
if (!this.state) {
55+
throw Error("State not initialized!");
56+
}
57+
this.setState({
58+
...this.state,
59+
...updates
60+
});
61+
}
62+
}

daemonist/src/daemonist/daemon.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { DynalistModel } from "../dynalist/dynalist-model";
2+
import { DynalistClient } from "../api/dynalist-client";
3+
import { MutableDaemonistNodeTree, DaemonState } from "./daemon-node";
4+
import { Daemonist } from "./daemonist";
5+
6+
export interface Daemon<State extends DaemonState = DaemonState> {
7+
defaultState: State;
8+
transform: (
9+
root: MutableDaemonistNodeTree<State>,
10+
client: DynalistClient
11+
) => //daemonist: Daemonist // should I refactor this out?
12+
Promise<DynalistModel.PotentialNodeTree[]>;
13+
isSummoned: (root: DynalistModel.ConcreteNodeTree) => boolean;
14+
}
15+
16+
export abstract class NamedDaemon<State extends DaemonState = DaemonState>
17+
implements Daemon<State> {
18+
constructor(public readonly name: string) {}
19+
20+
public abstract defaultState;
21+
22+
public abstract transform: (
23+
root: MutableDaemonistNodeTree<State>,
24+
client: DynalistClient
25+
) => Promise<DynalistModel.PotentialNodeTree[]>;
26+
27+
public isSummoned = (root: DynalistModel.ConcreteNodeTree): boolean => {
28+
// TODO: refactor into recursive wrappers
29+
const tree = new MutableDaemonistNodeTree(root, this.defaultState);
30+
return (
31+
tree.isActivated &&
32+
tree.invocationString.split(`[, ]`).includes(this.name)
33+
);
34+
};
35+
}

daemonist/src/daemonist.ts daemonist/src/daemonist/daemonist.ts

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { DynalistModel } from "./dynalist-model";
2-
import { DynalistClient } from "./api/dynalist-client";
1+
import { DynalistModel } from "../dynalist/dynalist-model";
2+
import { DynalistClient } from "../api/dynalist-client";
33
import { Daemon } from "./daemon";
4-
import { MutableConcreteNodeTree } from "./api/tree-util";
5-
import { API } from "./api/api-model";
4+
import { MutableDaemonistNodeTree } from "./daemon-node";
5+
import { MutablePotentialNodeTree } from "../dynalist/tree-util";
66

7-
export const INVOCATION = /#?[🏺😈👿🤖]\((.*)\)/u;
7+
export const INVOCATION = /#?[🏺😈👿🤖]\(([a-zA-Z, 0-9_]*)\)/u;
88
const isActivated = (node: DynalistModel.ConcreteNode) =>
9-
!!INVOCATION.exec(node.note);
10-
const INDEX_TITLE = "(א)";
9+
INVOCATION.exec(node.note) !== null;
10+
export const STATE = /\[(.*)\]\(\)/;
1111

1212
export class Daemonist {
1313
public api: DynalistClient;
@@ -33,7 +33,12 @@ export class Daemonist {
3333
console.log("Got activate node trees.");
3434
await Promise.all(
3535
trees.map(tree =>
36-
daemon.transform(tree, this.api).then(this.api.applyChanges)
36+
daemon
37+
.transform(
38+
new MutableDaemonistNodeTree(tree, daemon.defaultState),
39+
this.api
40+
)
41+
.then(this.api.applyChanges)
3742
)
3843
);
3944
};
@@ -69,8 +74,4 @@ export class Daemonist {
6974
.map(node => node.key))
7075
);
7176
};
72-
73-
private getStateDocument(): Promise<DynalistModel.Document> {
74-
return this.api.getDocumentByTitle(INDEX_TITLE, true);
75-
}
7677
}

daemonist/src/daemonist/util.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { DynalistModel } from "../dynalist/dynalist-model";
2+
3+
export function unpackDynalistLink(link: string): DynalistModel.NodeKey | null {
4+
const LINK = /\(https?:\/\/dynalist.io\/d\/(.*)#z=(.*)\)/;
5+
const matches = LINK.exec(link);
6+
if (matches === null) return null;
7+
const [_, documentId, nodeId] = matches;
8+
return { documentId, nodeId };
9+
}
10+
11+
export function yesterday(): Date {
12+
const day = new Date();
13+
day.setDate(day.getDate() - 1);
14+
return day;
15+
}

daemonist/src/daemons/dateDaemon.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import { INVOCATION } from "../daemonist";
2-
import { DynalistModel } from "../dynalist-model";
3-
import { NamedDaemon } from "../daemon";
4-
import { MutableConcreteNodeTree } from "../api/tree-util";
1+
import { DynalistModel } from "../dynalist/dynalist-model";
2+
import { NamedDaemon } from "../daemonist/daemon";
3+
import { MutablePotentialNodeTree } from "../dynalist/tree-util";
4+
import { MutableDaemonistNodeTree } from "../daemonist/daemon-node";
55

6-
export class DateDaemon extends NamedDaemon {
6+
export class DateDaemon extends NamedDaemon<{}> {
7+
public defaultState = {};
78
constructor(name?: string) {
89
super(name || "date");
910
}
1011

1112
public transform = async (
1213
root: DynalistModel.PotentialNodeTree
1314
): Promise<DynalistModel.PotentialNodeTree[]> => {
14-
const mutable_root = new MutableConcreteNodeTree(root);
15+
const mutable_root = new MutableDaemonistNodeTree(root, this.defaultState);
1516
mutable_root.updateProperties({
16-
note: `${INVOCATION.exec(root.note)[0]}\n Last updated ${Date()}`
17+
note: `${mutable_root.invocationString}\nLast updated ${Date()}`
1718
});
1819
return [await mutable_root];
1920
};

daemonist/src/daemons/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { DateDaemon } from "./dateDaemon";
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NamedDaemon } from "../daemonist/daemon";
2+
import { DynalistModel } from "../dynalist/dynalist-model";
3+
import {
4+
MutableDaemonistNodeTree,
5+
DaemonState
6+
} from "../daemonist/daemon-node";
7+
import { Logger, LoggerState } from "./logger";
8+
import { Daemonist } from "../daemonist/daemonist";
9+
import { DynalistClient } from "../api/dynalist-client";
10+
11+
export interface DashboardDaemonState extends DaemonState {
12+
[childIndex: number]: LoggerState; // TODO: use children as concrete
13+
}
14+
15+
export class DashboardDaemon extends NamedDaemon<DashboardDaemonState> {
16+
constructor() {
17+
super("dashboard");
18+
}
19+
20+
defaultState: DashboardDaemonState = {};
21+
22+
transform = async (
23+
root: MutableDaemonistNodeTree<DashboardDaemonState>,
24+
client: DynalistClient
25+
): Promise<DynalistModel.PotentialNodeTree[]> => {
26+
const now = new Date();
27+
const newTrees: DynalistModel.PotentialNodeTree[] = [];
28+
for (let index = 0; index < root.children.length; ++index) {
29+
const child = root.children[index];
30+
const logger = Logger.attemptConstruct(child);
31+
if (
32+
logger &&
33+
(!root.state[index] ||
34+
now.toLocaleDateString() !==
35+
new Date(root.state[index].lastRun).toLocaleDateString())
36+
) {
37+
// run logger
38+
console.log(`Running logger on child ${index}`);
39+
newTrees.push(await logger.getChange(client));
40+
if (logger.clears) child.children = [];
41+
root.updateState({ [index]: { lastRun: new Date() } });
42+
}
43+
}
44+
newTrees.push(root);
45+
return newTrees;
46+
};
47+
}

daemonist/src/dashboard/logger.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { DynalistModel } from "../dynalist/dynalist-model";
2+
import { unpackDynalistLink, yesterday } from "../daemonist/util";
3+
import { DynalistClient } from "../api/dynalist-client";
4+
import { MutableNodeTree } from "../dynalist/tree-util";
5+
6+
export interface LoggerState {
7+
lastRun: Date;
8+
}
9+
10+
export class Logger {
11+
destinationNodeKey: DynalistModel.NodeKey;
12+
clears: boolean;
13+
14+
static attemptConstruct(nodeTree: MutableNodeTree): Logger | null {
15+
try {
16+
return new Logger(nodeTree);
17+
} catch {
18+
return null;
19+
}
20+
}
21+
22+
static parseClears(note: string): boolean {
23+
return note.includes("and clears");
24+
}
25+
26+
async getChange(
27+
client: DynalistClient
28+
): Promise<DynalistModel.PotentialNodeTree> {
29+
const dstTree = await client.getMutableNodeTreeByKey(
30+
this.destinationNodeKey
31+
);
32+
const logEntry = this.nodeTree.deepClone();
33+
logEntry.updateProperties({
34+
content: yesterday().toLocaleDateString(),
35+
note: ""
36+
});
37+
38+
dstTree.pushSubtree(logEntry);
39+
return dstTree;
40+
}
41+
42+
constructor(public nodeTree: MutableNodeTree) {
43+
if (
44+
!nodeTree.note ||
45+
!nodeTree.note.toLowerCase().startsWith("writes to") ||
46+
!unpackDynalistLink(nodeTree.note)
47+
)
48+
throw new Error("Not a logger node. Use canConstruct!");
49+
this.destinationNodeKey = unpackDynalistLink(nodeTree.note);
50+
this.clears = Logger.parseClears(nodeTree.note);
51+
}
52+
}

daemonist/src/dynalist-model.ts daemonist/src/dynalist/dynalist-model.ts

+3
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,17 @@ export namespace DynalistModel {
5555

5656
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
5757

58+
/** Not identified with any position in Dynalist. */
5859
export interface AbstractNodeTree extends AbstractNode {
5960
children: AbstractNodeTree[];
6061
}
6162

63+
/** Root is identified with existing Dynalist nodes. */
6264
export interface PotentialNodeTree extends AbstractNodeTree {
6365
key: NodeKey;
6466
}
6567

68+
/** Entire tree exists in Dynalist. */
6669
export interface ConcreteNodeTree extends PotentialNodeTree {
6770
children: ConcreteNodeTree[];
6871
}

0 commit comments

Comments
 (0)