Skip to content

Commit cca9511

Browse files
committed
progress on prompts for summarizing journal entries
1 parent 51c8a01 commit cca9511

File tree

113 files changed

+1922
-9028
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+1922
-9028
lines changed

exercises/03.advanced-tools/01.problem.db/src/db/index.ts

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,44 @@ export class DB {
6767
const stmt = this.#db.prepare(
6868
sql`SELECT * FROM entries ORDER BY created_at DESC`,
6969
)
70-
const entries = stmt.all()
71-
return z
72-
.array(entrySchema)
73-
.parse(entries.map((entry) => snakeToCamel(entry)))
70+
const entries = stmt.all().map((entry) => snakeToCamel(entry))
71+
return z.array(entrySchema).parse(entries)
72+
}
73+
74+
async getEntriesWithTags() {
75+
const entriesStmt = this.#db.prepare(
76+
sql`SELECT * FROM entries ORDER BY created_at DESC`,
77+
)
78+
const entries = entriesStmt.all().map((entry) => snakeToCamel(entry))
79+
80+
// Query all tags for all entries
81+
const tagsStmt = this.#db.prepare(sql`
82+
SELECT et.entry_id, t.id as tag_id, t.name as tag_name
83+
FROM entry_tags et
84+
JOIN tags t ON et.tag_id = t.id
85+
`)
86+
const tagRows = tagsStmt.all().map((row) => snakeToCamel(row))
87+
88+
// Build a map of entryId to tags
89+
const entryIdToTags = new Map<number, Array<{ id: number; name: string }>>()
90+
for (const row of tagRows as any[]) {
91+
const { entryId, tagId, tagName } = row
92+
if (!entryIdToTags.has(entryId)) {
93+
entryIdToTags.set(entryId, [])
94+
}
95+
entryIdToTags.get(entryId)!.push({ id: tagId, name: tagName })
96+
}
97+
98+
const entryWithTagSchema = entrySchema.extend({
99+
tags: z.array(z.object({ id: z.number(), name: z.string() })),
100+
})
101+
102+
const entriesWithTags = entries.map((entry: any) => ({
103+
...entry,
104+
tags: entryIdToTags.get(entry.id) ?? [],
105+
}))
106+
107+
return z.array(entryWithTagSchema).parse(entriesWithTags)
74108
}
75109

76110
async getEntry(id: number) {
@@ -85,10 +119,10 @@ export class DB {
85119
WHERE et.entry_id = ?
86120
ORDER BY t.name
87121
`)
88-
const tagsResult = tagsStmt.all(id)
122+
const tagsResult = tagsStmt.all(id).map((tag) => snakeToCamel(tag))
89123
const tags = z
90124
.array(z.object({ id: z.number(), name: z.string() }))
91-
.parse(tagsResult.map((result: any) => snakeToCamel(result)))
125+
.parse(tagsResult)
92126
return { ...entry, tags }
93127
}
94128

@@ -97,10 +131,8 @@ export class DB {
97131
const stmt = this.#db.prepare(
98132
sql`SELECT * FROM entries ORDER BY created_at DESC`,
99133
)
100-
const results = stmt.all()
101-
return z
102-
.array(entrySchema)
103-
.parse(results.map((result: any) => snakeToCamel(result)))
134+
const results = stmt.all().map((result) => snakeToCamel(result))
135+
return z.array(entrySchema).parse(results)
104136
}
105137

