"""План генерации данных стенда: сколько чего создать на заданном масштабе. Обоснование чисел масштаба 1 ---------------------------- * Размер листа складывается из шаблонных строк (дерево статей из сида, создаётся самой БД) и добавленных строк (`extra_lines_per_sheet`, POST .../line). Шаблон у FORM_2/AHR — 566 строк, у FORM_1/AHR — 522, у FORM_4/STRUCTURE — 775 (+ программы, проекты и все строки AHR/CAP/OPER). `extra_lines_per_sheet = 500` даёт у `rf` лист FORM_2/AHR ~1090 строк (требование: самый большой лист rf >= 1000). Строки раскладываются по листьям дерева статей по кругу, так что покрыто всё дерево. На листах FORM_4 строк в 5 раз меньше (FORM4_LINES_DIVISOR, причина ниже); лист FORM_4/STRUCTURE содержит 775 строк шаблона и от числа добавленных строк не зависит. * Формы: FORM_1 — по ССП, FORM_2 — по ССП и РФ (свод 2 «РФ» читает именно FORM_2 РФ), FORM_4 — по РФ. 3 ССП и 6 РФ: в сводах 1 и 2 несколько организаций с правдоподобным разбросом, а объём остаётся таким, чтобы полная генерация шла десятки минут, а не часы (каждый add_line/PATCH — отдельный запрос, добавление строки ~0.05-0.15 с и растёт с размером листа). * `vsp_per_rf = 30`: ВСП на РФ; на каждый ВСП по одной строке на сателлитных листах FORM_2 (AHR_RENT/UTILITY/SECURITY) и источник `vsp_id` для строк FORM_4. * `projects_per_rf = 4`: проекты FORM_3 (по 2 отчёта LIMIT/CURRENT_EXPENSES за 2026, у половины ещё 2027), по 616 строк в отчёте каждого — отчёт проекта тоже >1000 строк с шаблоном. * `fill_ratio = 0.6`: доля строк, в которые записаны значения; остальные пустые, как в жизни. * Масштаб 2 удваивает org_units_rf, vsp_per_rf, extra_lines_per_sheet, projects_per_rf (и org_units_ssp, чтобы своды росли по обеим осям); объём строк растёт ~4x, а время генерации ~4-5x, а не 2x: число организаций и число строк на лист удваиваются одновременно, плюс add_line на вдвое больших листах медленнее. Замер масштаба 1 и оценка масштаба 2 — в отчёте задачи. """ from __future__ import annotations from dataclasses import dataclass YEAR = 2026 PROGRAMS_PER_SHEET = 2 # FORM_4: программ на каждый лист AHR/CAP/OPER формы PROJECTS_PER_PROGRAM = 3 # FORM_4: проектов на программу PASSWORD = "perf" # FORM_4: добавление строки и правка ячеек стоят O(число строк листа) на стороне БД (замер: add_line # 0.2 с на пустом листе -> 1.6 с на 300 строках; батч из 200 ячеек 9-11 с), тогда как у FORM_1/2 # 0.05-0.15 с при 500. Поэтому на листах FORM_4 строк в DIVISOR раз меньше; размер самого большого # листа rf (>= 1000) даёт FORM_2/AHR, а не FORM_4. Находка по приложению, см. отчёт. FORM4_LINES_DIVISOR = 5 @dataclass(frozen=True) class GenPlan: org_units_ssp: int org_units_rf: int vsp_per_rf: int extra_lines_per_sheet: int projects_per_rf: int fill_ratio: float def lines_per_sheet(self, form_type: str) -> int: """Сколько строк добавляется на каждый лист (AHR/CAP/OPER) формы данного типа.""" n = self.extra_lines_per_sheet return n // FORM4_LINES_DIVISOR if form_type == "FORM_4" else n @property def forms_form1(self) -> int: return self.org_units_ssp @property def forms_form2(self) -> int: return self.org_units_ssp + self.org_units_rf @property def forms_form4(self) -> int: return self.org_units_rf @property def programs(self) -> int: return self.org_units_rf * 3 * PROGRAMS_PER_SHEET @property def form4_projects(self) -> int: return self.programs * PROJECTS_PER_PROGRAM def plan_for_scale(scale: int) -> GenPlan: if not isinstance(scale, int) or scale < 1: raise ValueError(f"scale должен быть целым >= 1, получено {scale!r}") return GenPlan( org_units_ssp=3 * scale, org_units_rf=6 * scale, vsp_per_rf=30 * scale, extra_lines_per_sheet=500 * scale, projects_per_rf=4 * scale, fill_ratio=0.6, )