Sfoglia il codice sorgente

Upload source files

HanamileH 2 settimane fa
parent
commit
ed208b2001
5 ha cambiato i file con 486 aggiunte e 0 eliminazioni
  1. 4 0
      src/.env
  2. 89 0
      src/db.py
  3. 382 0
      src/gui.py
  4. 8 0
      src/main.py
  5. 3 0
      src/requirements.txt

+ 4 - 0
src/.env

@@ -0,0 +1,4 @@
+DB_NAME=exam
+DB_HOST=localhost
+DB_PORT=5432
+DB_SCHEMA=public

+ 89 - 0
src/db.py

@@ -0,0 +1,89 @@
+import psycopg2
+from psycopg2.extras import DictCursor
+import os
+from dotenv import load_dotenv
+
+# Список организаций, сотрудников и корреспонденций
+organizations = []
+employees = []
+correspondencies = []
+
+# Подключение к базе данных
+conn = None
+
+# Инициализация подключения к базе данных
+def init_connection(username, password):
+   global conn
+   # Загрузка переменных окружения из .env файла
+   load_dotenv()
+
+   # Подключение к базе данных
+   conn = psycopg2.connect(
+      dbname=os.getenv("DB_NAME"),
+      user=username,
+      password=password,
+      host=os.getenv("DB_HOST"),
+      port=os.getenv("DB_PORT")
+   )
+
+   # Изменяем текущую схему
+   with conn.cursor() as cur:
+      cur.execute(f"SET search_path TO {os.getenv('DB_SCHEMA')};")
+
+
+# Получение всех организаций
+def get_all_organizations():
+   global organizations
+   with conn.cursor(cursor_factory=DictCursor) as cur:
+      cur.execute("SELECT id, name, legal_form FROM organization;")
+      organizations = cur.fetchall()
+
+# Получение всех сотрудников
+def get_all_employees():
+   global employees
+   with conn.cursor(cursor_factory=DictCursor) as cur:
+      cur.execute("SELECT id, name, lastname, patronymic, organization_id FROM employee;")
+      employees = cur.fetchall()
+
+# Получение всех корреспонденций
+def get_all_correspondencies():
+   global correspondencies
+   with conn.cursor(cursor_factory=DictCursor) as cur:
+      cur.execute("SELECT id, document_type, document_number, receipt_date, execution_date, employee_id FROM correspondence;")
+      correspondencies = cur.fetchall()
+
+# Добавление новой корреспонденции
+def add_correspondence(document_type, document_number, receipt_date, execution_date, employee_id):
+   with conn.cursor() as cur:
+      cur.execute(
+         "INSERT INTO correspondence (document_type, document_number, receipt_date, execution_date, employee_id) VALUES (%s, %s, %s, %s, %s) RETURNING id;",
+         (document_type, document_number, receipt_date, execution_date, employee_id)
+      )
+      new_id = cur.fetchone()[0]
+      conn.commit()
+      return new_id
+   
+# Редактирование существующей корреспонденции
+def edit_correspondence(correspondence_id, document_type, document_number, receipt_date, execution_date, employee_id):
+   with conn.cursor() as cur:
+      cur.execute(
+         "UPDATE correspondence SET document_type = %s, document_number = %s, receipt_date = %s, execution_date = %s, employee_id = %s WHERE id = %s;",
+         (document_type, document_number, receipt_date, execution_date, employee_id, correspondence_id)
+      )
+      conn.commit()
+
+# Удаление корреспонденции
+def delete_correspondence(correspondence_id):
+   with conn.cursor() as cur:
+      cur.execute("DELETE FROM correspondence WHERE id = %s;", (correspondence_id,))
+      conn.commit()
+
+# Подсчитать количество корреспонденций для выбранной
+def count_correspondencies_by_organization(organization_id):
+   with conn.cursor() as cur:
+      cur.execute(
+         "SELECT COUNT(*) FROM correspondence c JOIN employee e ON c.employee_id = e.id WHERE e.organization_id = %s;",
+         (organization_id,)
+      )
+      count = cur.fetchone()[0]
+      return count

+ 382 - 0
src/gui.py

