summaryrefslogtreecommitdiff
path: root/main.go
blob: c1dd3690fd61a427eb105f86601fdff475e249e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// This program looks up words fromm Wiktionary, and creates Anki flashcards
// from them.
package main

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

const (
	rawDictionary = "/home/david/work/french-wiktionary-flashcards/raw-wiktextract-data.jsonl"
	dictionary    = "/home/david/work/french-wiktionary-flashcards/raw-wiktextract-data.sqlite3"

	apiURL    = "http://localhost:8765"
	deckName  = "Français"
	modelName = "Basic-830ae"
)

type addNote struct {
	Action  string        `json:"action"`
	Version int           `json:"version"`
	Params  addNoteParams `json:"params"`
}

type addNoteParams struct {
	Note note `json:"note"`
}

type note struct {
	DeckName  string `json:"deckName"`
	ModelName string `json:"modelName"`
	// Fields will not be trivial to generalize
	Fields  fields  `json:"fields"`
	Options options `json:"options"`
}

type fields struct {
	Front string `json:"Front"`
	Back  string `json:"Back"`
}

type options struct {
	AllowDuplicate bool   `json:"allowDuplicate"`
	DuplicateScope string `json:"duplicateScope"`
}

func main() {
	db, err := setupDatabase()
	if err != nil {
		log.Fatalf("setting up database: %s", err)
	}
	defer db.Close()

	// We're going to start this app very simply! The first iteration will take
	// a word as its first command-line argument. We will search for the word in
	// the dictionary and create a new Anki card using the first exact match.
	//
	// In the future, we may make it easy for the user to edit cards (flag?),
	// and possibly implement a TUI to choose definitions more interactively as
	// well (e.g. search with partial matches).
	//
	// I would also like to make more things less hard-coded.
	if len(os.Args) < 2 {
		log.Fatalf("no word was provided")
	}
	word := os.Args[1]

	var definition string
	row := db.QueryRow(`select definition from words where word = ? limit 1`, word)
	err = row.Scan(&definition)
	if err != nil {
		log.Fatalf("looking up '%s': %s", word, err)
	}

	c := http.DefaultClient
	c.Timeout = 5 * time.Second

	// Create the card
	noteRequest := addNote{
		Action:  "addNote",
		Version: 6,
		Params: addNoteParams{
			Note: note{
				DeckName:  deckName,
				ModelName: modelName,
				Fields: fields{
					Front: word,
					Back:  definition,
				},
				Options: options{
					AllowDuplicate: false,
					DuplicateScope: "deck",
				},
			},
		},
	}

	jsonBytes, err := json.Marshal(noteRequest)
	if err != nil {
		log.Fatalf("marshaling JSON: %s", err)
	}

	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBytes))
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.Do(req)
	if err != nil {
		log.Fatalf("making request: %s", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	var jsonResp struct {
		Error string `json:"error"`
	}

	json.Unmarshal(body, &jsonResp)
	if jsonResp.Error != "" {
		log.Fatalf("creating card: %s", jsonResp.Error)
	}

}