diff --git a/README.md b/README.md index a8475de..7ecb82b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ -# covid19dd +# 🧾 Π‘ΠΊΡ€ΠΈΠΏΡ‚ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ XML для ΠΏΠ»Π°Π½Π° диспансСризации + +## πŸ“‹ НазначСниС +Π‘ΠΊΡ€ΠΈΠΏΡ‚ автоматичСски Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅Ρ‚ XML-Ρ„Π°ΠΉΠ» ΠΏΠ»Π°Π½Π° диспансСризации ΠΏΠΎ Π΄Π°Π½Π½Ρ‹ΠΌ ΠΈΠ· CSV-Ρ„Π°ΠΉΠ»Π° (списка ΠΏΠ°Ρ†ΠΈΠ΅Π½Ρ‚ΠΎΠ²). + +## βš™οΈ Как Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ +1. ΠŸΡ€ΠΈ запускС скрипт Π·Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Π΅Ρ‚ **ΠΊΠΎΠ΄ МО** (6 Ρ†ΠΈΡ„Ρ€). +2. Π‘ΠΊΡ€ΠΈΠΏΡ‚ ΠΈΡ‰Π΅Ρ‚ CSV-Ρ„Π°ΠΉΠ» с Ρ‚Π°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΈΠΌΠ΅Π½Π΅ΠΌ `<ΠΊΠΎΠ΄>.csv`. +3. Π—Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Π΅Ρ‚ **Π½ΠΎΠΌΠ΅Ρ€ ΠΏΠ°ΠΊΠ΅Ρ‚Π°**. +4. ΠžΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚ CSV, удаляСт Π΄ΡƒΠ±Π»ΠΈ ΠΏΠΎ полису, исправляСт Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Π΄Π°Ρ‚Ρ‹ ΠΈ создаёт XML. + +## 🧱 Π€ΠΎΡ€ΠΌΠ°Ρ‚ Π²Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ CSV +Π€Π°ΠΉΠ» `UTF-8`, Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚Π΅Π»ΡŒ `;`: + +``` +Ѐамилия;Имя;ΠžΡ‚Ρ‡Π΅ΡΡ‚Π²ΠΎ;Π”Π°Ρ‚Π°_роТдСния;НомСр_полиса;Пол +``` + +## πŸ“€ Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ +Π€Π°ΠΉΠ» XML Π²ΠΈΠ΄Π°: +``` +D-M<ΠΊΠΎΠ΄>-F35-2026-<Π½ΠΎΠΌΠ΅Ρ€>.xml +``` + +## πŸͺ΅ Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ +Π›ΠΎΠ³ΠΈ Π·Π°ΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ Π² `app.log`. + +## πŸš€ Запуск +``` +python poterya_kodcsv_scheta.py +``` diff --git a/poterya_kodcsv_scheta.py b/poterya_kodcsv_scheta.py new file mode 100644 index 0000000..c47de03 --- /dev/null +++ b/poterya_kodcsv_scheta.py @@ -0,0 +1,157 @@ +from datetime import datetime +from xml.dom import minidom +from dataclasses import dataclass +import os +import csv +import logging +from typing import List + +logger = logging.getLogger('application') +logger.setLevel(logging.INFO) +fh = logging.FileHandler('app.log', encoding='utf-8') +logger.addHandler(fh) + + +class DNaPlanGenerator: + + @dataclass + class DataNode: + fam: str = '' + im: str = '' + ot: str = '' + dr: str = '' + w: str = '' + npolis: str = '' + mes: str = '' + nzap: str = '' + mcode: str = '' + + def to_dict(self) -> dict: + return { + 'N_ZAP': self.nzap, + 'FAM': self.fam, + 'IM': self.im, + 'OT': self.ot, + 'W': self.w, + 'DR': self.dr, + 'NPOLIS': self.npolis, + 'MDP': '0', + 'SMOCOD': '35003', + 'DISP_TYP': '6', + 'MES': self.mes, + } + + def __init__(self, mo_code: str, year: int, month: int): + self.mo_code = mo_code + self.year = 2026 + self.month = month + self.doc = minidom.Document() + self.root = self.doc.createElement('ZL_LIST') + self.xml_name = f'D-M{self.mo_code}-F35-2026-{month}' + self.root.appendChild(self.header()) + self.doc.appendChild(self.root) + + def header(self): + zglv_tag = self.doc.createElement('ZGLV') + + fname_tag = self.doc.createElement('FILENAME') + fname_tag.appendChild(self.doc.createTextNode(self.xml_name)) + + data_tag = self.doc.createElement('DATA') + data_tag.appendChild(self.doc.createTextNode( + datetime.now().strftime('%Y-%m-%d'))) + + mo_tag = self.doc.createElement('CODE_MO') + mo_tag.appendChild(self.doc.createTextNode(self.mo_code)) + + year_tag = self.doc.createElement('YEAR') + year_tag.appendChild(self.doc.createTextNode(str(self.year))) + + r_tag = self.doc.createElement('R') + r_tag.appendChild(self.doc.createTextNode(str(self.month))) + + for item in (fname_tag, data_tag, mo_tag, year_tag, r_tag): + zglv_tag.appendChild(item) + + return zglv_tag + + def fill(self, data: List[dict]): + for node_data in data: + node = self.DataNode(**node_data) + zap_tag = self.doc.createElement('ZAP') + for tag_name, tag_value in node.to_dict().items(): + t = self.doc.createElement(tag_name) + t.appendChild(self.doc.createTextNode(str(tag_value))) + zap_tag.appendChild(t) + self.root.appendChild(zap_tag) + + def save(self): + xml_path = os.path.join(os.getcwd(), f'{self.xml_name}.xml') + with open(xml_path, 'w', encoding='cp1251') as f: + f.write(self.doc.toprettyxml(indent='\t', + encoding='windows-1251').decode('cp1251')) + logger.info(f'Π€Π°ΠΉΠ» ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ создан: {xml_path}') + + +if __name__ == '__main__': + mo_code = input('Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΊΠΎΠ΄ МО (6 Ρ†ΠΈΡ„Ρ€, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 352530): ').strip() + + if not (mo_code.isdigit() and len(mo_code) == 6): + print('НСкоррСктный ΠΊΠΎΠ΄ МО. НуТно ввСсти Ρ€ΠΎΠ²Π½ΠΎ 6 Ρ†ΠΈΡ„Ρ€, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ 352530.') + exit(1) + + number_package = input('Π’Π²Π΅Π΄ΠΈΡ‚Π΅ порядковый Π½ΠΎΠΌΠ΅Ρ€ ΠΏΠ°ΠΊΠ΅Ρ‚Π°: ') + year_now = datetime.now().year + + csv_filename = f"{mo_code}.csv" + csv_path = os.path.join(os.getcwd(), csv_filename) + + if not os.path.exists(csv_path): + print('Π€Π°ΠΉΠ» Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½!') + exit(1) + + data_list = [] + seen_policies = set() + mes_counter = 1 + + with open(csv_path, encoding='utf-8') as f: + reader = csv.reader(f, delimiter=';') + for i, row in enumerate(reader, start=1): + try: + fam, im, ot, dr, npolis, w = row[:6] + + if npolis in seen_policies: + continue + seen_policies.add(npolis) + + try: + dr_obj = datetime.strptime(dr, '%d.%m.%Y') + dr_iso = dr_obj.strftime('%Y-%m-%d') + except ValueError: + logger.warning(f'Ошибка Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π΄Π°Ρ‚Ρ‹ Π² строкС {i}: {dr}') + dr_iso = dr + + data_list.append({ + 'fam': fam, + 'im': im, + 'ot': ot, + 'dr': dr_iso, + 'w': w, + 'npolis': npolis, + 'mes': mes_counter, + 'nzap': str(i), + 'mcode': mo_code + }) + + mes_counter += 1 + if mes_counter > 12: + mes_counter = 1 + + except Exception as e: + logger.warning(f'Ошибка Π² строкС {i}: {e}') + + generator = DNaPlanGenerator(mo_code, year_now, int(number_package)) + generator.fill(data_list) + generator.save() + + print('βœ… XML-Ρ„Π°ΠΉΠ» ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ создан!')