# -*- coding: utf-8 -*- """ Убирает точные дубли позиций прайса (одинаковый раздел+название+цена, возникшие из-за пересечения источников: старый сайт Bitrix + 3 файла 1С). Если у одной и той же услуги в разных источниках цена расходится — оставляет по одной строке на каждую цену и подписывает источник в скобках, а не молча выбирает одну. """ import csv, io import build_price_map as b LABEL_ORDER = ['старый прайс', 'ЦОМ', 'Остео', 'Дети'] def esc(s): # В PostgreSQL со standard_conforming_strings обратный слэш — обычный символ, # удваивать его нельзя: имена приезжали в базу с лишним слэшем. return "'" + str(s).replace("'", "''") + "'" # ---------- Bitrix-происхождение (уже в базе) ---------- bitrix = [] # (id, slug, name, price) with io.open('bitrix_items.csv', encoding='utf-8') as f: for row in csv.reader(f): bitrix.append((int(row[0]), row[1], row[2], float(row[3]))) # ---------- 1С: те же три файла, но с меткой источника ---------- DEPT_LABEL = {'D:/ЦОМ.xlsx': 'ЦОМ', 'D:/Остео.xlsx': 'Остео', 'D:/Дана Дети.xls': 'Дети'} c1c = [] # (label, slug, name, price) for path, kind, ncol, pcol in b.FILES: rows = b.parse_xlsx(path) if kind == 'xlsx' else b.parse_xls(path) items = b.extract(rows, ncol, pcol) label = DEPT_LABEL[path] for sec, name, price in items: key = (sec or '').strip().lower() slug = b.MAP.get(key, 'prochee-price') c1c.append((label, slug, name.strip(), price)) # ---------- группировка: (slug, name) -> {price: {labels}} ---------- groups = {} # (slug, name) -> {price: set(labels)} bitrix_by_key = {} # (slug, name) -> [(id, price), ...] for pid, slug, name, price in bitrix: key = (slug, name) bitrix_by_key.setdefault(key, []).append((pid, price)) groups.setdefault(key, {}).setdefault(price, set()).add('старый прайс') for label, slug, name, price in c1c: key = (slug, name) groups.setdefault(key, {}).setdefault(price, set()).add(label) updates = [] # (id, new_name) inserts = [] # (slug, name, price) deletes = [] # id (bitrix rows made redundant — only happens if a bitrix row # duplicates another bitrix row exactly; not expected, kept for safety) for (slug, name), pricemap in groups.items(): bitrix_rows = bitrix_by_key.get((slug, name), []) if len(pricemap) == 1: # все источники сходятся в одной цене — ничего не меняем/не плодим if not bitrix_rows: (price,) = pricemap.keys() inserts.append((slug, name, price)) # если bitrix_rows уже есть — они и представляют эту цену, 1С дублировать не нужно continue # расхождение цен — подписываем каждую цену источниками bitrix_prices = {p for _, p in bitrix_rows} for price, labels in pricemap.items(): label_str = ', '.join(l for l in LABEL_ORDER if l in labels) new_name = '%s (%s)' % (name, label_str) if price in bitrix_prices: for pid, p in bitrix_rows: if p == price: updates.append((pid, new_name)) else: inserts.append((slug, new_name, price)) o = io.open('seed_dedup.sql', 'w', encoding='utf-8') o.write('BEGIN;\n') # сначала снести прежнюю "плоскую" заливку 1С (3585 строк без дедупа), затем # переименовать конфликтующие bitrix-строки и вставить заново уже с разметкой o.write("DELETE FROM price_items WHERE legacy_id IS NULL;\n") for pid, new_name in updates: o.write("UPDATE price_items SET name=%s WHERE id=%d;\n" % (esc(new_name), pid)) for slug, name, price in inserts: o.write("INSERT INTO price_items (section_id,name,price,sort,published) " "SELECT id,%s,%s,100,true FROM price_sections WHERE slug=%s;\n" % ( esc(name), price, esc(slug))) o.write('COMMIT;\n') o.close() print('групп (раздел,название):', len(groups)) print('групп с расхождением цены:', sum(1 for v in groups.values() if len(v) > 1)) print('обновлений имени (bitrix-строки):', len(updates)) print('новых вставок:', len(inserts))