@@ -0,0 +1,382 @@
+from datetime import datetime
+from ara_imgui import imgui
+import psycopg2
+import db
+
+# Страница компаний
+def gui_organizations():
+   imgui.text("Список компаний:")
+
+   # Таблица значений
+   if imgui.begin_child("##organizations_table"):
+      imgui.columns(3)
+
+      # Заголовки столбцов
+      for title in ["id", "Наименование", "Тип"]:
+         imgui.text(title)
+         imgui.next_column()
+
+      imgui.separator()
+
+      # Значения
+      for o in db.organizations:
+         imgui.text(str(o["id"]))
+         imgui.next_column()
+         imgui.text(o["name"])
+         imgui.next_column()
+         imgui.text(o["legal_form"])
+         imgui.next_column()
+
+      imgui.columns(1)
+      imgui.end_child()
+
+# Страница сотрудников
+def gui_employees():
+   imgui.text("Список сотрудников:")
+
+   # Таблица значений
+   if imgui.begin_child("##employees_table"):
+      imgui.columns(5)
+
+      # Заголовки столбцов
+      for title in ["id", "Имя", "Фамилия", "Отчество", "Компания"]:
+         imgui.text(title)
+         imgui.next_column()
+
+      imgui.separator()
+
+      # Значения
+      for e in db.employees:
+         org = [o for o in db.organizations if o["id"] == e["organization_id"]][0]
+         imgui.text(str(e["id"]))
+         imgui.next_column()
+         imgui.text(e["name"])
+         imgui.next_column()
+         imgui.text(e["lastname"])
+         imgui.next_column()
+         imgui.text(e["patronymic"])
+         imgui.next_column()
+         imgui.text(f'{org["legal_form"]} "{org["name"]}"')
+         imgui.next_column()
+
+      imgui.columns(1)
+      imgui.end_child()
+
+# Страница корреспонденций
+DOCUMENT_TYPES = [
+   'информационный',
+   'организационно-распорядительный',
+   'запросно-ответный',
+   'инициативный',
+   'другой'
+]
+
+selected_correspondence_id = None # Выбранная корреспонденция (для редактирования)
+total_correspondencies = None # Общее количество корреспонденций для выбранной компании
+
+selected_employee_pos = 0     # Выбранный сотрудник
+selected_organization_pos = 0 # Выбранная компания
+receipt_date_input = ""       # Введенная дата поступления
+execution_date_input = ""     # Введенная дата исполнения
+document_type_pos = 0         # Выбраный тип документа
+document_number_input = ""    # Введенный номер документа
+
+search_document_name = "" # Имя докумнета для поиска
+
+error_text = None # Текст ошибки (если есть)
+
+def gui_correspondencies():
+   global selected_correspondence_id
+   global total_correspondencies
+   global selected_employee_pos, selected_organization_pos
+   global receipt_date_input, execution_date_input
+   global document_type_pos, document_number_input
+   global search_document_name
+   global error_text
+
+   # Данные для списка компаний
+   organizations_list = [f'{o["legal_form"]} "{o["name"]}"' for o in db.organizations]
+   selected_organization_id = db.organizations[selected_organization_pos]["id"]
+
+   # Выпадающий список для выбора компании
+   updated, selected_organization_pos = imgui.combo("Компания", selected_organization_pos, organizations_list)
+
+   if updated or (total_correspondencies is None):
+      selected_organization_id = db.organizations[selected_organization_pos]["id"]
+      total_correspondencies = db.count_correspondencies_by_organization(selected_organization_id)
+
+
+   imgui.text(f"Всего корреспонденций: {total_correspondencies}")
+
+   # Панель редактирования
+   imgui.separator()
+   imgui.text("Добавить корреспонденцию")
+   
+   # Дата поступления
+   if imgui.button("Сегодня ##1"):
+      receipt_date_input = datetime.today().strftime("%d-%m-%Y")
+
+   imgui.same_line()
+
+   receipt_date_input = imgui.input_text("Дата поступления", receipt_date_input)[1]
+   if imgui.is_item_hovered():
+      imgui.set_tooltip("В формате ДД-ММ-ГГГГ")
+      
+   # Дата исполнения
+   if imgui.button("Сегодня ##2"):
+      execution_date_input = datetime.today().strftime("%d-%m-%Y")
+
+   imgui.same_line()
+
+   execution_date_input = imgui.input_text("Дата исполнения (опционально)", execution_date_input)[1]
+   if imgui.is_item_hovered():
+      imgui.set_tooltip("В формате ДД-ММ-ГГГГ")
+
+   # Данные для списка сотрудников (только сотрудники выбранной компании)
+   employees_list = [f'{e["lastname"]} {e["name"]} {e["patronymic"]}' for e in db.employees if e["organization_id"] == selected_organization_id]
+
+   # Выпадающие список для выбора сотрудника
+   selected_employee_pos = imgui.combo("Сотрудник", selected_employee_pos, employees_list)[1]
+
+   # Выпадающий список для выбора типа документа
+   document_type_pos = imgui.combo("Тип документа", document_type_pos, DOCUMENT_TYPES)[1]
+
+   # Ввод номера документа
+   document_number_input = imgui.input_text("Номер документа", document_number_input)[1]
+
+   if selected_correspondence_id:
+      # Кнопка сохранения изменений
+      if imgui.button("Сохранить изменения"):
+         error_text = None
+
+         # Парсим введенные даты
+         try:
+            receipt_date = datetime.strptime(receipt_date_input, "%d-%m-%Y")
+
+         except ValueError:
+            error_text = "Неверный формат даты поступления"
+            return
+         
+         try:
+            execution_date = datetime.strptime(execution_date_input, "%d-%m-%Y") if execution_date_input else None
+
+         except ValueError:
+            error_text = "Неверный формат даты исполнения"
+            return
+         
+         # Проверяем, что дата исполнения не раньше даты поступления
+         if execution_date and execution_date < receipt_date:
+            error_text = "Дата исполнения не может быть раньше даты поступления"
+            return
+         
+         # Проверяем, что номер документа не пустой
+         if not document_number_input:
+            error_text = "Номер документа не может быть пустым"
+            return
+         
+         # Изменяем корреспонденцию в базе данных
+         db.edit_correspondence(
+            correspondence_id=selected_correspondence_id,
+            document_type=DOCUMENT_TYPES[document_type_pos],
+            document_number=document_number_input,
+            receipt_date=receipt_date,
+            execution_date=execution_date,
+            employee_id=db.employees[selected_employee_pos]["id"]
+         )
+         db.get_all_correspondencies()
+         selected_correspondence_id = None
+         receipt_date_input = ""
+         execution_date_input = ""
+         document_type_pos = 0
+         document_number_input = ""
+         selected_organization_pos = 0
+         selected_employee_pos = 0
+
+      # Кнопка отмены редактирования
+      imgui.same_line()
+
+      if imgui.button("Отмена"):
+         selected_correspondence_id = None
+         receipt_date_input = ""
+         execution_date_input = ""
+         document_type_pos = 0
+         document_number_input = ""
+         selected_organization_pos = 0
+         selected_employee_pos = 0
+         error_text = None
+
+   else:
+      # Кнопка добавления новой корреспонденции
+      if imgui.button("Добавить"):
+         error_text = None
+
+         # Парсим введенные даты
+         try:
+            receipt_date = datetime.strptime(receipt_date_input, "%d-%m-%Y")
+
+         except ValueError:
+            error_text = "Неверный формат даты поступления"
+            return
+         
+         try:
+            execution_date = datetime.strptime(execution_date_input, "%d-%m-%Y") if execution_date_input else None
+
+         except ValueError:
+            error_text = "Неверный формат даты исполнения"
+            return
+         
+         # Проверяем, что дата исполнения не раньше даты поступления
+         if execution_date and execution_date < receipt_date:
+            error_text = "Дата исполнения не может быть раньше даты поступления"
+            return
+         
+         # Проверяем, что номер документа не пустой
+         if not document_number_input:
+            error_text = "Номер документа не может быть пустым"
+            return
+
+         # Добавляем новую корреспонденцию в базу данных
+         try:
+            selected_employee_id = db.employees[selected_employee_pos]["id"]
+            db.add_correspondence(
+               document_type=DOCUMENT_TYPES[document_type_pos],
+               document_number=document_number_input,
+               receipt_date=receipt_date,
+               execution_date=execution_date,
+               employee_id=selected_employee_id
+            )
+
+            # Обновляем список корреспонденций
+            db.get_all_correspondencies()
+         except psycopg2.errors.UniqueViolation:
+            error_text = "Корреспонденция с таким номером уже существует"
+
+   # Выводим текст ошибки (если есть)
+   if error_text:
+      imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.3, 1.0), error_text)
+
+   # Панель поиска
+   imgui.separator()
+   search_document_name = imgui.input_text("Поиск по номеру документа", search_document_name)[1]
+
+   # Список всех корреспонденций
+   if imgui.begin_child("##correspondencies_table"):
+      imgui.columns(6)
+
+      # Заголовки столбцов
+      for title in ["Дата поступления", "Дата исполнения", "Сотрудник", "Тип документа", "Номер документа", "Действия"]:
+         imgui.text(title)
+         imgui.next_column()
+
+      imgui.separator()
+
+      # Значения
+      for i, c in enumerate(db.correspondencies):
+         # Данные сотрудника и компании для текущей корреспонденции
+         employee = next(e for e in db.employees if e["id"] == c["employee_id"])
+         organization = next(o for o in db.organizations if o["id"] == employee["organization_id"])
+
+         if organization["id"] != selected_organization_id:
+            continue
+
+         if search_document_name.lower() not in c["document_number"].lower():
+            continue
+
+         # Дата поступления/исполнения
+         imgui.text(c["receipt_date"].strftime("%d-%m-%Y"))
+         imgui.next_column()
+
+         imgui.text(c["execution_date"].strftime("%d-%m-%Y") if c["execution_date"] else "-")
+         imgui.next_column()
+
+         # Сотрудник
+         imgui.text(f'{employee["lastname"]} {employee["name"]} {employee["patronymic"]}')
+         imgui.next_column()
+
+         # Тип и номер документа
+         imgui.text(c["document_type"])
+         imgui.next_column()
+         imgui.text(c["document_number"])
+         imgui.next_column()
+
+         # Действия
+         if imgui.button(f"Изменить ##{i}"):
+            selected_correspondence_id = c["id"]
+            receipt_date_input = c["receipt_date"].strftime("%d-%m-%Y")
+            execution_date_input = c["execution_date"].strftime("%d-%m-%Y") if c["execution_date"] else ""
+            document_type_pos = DOCUMENT_TYPES.index(c["document_type"])
+            document_number_input = c["document_number"]
+            selected_employee_pos = employees_list.index(f'{employee["lastname"]} {employee["name"]} {employee["patronymic"]}')
+            error_text = None
+
+         imgui.same_line()
+         
+         if imgui.button(f"Удалить ##{i}"):
+            db.delete_correspondence(c["id"])
+            db.get_all_correspondencies()
+            selected_correspondence_id = None
+
+         imgui.next_column()
+
+      imgui.columns(1)
+      imgui.end_child()
+
+# Страница авторизации
+connected = False
+db_username = ""
+db_password = ""
+auth_error_text = None
+def gui_auth():
+   global connected
+   global db_username, db_password, auth_error_text
+
+   imgui.text("Авторизация")
+
+   imgui.text("Введите имя пользователя и пароль для подключения к базе данных")
+
+   db_username = imgui.input_text("Имя пользователя", db_username)[1]
+   db_password = imgui.input_text("Пароль", db_password)[1]
+
+   if auth_error_text:
+      imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.3, 1.0), auth_error_text)
+
+   if imgui.button("Подключиться"):
+      try:
+         init(username=db_username, password=db_password)
+         connected = True
+      except psycopg2.OperationalError:
+         auth_error_text = "Ошибка подключения к базе данных. Проверьте имя пользователя и пароль."
+
+
+# Главное меню
+selected_tab = 0 # Выбранная страница
+
+def gui_main():
+   global selected_tab
+
+   if not connected:
+      gui_auth()
+   else:
+      for i, title in enumerate(["Компании","Сотрудники", "Корреспонденция"]):
+         if imgui.button(title):
+            selected_tab = i
+
+         if i < 2:
+            imgui.same_line()
+
+
+      if selected_tab == 0:
+         gui_organizations()
+      elif selected_tab == 1:
+         gui_employees()
+      elif selected_tab == 2:
+         gui_correspondencies()
+      else:
+         imgui.text("Ошибка: неизвестная страница")
+
+
+def init(username, password):
+   db.init_connection(username=username, password=password)
+   db.get_all_organizations()
+   db.get_all_employees()
+   db.get_all_correspondencies()

+ 8 - 0
src/main.py

@@ -0,0 +1,8 @@
+from ara_imgui import App
+from gui import gui_main
+
+app = App("Менеджер корреспонденций")
+
+app.load_font(font_size=18)
+app.apply_theme("light")
+app.run(gui_main)

+ 3 - 0
src/requirements.txt

@@ -0,0 +1,3 @@
+ara_imgui
+psycopg2-binary
+python-dotenv