Files
PTSRaiAgent/scripts/build_meds_reference.py
Arxip222andClaude Opus 5.5 464b474abd feat(web): questionnaires, diary and medication module in the patient app
- Focus screen renders versioned questionnaire items (iq: buttons): section chip, position
  (adaptive forms without "of N"), recall date, pause / help-now actions, optional skip,
  sleep-hours input, 0-10 scale anchors, RUS-AUDIT item 2 standard-drink table.
- Paused / held-for-review questionnaires get their own screen.
- Today: intake and diary tasks, review notice, therapy-day card on days 1/7/14.
- Drug card /meds/:ref from data/meds/meds_reference.json (draft built from the Minzdrav
  guidelines: depression 2024, PTSD 2023) — expected effect, course, side effects, red flags;
  no doses shown. "Report a side effect" always available (med:side_effects:<ref>).
- API: /instruments/catalog, /meds/reference; today gains questionnaire state, reference id,
  indication and therapy day; demo persona seeds the dashboard dose history.
- INTAKE_DEMO=1 (demo clock only) opens the questionnaires on synthetic data without touching
  the clinical release gate in config/intake_runtime.yaml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 08:53:13 +03:00

543 lines
21 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Builds data/meds/meds_reference.json — the drug reference of the medication module.
Source of every value: the two Minzdrav clinical guidelines in config/rag/corpus/ —
* «Депрессивный эпизод, рекуррентное депрессивное расстройство» (2024): прил. А3 табл. 2 (дозы),
табл. 3 (профили побочных эффектов), разд. 3.1–3.6, прил. Б5, В;
* «Посттравматическое стрессовое расстройство» (2023, ID 753): разд. 3.2, прил. А3 (дозы).
Patient side-effect items: effects rated «++»/«+++» in табл. 3 (antidepressants) or named in the КР text
(other drugs), worded for a patient. Nothing here is a recommendation: the app only follows the doctor's
prescription (module spec, «Границы»). Texts and draft parameters must be reviewed by a psychiatrist.
Run: uv run python -m scripts.build_meds_reference
"""
import json
from pathlib import Path
OUT = Path(__file__).resolve().parents[1] / "data" / "meds" / "meds_reference.json"
DEP = "КР депрессия 2024"
PTSD = "КР ПТСР 2023 (ID 753)"
# Patient wording of the КР табл. 3 columns (and of effects named in the КР text).
SE = {
"anticholinergic": "Сухость во рту или запоры",
"nausea": "Тошнота",
"sedation": "Сонливость днём",
"insomnia": "Бессонница или беспокойство",
"sexual": "Сексуальные проблемы",
"orthostatic": "Головокружение при вставании",
"weight": "Прибавка веса",
"headache": "Головная боль",
"dizziness": "Головокружение",
"coordination": "Нарушение координации",
"memory": "Снижение внимания и памяти",
"akathisia": "Беспокойство, невозможность усидеть на месте",
"eps": "Скованность или дрожь в теле",
"skin": "Кожная сыпь или зуд",
}
RF_BLEEDING = "Необычные кровотечения или синяки"
RF_EARLY = "Мысли о самоубийстве или резкое усиление тревоги в первые недели приёма"
RF_JAUNDICE = "Желтушность кожи или глаз"
RF_RASH = "Кожная сыпь"
RF_PRIAPISM = "Длительная болезненная эрекция"
RF_UTERINE = "Маточное кровотечение"
TCA_MON = "Артериальное давление и ЭКГ"
AP_MON = "Вес и признаки метаболического синдрома"
def ad(id_, name, cls, start, lo, hi, items, *, ptsd=None, red=(), mon=(), withdrawal=None, overdose=False, note=None):
"""Antidepressant: doses from КР депрессия табл. 2, side effects from табл. 3."""
d = {
"id": id_,
"name_ru": name,
"group": "Антидепрессант",
"class": cls,
"indications": {
"depression": {
"start_mg": start,
"min_mg": lo,
"max_mg": hi,
"off_label": False,
"expected_response": "3–4 недели",
"course": "После улучшения лечение продолжают в той же дозе ещё 4–6 месяцев",
"source": f"{DEP}, прил. А3 табл. 2; разд. 3.1",
}
},
"diary_items": [SE[i] for i in items],
"diary_items_source": f"{DEP}, прил. А3 табл. 3 (++/+++)",
"red_flags": [RF_EARLY, *red],
"monitoring": list(mon),
"withdrawal_risk": withdrawal,
"overdose_danger": overdose,
"max_course_days": None,
}
if ptsd:
d["indications"]["ptsd"] = ptsd
if note:
d["note"] = note
return d
def ptsd_ind(lo, hi, off_label, start=None):
return {
"start_mg": start,
"min_mg": lo,
"max_mg": hi,
"off_label": off_label,
"expected_response": "4–6 недель",
"course": "Обычно 6–12 месяцев после стабилизации",
"source": f"{PTSD}, разд. 3.2; прил. А3",
}
def other(
id_, name, cls, lo, hi, off_label, items, *, red=(), mon=(), withdrawal=None, course_days=None, src, note=None
):
"""Non-antidepressant used for PTSD: doses from КР ПТСР прил. А3 / разд. 3.2, effects named in the text."""
ind = ptsd_ind(lo, hi, off_label)
ind["expected_response"] = None
ind["course"] = None
d = {
"id": id_,
"name_ru": name,
"group": next(g for key, g in GROUPS.items() if key in cls),
"class": cls,
"indications": {"ptsd": ind},
"diary_items": [SE[i] for i in items],
"diary_items_source": src,
"red_flags": list(red),
"monitoring": list(mon),
"withdrawal_risk": withdrawal,
"overdose_danger": False,
"max_course_days": course_days,
}
if note:
d["note"] = note
return d
# Patient-facing drug group by the КР class wording.
GROUPS = {
"бензодиазепин": "Анксиолитик (транквилизатор)",
"Анксиолитик": "Анксиолитик",
"Антипсихотическое": "Антипсихотик",
"Противоэпилептический": "Противоэпилептический препарат",
}
SSRI = ["nausea", "insomnia", "sexual", "headache"] # табл. 3 + головная боль из разд. 3.1 (СИОЗС)
BENZO = ["sedation", "dizziness", "coordination", "memory"] # КР ПТСР 3.2: седация, головокружение, координация…
BENZO_SRC = f"{PTSD}, разд. 3.2 (комментарии о производных бензодиазепина)"
AP_SRC = f"{PTSD}, разд. 3.2: акатизия, экстрапирамидные расстройства, метаболический синдром"
DRUGS = [
ad(
"paroxetine",
"Пароксетин",
"СИОЗС",
20,
20,
50,
SSRI,
ptsd=ptsd_ind(20, 50, False),
red=[RF_BLEEDING],
withdrawal="high",
),
ad("sertraline", "Сертралин", "СИОЗС", 50, 50, 200, SSRI, ptsd=ptsd_ind(25, 200, False), red=[RF_BLEEDING]),
ad(
"fluvoxamine",
"Флувоксамин",
"СИОЗС",
50,
100,
300,
SSRI,
red=[RF_BLEEDING],
note="Стартовая доза по табл. 2: 50–100 мг",
),
ad(
"fluoxetine",
"Флуоксетин",
"СИОЗС",
20,
20,
80,
SSRI,
ptsd=ptsd_ind(20, 40, True),
red=[RF_BLEEDING],
withdrawal="low",
),
ad("citalopram", "Циталопрам", "СИОЗС", 20, 20, 40, SSRI, red=[RF_BLEEDING]),
ad("escitalopram", "Эсциталопрам", "СИОЗС", 10, 10, 20, SSRI, red=[RF_BLEEDING]),
ad(
"venlafaxine",
"Венлафаксин",
"СИОЗСН",
75,
75,
375,
["nausea", "insomnia", "sexual"],
ptsd=ptsd_ind(75, 225, True),
mon=["Артериальное давление"],
withdrawal="high",
note="Депрессия: >225 мг/сут — в стационарных условиях",
),
ad("duloxetine", "Дулоксетин", "СИОЗСН", 60, 60, 120, ["nausea", "insomnia"]),
ad(
"amitriptyline",
"Амитриптилин",
"ТЦА",
50,
150,
300,
["anticholinergic", "sedation", "orthostatic", "weight"],
ptsd=ptsd_ind(75, 150, True),
mon=[TCA_MON],
withdrawal="high",
overdose=True,
note="Депрессия: старт 50–75 мг; >150 мг/сут — в стационарных условиях",
),
ad(
"imipramine",
"Имипрамин",
"ТЦА",
25,
150,
300,
["anticholinergic", "insomnia", "orthostatic", "weight"],
ptsd=ptsd_ind(75, 250, True),
mon=[TCA_MON],
withdrawal="high",
overdose=True,
note="Депрессия: старт 25–75 мг; >200 мг/сут — в стационарных условиях",
),
ad(
"clomipramine",
"Кломипрамин",
"ТЦА",
50,
100,
250,
["anticholinergic", "sexual", "orthostatic", "weight"],
mon=[TCA_MON],
withdrawal="high",
overdose=True,
note="Старт 50–75 мг; летальность при передозировке средняя",
),
ad(
"pipofezine",
"Пипофезин",
"ТЦА (АТХ — другие антидепрессанты)",
25,
150,
500,
[],
note="В табл. 3 КР не описан — побочные эффекты уточнять у врача; старт 25–50 мг",
),
ad("mianserin", "Миансерин", "НаССА", 30, 60, 90, ["sedation"], mon=["Формула крови (по КР — изменения редко)"]),
ad(
"mirtazapine",
"Миртазапин",
"НаССА",
15,
15,
45,
["sedation", "weight"],
ptsd=ptsd_ind(30, 60, True),
note="Старт 15–30 мг",
),
ad(
"trazodone",
"Тразодон",
"Специфический серотонинергический АД",
100,
150,
600,
["sedation", "sexual"],
red=[RF_PRIAPISM],
note=">450 мг/сут — в стационарных условиях",
),
ad(
"pirlindole",
"Пирлиндол",
"Ингибитор МАО-А",
50,
150,
400,
[],
note="В табл. 3 КР не описан — побочные эффекты уточнять у врача; старт 50–75 мг",
),
ad(
"agomelatine",
"Агомелатин",
"Мелатонинергический АД",
25,
25,
50,
[],
red=[RF_JAUNDICE],
mon=["Билирубин, АСТ, АЛТ, щелочная фосфатаза — в начале лечения и при повышении дозы"],
),
ad("vortioxetine", "Вортиоксетин", "Серотонинергический модулятор", 10, 10, 20, ["nausea", "insomnia"]),
# КР ПТСР: анксиолитики, антипсихотические средства, противоэпилептические препараты
other(
"diazepam",
"Диазепам",
"Производное бензодиазепина",
5,
15,
False,
BENZO,
withdrawal="high",
course_days=28,
src=BENZO_SRC,
note="Курс не более 4 недель, в тяжёлых случаях до 8–12 недель",
),
other(
"lorazepam",
"Лоразепам",
"Производное бензодиазепина",
2,
10,
False,
BENZO,
withdrawal="high",
course_days=21,
src=BENZO_SRC,
note="Дозы — из текста разд. 3.2 (в табл. А3 не приведён)",
),
other(
"clonazepam",
"Клоназепам",
"Производное бензодиазепина",
1,
4,
False,
BENZO,
withdrawal="high",
course_days=21,
src=BENZO_SRC,
note="Дозы — из текста разд. 3.2 (в табл. А3 не приведён)",
),
other(
"alprazolam",
"Алпразолам",
"Производное бензодиазепина",
0.25,
4.5,
False,
BENZO,
withdrawal="high",
course_days=90,
src=BENZO_SRC,
note="Курс до 3 месяцев; отмена в течение 2–6 недель",
),
other(
"tofisopam",
"Тофизопам",
"Производное бензодиазепина (дневной транквилизатор)",
50,
300,
False,
[],
course_days=84,
src=f"{PTSD}, разд. 3.2",
note="По КР не вызывает физической зависимости и седации; курс 4–12 недель",
),
other(
"etifoxine",
"Этифоксин",
"Анксиолитик",
150,
200,
False,
["sedation", "skin"],
red=[RF_JAUNDICE, RF_UTERINE],
course_days=42,
src=f"{PTSD}, разд. 3.2",
note="Курс от нескольких дней до 4–6 недель",
),
other(
"hydroxyzine",
"Гидроксизин",
"Анксиолитик",
25,
100,
True,
["sedation"],
course_days=56,
src=f"{PTSD}, разд. 3.2",
note="Курс обычно 4–8 недель",
),
other(
"buspirone",
"Буспирон",
"Анксиолитик",
20,
30,
False,
[],
src=f"{PTSD}, разд. 3.2",
note="Эффект с 7–14 дня, максимум к 4-й неделе",
),
other(
"alimemazine",
"Алимемазин",
"Антипсихотическое средство",
15,
80,
False,
[],
mon=[AP_MON],
src=f"{PTSD}, разд. 3.2",
note="По КР побочные эффекты редки и слабо выражены",
),
other(
"risperidone",
"Рисперидон",
"Антипсихотическое средство",
0.5,
8,
True,
["akathisia", "eps", "weight"],
mon=[AP_MON],
src=AP_SRC,
),
other(
"quetiapine",
"Кветиапин",
"Антипсихотическое средство",
50,
300,
True,
["akathisia", "eps", "weight"],
mon=[AP_MON],
src=AP_SRC,
),
other(
"olanzapine",
"Оланзапин",
"Антипсихотическое средство",
5,
20,
True,
["weight", "sedation"],
mon=[AP_MON],
src=f"{DEP}, разд. 3.3 (увеличение веса, седация); {PTSD}, разд. 3.2",
),
other(
"tiapride", "Тиаприд", "Антипсихотическое средство", 200, 300, False, [], mon=[AP_MON], src=f"{PTSD}, разд. 3.2"
),
other(
"chlorprothixene",
"Хлорпротиксен",
"Антипсихотическое средство",
25,
150,
True,
[],
mon=[AP_MON],
src=f"{PTSD}, разд. 3.2",
note="Расхождение в КР ПТСР: в тексте 15–100 мг, в табл. А3 — 25–150 мг",
),
other(
"lamotrigine",
"Ламотриджин",
"Противоэпилептический препарат",
25,
500,
True,
[],
red=[RF_RASH],
src=f"{PTSD}, разд. 3.2",
note="Частые кожные высыпания вплоть до токсического эпидермального некролиза",
),
other("topiramate", "Топирамат", "Противоэпилептический препарат", 200, 400, True, [], src=f"{PTSD}, разд. 3.2"),
]
TEXTS = {
"expect_effect_depression": (
"Лекарства от депрессии действуют не сразу. Первые изменения иногда заметны уже через 1–2 недели, "
"а эффект врач оценивает через 3–4 недели. Принимайте препарат по схеме, даже если пока не чувствуете разницы."
),
"expect_effect_ptsd": (
"Эффект препарата при ПТСР развивается постепенно — его оценивают через 4–6 недель. В первую неделю тревога "
"может временно усилиться: это известная реакция, сообщите о ней врачу. Не прекращайте приём самостоятельно."
),
"dont_stop_alone": (
"Не прекращайте приём самостоятельно: резкая отмена может ухудшить самочувствие, а лечение — потерять эффект. "
"Если препарат мешает жить или вы в нём сомневаетесь, обсудите это с врачом — он может изменить дозу "
"или подобрать другой."
),
"why_continue_depression": (
"Даже когда становится лучше, лечение продолжают ещё несколько месяцев в той же дозе: так ниже риск, "
"что депрессия вернётся."
),
"withdrawal_missed": (
"Вы пропустили приём два дня подряд. При резком прекращении этого препарата бывают неприятные ощущения: "
"головокружение, раздражительность, нарушения сна, состояние как при гриппе. Вернитесь к обычной схеме "
"и сообщите врачу, если стало хуже."
),
"ran_out": (
"Лекарство закончилось? Не откладывайте — попросите у врача новый рецепт: "
"перерыв в приёме может ухудшить самочувствие."
),
"red_flag": "Этот симптом нужно обсудить с врачом сегодня. Если состояние тяжёлое — вызывайте скорую: 103 или 112.",
"side_effects_general": (
"Спасибо, что сообщили. Побочные эффекты часто ослабевают в первые недели. Если они мешают жить — это повод "
"обсудить с врачом дозу или замену препарата. Не меняйте схему сами."
),
"instruction_fallback": "Уточните у врача или в инструкции к препарату.",
}
RULES = [
{"id": "M1", "params": {"missed_days_in_row": 2}, "status": "черновик"},
{"id": "M2", "params": {}, "status": "черновик"},
{"id": "M3", "params": {"ran_out_days_in_row": 2}, "status": "черновик"},
{"id": "M4", "params": {"default_max_course_days": 21}, "status": "КР ПТСР 3.2"},
{
"id": "M5",
"params": {"check_day": 28, "baseline_lookback_days": 14, "phq9_min_drop_pct": 50, "pcl5_min_drop": 10},
"status": "черновик",
},
{"id": "M6", "params": {}, "status": "КР депрессия 3.1.3"},
{"id": "M7", "params": {}, "status": "черновик"},
{"id": "M8", "params": {"fibser_burden_min": 5, "side_effect_misses_in_7_days": 2}, "status": "черновик"},
{"id": "M9", "params": {}, "status": "в сводке врачу"},
{"id": "M10", "params": {}, "status": "приложения А3 обеих КР"},
]
def main() -> None:
OUT.parent.mkdir(parents=True, exist_ok=True)
doc = {
"version": "draft-2026-10-04",
"status": "черновик: собран по КР Минздрава, тексты и параметры вычитывает психиатр",
"sources": [
{
"id": "kr_depression_2024",
"title": "Депрессивный эпизод, рекуррентное депрессивное расстройство",
"year": 2024,
"file": "docs/source/rag/kr_depression.pdf",
},
{
"id": "kr_ptsd_2023",
"title": "Посттравматическое стрессовое расстройство",
"year": 2023,
"kr_id": 753,
"file": "docs/source/rag/kr_ptsd.pdf",
},
],
"drugs": DRUGS,
"texts": TEXTS,
"rules": RULES,
}
OUT.write_text(json.dumps(doc, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(f"{OUT}: {len(DRUGS)} drugs")
if __name__ == "__main__":
main()