From b06800d1ebdb383857875f00c0a2bf70e23002bb Mon Sep 17 00:00:00 2001 From: caspersimon <165684406+caspersimon@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:11:10 +0100 Subject: [PATCH 1/5] Integrate homework materials and improve study DB curation --- .DS_Store | Bin 10244 -> 14340 bytes AGENTS.md | 15 +- Makefile | 2 +- app/card-interactions.js | 7 + app/preview-card-render.js | 30 +- app/preview-editing.js | 9 +- app/render-card-sections.js | 10 +- app/state-and-init.js | 4 +- app/view-and-data.js | 109 +++- data/study_db.json | 582 +++++++++++++++--- data/templates/week_template.json | 13 +- docs/curation/TOPIC_MERGING_GUIDELINES.md | 20 +- docs/data/DATASET_INFO.md | 2 + materials/homework/week 1/Bonus_Exercise.py | 7 + .../homework/week 1/Bonus_Exercise_Solved.py | 17 + materials/homework/week 1/Exercise 1.py | 14 + materials/homework/week 1/Exercise_1.2.py | 21 + materials/homework/week 1/Exercise_1.3.py | 30 + materials/homework/week 1/Exercise_1.4.py | 19 + materials/homework/week 1/Exercise_1.5.py | 18 + materials/homework/week 1/Exercise_1.6.py | 15 + materials/homework/week 1/Exercise_1.7.py | 14 + materials/homework/week 1/Exercise_1.8.py | 13 + materials/homework/week 1/Exercise_1.9.py | 17 + .../homework/week 1/Solutions week 1 (1).txt | 51 ++ materials/homework/week 2/Exercise_2.1.py | 31 + materials/homework/week 2/Exercise_2.2.py | 37 ++ materials/homework/week 2/Exercise_2.3.py | 22 + materials/homework/week 2/Exercise_2.4.py | 26 + materials/homework/week 2/Exercise_2.5.py | 37 ++ materials/homework/week 2/Exercise_2.6.py | 22 + materials/homework/week 2/Exercise_2.7.py | 29 + materials/homework/week 2/Exercise_2.8.py | 35 ++ materials/homework/week 2/Exercise_2.9.py | 46 ++ .../homework/week 2/Solutions week 2.txt | 120 ++++ materials/homework/week 3/Exercise_3.1.py | 38 ++ materials/homework/week 3/Exercise_3.2.py | 33 + materials/homework/week 3/Exercise_3.3.py | 38 ++ materials/homework/week 3/Exercise_3.4.py | 39 ++ materials/homework/week 3/Exercise_3.5.py | 51 ++ materials/homework/week 3/Exercise_3.6.py | 48 ++ materials/homework/week 3/Exercise_3.7.py | 33 + materials/homework/week 3/Exercise_3.8.py | 37 ++ materials/homework/week 3/Exercise_3.9.py | 15 + .../homework/week 3/Solutions week 3.txt | 102 +++ pipelines/ai_sections/common.py | 11 + pipelines/shared/study_database.py | 20 + pipelines/study_database/curation.py | 18 +- pipelines/study_database/validators.py | 49 +- pipelines/topic_cards/assembly.py | 47 +- pipelines/topic_cards/pipeline.py | 4 +- scripts/add_week_material.py | 6 +- scripts/integrate_homework_material.py | 469 ++++++++++++++ ...test_integrate_homework_material_script.py | 53 ++ tests/test_study_db_integrity.py | 4 +- tests/test_topic_cards_integrity.py | 12 +- topic_cards.json | 508 +++++++++++---- 57 files changed, 2838 insertions(+), 241 deletions(-) create mode 100644 materials/homework/week 1/Bonus_Exercise.py create mode 100644 materials/homework/week 1/Bonus_Exercise_Solved.py create mode 100644 materials/homework/week 1/Exercise 1.py create mode 100644 materials/homework/week 1/Exercise_1.2.py create mode 100644 materials/homework/week 1/Exercise_1.3.py create mode 100644 materials/homework/week 1/Exercise_1.4.py create mode 100644 materials/homework/week 1/Exercise_1.5.py create mode 100644 materials/homework/week 1/Exercise_1.6.py create mode 100644 materials/homework/week 1/Exercise_1.7.py create mode 100644 materials/homework/week 1/Exercise_1.8.py create mode 100644 materials/homework/week 1/Exercise_1.9.py create mode 100644 materials/homework/week 1/Solutions week 1 (1).txt create mode 100644 materials/homework/week 2/Exercise_2.1.py create mode 100644 materials/homework/week 2/Exercise_2.2.py create mode 100644 materials/homework/week 2/Exercise_2.3.py create mode 100644 materials/homework/week 2/Exercise_2.4.py create mode 100644 materials/homework/week 2/Exercise_2.5.py create mode 100644 materials/homework/week 2/Exercise_2.6.py create mode 100644 materials/homework/week 2/Exercise_2.7.py create mode 100644 materials/homework/week 2/Exercise_2.8.py create mode 100644 materials/homework/week 2/Exercise_2.9.py create mode 100644 materials/homework/week 2/Solutions week 2.txt create mode 100644 materials/homework/week 3/Exercise_3.1.py create mode 100644 materials/homework/week 3/Exercise_3.2.py create mode 100644 materials/homework/week 3/Exercise_3.3.py create mode 100644 materials/homework/week 3/Exercise_3.4.py create mode 100644 materials/homework/week 3/Exercise_3.5.py create mode 100644 materials/homework/week 3/Exercise_3.6.py create mode 100644 materials/homework/week 3/Exercise_3.7.py create mode 100644 materials/homework/week 3/Exercise_3.8.py create mode 100644 materials/homework/week 3/Exercise_3.9.py create mode 100644 materials/homework/week 3/Solutions week 3.txt create mode 100644 scripts/integrate_homework_material.py create mode 100644 tests/test_integrate_homework_material_script.py diff --git a/.DS_Store b/.DS_Store index 2091d972d01f99d9d65a6517a88e506826d2be4a..c6824086392a75f33418100d9ed1488b15d5d487 100644 GIT binary patch literal 14340 zcmeGiTa4UR^&amgYbTk;`^aPSoMhCn4Vz?ln|DgGuPE8PHpy)Em828T*x5Dl*n{oe z&2|Y2YN3cqRa7+v3M#Kkkf@Z>3JHNgq(CFJ;*lt=1Vt275Fe$eASxdHDCgemnem-R z0!V-$_Q-SXdmi`P<8$sk=h%c0=$_Eq327sQ$avCLorix95)!9v=%8?VmK`>q9~mN?(iTA~J?FVfqyEF-eMe*q=d;CUFq%k@X};ru!@>leU~R zQsS_Zi|rHdvV)M`-T)%k^jv!KuFF@u*cj_^1e5q#N6*8tJ31l_L>h=R5NRONz-^}i zy7%HC&*g3OA88=cK%{|!2I%?_$CI|5L3^IJZ0W#18p2aJ4dK~=*BFqqzIf1f2JLy? zGE$^rq}Xy&2vQ8-6~SHep}lsAl1~)nq4Q$@?803a@?6p`kFuCb!+Z%dN<%WRyu#Jr3X!8 zDy25{IJU2uw&5}YQ#TOh#E@z0&Q!NEVe1~FO>%&&C`w9QJTue2X>)75ecPsUt?`*{ z9c``gEt@x=JEzDC5*v0LO`Wl_w)1iRSRq&%Nvz~CKZN%cbfJ40D~*y;vI<~ptUZ}= zvN^_V{>~p&!SW`1xO!@iM?F?lcYa(lVDYZVMS(yn-^NlXcqR7}c-^;j;WgT`r(>EP^WK=slV4U?Sbl49r z%CLqZ0Y~MU>8m^+$L;HZO zWsMtZLw#fOlICTynvjPl(&h&yOn;j7##T13Qq-@@Cv+6%prcbto#U@;zEhTu)BYaU zz<=HyYiVAqsG2L~+f(1bW?M{J|4>dG-oOB)vN+7jC)tNWFqNj^i~ zc~5MoTo0|vFh1Wb%I)#)uI3(Dy&~UB$3CEWet*uei&E{8 ziSO#C*p;9rr74dXv@~{DNm|HuTxAAvE%C?)$;0GR#aknBam36EF!g@Bn-S9)ypnEhA+ZX@D+Flz6Rff@4@%s1-J^Yz-#bJ_!YbXe}Z=G*4O~t(4-@UD7?$ z`=p)HE@`iHL>iUGq;W34l=s^c;=H73yszY#-ej;MNiSA&xxRh-_8*8H`d@OnpU$UF z*0f;ZqQ$GI=WW~BQ?O5N(Rm{dI|uUz!918hF#jtxn7Ec0d=} zd=ieqFpR(`+Pwi|FwSh>N6Vjrv+!ZKAFcnRX#W@B5qK0X!ej7R_&j`p`M}f62cCs* z!MEW%a0Q-+m*9u+WB3VNgX{1r{2Jbbzri~twocy}gbstM2K({#4e!_ zcib|tu;bRXHZsA=u)-?2)_3{$%b#N}I4TSmdetG{98*xqoM*U7j*{J^i}Yg9wHrgU zEYZc_iN16ZCR~RLZw5m{I%y0SMQj!Tekc?GmS78WpZ%K|6rltoJnyfLOsOPpc(69O6xSHa91E*&()K$yC8?MV?Dp7>jHUe4jsv2!IKSlfq3&6qh}=Cqi!3MAB&1w4g;xYt=TlvX5Sv zdxrVc24y%+9FI9jMLanFv+m`mNt?`a6?S<0Y#?2f5h0+_<8JkboAPb#lCZEm2&-2;*Yihk%LgT4% z!-9r|3!5-M#?*A_@|4<`)-q?tTv|KCI+x>iTN6eh;}2EF8cOhv8v4D(u7T+sR^*zB z9gNo=*D&nkW3H+9pFZsw{&46zT!1U9yN#S+wLRa+_SvVMiex&lhJ2YkM}9!AWA6M{sDXM&V7+K7=FI!x0Orhtm@DU4 zg=mU%DAaOR1RR3~&f`VlnG24$=jD@OSlID?_lJ6CW6|-L5ry}mN^rsP_R?XCgI(ohlt`3T3H#egUXb zP!P-_%KytU``-MAn{xHcKI|UVu>>cH@_+WuKFa@@+eUwp25u7#%;NKGqu>AS#x*u4vfrkTL1t6 delta 335 zcmZoEXbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~3gIoZI3MH0wo&;nvcpg4nPPJVJy zPJR+d(Plx$Z)}ql7}suQ=V0Mr)SAqr;K&@)_HJ^3f&|;`cl%hbFQ1&JAO&OgDM&M= zDNJ6i;LpVDGWm}}4x_;2OvTAerwbZPVn2zBcQ;@ky4zTla!yI zGdWRGe6pLQ;$$_cxXBLEypt8B)?FtG2py$3b pPv$o item.id); } + if (!draft.selected.homeworkRecommended.length) { + draft.selected.homeworkRecommended = homeworkSplit.recommended.map((item) => item.id); + } } rerenderCurrentCard(); return; @@ -240,6 +244,9 @@ function sectionToSelectionKey(section) { keyPoints: "keyPoints", recommended: "recommended", additional: "additional", + homeworkRecommended: "homeworkRecommended", + homeworkAdditional: "homeworkAdditional", + homework: "homework", }; return map[section] || ""; } diff --git a/app/preview-card-render.js b/app/preview-card-render.js index 60ac39d..1317765 100644 --- a/app/preview-card-render.js +++ b/app/preview-card-render.js @@ -56,6 +56,26 @@ function buildPreviewCard(card, selection, layout) { } } + if (selection.sections.homeworkRecommended) { + const homeworkRecommended = getSelectedSourceItemsForPreview(card, selection, "homeworkRecommended"); + if (homeworkRecommended.length) { + sectionHtml.push(`
Homework Recommended
`); + homeworkRecommended.forEach((sourceItem) => { + sectionHtml.push(renderPreviewSourceItem(card, selection, sourceItem, "homeworkRecommended")); + }); + } + } + + if (selection.sections.homeworkAdditional) { + const homeworkAdditional = getSelectedSourceItemsForPreview(card, selection, "homeworkAdditional"); + if (homeworkAdditional.length) { + sectionHtml.push(`
Homework Additional
`); + homeworkAdditional.forEach((sourceItem) => { + sectionHtml.push(renderPreviewSourceItem(card, selection, sourceItem, "homeworkAdditional")); + }); + } + } + if (!sectionHtml.length) { sectionHtml.push(`

