Skip to content

Commit aa4e9b3

Browse files
committed
cosmetic improvements
1 parent 40ebe86 commit aa4e9b3

File tree

7 files changed

+145
-69
lines changed

7 files changed

+145
-69
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
.idea/
22
cov_profile/
3+
4+
.~lock*
5+
*.review.yaml

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"tasks": {
3-
"run": "deno run --allow-read --allow-write --allow-env=NODE_ENV src/cli.ts",
3+
"run": "deno run --allow-read --allow-write --allow-env=NODE_ENV --watch src/cli.ts",
44
"migrate": "deno run --allow-read --allow-write src/tools/migrate.ts",
55
"test": "./scripts/test.sh",
66
"test-lcov": "./scripts/test-lcov.sh"

src/cleanup.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { green, red, yellow } from "@std/fmt/colors";
2+
import { Card } from "./card.ts";
3+
import { newReviewItem, ReviewItem, saveReviews } from "./review.ts";
4+
5+
// My apologies to the poor reader of this piece of shit of a code.
6+
async function cleanupReviews(
7+
reviewfile: string,
8+
deck: Card[],
9+
reviewMap: Map<string, ReviewItem>,
10+
): Promise<Map<string, ReviewItem>> {
11+
// update old items
12+
const backCardSet = new Set<string>();
13+
for (const card of deck) {
14+
backCardSet.add(card.back);
15+
}
16+
// remove those review items from reviewMap that are not in backCardSet
17+
for (const { front, back } of reviewMap.values()) {
18+
if (backCardSet.has(back)) {
19+
continue;
20+
}
21+
22+
reviewMap.delete(front);
23+
console.log(yellow("! " + front));
24+
}
25+
26+
// add new items
27+
for (const card of deck) {
28+
if (reviewMap.has(card.front)) {
29+
continue;
30+
}
31+
32+
reviewMap.set(card.front, newReviewItem(card));
33+
34+
console.log(green("+ " + card.front));
35+
}
36+
37+
// delete old items
38+
//
39+
// build a set of current cards
40+
const frontCardSet = new Set<string>();
41+
for (const card of deck) {
42+
frontCardSet.add(card.front);
43+
}
44+
// remove those review items from reviewMap that are not in frontCardSet
45+
for (const reviewItemKey of reviewMap.keys()) {
46+
if (!frontCardSet.has(reviewItemKey)) {
47+
reviewMap.delete(reviewItemKey);
48+
console.log("- " + red(reviewItemKey));
49+
}
50+
}
51+
52+
// save cleanups
53+
await saveReviews(reviewfile, reviewMap);
54+
55+
return reviewMap;
56+
}
57+
58+
export { cleanupReviews };

src/cli.ts

Lines changed: 39 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
import { Input } from "@cliffy/prompt";
2-
import { parseArgs } from "@std/cli/parse-args";
3-
import { green, red, yellow } from "@std/fmt/colors";
4-
import { loadDeck } from "./deck.ts";
2+
import { cleanupReviews } from "./cleanup.ts";
3+
import { parseAppArgs } from "./cmd/cli/args.ts";
54
import { shuffle } from "./collections.ts";
5+
import { loadDeck } from "./deck.ts";
66
import { ratio } from "./levenshtein.ts";
7+
import { deriveReviewfile } from "./pathutil.ts";
78
import {
89
loadReviews,
9-
newReviewItem,
1010
practice,
1111
ReviewItem,
1212
saveReviews,
1313
score2grade,
1414
} from "./review.ts";
15-
import { deriveReviewfile } from "./pathutil.ts";
1615

17-
const args = parseArgs(Deno.args) as {
18-
deck: string;
19-
};
16+
const {
17+
debug,
18+
deck: deckfile,
19+
} = parseAppArgs(Deno.args);
2020

21-
const deckfile = args.deck;
2221
const reviewfile = deriveReviewfile(deckfile);
2322

24-
console.log({ deckfile, reviewfile });
23+
if (debug) {
24+
console.log({ deckfile, reviewfile });
25+
}
2526

2627
const deck = await loadDeck(deckfile);
2728

@@ -32,54 +33,12 @@ try {
3233
reviewMap = new Map();
3334
}
3435

35-
// update old items
36-
const backCardSet = new Set<string>();
37-
for (const card of deck) {
38-
backCardSet.add(card.back);
39-
}
40-
// remove those review items from reviewMap that are not in backCardSet
41-
for (const { front, back } of reviewMap.values()) {
42-
if (backCardSet.has(back)) {
43-
continue;
44-
}
45-
46-
reviewMap.delete(front);
47-
console.log(yellow("! " + front));
48-
}
49-
50-
// add new items
51-
for (const card of deck) {
52-
if (reviewMap.has(card.front)) {
53-
continue;
54-
}
55-
56-
reviewMap.set(card.front, newReviewItem(card));
57-
58-
console.log(green("+ " + card.front));
59-
}
60-
61-
// delete old items
62-
//
63-
// build a set of current cards
64-
const frontCardSet = new Set<string>();
65-
for (const card of deck) {
66-
frontCardSet.add(card.front);
67-
}
68-
// remove those review items from reviewMap that are not in frontCardSet
69-
for (const reviewItemKey of reviewMap.keys()) {
70-
if (!frontCardSet.has(reviewItemKey)) {
71-
reviewMap.delete(reviewItemKey);
72-
console.log("- " + red(reviewItemKey));
73-
}
74-
}
75-
76-
// save cleanups
77-
await saveReviews(reviewfile, reviewMap);
36+
reviewMap = await cleanupReviews(reviewfile, deck, reviewMap);
7837

7938
// get items for review
8039
const dueDateItems: ReviewItem[] = [];
8140
for (const review of reviewMap.values()) {
82-
if (new Date(review.dueDate) < new Date()) {
41+
if (new Date(review.dueDate) <= new Date()) {
8342
dueDateItems.push(review);
8443
}
8544
}
@@ -91,29 +50,39 @@ console.log("To review:", dueDateItems.length);
9150
for (const review of dueDateItems) {
9251
console.log(review.front);
9352

94-
let answer: string = await Input.prompt("");
53+
const answer = await Input.prompt("").then((s) => (
54+
// trim input and replace all multi-space characters with just one
55+
s.trim().replaceAll(" +", " ")
56+
));
9557

96-
// trip and replace all multi-space characters with just one
97-
answer = answer.replaceAll(" +", " ").trim();
58+
console.clear();
9859

99-
if (answer === ":skip") {
100-
if (!review.skipped) {
101-
review.skipped = 0;
102-
}
103-
review.skipped += 1;
60+
switch (answer) {
61+
case ":skip": {
62+
if (!review.skipped) {
63+
review.skipped = 0;
64+
}
65+
review.skipped += 1;
66+
67+
reviewMap.set(review.front, review);
68+
await saveReviews(reviewfile, reviewMap);
10469

105-
reviewMap.set(review.front, review);
106-
await saveReviews(reviewfile, reviewMap);
70+
console.log("skipped");
71+
console.log("correct:", review.back);
72+
console.log();
10773

108-
continue;
74+
continue;
75+
}
76+
case "":
77+
continue;
10978
}
11079

11180
const score = ratio(review.back, answer);
11281

11382
if (score === 1) {
11483
console.log("✅ Correct!");
11584
} else {
116-
console.log("☑️ Wrong! Score:", score);
85+
console.log("☑️ Wrong! Score:", formatPercentange(score));
11786

11887
const want = review.back;
11988

@@ -126,3 +95,7 @@ for (const review of dueDateItems) {
12695
reviewMap.set(review.front, practice(review, score2grade(score)));
12796
await saveReviews(reviewfile, reviewMap);
12897
}
98+
99+
function formatPercentange(f: number): string {
100+
return `${Math.round(f * 100)}%`;
101+
}

src/cmd/cli/args.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { assert, assertEquals, assertFalse } from "@std/assert";
2+
import { parseAppArgs } from "./args.ts";
3+
4+
Deno.test(async function test_parseAppArgs(t) {
5+
await t.step("parse --deck", () => {
6+
const { deck } = parseAppArgs(["--deck", "../decks/svenska.csv"]);
7+
8+
assertEquals(deck, "../decks/svenska.csv");
9+
});
10+
11+
await t.step("parse --debug", async (t) => {
12+
await t.step("debug: true", () => {
13+
const { debug } = parseAppArgs(["--debug", "1"]);
14+
15+
assert(debug);
16+
});
17+
18+
await t.step("debug: false", () => {
19+
const { debug } = parseAppArgs([]);
20+
21+
assertFalse(debug);
22+
});
23+
});
24+
});

src/cmd/cli/args.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { parseArgs } from "@std/cli/parse-args";
2+
3+
type AppArgs = {
4+
deck: string;
5+
debug: boolean;
6+
};
7+
8+
function parseAppArgs(cliArgs: string[]): AppArgs {
9+
const appArgs = parseArgs(cliArgs) as AppArgs;
10+
11+
return appArgs;
12+
}
13+
14+
export type { AppArgs };
15+
16+
export { parseAppArgs };

src/review.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1+
import { parse as parseYaml, stringify as stringifyYaml } from "@std/yaml";
12
import dayjs from "dayjs";
23
import { supermemo, SuperMemoGrade, SuperMemoItem } from "supermemo";
34
import { Card } from "./card.ts";
4-
import { parse as parseYaml, stringify as stringifyYaml } from "@std/yaml";
55

66
interface ReviewItem extends Card, SuperMemoItem {
77
dueDate: string;
88
skipped?: number;
99
}
1010

11+
// TODO: split loadReviews and filterReviews
1112
async function loadReviews(
1213
filename: string,
1314
): Promise<Map<string, ReviewItem>> {
1415
const s = await Deno.readTextFile(filename);
1516

16-
const reviews = parseYaml(s) as ReviewItem[];
17+
let reviews = parseYaml(s) as ReviewItem[];
18+
reviews = reviews.filter((item) => !item.skipped || item.skipped <= 3);
1719

1820
const ret: Map<string, ReviewItem> = new Map();
1921
for (const review of reviews) {

0 commit comments

Comments
 (0)