feat(registry): add new endpoints for registers, exchange, and backups; update routing and configurations
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 3m10s
CI/CD Pipeline / Run Tests (push) Successful in 3m35s
CI/CD Pipeline / Telegram Notify Success (push) Has been skipped
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m26s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m46s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 3m10s
CI/CD Pipeline / Run Tests (push) Successful in 3m35s
CI/CD Pipeline / Telegram Notify Success (push) Has been skipped
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m26s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m46s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
This commit is contained in:
40
src/apps/parsers/migrations/0009_auto_20260304_1159.py
Normal file
40
src/apps/parsers/migrations/0009_auto_20260304_1159.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 3.2.25 on 2026-03-04 11:59
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("registers", "0002_auto_20260304_1038"),
|
||||
("parsers", "0008_add_load_log_unique_constraint"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="financialreport",
|
||||
name="registry_organization",
|
||||
field=models.ForeignKey(blank=True, help_text="Связь с организацией из приложения реестров", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="financial_reports", to="registers.organization", verbose_name="организация из реестров"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="industrialcertificaterecord",
|
||||
name="registry_organization",
|
||||
field=models.ForeignKey(blank=True, help_text="Связь с организацией из приложения реестров", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="industrial_certificate_records", to="registers.organization", verbose_name="организация из реестров"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="inspectionrecord",
|
||||
name="registry_organization",
|
||||
field=models.ForeignKey(blank=True, help_text="Связь с организацией из приложения реестров", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="inspection_records", to="registers.organization", verbose_name="организация из реестров"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="manufacturerrecord",
|
||||
name="registry_organization",
|
||||
field=models.ForeignKey(blank=True, help_text="Связь с организацией из приложения реестров", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="manufacturer_records", to="registers.organization", verbose_name="организация из реестров"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="procurementrecord",
|
||||
name="registry_organization",
|
||||
field=models.ForeignKey(blank=True, help_text="Связь с организацией из приложения реестров", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="procurement_records", to="registers.organization", verbose_name="организация из реестров"),
|
||||
),
|
||||
]
|
||||
166
src/apps/parsers/migrations/0010_link_registry_organizations.py
Normal file
166
src/apps/parsers/migrations/0010_link_registry_organizations.py
Normal file
@@ -0,0 +1,166 @@
|
||||
# Generated by Django 3.2.25 on 2026-03-04
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def _normalize_identifier(value):
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
as_str = str(value).strip()
|
||||
if not as_str or not as_str.isdigit():
|
||||
return None
|
||||
|
||||
return int(as_str)
|
||||
|
||||
|
||||
def _resolve_organization_id(*, inn, ogrn, by_pair, by_inn, by_ogrn):
|
||||
if inn is not None and ogrn is not None:
|
||||
by_pair_id = by_pair.get((inn, ogrn))
|
||||
if by_pair_id is not None:
|
||||
return by_pair_id
|
||||
|
||||
if inn is not None and inn in by_inn:
|
||||
return by_inn[inn]
|
||||
|
||||
if ogrn is not None and ogrn in by_ogrn:
|
||||
return by_ogrn[ogrn]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _build_organization_lookup(organization_model):
|
||||
by_pair = {}
|
||||
by_inn_candidates = defaultdict(list)
|
||||
by_ogrn_candidates = defaultdict(list)
|
||||
|
||||
organizations = organization_model.objects.all().values("id", "mn_inn", "mn_ogrn")
|
||||
for organization in organizations:
|
||||
organization_id = organization["id"]
|
||||
inn = organization["mn_inn"]
|
||||
ogrn = organization["mn_ogrn"]
|
||||
|
||||
by_pair[(inn, ogrn)] = organization_id
|
||||
by_inn_candidates[inn].append(organization_id)
|
||||
by_ogrn_candidates[ogrn].append(organization_id)
|
||||
|
||||
by_inn = {
|
||||
inn: ids[0]
|
||||
for inn, ids in by_inn_candidates.items()
|
||||
if len(set(ids)) == 1
|
||||
}
|
||||
by_ogrn = {
|
||||
ogrn: ids[0]
|
||||
for ogrn, ids in by_ogrn_candidates.items()
|
||||
if len(set(ids)) == 1
|
||||
}
|
||||
|
||||
return by_pair, by_inn, by_ogrn
|
||||
|
||||
|
||||
def _link_model_records(
|
||||
*,
|
||||
model,
|
||||
inn_field,
|
||||
ogrn_field,
|
||||
by_pair,
|
||||
by_inn,
|
||||
by_ogrn,
|
||||
batch_size=1000,
|
||||
):
|
||||
field_names = ["id"]
|
||||
if inn_field:
|
||||
field_names.append(inn_field)
|
||||
if ogrn_field:
|
||||
field_names.append(ogrn_field)
|
||||
|
||||
queryset = model.objects.filter(registry_organization__isnull=True).only(*field_names)
|
||||
updates = []
|
||||
|
||||
for record in queryset.iterator(chunk_size=batch_size):
|
||||
inn = _normalize_identifier(getattr(record, inn_field)) if inn_field else None
|
||||
ogrn = _normalize_identifier(getattr(record, ogrn_field)) if ogrn_field else None
|
||||
|
||||
organization_id = _resolve_organization_id(
|
||||
inn=inn,
|
||||
ogrn=ogrn,
|
||||
by_pair=by_pair,
|
||||
by_inn=by_inn,
|
||||
by_ogrn=by_ogrn,
|
||||
)
|
||||
if organization_id is None:
|
||||
continue
|
||||
|
||||
record.registry_organization_id = organization_id
|
||||
updates.append(record)
|
||||
|
||||
if len(updates) >= batch_size:
|
||||
model.objects.bulk_update(updates, ["registry_organization"], batch_size=batch_size)
|
||||
updates = []
|
||||
|
||||
if updates:
|
||||
model.objects.bulk_update(updates, ["registry_organization"], batch_size=batch_size)
|
||||
|
||||
|
||||
def link_registry_organizations(apps, schema_editor):
|
||||
organization_model = apps.get_model("registers", "Organization")
|
||||
industrial_model = apps.get_model("parsers", "IndustrialCertificateRecord")
|
||||
manufacturer_model = apps.get_model("parsers", "ManufacturerRecord")
|
||||
inspection_model = apps.get_model("parsers", "InspectionRecord")
|
||||
procurement_model = apps.get_model("parsers", "ProcurementRecord")
|
||||
financial_report_model = apps.get_model("parsers", "FinancialReport")
|
||||
|
||||
by_pair, by_inn, by_ogrn = _build_organization_lookup(organization_model)
|
||||
|
||||
_link_model_records(
|
||||
model=industrial_model,
|
||||
inn_field="inn",
|
||||
ogrn_field="ogrn",
|
||||
by_pair=by_pair,
|
||||
by_inn=by_inn,
|
||||
by_ogrn=by_ogrn,
|
||||
)
|
||||
_link_model_records(
|
||||
model=manufacturer_model,
|
||||
inn_field="inn",
|
||||
ogrn_field="ogrn",
|
||||
by_pair=by_pair,
|
||||
by_inn=by_inn,
|
||||
by_ogrn=by_ogrn,
|
||||
)
|
||||
_link_model_records(
|
||||
model=inspection_model,
|
||||
inn_field="inn",
|
||||
ogrn_field="ogrn",
|
||||
by_pair=by_pair,
|
||||
by_inn=by_inn,
|
||||
by_ogrn=by_ogrn,
|
||||
)
|
||||
_link_model_records(
|
||||
model=procurement_model,
|
||||
inn_field="customer_inn",
|
||||
ogrn_field="customer_ogrn",
|
||||
by_pair=by_pair,
|
||||
by_inn=by_inn,
|
||||
by_ogrn=by_ogrn,
|
||||
)
|
||||
_link_model_records(
|
||||
model=financial_report_model,
|
||||
inn_field=None,
|
||||
ogrn_field="ogrn",
|
||||
by_pair=by_pair,
|
||||
by_inn=by_inn,
|
||||
by_ogrn=by_ogrn,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("parsers", "0009_auto_20260304_1159"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(link_registry_organizations, migrations.RunPython.noop),
|
||||
]
|
||||
@@ -0,0 +1,91 @@
|
||||
# Generated by Django 3.2.25 on 2026-03-04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("parsers", "0010_link_registry_organizations"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="industrialcertificaterecord",
|
||||
name="issue_date_normalized",
|
||||
field=models.DateField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Нормализованная дата выдачи сертификата",
|
||||
null=True,
|
||||
verbose_name="дата выдачи (нормализованная)",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="industrialcertificaterecord",
|
||||
name="expiry_date_normalized",
|
||||
field=models.DateField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Нормализованная дата окончания действия",
|
||||
null=True,
|
||||
verbose_name="дата окончания (нормализованная)",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="inspectionrecord",
|
||||
name="start_date_normalized",
|
||||
field=models.DateField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Нормализованная дата начала проверки",
|
||||
null=True,
|
||||
verbose_name="дата начала (нормализованная)",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="inspectionrecord",
|
||||
name="end_date_normalized",
|
||||
field=models.DateField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Нормализованная дата окончания проверки",
|
||||
null=True,
|
||||
verbose_name="дата окончания (нормализованная)",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="procurementrecord",
|
||||
name="max_price_amount",
|
||||
field=models.DecimalField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
decimal_places=2,
|
||||
help_text="Нормализованная числовая НМЦ",
|
||||
max_digits=20,
|
||||
null=True,
|
||||
verbose_name="НМЦ (число)",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="procurementrecord",
|
||||
name="publish_date_normalized",
|
||||
field=models.DateField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Нормализованная дата публикации извещения",
|
||||
null=True,
|
||||
verbose_name="дата публикации (нормализованная)",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="procurementrecord",
|
||||
name="end_date_normalized",
|
||||
field=models.DateField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Нормализованная дата окончания подачи заявок",
|
||||
null=True,
|
||||
verbose_name="дата окончания (нормализованная)",
|
||||
),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user