cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package models
2
3import (
4 "encoding/json"
5 "time"
6)
7
8// Model defines the common interface that all domain models must implement
9type Model interface {
10 // GetID returns the primary key identifier
11 GetID() int64
12 // SetID sets the primary key identifier
13 SetID(id int64)
14 // GetTableName returns the database table name for this model
15 GetTableName() string
16 // GetCreatedAt returns when the model was created
17 GetCreatedAt() time.Time
18 // SetCreatedAt sets when the model was created
19 SetCreatedAt(t time.Time)
20 // GetUpdatedAt returns when the model was last updated
21 GetUpdatedAt() time.Time
22 // SetUpdatedAt sets when the model was last updated
23 SetUpdatedAt(t time.Time)
24}
25
26// Task represents a task item with TaskWarrior-inspired fields
27type Task struct {
28 ID int64 `json:"id"`
29 UUID string `json:"uuid"`
30 Description string `json:"description"`
31 // pending, completed, deleted
32 Status string `json:"status"`
33 // A-Z or empty
34 Priority string `json:"priority,omitempty"`
35 Project string `json:"project,omitempty"`
36 Tags []string `json:"tags,omitempty"`
37 Due *time.Time `json:"due,omitempty"`
38 Entry time.Time `json:"entry"`
39 Modified time.Time `json:"modified"`
40 // completion time
41 End *time.Time `json:"end,omitempty"`
42 // when task was started
43 Start *time.Time `json:"start,omitempty"`
44 Annotations []string `json:"annotations,omitempty"`
45}
46
47// Movie represents a movie in the watch queue
48type Movie struct {
49 ID int64 `json:"id"`
50 Title string `json:"title"`
51 Year int `json:"year,omitempty"`
52 // queued, watched, removed
53 Status string `json:"status"`
54 Rating float64 `json:"rating,omitempty"`
55 Notes string `json:"notes,omitempty"`
56 Added time.Time `json:"added"`
57 Watched *time.Time `json:"watched,omitempty"`
58}
59
60// TVShow represents a TV show in the watch queue
61type TVShow struct {
62 ID int64 `json:"id"`
63 Title string `json:"title"`
64 Season int `json:"season,omitempty"`
65 Episode int `json:"episode,omitempty"`
66 // queued, watching, watched, removed
67 Status string `json:"status"`
68 Rating float64 `json:"rating,omitempty"`
69 Notes string `json:"notes,omitempty"`
70 Added time.Time `json:"added"`
71 LastWatched *time.Time `json:"last_watched,omitempty"`
72}
73
74// Book represents a book in the reading list
75type Book struct {
76 ID int64 `json:"id"`
77 Title string `json:"title"`
78 Author string `json:"author,omitempty"`
79 // queued, reading, finished, removed
80 Status string `json:"status"`
81 // percentage 0-100
82 Progress int `json:"progress"`
83 Pages int `json:"pages,omitempty"`
84 Rating float64 `json:"rating,omitempty"`
85 Notes string `json:"notes,omitempty"`
86 Added time.Time `json:"added"`
87 Started *time.Time `json:"started,omitempty"`
88 Finished *time.Time `json:"finished,omitempty"`
89}
90
91// Note represents a markdown note
92type Note struct {
93 ID int64 `json:"id"`
94 Title string `json:"title"`
95 Content string `json:"content"`
96 Tags []string `json:"tags,omitempty"`
97 Archived bool `json:"archived"`
98 Created time.Time `json:"created"`
99 Modified time.Time `json:"modified"`
100 FilePath string `json:"file_path,omitempty"`
101}
102
103// Album represents a music album
104type Album struct {
105 ID int64 `json:"id"`
106 Title string `json:"title"`
107 Artist string `json:"artist"`
108 Genre string `json:"genre,omitempty"`
109 ReleaseYear int `json:"release_year,omitempty"`
110 Tracks []string `json:"tracks,omitempty"`
111 DurationSeconds int `json:"duration_seconds,omitempty"`
112 AlbumArtPath string `json:"album_art_path,omitempty"`
113 Rating int `json:"rating,omitempty"`
114 Created time.Time `json:"created"`
115 Modified time.Time `json:"modified"`
116}
117
118// MarshalTags converts tags slice to JSON string for database storage
119func (t *Task) MarshalTags() (string, error) {
120 if len(t.Tags) == 0 {
121 return "", nil
122 }
123 data, err := json.Marshal(t.Tags)
124 return string(data), err
125}
126
127// UnmarshalTags converts JSON string from database to tags slice
128func (t *Task) UnmarshalTags(data string) error {
129 if data == "" {
130 t.Tags = nil
131 return nil
132 }
133 return json.Unmarshal([]byte(data), &t.Tags)
134}
135
136// MarshalAnnotations converts annotations slice to JSON string for database storage
137func (t *Task) MarshalAnnotations() (string, error) {
138 if len(t.Annotations) == 0 {
139 return "", nil
140 }
141 data, err := json.Marshal(t.Annotations)
142 return string(data), err
143}
144
145// UnmarshalAnnotations converts JSON string from database to annotations slice
146func (t *Task) UnmarshalAnnotations(data string) error {
147 if data == "" {
148 t.Annotations = nil
149 return nil
150 }
151 return json.Unmarshal([]byte(data), &t.Annotations)
152}
153
154// IsCompleted returns true if the task is marked as completed
155func (t *Task) IsCompleted() bool {
156 return t.Status == "completed"
157}
158
159// IsPending returns true if the task is pending
160func (t *Task) IsPending() bool {
161 return t.Status == "pending"
162}
163
164// IsDeleted returns true if the task is deleted
165func (t *Task) IsDeleted() bool {
166 return t.Status == "deleted"
167}
168
169// HasPriority returns true if the task has a priority set
170func (t *Task) HasPriority() bool {
171 return t.Priority != ""
172}
173
174// IsWatched returns true if the movie has been watched
175func (m *Movie) IsWatched() bool {
176 return m.Status == "watched"
177}
178
179// IsQueued returns true if the movie is in the queue
180func (m *Movie) IsQueued() bool {
181 return m.Status == "queued"
182}
183
184// IsWatching returns true if the TV show is currently being watched
185func (tv *TVShow) IsWatching() bool {
186 return tv.Status == "watching"
187}
188
189// IsWatched returns true if the TV show has been watched
190func (tv *TVShow) IsWatched() bool {
191 return tv.Status == "watched"
192}
193
194// IsQueued returns true if the TV show is in the queue
195func (tv *TVShow) IsQueued() bool {
196 return tv.Status == "queued"
197}
198
199// IsReading returns true if the book is currently being read
200func (b *Book) IsReading() bool {
201 return b.Status == "reading"
202}
203
204// IsFinished returns true if the book has been finished
205func (b *Book) IsFinished() bool {
206 return b.Status == "finished"
207}
208
209// IsQueued returns true if the book is in the queue
210func (b *Book) IsQueued() bool {
211 return b.Status == "queued"
212}
213
214// ProgressPercent returns the reading progress as a percentage
215func (b *Book) ProgressPercent() int {
216 return b.Progress
217}
218
219func (t *Task) GetID() int64 { return t.ID }
220func (t *Task) SetID(id int64) { t.ID = id }
221func (t *Task) GetTableName() string { return "tasks" }
222func (t *Task) GetCreatedAt() time.Time { return t.Entry }
223func (t *Task) SetCreatedAt(time time.Time) { t.Entry = time }
224func (t *Task) GetUpdatedAt() time.Time { return t.Modified }
225func (t *Task) SetUpdatedAt(time time.Time) { t.Modified = time }
226
227func (m *Movie) GetID() int64 { return m.ID }
228func (m *Movie) SetID(id int64) { m.ID = id }
229func (m *Movie) GetTableName() string { return "movies" }
230func (m *Movie) GetCreatedAt() time.Time { return m.Added }
231func (m *Movie) SetCreatedAt(time time.Time) { m.Added = time }
232func (m *Movie) GetUpdatedAt() time.Time { return m.Added }
233func (m *Movie) SetUpdatedAt(time time.Time) { m.Added = time }
234
235func (tv *TVShow) GetID() int64 { return tv.ID }
236func (tv *TVShow) SetID(id int64) { tv.ID = id }
237func (tv *TVShow) GetTableName() string { return "tv_shows" }
238func (tv *TVShow) GetCreatedAt() time.Time { return tv.Added }
239func (tv *TVShow) SetCreatedAt(time time.Time) { tv.Added = time }
240func (tv *TVShow) GetUpdatedAt() time.Time { return tv.Added }
241func (tv *TVShow) SetUpdatedAt(time time.Time) { tv.Added = time }
242
243func (b *Book) GetID() int64 { return b.ID }
244func (b *Book) SetID(id int64) { b.ID = id }
245func (b *Book) GetTableName() string { return "books" }
246func (b *Book) GetCreatedAt() time.Time { return b.Added }
247func (b *Book) SetCreatedAt(time time.Time) { b.Added = time }
248func (b *Book) GetUpdatedAt() time.Time { return b.Added }
249func (b *Book) SetUpdatedAt(time time.Time) { b.Added = time }
250
251// MarshalTags converts tags slice to JSON string for database storage
252func (n *Note) MarshalTags() (string, error) {
253 if len(n.Tags) == 0 {
254 return "", nil
255 }
256 data, err := json.Marshal(n.Tags)
257 return string(data), err
258}
259
260// UnmarshalTags converts JSON string from database to tags slice
261func (n *Note) UnmarshalTags(data string) error {
262 if data == "" {
263 n.Tags = nil
264 return nil
265 }
266 return json.Unmarshal([]byte(data), &n.Tags)
267}
268
269// IsArchived returns true if the note is archived
270func (n *Note) IsArchived() bool {
271 return n.Archived
272}
273
274func (n *Note) GetID() int64 { return n.ID }
275func (n *Note) SetID(id int64) { n.ID = id }
276func (n *Note) GetTableName() string { return "notes" }
277func (n *Note) GetCreatedAt() time.Time { return n.Created }
278func (n *Note) SetCreatedAt(time time.Time) { n.Created = time }
279func (n *Note) GetUpdatedAt() time.Time { return n.Modified }
280func (n *Note) SetUpdatedAt(time time.Time) { n.Modified = time }
281
282// MarshalTracks converts tracks slice to JSON string for database storage
283func (a *Album) MarshalTracks() (string, error) {
284 if len(a.Tracks) == 0 {
285 return "", nil
286 }
287 data, err := json.Marshal(a.Tracks)
288 return string(data), err
289}
290
291// UnmarshalTracks converts JSON string from database to tracks slice
292func (a *Album) UnmarshalTracks(data string) error {
293 if data == "" {
294 a.Tracks = nil
295 return nil
296 }
297 return json.Unmarshal([]byte(data), &a.Tracks)
298}
299
300// HasRating returns true if the album has a rating set
301func (a *Album) HasRating() bool {
302 return a.Rating > 0
303}
304
305// IsValidRating returns true if the rating is between 1 and 5
306func (a *Album) IsValidRating() bool {
307 return a.Rating >= 1 && a.Rating <= 5
308}
309
310func (a *Album) GetID() int64 { return a.ID }
311func (a *Album) SetID(id int64) { a.ID = id }
312func (a *Album) GetTableName() string { return "albums" }
313func (a *Album) GetCreatedAt() time.Time { return a.Created }
314func (a *Album) SetCreatedAt(time time.Time) { a.Created = time }
315func (a *Album) GetUpdatedAt() time.Time { return a.Modified }
316func (a *Album) SetUpdatedAt(time time.Time) { a.Modified = time }