Загрузка данных


import shutil
from dataclasses import Field
import datetime as dt
import flet as ft
from fletable import EditableTable, ForeignKeyConfig, FieldConfig
from sqlalchemy import select, cast, or_
from sqlalchemy.types import String
from core.models import Appointment, User, Patient, Service, Doctor, AppStatus, Specialization
from core.db import session, engine
from src.header import Header


class AppForm(ft.Column):
    def __init__(self, mode, on_succes, aid=0):
        super().__init__()
        self.mode = mode
        self.on_success = on_succes
        self.aid = aid
        self.scroll = ft.ScrollMode.ALWAYS


        self.pat_dd = ft.Dropdown(label="Пациент")
        self.doctor_dd = ft.Dropdown(label="Доктор")
        self.service_dd = ft.Dropdown(label="Услуга")
        self.status_dd = ft.Dropdown(label="Статус")
        self.date_f = ft.TextField(label="Дата", read_only=True, on_click=lambda e: e.page.show_dialog(self.date_pick))

        def handle_date(e: ft.Event[ft.DatePicker]):
            self.date_f.value = (e.data.date() +  dt.timedelta(days=1)).strftime("%d.%m.%Y")

        self.date_pick = ft.DatePicker(on_change=handle_date)

        self.time_f = ft.TextField(label="Время", read_only=True, on_click=lambda e: e.page.show_dialog(self.time_pick))

        def handle_time(e: ft.Event[ft.TimePicker]):
            self.time_f.value = e.data.strftime("%H:%M")

        self.time_pick = ft.TimePicker(on_change=handle_time)

        self.is_paid_cb = ft.Checkbox(label="Оплачен?")
        self.photo = ft.Image(src="pictures/1.jpg")
        self.photo_text = ft.Text("pictures/1.jpg")
        self.alert = ft.AlertDialog(
            title="Предупреждение",
            icon=ft.Icons.WARNING,
            actions=[ft.Button("OK", on_click=lambda e: e.page.pop_dialog())]
        )

        self.controls = self.build()

    def build(self):
        photo_cont = ft.Container(
            content=self.photo,
            width=150,
            height=150,
            on_click=self.pick_image
        )

        pats = session.scalars(select(Patient))
        self.pat_dd.options = [
            ft.Dropdown(text=p.name, key=p.id) for p in pats
        ]

        pats = session.scalars(select(Doctor))
        self.doctor_dd.options = [
            ft.Dropdown(text=p.name, key=p.id) for p in pats
        ]

        pats = session.scalars(select(Service))
        self.service_dd.options = [
            ft.Dropdown(text=p.name, key=p.id) for p in pats
        ]

        pats = session.scalars(select(AppStatus))
        self.status_dd.options = [
            ft.Dropdown(text=p.name, key=p.id) for p in pats
        ]
        del_btn = ft.Button("Удалить", bgcolor=ft.Colors.RED, icon=ft.Icons.DELETE, on_click=self.delete)
        save_btn = ft.Button("Сохранить", icon=ft.Icons.SAVE, on_click=self.save)

        res = [self.doctor_dd, self.pat_dd, self.service_dd, self.status_dd, self.date_f, self.time_f, self.is_paid_cb, photo_cont, self.photo_text, save_btn]

        if self.mode == "edit":
            self.build_edit()
            res.append(del_btn)
        return res

    def build_edit(self):
        pass
        a = session.get(Appointment, self.aid)
        self.pat_dd.value = a.patient_id
        self.doctor_dd.value = a.doctor_id
        self.service_dd.value = a.service_id
        self.status_dd.value = a.app_status_id
        self.date_f.value = a.date.strftime("%d.%m.%Y")
        self.date_pick.value = a.date
        self.time_f.value = a.app_time.strftime("%H:%M")
        self.time_pick.value = a.app_time
        self.is_paid_cb.value = True
        self.photo.src = a.photo
        self.photo_text.value = a.photo

    async def pick_image(self):
        file = await ft.FilePicker().pick_files(file_type=ft.FilePickerFileType.IMAGE)
        if file:
            new_path = f"pictures/{file[0].name}"
            shutil.copy(file[0].path, new_path)
            self.photo.src = new_path
            self.photo_text.value = new_path

    def save(self):
        if self.mode == "add":
            a = Appointment()
            session.add(a)
        else:
            a = session.get(Appointment, self.aid)

        pat = self.pat_dd.value
        doctor = self.doctor_dd.value
        service = self.service_dd.value
        status = self.status_dd.value
        date_ = self.date_pick.value
        time_pick = self.time_pick.value
        is_paid = self.is_paid_cb.value
        photo = self.photo.src

        if None in [pat, doctor, service, status, date_, time_pick, is_paid, photo]:
            self.alert.content = ft.Text("Введите все данные")
            self.page.show_dialog(self.alert)
            return
        try:
            a.patient_id = pat
            a.doctor_id = doctor
            a.service_id = service
            a.app_status_id = status
            a.date = date_
            a.app_time = time_pick
            a.is_paid = int(is_paid)
            a.photo = photo

            session.commit()
            self.on_success()
            self.alert.content = ft.Text("Успех")
            self.page.pop_dialog()
            self.page.show_dialog(self.alert)

        except (ValueError, TypeError):
            self.alert.content = ft.Text("Число")
            self.page.show_dialog(self.alert)
            session.rollback()
        except Exception as e:
            print(e)
            session.rollback()

    def delete(self):
        def real_delete():
            a = session.get(Appointment, self.aid)
            session.delete(a)
            session.commit()
            self.on_success()

            self.page.pop_dialog()
            self.page.pop_dialog()
            self.alert.content = ft.Text("Успех")
            self.page.show_dialog(self.alert)

        delete_dlg = ft.AlertDialog(
            title="Подтверждение",
            content=ft.Text("Уверены что хотите удалить?"),
            actions=[
                ft.Button("Да", on_click=real_delete),
                ft.Button("НЕТ", on_clicl=lambda e: e.page.pop_dialog())
            ]
        )

        self.page.show_dialog(delete_dlg)






