Andrey Epifancev fb80d8c636 perf: генератор данных через API
Refs #202

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 22:08:30 +04:00

87 lines
5.2 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.

"""План генерации данных стенда: сколько чего создать на заданном масштабе.
Обоснование чисел масштаба 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,
)