Commit Graph

14 Commits

Author SHA1 Message Date
Jake Turner 0e78200653
chore(collections): update stale urls (#1148) 2026-08-04 03:39:50 +00:00
caweis 762a4a12ad
Add offline FDA drug reference (labels, interaction view, conditions, remedies) (#1040)
* feat(drug-reference): offline FDA drug labels, conditions, and remedies

Adds an offline medical-reference feature with three coupled layers:

- Drug Reference: full-text search over openFDA drug-label indications,
  a detail page per label, and a side-by-side single-drug comparison view.
  A two-phase background pipeline downloads the openFDA label parts to the
  storage volume (resumable) and ingests them into the search table.
- Conditions ("When to use what"): a curated spine of first-aid situations
  that maps each situation to matching OTC drugs, each linking back to its
  Drug Reference detail page.
- Curated remedies: hand-authored natural and home-remedy entries drawn from
  US-government public-domain sources (NCCIH, CDC, MedlinePlus, FDA), shown
  with their source links and the same safety disclaimers as the rest of the
  feature.

The drug-reference and conditions layers are intentionally coupled: the drug
detail page shows the situations a drug treats, and the conditions controller
reads the same drug_labels table.

Safety surfaces ship as written. The amber SafetyBanner ("informational only,
not medical advice, not an FDA endorsement, not a drug-interaction checker, in
an emergency call emergency services") renders on the condition pages and the
search page; the detail and comparison pages carry their own "not a cross-drug
interaction checker" callout; and every page carries the openFDA CC0 source
citation and no-FDA-affiliation footer.

Wiring on this branch:
- start/routes.ts: the /drug-reference and /conditions page GETs plus their
  /api/* groups.
- commands/queue/work.ts: the drug-download and drug-ingest queues, both at
  concurrency 1. The two drug queues get a per-queue stall override
  (lockDuration 1_800_000, maxStalledCount 3) because each part is one long
  stream; every other queue keeps the existing 300000 default.
- inertia/pages/home.tsx: Drug Reference and "When to use what" tiles. The
  icon and display_order are a starting point, open to change.
- types/kv_store.ts: the two drugReference.* keys the pipeline reads and writes.
- package.json: yauzl and stream-json (plus their @types), used by the ingest
  job to stream the label JSON out of the downloaded zips.

The app reads the conditions and remedy data from the compiled TS constants in
app/data/; the repo-root collections/*.json files are the browseable mirrors.
The natural-remedies standalone test reads collections/natural_remedies.json to
assert the two stay in sync, so that file is also a test fixture.

The four standalone tests pass (drug_interactions, drug_ingest_status,
conditions, natural_remedies). tsc reports no errors in the feature code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(drug-reference): drop a fork-internal comment reference

* fix(drug-reference): address review — defer FULLTEXT, fallback remedies prop, strip fork refs

- migration: wrap the FULLTEXT ALTER in this.defer so it runs after the
  deferred createTable (was silently swallowed, index never created)
- controller: add remedies:[] to the index() error fallback (required prop)
- strip fork-internal issue/spec references from ported comments
- tsconfig: exclude tests/standalone (node --experimental-strip-types only)
- correct the varchar(768) byte-math comment; extend remedy-spine test

* fix(drug-reference): make the interaction comparison readable at five drugs

The comparison view laid its columns out on an equal-fraction CSS grid
(repeat(N, minmax(0, 1fr))), so each added drug shrank every column; at the
five-drug maximum the FDA interaction text was squeezed into unreadable slivers.

Lay the columns out with flex instead: full-width and stacked on phones, then
fixed-width columns that scroll sideways from the sm: breakpoint up, so they
never shrink below a readable width. Theme the columns with the same palette as
the rest of the page (they were on stock gray), and give the headers a fixed
min-height so columns line up when drug names wrap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(collections): route a 'dataset' tier resource to the drug pipeline

Add an optional `type` discriminator to SpecResource ('zim' | 'dataset',
absent == 'zim'), so the tier installer can carry a DB-ingested resource
alongside ZIM files. ZimService.downloadCategoryTier branches on it: a
'dataset' resource dispatches the existing FDA download+ingest pipeline
instead of RunDownloadJob, guarded against duplicate dispatch by the drug
ingest status. Every existing manifest entry has no `type` and keeps the
exact ZIM path.

Widen InstalledResource.resource_type to include 'dataset' and exclude
dataset rows from the ZIM/map catalog-update scan (datasets aren't
filename-versioned; their freshness path is separate). No dataset rows are
written yet: the InstalledResource 'dataset' row on ingest-ready, the
manifest entry, install-gating, and the downloads-aggregator integration
are follow-up commits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(drug-reference): rework into an opt-in medicine-standard tier dataset

Reshapes the offline FDA drug reference from a built-in feature into curated
content installed by selecting the Medicine / Standard tier, per maintainer
direction.

- install-state: the ingest writes an installed_resources 'dataset' row on
  ready (version = the openFDA export_date), threaded installer to download to
  ingest; the tier-status math and the home-tile gate read it. Manual ingests
  write no row, so install-state stays tied to the curated path.
- manifest: declare the dataset in the medicine-standard tier (runtime fetches
  the remote manifest, so this also needs to land upstream).
- install-gating: the drug-reference home tiles render only when installed.
- uninstall: DrugReferenceService.uninstall() stops the two drug queues, deletes
  the on-disk parts, truncates drug_labels (schema kept), clears the KV markers,
  and drops the install row. Best-effort, logged, scoped to drug data only.
- downloads: the download phase reports the canonical {percent, downloadedBytes,
  totalBytes} shape as one drug-data card in the Active Downloads aggregator with
  cancel/remove; the heavy ingest stays in the IngestStatus surface with an
  Indexing handoff on the card.
- auto-update: a daily DrugAutoUpdateJob compares the manifest export_date and
  re-downloads when newer, gated on installed + no active job.

typecheck clean; the drug standalone suites pass. Three points are flagged in
code for the maintainer: the InstalledResource 'dataset' approach, the tier
home, and the export_date string format.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(downloads): keep the drug download card live across all parts

The Active Downloads card filtered to the deterministic jobId, but the
download's continuations run under auto-generated jobIds (only part 0 uses the
deterministic one). So the card tracked part 0 and then vanished while parts
2..N kept downloading. The queue is concurrency 1, so collapse to whichever
single part is in flight and report the deterministic jobId: one card tracks
aggregate progress through the whole download and cancel/remove still routes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(drug-reference): co-locate a persistent safety note with affirmative remedy guidance

Add RemedySafetyNote at the head of every natural-remedy section — the two on
Drug Reference and the one on "When to use what" — so the "informational only,
not medical advice, seek real medical care in an emergency" framing appears with
the guidance itself, not only in the page-top banner. Replaces the terse
per-section caveat with the same amber alert language as SafetyBanner.

Addresses the upstream #1040 review request that the disclaimer be unmistakable
and present wherever affirmative self-care guidance appears, not a one-time banner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(drug-reference): fold dataset freshness into the content-auto-update path

The drug dataset auto-updated on its own daily cron that ignored the
contentAutoUpdate.* master switch, so it would refresh even with content
auto-update turned off, and it didn't ride the content-update path the way
ZIMs and maps do.

Move the export_date freshness/apply orchestration onto
DrugReferenceService.attemptAutoUpdate(), add
ContentAutoUpdateService.attemptDrugDataset() gated on the same enabled +
window config, and have the hourly ContentAutoUpdateJob drive both. Retire
the standalone DrugAutoUpdateJob. The ZIM/map attempt() path is unchanged.

Addresses the upstream #1040 request to wire the openFDA export_date check
into the content updater so the dataset updates alongside ZIMs and maps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(drug-reference): strengthen the remedy note to consult a clinician before combining with meds

Widen the affirmative-remedy safety note from "talk to a clinician before use"
to explicitly cover using a remedy AND combining one with a medication the user
already takes — the interaction case is the higher-risk path for an off-grid
user self-treating.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(drug-reference): gate affirmative remedy content behind an off-by-default flag

Add drugReference.remediesEnabled (default off), independent of the tier
install. When off, the server emits no remedy data at any boundary — the
drug-reference page prop, conditions show, and the /api/conditions/drugs
situation search — and the "Natural" filter is hidden, so installing the
medicine-standard tier lights up the verbatim FDA label search and the
condition-to-OTC matching but not the hand-authored self-care and herbal
sections. No user-facing toggle: it is flipped on after a clinician content-pass.

Implements the upstream #1040 split-by-risk request: the regulated label content
ships with the tier; the authored remedy guidance stays gated until sign-off.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jake Turner <52841588+jakeaturner@users.noreply.github.com>
2026-08-04 03:38:33 +00:00
Chris Sherwood c693cd5cd8 content(collections): rebalance Survival & Preparedness and fix a broken URL
The category was 10.8 GB of which 89% was YouTube channel archives. Nothing
in Essential or Standard could be read without watching a video, which is
the wrong shape for the situations this content is for: low power, low
bandwidth, or just looking something up quickly.

Fixes a live 404. canadian_prepper_preppingfood_en_2025-09.zim no longer
exists upstream, so anyone installing the Comprehensive tier today hits a
broken download (#1171). openZIM renamed it, and because resource_id is
derived from the on-disk filename by parseZimFilename(), the id has to
change with it or the installed file would never match its catalog entry.

Adds 5 GB of searchable text across the three tiers:

  Essential      water treatment, food preparation, knots (144 MB total)
  Standard       Ready.gov, outdoors Q&A, amateur radio Q&A
  Comprehensive  post-disaster library, Hundred Rabbits

Amateur radio in particular filled a gap: communications is a core
preparedness topic and the category had nothing on it.

Text share goes from 11% to 36%. Essential grows by 144 MB, which is 6%,
and stops being video-only.

Deliberately not included: gardening.stackexchange and Gutenberg Agriculture
are already curated under Agriculture & Food, and survivorlibrary.com is the
single most on-mission corpus in the Kiwix library but is 252 GB.

All URLs verified with range requests; all ids verified to match their
filename base.

Refs #1171, #1149

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 12:30:09 -07:00
Chris Sherwood 60f3dd57c0 content(collections): add CD3WD to Survival & Preparedness comprehensive
CD3WD is a compilation of appropriate-technology and development
literature: food production and storage, water and sanitation,
construction, health, and village-scale manufacturing without
industrial supply chains. Requested in #1149.

The survival category was until now almost entirely YouTube channel
archives, with Project Gutenberg military science as the only text
reference. CD3WD adds a substantial practical reference at 581 MB,
which is the smallest resource in the category by a wide margin.

Verified live: HTTP 206 on a range request, 581,165,229 bytes.

Refs #1149

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 12:30:09 -07:00
builder555 4443799cc9 fix(Collections): update ZIM files to latest versions (#332)
* fix: update data sources to newer versions
* fix: bump spec version for wikipedia
2026-03-25 16:30:35 -07:00
Chris Sherwood 5b990b7323 fix(collections): update stale React devdocs ZIM URL
Kiwix skipped the January 2026 build of devdocs_en_react — the
2026-01 URL returns 404. Updated to 2026-02 which exists.

Closes #269

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 11:46:10 -07:00
Chris Sherwood 9422c76bc6 feat(collections): add Project Gutenberg ZIMs and fix broken education entry
Add Project Gutenberg books from the Library of Congress Classification
to relevant curated collection categories:

- Agriculture Comprehensive: Gutenberg Agriculture (LCC-S, 4.3 GB) —
  classic texts on farming, animal husbandry, and food preservation
- Survival Comprehensive: Gutenberg Military Science (LCC-U, 1.2 GB) —
  classic military strategy, tactics, and field manuals

Remove broken gutenberg_en_education entry from Education Standard tier.
The URL returned 404 — Kiwix only publishes LCC-coded Gutenberg ZIMs,
not topic-named ones. The pre-1928 educational philosophy texts were
also not practical enough for NOMAD's audience.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:08:09 -07:00
Jake Turner 32d206cfd7
feat: curated content system overhaul 2026-02-11 15:44:46 -08:00
Chris Sherwood 569dae057d fix(collections): correct devdocs ZIM filenames in Computing & Technology
Kiwix renamed devdocs files from `devdocs.io_en_*` to `devdocs_en_*`.
The old URLs returned 404, causing all Computing & Technology downloads
to silently fail during Easy Setup. Affects 9 resources across all 3
tiers (Python, JavaScript, HTML, CSS, Node.js, React, Git, Docker,
Linux/Bash docs).

Relates to #127

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 13:21:26 -08:00
Chris Sherwood 68f374e3a8 feat: Add dedicated Wikipedia Selector with smart package management
Adds a standalone Wikipedia selection section that appears prominently in both
the Easy Setup Wizard and Content Explorer. Features include:

- Six Wikipedia package options ranging from Quick Reference (313MB) to Complete
  Wikipedia with Full Media (99.6GB)
- Card-based radio selection UI with clear size indicators
- Smart replacement: downloads new package before deleting old one
- Status tracking: shows Installed, Selected, or Downloading badges
- "No Wikipedia" option for users who want to skip or remove Wikipedia

Technical changes:
- New wikipedia_selections database table and model
- New /api/zim/wikipedia and /api/zim/wikipedia/select endpoints
- WikipediaSelector component with consistent styling
- Integration with existing download queue system
- Callback updates status to 'installed' on successful download
- Wikipedia removed from tiered category system to avoid duplication

UI improvements:
- Added section dividers and icons (AI Models, Wikipedia, Additional Content)
- Consistent spacing between major sections in Easy Setup Wizard
- Content Explorer gets matching Wikipedia section with submit button

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:00:51 -08:00
Chris Sherwood 2921017191 feat(collections): Expand curated categories and improve tier modal UX
- Add 3 new curated categories: DIY & Repair, Agriculture & Food, Computing & Technology
- Reorganize content logically (moved DIY/food content from Survival to appropriate new categories)
- Update tier selection modal to show only each tier's own resources
- Add "(plus everything in X)" text for inherited tier content
- Reduces visual redundancy and makes tiers easier to compare

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 13:47:01 -08:00
Chris Sherwood 05441bd6a2 feat: Add Education & Reference category with 3 tiers
Essential (~5 GB):
- Wikipedia Top 45k articles (no images)
- Wikibooks (no images)

Standard (~19 GB, includes Essential):
- TED-Ed educational videos
- Wikiversity tutorials
- LibreTexts STEM (Math, Physics, Chemistry, Biology)
- Project Gutenberg Education

Comprehensive (~59 GB, includes Standard):
- Full Wikipedia (6M+ articles, no images)
- Wikibooks with images
- TED Conference talks
- LibreTexts Humanities, Engineering, Geosciences, Business

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:15:20 -08:00
Chris Sherwood b007f5e0fe feat: Add Survival & Preparedness category with 3 tiers
Essential (~138 MB):
- Food for Preppers, FOSS Cooking, Based.Cooking

Standard (~3.6 GB, includes Essential):
- Canadian Prepper: Winter Prepping & Bug Out Roll
- Gardening Q&A, Cooking Q&A

Comprehensive (~21 GB, includes Standard):
- Urban Prepper, Canadian Prepper: Prepping Food & Bug Out Concepts
- Learning Self-Reliance, iFixit Repair Guides, DIY Q&A

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:15:20 -08:00
Chris Sherwood 3cb5dceb1d feat: Add tiered collection categories UI
- Add kiwix-categories.json with Medicine category and 3 tiers
- Create CategoryCard component for displaying category cards
- Create TierSelectionModal for tier selection UI
- Integrate categories into Easy Setup wizard (Step 3)
- Add TypeScript types for categories and tiers
- Fallback to legacy flat collections if categories unavailable

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:15:20 -08:00