Initial commit
This commit is contained in:
commit
bcd7c35c5c
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
insurance_report.json
|
||||
21
data/appointments.csv
Normal file
21
data/appointments.csv
Normal file
@ -0,0 +1,21 @@
|
||||
appointment_id,patient_id,doctor,date,time,status
|
||||
A001,P001,Смирнова,20.04.2026,10:00,timetable
|
||||
A002,P001,Кузнецов,15.03.2026,14:30,completed
|
||||
A003,P002,Смирнова,18.03.2026,09:00,missed
|
||||
A004,P003,Кузнецов,22.04.2026,11:15,timetable
|
||||
A005,P002,Волкова,19.04.2026,15:45,timetable
|
||||
A006,P004,Смирнова,17.03.2026,13:20,completed
|
||||
A007,P005,Кузнецов,21.04.2026,08:30,timetable
|
||||
A008,P003,Волкова,23.03.2026,12:00,cancelled
|
||||
A009,P005,Смирнова,16.03.2026,16:10,completed
|
||||
A010,P004,Кузнецов,24.03.2026,09:30,timetable
|
||||
A011,P006,Волкова,23.04.2026,14:00,timetable
|
||||
A012,P006,Смирнова,25.04.2026,11:30,completed
|
||||
A013,P007,Кузнецов,24.04.2026,09:15,timetable
|
||||
A014,P007,Волкова,26.04.2026,16:45,scheduled
|
||||
A015,P008,Смирнова,22.04.2026,13:20,missed
|
||||
A016,P008,Кузнецов,27.04.2026,10:00,timetable
|
||||
A017,P009,Волкова,23.04.2026,12:10,missed
|
||||
A018,P009,Смирнова,28.04.2026,15:30,timetable
|
||||
A019,P010,Кузнецов,24.04.2026,08:45,missed
|
||||
A020,P010,Волкова,29.04.2026,14:50,timetable
|
||||
|
72
data/patients.json
Normal file
72
data/patients.json
Normal file
@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"patient_id": "P001",
|
||||
"full_name": "Пересчетов Иван Иванович",
|
||||
"birth_date": "15.06.1985",
|
||||
"insurance": "РосГосСтрах",
|
||||
"phone": "+7-999-123-4567"
|
||||
},
|
||||
{
|
||||
"patient_id": "P002",
|
||||
"full_name": "Выпивалова Анна Сергеевна",
|
||||
"birth_date": "23.11.1990",
|
||||
"insurance": "Согаз-МЕД",
|
||||
"phone": "89161234567"
|
||||
},
|
||||
{
|
||||
"patient_id": "P003",
|
||||
"full_name": "Перекатиполе Павел Николаевич",
|
||||
"birth_date": "10.03.1978",
|
||||
"insurance": "ЗастрахуйБратуху",
|
||||
"phone": "+7-903-111-2233"
|
||||
},
|
||||
{
|
||||
"patient_id": "P004",
|
||||
"full_name": "Пивоварова Елена Иннокентьевна",
|
||||
"birth_date": "01.07.2000",
|
||||
"insurance": "Макс-М",
|
||||
"phone": "+7-985-777-8899"
|
||||
},
|
||||
{
|
||||
"patient_id": "P005",
|
||||
"full_name": "Красноносов Дмитрий Евграфович",
|
||||
"birth_date": "30.12.1965",
|
||||
"insurance": "Согаз-МЕД",
|
||||
"phone": "8-800-555-3535"
|
||||
},
|
||||
{
|
||||
"patient_id": "P006",
|
||||
"full_name": "Зайцева-Кроликова Ольга Владимировна",
|
||||
"birth_date": "12.09.1995",
|
||||
"insurance": "РосГосСтрах",
|
||||
"phone": "+7-912-345-6789"
|
||||
},
|
||||
{
|
||||
"patient_id": "P007",
|
||||
"full_name": "Соколовский Артём Дмитриевич",
|
||||
"birth_date": "25.04.1988",
|
||||
"insurance": "Макс-М",
|
||||
"phone": "+7-922-111-2233"
|
||||
},
|
||||
{
|
||||
"patient_id": "P008",
|
||||
"full_name": "Лебедева-Гусева Мария Петровна",
|
||||
"birth_date": "03.11.2002",
|
||||
"insurance": "Согаз-МЕД",
|
||||
"phone": "89205556677"
|
||||
},
|
||||
{
|
||||
"patient_id": "P009",
|
||||
"full_name": "Грошкин Андрей Сергеевич",
|
||||
"birth_date": "19.07.1975",
|
||||
"insurance": "ЗастрахуйБратуху",
|
||||
"phone": "+7-951-444-5566"
|
||||
},
|
||||
{
|
||||
"patient_id": "P010",
|
||||
"full_name": "Тимофеева-Акакьева Екатерина Алексеевна",
|
||||
"birth_date": "28.02.1999",
|
||||
"insurance": "РосГосСтрах",
|
||||
"phone": "8-953-777-8899"
|
||||
}
|
||||
]
|
||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
223
src/main.py
Normal file
223
src/main.py
Normal file
@ -0,0 +1,223 @@
|
||||
import json
|
||||
import csv
|
||||
from datetime import datetime, date, timedelta
|
||||
|
||||
|
||||
#загрузка данных пациента из json
|
||||
def Load_Patients(filepath: str) -> list[dict]:
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"Ошибка: файл {filepath} не найден.")
|
||||
return []
|
||||
except json.JSONDecodeError:
|
||||
print(f"Ошибка: файл {filepath} содержит некорректный JSON.")
|
||||
return []
|
||||
|
||||
#загрузка записей из csv
|
||||
def Load_Appointments(filepath: str) -> list[dict]:
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
reader = csv.DictReader(f)
|
||||
return list(reader)
|
||||
except FileNotFoundError:
|
||||
print(f"Ошибка: файл {filepath} не найден.")
|
||||
return []
|
||||
except Exception as e:
|
||||
print(f"Ошибка при чтении CSV: {e}")
|
||||
return []
|
||||
|
||||
#вычисление возраста пацинта
|
||||
def Calculate_Age(birth_date_str: str) -> int:
|
||||
birth = datetime.strptime(birth_date_str, "%d.%m.%Y").date()
|
||||
today = date.today()
|
||||
age = today.year - birth.year - ((today.month, today.day) < (birth.month, birth.day))
|
||||
return age
|
||||
|
||||
def Validate_Patient(patient: dict) -> tuple[bool, str]:
|
||||
required_fields = {"patient_id", "full_name", "birth_date", "insurance", "phone"}
|
||||
missing = required_fields - patient.keys()
|
||||
if missing:
|
||||
return False, f"отсутствуют поля: {missing}"
|
||||
try:
|
||||
age = Calculate_Age(patient["birth_date"])
|
||||
if not (0 < age < 120):
|
||||
return False, f"возраст не корректен. Возраст: {age}"
|
||||
except Exception:
|
||||
return False, "некорректная дата рождения"
|
||||
|
||||
|
||||
phone = patient["phone"]
|
||||
allowed_chars = set("0123456789+- ")
|
||||
if not all(ch in allowed_chars for ch in phone) or len(phone)<6:
|
||||
return False, f"некорректный телефон: {phone}"
|
||||
|
||||
return True, ""
|
||||
|
||||
def Calculate_Average_Age(patients: list[dict]) -> float:
|
||||
if not patients:
|
||||
return 0.0
|
||||
total_age = sum(Calculate_Age(p["birth_date"]) for p in patients)
|
||||
return total_age / len(patients)
|
||||
|
||||
def Get_Patients_by_Insurance(patients: list[dict], insurer: str) -> list[dict]:
|
||||
insurer_lower = insurer.lower()
|
||||
return [p for p in patients if p["insurance"].lower() == insurer_lower]
|
||||
|
||||
def Count_Appointments_per_Patient(appointments: list[dict]) -> dict[str, int]:
|
||||
counts = {}
|
||||
for app in appointments:
|
||||
pid = app["patient_id"]
|
||||
counts[pid] = counts.get(pid, 0) + 1
|
||||
return counts
|
||||
|
||||
def Get_Missed_Appointments(appointments: list[dict]) -> list[dict]:
|
||||
return [app for app in appointments if app.get("status") == "missed"]
|
||||
|
||||
def Calculate_Doctor_Workload(appointments: list[dict], start_date: str, end_date: str) -> dict[str, int]:
|
||||
workload = {}
|
||||
start = datetime.strptime(start_date, "%d.%m.%Y").date()
|
||||
end = datetime.strptime(end_date, "%d.%m.%Y").date()
|
||||
|
||||
for app in appointments:
|
||||
try:
|
||||
app_date = datetime.strptime(app["date"], "%d.%m.%Y").date()
|
||||
if start <= app_date <= end:
|
||||
doctor = app["doctor"]
|
||||
workload[doctor] = workload.get(doctor, 0) + 1
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
return workload
|
||||
|
||||
def Generate_Weekly_Timetable(appointments: list[dict], start_date: str) -> list[dict]:
|
||||
start = datetime.strptime(start_date, "%d.%m.%Y").date()
|
||||
end = start + timedelta(days=6)
|
||||
|
||||
filtered = []
|
||||
for app in appointments:
|
||||
try:
|
||||
app_date = datetime.strptime(app["date"], "%d.%m.%Y").date()
|
||||
if start <= app_date <= end:
|
||||
filtered.append(app)
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
filtered.sort(key=lambda x: datetime.strptime(x["date"], "%d.%m.%Y"))
|
||||
return filtered
|
||||
|
||||
def Export_Insurance_Report(patients: list[dict], appointments: list[dict], filename: str) -> str:
|
||||
#словарь страховая -> количество пациентов
|
||||
insurance_patients = {}
|
||||
for p in patients:
|
||||
ins = p["insurance"]
|
||||
insurance_patients[ins] = insurance_patients.get(ins, 0) + 1
|
||||
|
||||
#словарь patient_id -> страховая
|
||||
patient_insurance = {p["patient_id"]: p["insurance"] for p in patients}
|
||||
|
||||
#число приёмов со статусом timetable или completed
|
||||
insurance_appointments = {}
|
||||
for app in appointments:
|
||||
status = app.get("status", "")
|
||||
if status in ("timetable", "completed"):
|
||||
pid = app["patient_id"]
|
||||
ins = patient_insurance.get(pid)
|
||||
if ins:
|
||||
insurance_appointments[ins] = insurance_appointments.get(ins, 0) + 1
|
||||
|
||||
#итоговый отчёт
|
||||
report = {}
|
||||
all_insurances = set(insurance_patients.keys()) | set(insurance_appointments.keys())
|
||||
for ins in all_insurances:
|
||||
report[ins] = {
|
||||
"patient_count": insurance_patients.get(ins, 0),
|
||||
"timetable_completed_appointments": insurance_appointments.get(ins, 0)
|
||||
}
|
||||
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, ensure_ascii=False, indent=4)
|
||||
|
||||
return f"Report saved to {filename}"
|
||||
|
||||
|
||||
|
||||
#Демонстрация
|
||||
def main():
|
||||
#1. Загрузка данных
|
||||
patients = Load_Patients('data/patients.json')
|
||||
if not patients:
|
||||
print("Нет данных о пациентах. Завершение.")
|
||||
return
|
||||
appointments = Load_Appointments('data/appointments.csv')
|
||||
if not appointments:
|
||||
print("Нет данных о записях. Завершение.")
|
||||
return
|
||||
|
||||
# 2.Валидация пациентов
|
||||
print("\n=== Проверка пациентов ===")
|
||||
for patient in patients:
|
||||
valid, msg = Validate_Patient(patient)
|
||||
if not valid:
|
||||
print(f"Ошибка у пациента {patient['patient_id']}: {msg}")
|
||||
|
||||
#3. Средний возраст
|
||||
avg_age = Calculate_Average_Age(patients)
|
||||
print(f"\n=== Средний возраст пациентов ===")
|
||||
print(f" {avg_age:.1f} лет")
|
||||
|
||||
#4.Пациенты по страховке
|
||||
insurer = "Согаз-МЕД"
|
||||
filtered_patients = Get_Patients_by_Insurance(patients, insurer)
|
||||
print(f"\n=== Пациенты со страховкой {insurer} ===")
|
||||
for p in filtered_patients:
|
||||
print(f" {p['full_name']} ({p['patient_id']})")
|
||||
|
||||
# 5. Количество приёмов на пациента
|
||||
appointment_counts = Count_Appointments_per_Patient(appointments)
|
||||
print("\n=== Количество приёмов по пациентам ===")
|
||||
for pid, count in appointment_counts.items():
|
||||
print(f" {pid}: {count}")
|
||||
|
||||
# 6.Пропущенные приёмы
|
||||
missed = Get_Missed_Appointments(appointments)
|
||||
print("\n=== Пропущенные приёмы ===")
|
||||
for app in missed:
|
||||
print(f" {app['appointment_id']} — пациент {app['patient_id']}, врач {app['doctor']}")
|
||||
|
||||
#7. Нагрузка врачей за текущий месяц
|
||||
today = datetime.today()
|
||||
start_month = date(today.year, today.month, 1).strftime("%d.%m.%Y")
|
||||
if today.month == 12:
|
||||
end_month = date(today.year + 1, 1, 1) - timedelta(days=1)
|
||||
else:
|
||||
end_month = date(today.year, today.month + 1, 1) - timedelta(days=1)
|
||||
end_month_str = end_month.strftime("%d.%m.%Y")
|
||||
|
||||
workload = Calculate_Doctor_Workload(appointments, start_month, end_month_str)
|
||||
months_ru = {
|
||||
1: "Январь", 2: "Февраль", 3: "Март", 4: "Апрель",
|
||||
5: "Май", 6: "Июнь", 7: "Июль", 8: "Август",
|
||||
9: "Сентябрь", 10: "Октябрь", 11: "Ноябрь", 12: "Декабрь"
|
||||
}
|
||||
day, month, year = map(int, start_month.split('.'))
|
||||
print(f"\n=== Нагрузка врачей за {months_ru[month]} {year} ===")
|
||||
for doctor, count in workload.items():
|
||||
print(f" {doctor}: {count} приёмов")
|
||||
|
||||
# 8.Расписание на неделю с понедельника
|
||||
days_from_monday = today.weekday() # понедельник = 0
|
||||
start_week = (today.date() - timedelta(days=days_from_monday)).strftime("%d.%m.%Y")
|
||||
weekly = Generate_Weekly_Timetable(appointments, start_week)
|
||||
print(f"\n=== Расписание на неделю с {start_week} ===")
|
||||
for app in weekly:
|
||||
print(f" {app['date']} {app['time']} — {app['doctor']}, пациент {app['patient_id']}")
|
||||
|
||||
# 9. Экспорт отчёта по страховкам
|
||||
result = Export_Insurance_Report(patients, appointments, 'insurance_report.json')
|
||||
print(f"\n=== {result} ===")
|
||||
|
||||
print("\n=== Демонстрация завершена ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user