class AppView(ft.View):
    def __init__(self, page: ft.Page):
        super().__init__()
        self._page = page
        self.route = "/app"
        self.scroll = ft.ScrollMode.ALWAYS

        self.user_id = self._page.session.store.get("id")
        self.user_role = self._page.session.store.get("role")


        self.app_list = ft.Column()

        self.search_f = ft.TextField(label="Поиск", icon=ft.Icons.SEARCH, on_change=self.build_list)
        self.spec_dd = ft.Dropdown(label="Специализация", on_select=self.build_list)
        self.sort_desc = None
        self.sort_btn = ft.Button("Сортировка по дате", on_click=self.sort)

        self.build_list()

        self.controls = self.build()

    def build(self):
        if self.user_role != "гость":
            u = session.get(User, self.user_id)
            u = u.name
        else:
            u = "Гость"

        specs = session.scalars(select(Specialization))
        self.spec_dd.options = [
            ft.DropdownOption(text="Все", key="all"),
            *[ft.DropdownOption(text=s.name, key=s.id) for s in specs]
        ]
        head = Header(u, self.user_role)
        title = ft.Text("Записи", size=24)
        row = ft.Row(
            controls=[
                self.spec_dd, self.search_f, self.sort_btn
            ],
            spacing=40,
        )
        if self.user_role == "Администратор":
            row.controls.append(ft.Button("Добавить", on_click=self.build_add))
            row.controls.append(ft.Button("Доктор", on_click=lambda e: e.page.go("/doctor")))
        back_btn = ft.Button("Назад", on_click=lambda e: e.page.go("/login"))
        res = [head, title, back_btn, self.app_list]
        if self.user_role in ["Регистратор", "Администратор"]:
            res.insert(3, row)
        return res

    def build_list(self):
        stmt = select(Appointment).join(Appointment.patient).join(Appointment.doctor).join(Appointment.service).join(Appointment.app_status)

        if self.search_f.value is not None and self.search_f.value != "":
            search = self.search_f.value.strip().lower()
            stmt = stmt.where(
                or_(
                    Patient.name.contains(search),
                    Doctor.name.contains(search),
                    Service.name.contains(search),
                    AppStatus.name.contains(search),
                    cast(Appointment.date, String).contains(search)
                )
            )

        if self.sort_desc is not None:
            if self.sort_desc:
                stmt = stmt.order_by(Appointment.date.desc())
            else:
                stmt = stmt.order_by(Appointment.date.asc())

        if self.spec_dd.value is not None and self.spec_dd.value != "all":
            stmt = stmt.join(Appointment.doctor).where(Doctor.specialization_id == self.spec_dd.value)

        try:
            self.app_list.controls.clear()
            apps = session.scalars(stmt)
            for a in apps:
                photo = ft.Image(src=a.photo)
                photo_cont = ft.Container(
                    content=photo,
                    padding=ft.Padding.all(5),
                    border=ft.Border.all(2, ft.Colors.BLACK),
                    width=150,
                    height=150,
                )

                text_cont = ft.Container(
                    content=ft.Text(
                        f"{a.patient.name}\n"
                        f"{a.doctor.name}\n"
                        f"{a.service.name}\n"
                        f"{a.date}\n"
                        f"{a.app_time}\n"
                        f"{a.app_status.name}\n"
                    ),
                    expand=True,
                    border=ft.Border.all(2, '#000000')
                )

                is_paid = "Оплачено" if a.is_paid == 1 else "Неоплачено"
                is_paid_cont = ft.Container(
                    content=ft.Text(f"{is_paid}"),
                    alignment=ft.Alignment(0, 0),
                    border=ft.Border.all(1, ft.Colors.BLACK_12),
                    width=100,
                    height=100,
                )

                ctrl = ft.Container(
                    content=ft.Row(
                        controls=[
                            photo_cont, text_cont, is_paid_cont
                        ],
                        alignment=ft.MainAxisAlignment.SPACE_BETWEEN
                    ),
                    border=ft.Border.all(1, ft.Colors.BLACK),
                    padding=ft.Padding.all(5)
                )

                if self.user_role == "Администратор":
                    ctrl.on_click = lambda e, aid=a.id: self.build_edit(aid)

                if a.app_status_id == 3:
                    ctrl.bgcolor = ft.Colors.GREY
                if a.patient.is_vip:
                    ctrl.bgcolor = "#2e8b57"
                self.app_list.controls.append(ctrl)
            self.page.update()
        except Exception as e:
            print(e)


    def build_add(self):

        add_dialog = ft.AlertDialog(
            title="Добавление",
            content=AppForm("add", self.build_list),
        )
        self.page.show_dialog(add_dialog)


    def build_edit(self, aid):
        edit_dlg = ft.AlertDialog(
            title=f"Редактириование товара {aid}",
            content=AppForm("edit", self.build_list, aid),
        )
        self.page.show_dialog(edit_dlg)

    def sort(self):
        if self.sort_btn.icon is None:
            self.sort_btn.icon = ft.Icons.ARROW_UPWARD
            self.sort_desc = True
        elif self.sort_btn.icon == ft.Icons.ARROW_UPWARD:
            self.sort_btn.icon = ft.Icons.ARROW_DOWNWARD
            self.sort_desc = False
        else:
            self.sort_btn.icon = None
            self.sort_desc = None

        self.build_list()
        self.page.update()