Notechondria

Version: 0.1.43 Build Date: 2026-04-19T14:00

What's Changed

Backend: split notes mega-app into courses + planner + notes

The notes app had accumulated 16 models spanning four unrelated concerns (course catalogue, planner/heatmap/calendar, note storage, and recycle-bin plumbing). This round carves out two new Django apps with real tables so each domain owns its schema, admin, and migrations cleanly.

Apps reorganized

New apps:

  • backend/courses/Course, CourseMedia, CourseSubscription, CourseOperationLog, CourseOperationTypeChoices. Plus the course_cover_path and course_media_path upload-path callables.
  • backend/planner/PlannerEvent, HeatmapActivity, HeatmapActivityTypeChoices, CalendarFeed.

Still in notes:

  • Note, NoteVersion, NoteBlock, NoteAttachment, NoteIndex, NoteActivitySession, RecycleBinEntry, Tag, ValidationRecord.
  • NoteBlockTypeChoices, RecycleBinItemTypeChoices.

Cross-app FKs are string-ref'd ("courses.Course", "notes.Note") so the model files don't need circular imports.

Migrations (full physical table rename)

Six new migrations land in three apps, carefully ordered so state and schema stay in lockstep:

  1. courses/0001_initial.pySeparateDatabaseAndState: state declares the four Course models with db_table="notes_<model>", DB operations are empty. At this point the tables physically remain under the old names but Django considers them owned by courses.
  2. planner/0001_initial.py — same pattern for PlannerEvent / HeatmapActivity / CalendarFeed, all with db_table="notes_<model>".
  3. notes/0016_remove_moved_models_from_notes_state.py — state-only: DeleteModel for all seven moved models and an AlterField that re-points Note.course_id from notes.course to courses.course. No DB writes.
  4. courses/0002_rename_tables.py — physically renames notes_coursecourses_course (and the three sibling tables) via AlterModelTable.
  5. planner/0002_rename_tables.py — physically renames notes_plannereventplanner_plannerevent (and the two sibling tables).
  6. courses/0003_normalize_table_names.py
    • planner/0003_normalize_table_names.py — drop the explicit db_table metadata so the state matches Django's default naming (same physical table name). Without these, makemigrations --dry-run reports a stale "rename table to (default)" every time.

Rollback-safe: every step is reversible via the inverse operation (AlterModelTable both ways, state-only deletes restore the old state on rollback).

Historic-migration compatibility shim

notes/models.py re-exports course_cover_path and course_media_path from courses.models so the pre-split migrations (0005, 0006, 0007, 0009) that reference notes.models.course_cover_path keep loading verbatim. No rewriting of historic migration files.

Import updates

All production-code references to moved models switched to their new homes:

creators/api.py and scripts/validate_course_template.py needed no changes — they only import from notes.services (still valid) and don't touch moved models directly.

No API URL changes — the refactor is purely internal. Frontend clients see no difference.

Files Changed

New

  • docs/versions/0.1.43.md (this file).
  • backend/courses/__init__.py, backend/courses/apps.py, backend/courses/models.py, backend/courses/admin.py, backend/courses/migrations/__init__.py, backend/courses/migrations/0001_initial.py, backend/courses/migrations/0002_rename_tables.py, backend/courses/migrations/0003_normalize_table_names.py.
  • backend/planner/__init__.py, backend/planner/apps.py, backend/planner/models.py, backend/planner/admin.py, backend/planner/migrations/__init__.py, backend/planner/migrations/0001_initial.py, backend/planner/migrations/0002_rename_tables.py, backend/planner/migrations/0003_normalize_table_names.py.
  • backend/notes/migrations/0016_remove_moved_models_from_notes_state.py.

Modified

  • VERSION: 0.1.42 → 0.1.43.
  • backend/notechondria/settings.py: added 'courses' and 'planner' to INSTALLED_APPS, ordered so FKs resolve cleanly (courses before planner before notes).
  • backend/notes/models.py: Course/CourseMedia/CourseSubscription/ CourseOperationLog/PlannerEvent/HeatmapActivity/CalendarFeed removed; Note.course_id re-pointed to "courses.Course" string ref; course_cover_path / course_media_path re-exported from courses.models for historic-migration compatibility.
  • backend/notes/admin.py: trimmed to note-only admin classes.
  • backend/notes/api.py, backend/notes/services.py, backend/notes/tests.py, backend/notes/management/commands/bootstrap_platform.py, backend/mcp/tools.py, backend/mcp/tests.py: imports updated to pull from new apps.

Verification

  • python manage.py check (settings_test) — 0 issues.
  • python manage.py migrate on a fresh SQLite DB — all 43 migrations apply cleanly, including the six new ones in the correct interleaved order.
  • python manage.py test notes creators mcp — all 118 tests pass.
  • python manage.py makemigrations --dry-run — only a pre-existing unrelated drift remains (alter_noteattachment_id — AutoField → BigAutoField carried over from 0.1.37). No drift from this refactor.

Notes / follow-ups

  • Migration order on production: the six migrations must apply in one migrate run so the rename + state-delete interleaves cleanly. Don't attempt a partial rollout of just courses without planner — the notes.0016 state-delete depends on both courses.0001 and planner.0001.
  • NoteAttachment id drift (AutoFieldBigAutoField) still surfaces in makemigrations --dry-run. Fold into a later round alongside any other id type cleanups.
  • Recycle bin + activity sessions still live in notes. A future round could extract them into their own activity or recycle_bin apps, but neither has enough surface area right now to justify the split — RecycleBinEntry is 1 model, NoteActivitySession is 1 model, both tightly coupled to Note.
  • Admin grouping: Django admin now shows three top-level sections (Notes, Courses, Planner) instead of the former single Notes section. This improves operator navigation and matches the user's feedback about "data structure not split cleanly by function."