No selected details for this topic.

`); } @@ -135,9 +155,15 @@ function renderPreviewItemActions(cardId, itemType, itemId, sectionKey) { } function getSelectedSourceItemsForPreview(card, selection, sectionKey) { - const split = getSourceSplit(card); const selectedIds = new Set(selection.selected[sectionKey] || []); - const bucket = sectionKey === "recommended" ? split.recommended : split.additional; + let bucket = []; + if (sectionKey === "recommended" || sectionKey === "additional") { + const split = getSourceSplit(card); + bucket = sectionKey === "recommended" ? split.recommended : split.additional; + } else if (sectionKey === "homeworkRecommended" || sectionKey === "homeworkAdditional") { + const split = getHomeworkSplit(card); + bucket = sectionKey === "homeworkRecommended" ? split.recommended : split.additional; + } return bucket.filter((item) => selectedIds.has(item.id)); } diff --git a/app/preview-editing.js b/app/preview-editing.js index 0e342c9..0643f3b 100644 --- a/app/preview-editing.js +++ b/app/preview-editing.js @@ -126,6 +126,9 @@ function deletePreviewItem(cardId, itemType, itemId, section) { } else if (itemType === "sourceItem") { selection.selected.recommended = (selection.selected.recommended || []).filter((id) => id !== itemId); selection.selected.additional = (selection.selected.additional || []).filter((id) => id !== itemId); + selection.selected.homeworkRecommended = (selection.selected.homeworkRecommended || []).filter((id) => id !== itemId); + selection.selected.homeworkAdditional = (selection.selected.homeworkAdditional || []).filter((id) => id !== itemId); + selection.selected.homework = (selection.selected.homework || []).filter((id) => id !== itemId); delete selection.overrides.sources[itemId]; } @@ -316,7 +319,8 @@ function findKeyPointDetail(card, detailId) { function findSourceItem(card, itemId) { const split = getSourceSplit(card); - return [...split.recommended, ...split.additional].find((item) => item.id === itemId) || null; + const homeworkSplit = getHomeworkSplit(card); + return [...split.recommended, ...split.additional, ...homeworkSplit.recommended, ...homeworkSplit.additional].find((item) => item.id === itemId) || null; } function sourceItemToEditableText(sourceItem) { @@ -336,6 +340,9 @@ function sourceItemToEditableText(sourceItem) { lines.push(sourceItem.item.question || ""); lines.push(sourceItem.item.code_examples?.map((example) => example.code || "").join("\n\n") || ""); } else { + if (sourceItem.sourceType === "homework" && sourceItem.item.source_origin) { + lines.push(`# source: ${sourceItem.item.source_origin}`); + } lines.push(sourceItem.item.source || ""); lines.push((sourceItem.item.outputs || []).join("\n")); } diff --git a/app/render-card-sections.js b/app/render-card-sections.js index b2cc1d8..c4a13e6 100644 --- a/app/render-card-sections.js +++ b/app/render-card-sections.js @@ -44,6 +44,7 @@ function renderTopicCard(card, draft) { .map(([exam, count]) => `${formatExamLabel(exam)} × ${count}`) .join(", "); const split = getSourceSplit(card); + const homeworkSplit = getHomeworkSplit(card); const keyPointGroupsForCard = keyPointGroups(card); const keyPointItemCount = keyPointGroupsForCard.reduce((count, group) => count + 1 + group.details.length, 0); const examSourceInfo = examSources || "No detailed exam-source mapping is available yet."; @@ -90,6 +91,8 @@ function renderTopicCard(card, draft) { ${renderSectionToggle("aiExamples", "Code Examples", usefulAIExamples(card).length, draft.sections.aiExamples, true)} ${renderSectionToggle("recommended", "Recommended for Cheat Sheet", split.recommended.length, draft.sections.recommended)} ${renderSectionToggle("additional", "Additional Snippets", split.additional.length, draft.sections.additional)} + ${renderSectionToggle("homeworkRecommended", "Homework Recommended", homeworkSplit.recommended.length, draft.sections.homeworkRecommended)} + ${renderSectionToggle("homeworkAdditional", "Homework Additional", homeworkSplit.additional.length, draft.sections.homeworkAdditional)}