Compare commits

...

2 Commits

Author SHA1 Message Date
admins afb2dc1b9e fix(dict): чистые service_id вместо Excel #REF! в справочнике
720 услуг имели #REF! в каноническом service_id (Excel-ошибка из исходного
справочника протекала в живой /search). Миграция scripts/fix_ref_ids.py
генерирует чистые id и каскадно обновляет price_item (service_id,
suggested_service_id) и learned_synonym. matched сохранён (11899), имена целы.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:37:14 +05:00
admins 3783f1be25 feat(web): кросс-ссылка на MedPrice в шапке (лого рядом через разделитель)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 10:22:09 +05:00
3 changed files with 55 additions and 2 deletions
+1
View File
@@ -34,3 +34,4 @@ extend-immutable-calls = ["fastapi.File", "fastapi.Query", "fastapi.Depends"]
# Разведочные скрипты этапа разбора форматов — черновики, не часть продукта. # Разведочные скрипты этапа разбора форматов — черновики, не часть продукта.
"scripts/proof/*" = ["E402", "E501", "E741", "B007"] "scripts/proof/*" = ["E402", "E501", "E741", "B007"]
"scripts/gen_fixtures.py" = ["E402", "E501", "E741"] "scripts/gen_fixtures.py" = ["E402", "E501", "E741"]
"scripts/fix_ref_ids.py" = ["E501"] # одноразовая миграция, SQL-строки длиннее 100
+49
View File
@@ -0,0 +1,49 @@
"""Починка справочника: заменить повреждённые Excel-ошибкой service_id (#REF!) на чистые.
В исходном справочнике у части услуг код тарификатора пришёл как Excel `#REF!`, и это
протекло в канонический service_id (напр. `#REF!-#REF!#459`) — видно в живом /search.
Имена услуг при этом корректны. Скрипт генерирует чистые уникальные id и каскадно
обновляет ссылки в price_item (service_id и suggested_service_id) и learned_synonym.
Запуск: python scripts/fix_ref_ids.py <db_path>
"""
import sqlite3
import sys
def fix(db_path: str) -> dict:
conn = sqlite3.connect(db_path)
existing = {r[0] for r in conn.execute("SELECT service_id FROM service")}
bad = [r[0] for r in conn.execute("SELECT service_id FROM service WHERE service_id LIKE '%#REF!%'")]
mapping: dict[str, str] = {}
i = 0
for old in bad:
while True:
new = f"d{i:04d}"
i += 1
if new not in existing:
break
existing.add(new)
mapping[old] = new
# learned_synonym ссылается на service_id? обновим, если колонка есть.
cols = {r[1] for r in conn.execute("PRAGMA table_info(learned_synonym)")}
for old, new in mapping.items():
conn.execute("UPDATE service SET service_id=? WHERE service_id=?", [new, old])
conn.execute("UPDATE price_item SET service_id=? WHERE service_id=?", [new, old])
conn.execute("UPDATE price_item SET suggested_service_id=? WHERE suggested_service_id=?", [new, old])
if "service_id" in cols:
conn.execute("UPDATE learned_synonym SET service_id=? WHERE service_id=?", [new, old])
conn.commit()
left = conn.execute("SELECT COUNT(*) FROM service WHERE service_id LIKE '%#REF!%'").fetchone()[0]
left_pi = conn.execute("SELECT COUNT(*) FROM price_item WHERE service_id LIKE '%#REF!%'").fetchone()[0]
matched = conn.execute("SELECT COUNT(*) FROM price_item WHERE service_id IS NOT NULL").fetchone()[0]
conn.close()
return {"fixed_services": len(mapping), "ref_left_service": left, "ref_left_price_item": left_pi, "matched_total": matched}
if __name__ == "__main__":
print(fix(sys.argv[1] if len(sys.argv) > 1 else "data/medarchive.db"))
+5 -2
View File
@@ -19,9 +19,12 @@
<div class="h-1 bg-brand w-full"></div> <div class="h-1 bg-brand w-full"></div>
<header class="max-w-5xl mx-auto px-5 pt-8 pb-2 flex items-center justify-between"> <header class="max-w-5xl mx-auto px-5 pt-8 pb-2 flex items-center justify-between">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2.5">
<span class="text-2xl font-bold">Med<span class="text-brand">Archive</span></span> <span class="text-2xl font-bold">Med<span class="text-brand">Archive</span></span>
<span class="text-xs text-tg bg-lg border border-[#ebedf0] rounded-full px-2 py-0.5">прайсы клиник-партнёров</span> <span class="h-6 w-px bg-[#d1d5db]"></span>
<a href="https://medprice.secondbrain.tools" title="Перейти в MedPrice — агрегатор цен на медуслуги"
aria-label="Открыть MedPrice" class="text-2xl font-bold opacity-55 hover:opacity-100 transition-opacity">Med<span class="text-[#0d9488]">Price</span></a>
<span class="text-xs text-tg bg-lg border border-[#ebedf0] rounded-full px-2 py-0.5 ml-3 hidden sm:inline">прайсы клиник-партнёров</span>
</div> </div>
<nav class="text-sm text-tg flex gap-4"> <nav class="text-sm text-tg flex gap-4">
<button onclick="show('search')" class="hover:text-ink">Поиск</button> <button onclick="show('search')" class="hover:text-ink">Поиск</button>