mirror of
https://github.com/ada-dmitry/osint_parser_ada.git
synced 2026-09-24 01:10:20 +00:00
feat: Add data models for disciplinary actions, organizations, and training centers
- Implemented DisciplinaryAction model for tracking disciplinary measures. - Created Organization model to represent auditing organizations with relevant details. - Developed TrainingCenter model for educational and methodological centers. - Added methods to convert models to dictionaries for easy export. feat: Implement parsers for auditors and organizations - Developed AuditorsParser to scrape and parse auditor registry data. - Created OrganizationsParser for scraping auditing organizations' registry. - Implemented GenericRegistryParser for handling generic tabular data. chore: Set up base parser functionality - Established BaseParser class with common methods for making requests and parsing HTML. - Added pagination handling and detailed page parsing capabilities. feat: Implement Excel export functionality - Created ExcelExporter class for exporting data to Excel files with formatting options. - Added support for exporting multiple sheets and organization data specifically. chore: Add logging utilities - Implemented setup_logger function for consistent logging across the application. - Configured logging to output to both console and file. chore: Update requirements and scripts - Added necessary dependencies for parsing, Excel handling, and logging. - Created run.sh and run.fish scripts for easy execution of the parser.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
Модели данных
|
||||
"""
|
||||
|
||||
from .organization import Organization
|
||||
from .auditor import Auditor
|
||||
from .certificate import Certificate
|
||||
from .training_center import TrainingCenter
|
||||
from .disciplinary_action import DisciplinaryAction
|
||||
from .audit_network import AuditNetwork
|
||||
|
||||
__all__ = [
|
||||
"Organization",
|
||||
"Auditor",
|
||||
"Certificate",
|
||||
"TrainingCenter",
|
||||
"DisciplinaryAction",
|
||||
"AuditNetwork",
|
||||
]
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
Модель данных для сети аудиторских организаций
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuditNetwork:
|
||||
"""Модель сети аудиторских организаций"""
|
||||
|
||||
# Основная информация
|
||||
name: str
|
||||
network_type: str # "Российская" или "Международная"
|
||||
registration_number: Optional[str] = None
|
||||
|
||||
# Участники
|
||||
member_organizations: List[str] = field(default_factory=list)
|
||||
member_count: Optional[int] = None
|
||||
|
||||
# Дополнительная информация
|
||||
country: Optional[str] = None
|
||||
headquarters: Optional[str] = None
|
||||
website: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
# Контакты
|
||||
contact_person: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
|
||||
# Метаинформация
|
||||
source_url: Optional[str] = None
|
||||
parsed_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Преобразование в словарь для экспорта"""
|
||||
return {
|
||||
"Название сети": self.name,
|
||||
"Тип": self.network_type,
|
||||
"Регистрационный номер": self.registration_number or "",
|
||||
"Количество участников": self.member_count or "",
|
||||
"Участники": ", ".join(self.member_organizations[:10]), # Первые 10
|
||||
"Страна": self.country or "",
|
||||
"Штаб-квартира": self.headquarters or "",
|
||||
"Сайт": self.website or "",
|
||||
"Описание": self.description or "",
|
||||
"Контактное лицо": self.contact_person or "",
|
||||
"Телефон": self.phone or "",
|
||||
"Email": self.email or "",
|
||||
"URL источника": self.source_url or "",
|
||||
"Дата сбора данных": self.parsed_at.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Модель данных для аудитора
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class Auditor:
|
||||
"""Модель аудитора или индивидуального аудитора"""
|
||||
|
||||
# Основная информация
|
||||
full_name: str
|
||||
ornz: str # Основной регистрационный номер записи
|
||||
certificate_number: str
|
||||
region: str
|
||||
status: Optional[str] = None
|
||||
|
||||
# Дополнительная информация
|
||||
inn: Optional[str] = None
|
||||
snils: Optional[str] = None
|
||||
qualification: Optional[str] = None
|
||||
|
||||
# Организация (для аудиторов)
|
||||
organization_name: Optional[str] = None
|
||||
organization_inn: Optional[str] = None
|
||||
|
||||
# Даты
|
||||
certificate_issue_date: Optional[datetime] = None
|
||||
membership_start_date: Optional[datetime] = None
|
||||
membership_end_date: Optional[datetime] = None
|
||||
|
||||
# Дополнительные данные
|
||||
education: Optional[str] = None
|
||||
experience_years: Optional[int] = None
|
||||
specializations: List[str] = field(default_factory=list)
|
||||
|
||||
# Метаинформация
|
||||
source_url: Optional[str] = None
|
||||
parsed_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Преобразование в словарь для экспорта"""
|
||||
return {
|
||||
"ФИО": self.full_name,
|
||||
"ОРНЗ": self.ornz,
|
||||
"Номер аттестата": self.certificate_number,
|
||||
"Регион": self.region,
|
||||
"Статус": self.status or "",
|
||||
"ИНН": self.inn or "",
|
||||
"СНИЛС": self.snils or "",
|
||||
"Квалификация": self.qualification or "",
|
||||
"Организация": self.organization_name or "",
|
||||
"ИНН организации": self.organization_inn or "",
|
||||
"Дата выдачи аттестата": (
|
||||
self.certificate_issue_date.strftime("%d.%m.%Y")
|
||||
if self.certificate_issue_date
|
||||
else ""
|
||||
),
|
||||
"Дата начала членства": (
|
||||
self.membership_start_date.strftime("%d.%m.%Y")
|
||||
if self.membership_start_date
|
||||
else ""
|
||||
),
|
||||
"Дата окончания членства": (
|
||||
self.membership_end_date.strftime("%d.%m.%Y")
|
||||
if self.membership_end_date
|
||||
else ""
|
||||
),
|
||||
"Образование": self.education or "",
|
||||
"Стаж (лет)": self.experience_years or "",
|
||||
"Специализации": ", ".join(self.specializations),
|
||||
"URL источника": self.source_url or "",
|
||||
"Дата сбора данных": self.parsed_at.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
"""
|
||||
Модель данных для квалификационного аттестата
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class Certificate:
|
||||
"""Модель квалификационного аттестата аудитора"""
|
||||
|
||||
# Основная информация
|
||||
certificate_number: str
|
||||
auditor_full_name: str
|
||||
issue_date: datetime
|
||||
status: str
|
||||
|
||||
# Дополнительная информация
|
||||
qualification_type: Optional[str] = None
|
||||
issuer: Optional[str] = None
|
||||
validity_period: Optional[str] = None
|
||||
|
||||
# Данные аудитора
|
||||
auditor_inn: Optional[str] = None
|
||||
auditor_snils: Optional[str] = None
|
||||
|
||||
# Причина аннулирования (для аннулированных)
|
||||
cancellation_reason: Optional[str] = None
|
||||
cancellation_date: Optional[datetime] = None
|
||||
|
||||
# Метаинформация
|
||||
source_url: Optional[str] = None
|
||||
parsed_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Преобразование в словарь для экспорта"""
|
||||
return {
|
||||
"Номер аттестата": self.certificate_number,
|
||||
"ФИО аудитора": self.auditor_full_name,
|
||||
"Дата выдачи": (
|
||||
self.issue_date.strftime("%d.%m.%Y") if self.issue_date else ""
|
||||
),
|
||||
"Статус": self.status,
|
||||
"Тип квалификации": self.qualification_type or "",
|
||||
"Выдан": self.issuer or "",
|
||||
"Срок действия": self.validity_period or "",
|
||||
"ИНН аудитора": self.auditor_inn or "",
|
||||
"СНИЛС аудитора": self.auditor_snils or "",
|
||||
"Причина аннулирования": self.cancellation_reason or "",
|
||||
"Дата аннулирования": (
|
||||
self.cancellation_date.strftime("%d.%m.%Y")
|
||||
if self.cancellation_date
|
||||
else ""
|
||||
),
|
||||
"URL источника": self.source_url or "",
|
||||
"Дата сбора данных": self.parsed_at.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Модель данных для дисциплинарной меры
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class DisciplinaryAction:
|
||||
"""Модель меры дисциплинарного воздействия"""
|
||||
|
||||
# Основная информация
|
||||
subject_name: str # ФИО аудитора или название организации
|
||||
subject_type: str # "auditor" или "organization"
|
||||
ornz: str
|
||||
action_type: str # Тип меры воздействия
|
||||
|
||||
# Детали
|
||||
violation_description: str
|
||||
decision_date: datetime
|
||||
decision_number: Optional[str] = None
|
||||
|
||||
# Дополнительная информация
|
||||
inn: Optional[str] = None
|
||||
region: Optional[str] = None
|
||||
effective_date: Optional[datetime] = None
|
||||
expiry_date: Optional[datetime] = None
|
||||
|
||||
# Решение
|
||||
decision_body: Optional[str] = None
|
||||
appeal_status: Optional[str] = None
|
||||
|
||||
# Метаинформация
|
||||
source_url: Optional[str] = None
|
||||
parsed_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Преобразование в словарь для экспорта"""
|
||||
return {
|
||||
"Субъект": self.subject_name,
|
||||
"Тип субъекта": (
|
||||
"Аудитор" if self.subject_type == "auditor" else "Организация"
|
||||
),
|
||||
"ОРНЗ": self.ornz,
|
||||
"Мера воздействия": self.action_type,
|
||||
"Описание нарушения": self.violation_description,
|
||||
"Дата решения": (
|
||||
self.decision_date.strftime("%d.%m.%Y") if self.decision_date else ""
|
||||
),
|
||||
"Номер решения": self.decision_number or "",
|
||||
"ИНН": self.inn or "",
|
||||
"Регион": self.region or "",
|
||||
"Дата вступления в силу": (
|
||||
self.effective_date.strftime("%d.%m.%Y") if self.effective_date else ""
|
||||
),
|
||||
"Дата окончания": (
|
||||
self.expiry_date.strftime("%d.%m.%Y") if self.expiry_date else ""
|
||||
),
|
||||
"Орган принявший решение": self.decision_body or "",
|
||||
"Статус обжалования": self.appeal_status or "",
|
||||
"URL источника": self.source_url or "",
|
||||
"Дата сбора данных": self.parsed_at.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Модель данных для аудиторской организации
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class Organization:
|
||||
"""Модель аудиторской организации"""
|
||||
|
||||
# Основная информация
|
||||
name: str
|
||||
ornz: str # Основной регистрационный номер записи
|
||||
inn: str
|
||||
region: str
|
||||
status: Optional[str] = None
|
||||
|
||||
# Дополнительная информация (заполняется при детальном парсе)
|
||||
full_name: Optional[str] = None
|
||||
ogrn: Optional[str] = None
|
||||
kpp: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
website: Optional[str] = None
|
||||
director: Optional[str] = None
|
||||
|
||||
# Даты
|
||||
registration_date: Optional[datetime] = None
|
||||
membership_start_date: Optional[datetime] = None
|
||||
membership_end_date: Optional[datetime] = None
|
||||
|
||||
# Дополнительные данные
|
||||
auditors_count: Optional[int] = None
|
||||
certificates: List[str] = field(default_factory=list)
|
||||
networks: List[str] = field(default_factory=list)
|
||||
|
||||
# Метаинформация
|
||||
source_url: Optional[str] = None
|
||||
parsed_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Преобразование в словарь для экспорта"""
|
||||
return {
|
||||
"Наименование": self.name,
|
||||
"Полное наименование": self.full_name or "",
|
||||
"ОРНЗ": self.ornz,
|
||||
"ИНН": self.inn,
|
||||
"КПП": self.kpp or "",
|
||||
"ОГРН": self.ogrn or "",
|
||||
"Регион": self.region,
|
||||
"Статус": self.status or "",
|
||||
"Адрес": self.address or "",
|
||||
"Телефон": self.phone or "",
|
||||
"Email": self.email or "",
|
||||
"Сайт": self.website or "",
|
||||
"Руководитель": self.director or "",
|
||||
"Дата регистрации": (
|
||||
self.registration_date.strftime("%d.%m.%Y")
|
||||
if self.registration_date
|
||||
else ""
|
||||
),
|
||||
"Дата начала членства": (
|
||||
self.membership_start_date.strftime("%d.%m.%Y")
|
||||
if self.membership_start_date
|
||||
else ""
|
||||
),
|
||||
"Дата окончания членства": (
|
||||
self.membership_end_date.strftime("%d.%m.%Y")
|
||||
if self.membership_end_date
|
||||
else ""
|
||||
),
|
||||
"Количество аудиторов": self.auditors_count or "",
|
||||
"Сертификаты": ", ".join(self.certificates),
|
||||
"Сети": ", ".join(self.networks),
|
||||
"URL источника": self.source_url or "",
|
||||
"Дата сбора данных": self.parsed_at.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Модель данных для учебно-методического центра
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class TrainingCenter:
|
||||
"""Модель учебно-методического центра (УМЦ)"""
|
||||
|
||||
# Основная информация
|
||||
name: str
|
||||
registration_number: str
|
||||
inn: str
|
||||
region: str
|
||||
status: Optional[str] = None
|
||||
|
||||
# Дополнительная информация
|
||||
full_name: Optional[str] = None
|
||||
ogrn: Optional[str] = None
|
||||
kpp: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
website: Optional[str] = None
|
||||
director: Optional[str] = None
|
||||
|
||||
# Аккредитация
|
||||
accreditation_date: Optional[datetime] = None
|
||||
accreditation_number: Optional[str] = None
|
||||
programs: List[str] = field(default_factory=list)
|
||||
|
||||
# Даты
|
||||
registration_date: Optional[datetime] = None
|
||||
exclusion_date: Optional[datetime] = None
|
||||
exclusion_reason: Optional[str] = None
|
||||
|
||||
# Метаинформация
|
||||
source_url: Optional[str] = None
|
||||
parsed_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Преобразование в словарь для экспорта"""
|
||||
return {
|
||||
"Наименование": self.name,
|
||||
"Полное наименование": self.full_name or "",
|
||||
"Регистрационный номер": self.registration_number,
|
||||
"ИНН": self.inn,
|
||||
"КПП": self.kpp or "",
|
||||
"ОГРН": self.ogrn or "",
|
||||
"Регион": self.region,
|
||||
"Статус": self.status or "",
|
||||
"Адрес": self.address or "",
|
||||
"Телефон": self.phone or "",
|
||||
"Email": self.email or "",
|
||||
"Сайт": self.website or "",
|
||||
"Руководитель": self.director or "",
|
||||
"Дата аккредитации": (
|
||||
self.accreditation_date.strftime("%d.%m.%Y")
|
||||
if self.accreditation_date
|
||||
else ""
|
||||
),
|
||||
"Номер аккредитации": self.accreditation_number or "",
|
||||
"Программы": ", ".join(self.programs),
|
||||
"Дата регистрации": (
|
||||
self.registration_date.strftime("%d.%m.%Y")
|
||||
if self.registration_date
|
||||
else ""
|
||||
),
|
||||
"Дата исключения": (
|
||||
self.exclusion_date.strftime("%d.%m.%Y") if self.exclusion_date else ""
|
||||
),
|
||||
"Причина исключения": self.exclusion_reason or "",
|
||||
"URL источника": self.source_url or "",
|
||||
"Дата сбора данных": self.parsed_at.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
}
|
||||
Reference in New Issue
Block a user