106138
async updateEntry(
@@ -180,10 +212,8 @@ export class DB {
180212

181213
async getTags() {
182214
const stmt = this.#db.prepare(sql`SELECT * FROM tags ORDER BY name`)
183-
const results = stmt.all()
184-
return z
185-
.array(tagSchema)
186-
.parse(results.map((result: any) => snakeToCamel(result)))
215+
const results = stmt.all().map((result) => snakeToCamel(result))
216+
return z.array(tagSchema).parse(results)
187217
}
188218

189219
async getTag(id: number) {
@@ -195,10 +225,10 @@ export class DB {
195225

196226
async listTags() {
197227
const stmt = this.#db.prepare(sql`SELECT id, name FROM tags ORDER BY name`)
198-
const results = stmt.all()
228+
const results = stmt.all().map((result) => snakeToCamel(result))
199229
return z
200230
.array(z.object({ id: z.number(), name: z.string() }))
201-
.parse(results.map((result: any) => snakeToCamel(result)))
231+
.parse(results)
202232
}
203233

204234
async updateTag(id: number, tag: Partial<z.input<typeof newTagSchema>>) {
@@ -299,9 +329,7 @@ export class DB {
299329
WHERE et.entry_id = ?
300330
ORDER BY t.name
301331
`)
302-
const results = stmt.all(entryId)
303-
return z
304-
.array(tagSchema)
305-
.parse(results.map((result: any) => snakeToCamel(result)))
332+
const results = stmt.all(entryId).map((result) => snakeToCamel(result))
333+
return z.array(tagSchema).parse(results)
306334
}
307335
}
Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
// Helper function to format SQL queries
2-
export function sql(strings: TemplateStringsArray, ...values: Array<string>) {
3-
const joined = strings.reduce((result, str, i) => {
4-
return result + str + (values[i] || "");
5-
}, "");
6-
return joined
7-
.split("\n")
8-
.map((line) => line.trim())
9-
.filter(Boolean)
10-
.join(" ");
2+
export function sql(
3+
strings: TemplateStringsArray,
4+
...values: Array<string | number>
5+
) {
6+
const joined = strings.reduce((result, str, i) => {
7+
return result + str + (values[i] || '')
8+
}, '')
9+
return joined
10+
.split('\n')
11+
.map((line) => line.trim())
12+
.filter(Boolean)
13+
.join(' ')
1114
}
1215

1316
export function snakeToCamel<T extends Record<string, unknown>>(obj: T) {
14-
const entries = Object.entries(obj).map(([key, value]) => {
15-
const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
16-
letter.toUpperCase(),
17-
);
18-
return [camelKey, value];
19-
});
20-
return Object.fromEntries(entries) as {
21-
[K in keyof T as K extends string
22-
? K extends `${string}_${string}`
23-
? never
24-
: K
25-
: K]: T[K];
26-
} & {
27-
[K in keyof T as K extends `${infer Start}_${infer Letter}${infer Rest}`
28-
? `${Start}${Uppercase<Letter>}${Rest}`
29-
: never]: T[K];
30-
};
17+
const entries = Object.entries(obj).map(([key, value]) => {
18+
const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
19+
letter.toUpperCase(),
20+
)
21+
return [camelKey, value]
22+
})
23+
return Object.fromEntries(entries) as {
24+
[K in keyof T as K extends string
25+
? K extends `${string}_${string}`
26+
? never
27+
: K
28+
: K]: T[K]
29+
} & {
30+
[K in keyof T as K extends `${infer Start}_${infer Letter}${infer Rest}`
31+
? `${Start}${Uppercase<Letter>}${Rest}`
32+
: never]: T[K]
33+
}
3134
}

exercises/03.advanced-tools/01.solution.db/src/db/index.ts

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,44 @@ export class DB {
6767
const stmt = this.#db.prepare(
6868
sql`SELECT * FROM entries ORDER BY created_at DESC`,
6969
)
70-
const entries = stmt.all()
71-
return z
72-
.array(entrySchema)
73-
.parse(entries.map((entry) => snakeToCamel(entry)))
70+
const entries = stmt.all().map((entry) => snakeToCamel(entry))
71+
return z.array(entrySchema).parse(entries)
72+
}
73+
74+
async getEntriesWithTags() {
75+
const entriesStmt = this.#db.prepare(
76+
sql`SELECT * FROM entries ORDER BY created_at DESC`,
77+
)
78+
const entries = entriesStmt.all().map((entry) => snakeToCamel(entry))
79+
80+
// Query all tags for all entries
81+
const tagsStmt = this.#db.prepare(sql`
82+
SELECT et.entry_id, t.id as tag_id, t.name as tag_name
83+
FROM entry_tags et
84+
JOIN tags t ON et.tag_id = t.id
85+
`)
86+
const tagRows = tagsStmt.all().map((row) => snakeToCamel(row))
87+
88+
// Build a map of entryId to tags
89+
const entryIdToTags = new Map<number, Array<{ id: number; name: string }>>()
90+
for (const row of tagRows as any[]) {
91+
const { entryId, tagId, tagName } = row
92+
if (!entryIdToTags.has(entryId)) {
93+
entryIdToTags.set(entryId, [])
94+
}
95+
entryIdToTags.get(entryId)!.push({ id: tagId, name: tagName })
96+
}
97+
98+
const entryWithTagSchema = entrySchema.extend({
99+
tags: z.array(z.object({ id: z.number(), name: z.string() })),
100+
})
101+
102+
const entriesWithTags = entries.map((entry: any) => ({
103+
...entry,
104+
tags: entryIdToTags.get(entry.id) ?? [],
105+
}))
106+
107+
return z.array(entryWithTagSchema).parse(entriesWithTags)
74108
}
75109

76110
async getEntry(id: number) {
@@ -85,10 +119,10 @@ export class DB {
85119
WHERE et.entry_id = ?
86120
ORDER BY t.name
87121
`)
88-
const tagsResult = tagsStmt.all(id)
122+
const tagsResult = tagsStmt.all(id).map((tag) => snakeToCamel(tag))
89123
const tags = z
90124
.array(z.object({ id: z.number(), name: z.string() }))
91-
.parse(tagsResult.map((result: any) => snakeToCamel(result)))
125+
.parse(tagsResult)
92126
return { ...entry, tags }
93127
}
94128

@@ -97,10 +131,8 @@ export class DB {
97131
const stmt = this.#db.prepare(
98132
sql`SELECT * FROM entries ORDER BY created_at DESC`,
99133
)
100-
const results = stmt.all()
101-
return z
102-
.array(entrySchema)
103-
.parse(results.map((result: any) => snakeToCamel(result)))
134+
const results = stmt.all().map((result) => snakeToCamel(result))
135+
return z.array(entrySchema).parse(results)
104136
}
105137

106138
async updateEntry(
@@ -180,10 +212,8 @@ export class DB {
180212

181213
async getTags() {
182214
const stmt = this.#db.prepare(sql`SELECT * FROM tags ORDER BY name`)
183-
const results = stmt.all()
184-
return z
185-
.array(tagSchema)
186-
.parse(results.map((result: any) => snakeToCamel(result)))
215+
const results = stmt.all().map((result) => snakeToCamel(result))
216+
return z.array(tagSchema).parse(results)
187217
}
188218

189219
async getTag(id: number) {
@@ -195,10 +225,10 @@ export class DB {
195225

196226
async listTags() {
197227
const stmt = this.#db.prepare(sql`SELECT id, name FROM tags ORDER BY name`)
198-
const results = stmt.all()
228+
const results = stmt.all().map((result) => snakeToCamel(result))
199229
return z
200230
.array(z.object({ id: z.number(), name: z.string() }))
201-
.parse(results.map((result: any) => snakeToCamel(result)))
231+
.parse(results)
202232
}
203233

204234
async updateTag(id: number, tag: Partial<z.input<typeof newTagSchema>>) {
@@ -299,9 +329,7 @@ export class DB {
299329
WHERE et.entry_id = ?
300330
ORDER BY t.name
301331
`)
302-
const results = stmt.all(entryId)
303-
return z
304-
.array(tagSchema)
305-
.parse(results.map((result: any) => snakeToCamel(result)))
332+
const results = stmt.all(entryId).map((result) => snakeToCamel(result))
333+
return z.array(tagSchema).parse(results)
306334
}
307335
}
Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
// Helper function to format SQL queries
2-
export function sql(strings: TemplateStringsArray, ...values: Array<string>) {
3-
const joined = strings.reduce((result, str, i) => {
4-
return result + str + (values[i] || "");
5-
}, "");
6-
return joined
7-
.split("\n")
8-
.map((line) => line.trim())
9-
.filter(Boolean)
10-
.join(" ");
2+
export function sql(
3+
strings: TemplateStringsArray,
4+
...values: Array<string | number>
5+
) {
6+
const joined = strings.reduce((result, str, i) => {
7+
return result + str + (values[i] || '')
8+
}, '')
9+
return joined
10+
.split('\n')
11+
.map((line) => line.trim())
12+
.filter(Boolean)
13+
.join(' ')
1114
}
1215

1316
export function snakeToCamel<T extends Record<string, unknown>>(obj: T) {
14-
const entries = Object.entries(obj).map(([key, value]) => {
15-
const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
16-
letter.toUpperCase(),
17-
);
18-
return [camelKey, value];
19-
});
20-
return Object.fromEntries(entries) as {
21-
[K in keyof T as K extends string
22-
? K extends `${string}_${string}`
23-
? never
24-
: K
25-
: K]: T[K];
26-
} & {
27-
[K in keyof T as K extends `${infer Start}_${infer Letter}${infer Rest}`
28-
? `${Start}${Uppercase<Letter>}${Rest}`
29-
: never]: T[K];
30-
};
17+
const entries = Object.entries(obj).map(([key, value]) => {
18+
const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
19+
letter.toUpperCase(),
20+
)
21+
return [camelKey, value]
22+
})
23+
return Object.fromEntries(entries) as {
24+
[K in keyof T as K extends string
25+
? K extends `${string}_${string}`
26+
? never
27+
: K
28+
: K]: T[K]
29+
} & {
30+
[K in keyof T as K extends `${infer Start}_${infer Letter}${infer Rest}`
31+
? `${Start}${Uppercase<Letter>}${Rest}`
32+
: never]: T[K]
33+
}
3134
}

0 commit comments

Comments
 (0)