v1.0.178: генератор тестовых файлов (схема Опуса). 61 файл: 10 контрактов × (договор+спека v1+v2+допники) + ошибки + ZIP + дубликаты. Комментарий в каждом файле.

This commit is contained in:
“Naeel”
2026-06-25 11:03:41 +04:00
parent c022133de0
commit 3e548ad2a4
78 changed files with 2433 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
"""
pools.py — Пулы данных для генератора тестовых документов.
Схема Опуса: 10 компаний, пул из ~12 услуг, цены ±20% от базовых,
вариативность дат, номеров, названий.
Все данные основаны на реальных файлах из примеры_договоров_для_ИИ/.
"""
import random
# ── Компании ─────────────────────────────────────────────────
COMPANIES = [
{"prefix": "XXX001", "legal": 'ЗАО "XXX001"', "dir": "Генерального директора"},
{"prefix": "XXX002", "legal": 'ООО "XXX002"', "dir": "Генерального директора"},
{"prefix": "XXX003", "legal": 'АО "XXX003"', "dir": "Директора"},
{"prefix": "XXX004", "legal": 'ПАО "XXX004"', "dir": "Генерального директора"},
{"prefix": "XXX005", "legal": 'ООО "XXX005"', "dir": "Директора"},
{"prefix": "XXX006", "legal": 'ЗАО "XXX006"', "dir": "Генерального директора"},
{"prefix": "XXX007", "legal": 'АО "XXX007"', "dir": "Директора"},
{"prefix": "XXX008", "legal": 'ООО "XXX008"', "dir": "Генерального директора"},
{"prefix": "XXX009", "legal": 'ИП "XXX009"', "dir": "Индивидуального предпринимателя"},
{"prefix": "XXX010", "legal": 'ПАО "XXX010"', "dir": "Генерального директора"},
# Кириллический edge (уже есть в примерах)
{"prefix": "ХХХ002", "legal": 'ООО "ХХХ002"', "dir": "Генерального директора"},
]
PROVIDER = 'ООО "НУБЕС"'
PROVIDER_DIR = "Генерального директора Степаненко В.И."
CITY = "г. Москва"
ADDRESS = "115404, г. Москва, 1-я Стекольная 7с5"
# ── Номера договоров ─────────────────────────────────────────
# Базовые номера из примеров + вариации
BASE_NUMBERS = ["03700", "01200", "01300", "04100", "05200", "06800", "07400", "08100", "09900", "10500"]
def contract_number(company_idx, variant=0):
"""Сгенерировать номер договора: БАЗА[_суффикс]."""
base = BASE_NUMBERS[company_idx % len(BASE_NUMBERS)]
if variant > 0:
return f"{base}_{variant}"
return base
# ── Даты ─────────────────────────────────────────────────────
def random_date(base_year=2025, base_month=12, spread_months=7):
"""Случайная дата ±spread_months от базовой. Возвращает DD.MM.YYYY."""
import datetime
base = datetime.date(base_year, base_month, 1)
offset = random.randint(0, spread_months * 30)
d = base + datetime.timedelta(days=offset)
return d.strftime("%d.%m.%Y")
# ── Пул услуг и базовых цен (из реальных файлов) ────────────
# (наименование, базовая_цена, мин_объём, макс_объём, категория)
# категория: 'rack' | 'power' | 'network' | 'cloud' | 'ip' | 'cross'
SERVICES = [
("Аренда стойко-места", 213905.44, 1, 5, "rack"),
("Аренда PDU: 3Ф 32А вертикальный", 2511.17, 1, 5, "power"),
("Организация L2 канала без резерва, в составе:\n Скорость порта: 1 Гбит/с", 11016.36, 1, 3, "network"),
("Организация L2 канала без резерва, в составе:\n Скорость порта: 10 Гбит/с", 28300.00, 1, 2, "network"),
("Next Generation Cloud (Intel 3.0 ГГц), в составе:\n Количество vCPU: 8, RAM: 32 ГБ", 76704.00, 1, 3, "cloud"),
("Next Generation Cloud (AMD 4.0 ГГц), в составе:\n Количество vCPU: 4, RAM: 16 ГБ", 123823.00, 1, 2, "cloud"),
("Аренда IPv4 адреса /32", 157.28, 1, 16, "ip"),
("Аренда IPv4 подсети /30", 629.10, 1, 4, "ip"),
("Организация cross-connect: ВОЛС, 2×SMF", 4192.40, 1, 5, "cross"),
("Организация cross-connect: UTP", 840.94, 1, 5, "cross"),
("Интернет без защиты от DDoS, в составе:\n Полоса Интернет: 100 Мбит/с", 7015.00, 1, 3, "network"),
("Аренда порта без резерва, в составе:\n Скорость порта: 1 Гбит/с", 1525.00, 1, 3, "network"),
("Аренда публичной подсети (Длина префикса: /30 — 1 IPv4 адрес)", 589.66, 1, 4, "ip"),
]
# ── Функции вариативности ────────────────────────────────────
def vary_price(base, pct=0.20):
"""Случайное изменение цены ±pct, округление до копеек."""
factor = 1.0 + random.uniform(-pct, pct)
return round(base * factor, 2)
def vary_qty(min_q, max_q):
return random.randint(min_q, max_q)
def format_price(p):
"""Формат цены с пробелами-разделителями: 213 905,44."""
s = f"{p:,.2f}"
# 213,905.44 → 213 905,44
return s.replace(",", " ").replace(".", ",").replace(" ", " ")
def pick_services(count=5, with_prices=True):
"""Выбрать случайный набор услуг. Возвращает [(name, price, qty, sum), ...]."""
selected = random.sample(SERVICES, min(count, len(SERVICES)))
result = []
total = 0.0
for i, (name, base_price, min_q, max_q, cat) in enumerate(selected, 1):
price = vary_price(base_price) if with_prices else base_price
qty = vary_qty(min_q, max_q)
s = round(price * qty, 2)
total += s
result.append({
"num": i,
"name": name,
"price": price,
"price_str": format_price(price),
"qty": qty,
"sum": s,
"sum_str": format_price(s),
"cat": cat,
})
return result, round(total, 2), format_price(round(total, 2))