dana-medical-site/migrations/import/export_seed.py
2026-10-14 13:33:13 +00:00

230 lines
9.5 KiB
Python
Raw 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.

# -*- coding: utf-8 -*-
"""Читает дамп Bitrix db.sql и генерирует seed.sql для новой схемы dana."""
import sqlread, io, re, html
P = 'db.sql'
OUT = io.open('seed.sql', 'w', encoding='utf-8')
def esc(s):
if s is None:
return "NULL"
# В PostgreSQL со standard_conforming_strings обратный слэш — обычный символ,
# удваивать его нельзя: имена приезжали в базу с лишним слэшем.
return "'" + str(s).replace("'", "''") + "'"
def num(s):
if s is None or s == '':
return "NULL"
m = re.search(r'-?\d+[.,]?\d*', str(s).replace(' ', ''))
if not m:
return "NULL"
return m.group(0).replace(',', '.')
def clean(s):
if not s:
return ''
return html.unescape(s).strip()
def w(line=''):
OUT.write(line + '\n')
# ---- собрать всё в память ----
sections = {} # (iblock, id) -> row
for r in sqlread.rows(P, 'b_iblock_section'):
sections[(r['IBLOCK_ID'], r['ID'])] = r
elements = {} # id -> row (только нужные инфоблоки)
NEED_IB = {'30', '29', '24', '50'}
for r in sqlread.rows(P, 'b_iblock_element'):
if r['IBLOCK_ID'] in NEED_IB:
elements[r['ID']] = r
# свойства: (element_id, prop_id) -> [values]
props = {}
NEED_PROP = {
# услуги
'339', '377', '360', '345',
# врачи
'312', '310', '327', '328', '329', '330', '331', '332', '333', '725', '325', '309',
# цены
'192', '195',
# чек-апы
'697',
}
for r in sqlread.rows(P, 'b_iblock_element_property'):
pid = r.get('IBLOCK_PROPERTY_ID')
if pid in NEED_PROP:
eid = r.get('IBLOCK_ELEMENT_ID')
v = r.get('VALUE')
if v is None or v == '':
continue
props.setdefault((eid, pid), []).append(v)
def prop1(eid, pid):
vs = props.get((eid, pid))
return vs[0] if vs else None
def propN(eid, pid):
return props.get((eid, pid), [])
w("-- Сгенерировано export_seed.py из бэкапа Bitrix 28.08.2026")
w("BEGIN;")
w()
# ---------- УСЛУГИ (iblock 30) ----------
w("-- Направления (секции iblock 30)")
svc_dir_code = {} # section_id -> code
for (ib, sid), s in sections.items():
if ib != '30' or s.get('ACTIVE') != 'Y':
continue
code = s.get('CODE') or ('dir-' + sid)
svc_dir_code[sid] = code
w("INSERT INTO services (slug, path, name, sort, published, show_in_menu, legacy_id) "
"VALUES (%s, %s, %s, %s, true, true, %s) ON CONFLICT (path) DO NOTHING;" % (
esc(code), esc(code), esc(clean(s.get('NAME'))), num(s.get('SORT')) or '100', 'NULL'))
w()
w("-- Услуги (элементы iblock 30)")
for eid, e in elements.items():
if e['IBLOCK_ID'] != '30' or e.get('ACTIVE') != 'Y':
continue
sid = e.get('IBLOCK_SECTION_ID')
dir_code = svc_dir_code.get(sid)
if not dir_code:
continue
code = e.get('CODE') or ('svc-' + eid)
path = dir_code + '/' + code
lead = clean(e.get('PREVIEW_TEXT'))
detail = e.get('DETAIL_TEXT') or ''
blocks = "'[]'::jsonb" if not detail.strip() else \
"jsonb_build_array(jsonb_build_object('type','html','html'," + esc(detail) + "))"
w("INSERT INTO services (parent_id, slug, path, name, lead, blocks, sort, published, legacy_id) "
"SELECT p.id, %s, %s, %s, %s, %s, %s, true, %s FROM services p WHERE p.path=%s "
"ON CONFLICT (path) DO NOTHING;" % (
esc(code), esc(path), esc(clean(e.get('NAME'))), esc(lead), blocks,
num(e.get('SORT')) or '100', eid, esc(dir_code)))
w()
# ---------- СПЕЦИАЛЬНОСТИ + ВРАЧИ (iblock 29) ----------
w("-- Специальности (секции iblock 29)")
spec_code = {}
for (ib, sid), s in sections.items():
if ib != '29' or s.get('ACTIVE') != 'Y':
continue
code = s.get('CODE') or ('spec-' + sid)
spec_code[sid] = code
name_pl = clean(s.get('NAME'))
w("INSERT INTO specialties (slug, name, name_plural, sort, published) "
"VALUES (%s, %s, %s, %s, true) ON CONFLICT (slug) DO NOTHING;" % (
esc(code), esc(name_pl), esc(name_pl), num(s.get('SORT')) or '100'))
w()
w("-- Врачи (элементы iblock 29)")
for eid, e in elements.items():
if e['IBLOCK_ID'] != '29' or e.get('ACTIVE') != 'Y':
continue
fio = clean(e.get('NAME')).split()
last = fio[0] if fio else clean(e.get('NAME'))
first = fio[1] if len(fio) > 1 else ''
middle = ' '.join(fio[2:]) if len(fio) > 2 else ''
code = e.get('CODE') or ('doc-' + eid)
post = clean(prop1(eid, '312'))
qual = clean(prop1(eid, '327'))
work = clean(prop1(eid, '328'))
bio = clean(prop1(eid, '329'))
medflex = clean(prop1(eid, '725'))
hide = prop1(eid, '333')
provider = 'medflex' if medflex else 'none'
# Стаж («10 лет») переводим в год начала практики от даты последней правки
# карточки. Раньше текст стажа уходил в quote — цитату, а не стаж.
start_year = 'NULL'
m_work = re.search(r'(\d+)', work or '')
ts = e.get('TIMESTAMP_X') or ''
if m_work and re.match(r'\d{4}', ts) and 0 < int(m_work.group(1)) < 70:
start_year = str(int(ts[:4]) - int(m_work.group(1)))
w("INSERT INTO doctors (slug, last_name, first_name, middle_name, position, category, "
"practice_start_year, bio, booking_provider, medflex_user, sort, published, legacy_id) "
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,true,%s) ON CONFLICT (slug) DO NOTHING;" % (
esc(code), esc(last), esc(first), esc(middle), esc(post), esc(qual),
start_year, esc(bio), esc(provider), esc(medflex),
num(e.get('SORT')) or '100', eid))
# специальность врача = его секция
sid = e.get('IBLOCK_SECTION_ID')
if sid in spec_code:
w("INSERT INTO doctor_specialties (doctor_id, specialty_id, is_primary) "
"SELECT d.id, sp.id, true FROM doctors d, specialties sp "
"WHERE d.legacy_id=%s AND sp.slug=%s ON CONFLICT DO NOTHING;" % (eid, esc(spec_code[sid])))
# услуги врача (LINK_SERVICES=325 -> element id услуги)
for sv in propN(eid, '325'):
w("INSERT INTO doctor_services (doctor_id, service_id) "
"SELECT d.id, s.id FROM doctors d, services s "
"WHERE d.legacy_id=%s AND s.legacy_id=%s ON CONFLICT DO NOTHING;" % (eid, esc(sv)))
w()
# ---------- ЦЕНЫ (iblock 24) ----------
w("-- Разделы прайса (секции iblock 24)")
price_sec_code = {}
for (ib, sid), s in sections.items():
if ib != '24' or s.get('ACTIVE') != 'Y':
continue
code = s.get('CODE') or ('price-' + sid)
price_sec_code[sid] = (code, s.get('IBLOCK_SECTION_ID'))
w("INSERT INTO price_sections (slug, name, sort, published, legacy_id) "
"VALUES (%s,%s,%s,true,%s) ON CONFLICT (slug) DO NOTHING;" % (
esc(code), esc(clean(s.get('NAME'))), num(s.get('SORT')) or '100', sid))
w("-- родители разделов прайса")
for sid, (code, parent) in price_sec_code.items():
if parent and parent in price_sec_code:
w("UPDATE price_sections SET parent_id=(SELECT id FROM price_sections WHERE legacy_id=%s) "
"WHERE legacy_id=%s;" % (parent, sid))
w()
w("-- Позиции прайса (элементы iblock 24)")
for eid, e in elements.items():
if e['IBLOCK_ID'] != '24' or e.get('ACTIVE') != 'Y':
continue
sid = e.get('IBLOCK_SECTION_ID')
if sid not in price_sec_code:
continue
raw = prop1(eid, '192') or prop1(eid, '195')
price = num(raw)
if price == 'NULL':
continue
old = num(prop1(eid, '193'))
w("INSERT INTO price_items (section_id, name, price, sort, published, legacy_id) "
"SELECT ps.id, %s, %s, %s, true, %s FROM price_sections ps WHERE ps.legacy_id=%s;" % (
esc(clean(e.get('NAME'))), price, num(e.get('SORT')) or '100', eid, sid))
w()
# ---------- связь цена<->услуга через LINK_PRICES (360) на услуге ----------
w("-- Привязка позиций прайса к услугам (LINK_PRICES на услуге)")
for eid, e in elements.items():
if e['IBLOCK_ID'] != '30' or e.get('ACTIVE') != 'Y':
continue
for pr in propN(eid, '360'):
w("UPDATE price_items SET service_id=(SELECT id FROM services WHERE legacy_id=%s) "
"WHERE legacy_id=%s AND service_id IS NULL;" % (eid, esc(pr)))
w()
# ---------- ЧЕК-АПЫ (iblock 50) ----------
w("-- Чек-апы (элементы iblock 50)")
checkup_aud = {}
for (ib, sid), s in sections.items():
if ib == '50':
checkup_aud[sid] = 'kids' if 'етск' in (s.get('NAME') or '') else 'adults'
for eid, e in elements.items():
if e['IBLOCK_ID'] != '50' or e.get('ACTIVE') != 'Y':
continue
code = e.get('CODE') or ('checkup-' + eid)
aud = checkup_aud.get(e.get('IBLOCK_SECTION_ID'), 'adults')
lead = clean(e.get('PREVIEW_TEXT'))
detail = e.get('DETAIL_TEXT') or ''
blocks = "'[]'::jsonb" if not detail.strip() else \
"jsonb_build_array(jsonb_build_object('type','html','html'," + esc(detail) + "))"
w("INSERT INTO checkups (slug, audience, name, lead, blocks, sort, published, legacy_id) "
"VALUES (%s,%s,%s,%s,%s,%s,true,%s) ON CONFLICT (slug) DO NOTHING;" % (
esc(code), esc(aud), esc(clean(e.get('NAME'))), esc(lead), blocks,
num(e.get('SORT')) or '100', eid))
w()
w("COMMIT;")
OUT.close()
print("seed.sql written")