Compare commits

3 Commits

Author SHA1 Message Date
b024ea50b2 diplom 2025-05-25 23:38:58 +05:00
cedb00bb4c fix loading 2025-05-14 01:01:07 +05:00
jze9
e8343313a9 test 2025-04-08 15:05:37 +05:00
38 changed files with 10255 additions and 907 deletions

165
app/.gitignore vendored
View File

@@ -1,163 +1,2 @@
# Byte-compiled / optimized / DLL files data_base_hesh
__pycache__/ __pycache__
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
*.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Flet
storage/

View File

@@ -1,81 +1,2 @@
# App app # user_app
user_app_copp
## Run the app
### uv
Run as a desktop app:
```
uv run flet run
```
Run as a web app:
```
uv run flet run --web
```
### Poetry
Install dependencies from `pyproject.toml`:
```
poetry install
```
Run as a desktop app:
```
poetry run flet run
```
Run as a web app:
```
poetry run flet run --web
```
For more details on running the app, refer to the [Getting Started Guide](https://flet.dev/docs/getting-started/).
## Build the app
### Android
```
flet build apk -v
```
For more details on building and signing `.apk` or `.aab`, refer to the [Android Packaging Guide](https://flet.dev/docs/publish/android/).
### iOS
```
flet build ipa -v
```
For more details on building and signing `.ipa`, refer to the [iOS Packaging Guide](https://flet.dev/docs/publish/ios/).
### macOS
```
flet build macos -v
```
For more details on building macOS package, refer to the [macOS Packaging Guide](https://flet.dev/docs/publish/macos/).
### Linux
```
flet build linux -v
```
For more details on building Linux package, refer to the [Linux Packaging Guide](https://flet.dev/docs/publish/linux/).
### Windows
```
flet build windows -v
```
For more details on building Windows package, refer to the [Windows Packaging Guide](https://flet.dev/docs/publish/windows/).

8844
app/assets/test.json Normal file

File diff suppressed because it is too large Load Diff

317
app/designer.py Normal file
View File

@@ -0,0 +1,317 @@
import flet as ft
from flet.core.types import OptionalControlEventCallable
import json
import requests
color = [
"#f0ece2",
"#dfd3c3",
"#c7b198",
"#596e79",
"#7E93A0FF"
]
class DataPicer(ft.DatePicker):
def __init__(self,creation_time, handle_dismissal: OptionalControlEventCallable = None,handle_change: OptionalControlEventCallable = None):
import datetime
super().__init__(
current_date=creation_time,
first_date=datetime.datetime(year=2023, month=10, day=1),
last_date=datetime.datetime(year=2027, month=10, day=1),
on_change=handle_change,
on_dismiss=handle_dismissal,
)
class JsonJober:
@classmethod
def read_json(cls, path_file: str = ""):
"""Чтение json файла"""
with open(path_file, 'r', encoding='utf-8') as file:
data = json.load(file)
return data if isinstance(data, list) else [data]
class Text(ft.Text):
def __init__(self, value: str = ""):
super().__init__(
value=value,
size=24,
color=color[3]
)
class Loger(ft.SnackBar):
def __init__(self, text):
super().__init__(
content=ft.Text(value=text, color=color[3], size=24)
)
class Field(ft.TextField):
def __init__(self, label: str = "", password: bool = False,width:int=0):
super().__init__(
max_lines=1,
border_color=color[3],
color=color[3],
text_size=24,
label=label,
password=password,
can_reveal_password=password,
label_style=ft.TextStyle(
color=color[3],
size=24
),
)
if width == 0:
self.expand=True,
else:
self.expand=False
self.width=width
class Button(ft.ElevatedButton):
def __init__(self, text: str = "", on_click: OptionalControlEventCallable = None):
super().__init__(
color=color[3],
bgcolor=color[1],
text=text,
on_click=on_click,
style=ft.ButtonStyle(
overlay_color=color[4],
text_style=ft.TextStyle(
size=24
)
)
)
class TextCellFilter(ft.Container):
def __init__(self,id:int=0, value: str = "", on_click: OptionalControlEventCallable = None):
self.id = id
super().__init__(
border=ft.border.all(1, color[3]),
border_radius=ft.border_radius.all(5),
content=ft.Text(
value=value,
no_wrap=False,
size=24,
color=color[3],
),
on_click=on_click
)
class DownList(ft.Container):
def __init__(self, page: ft.Page, column=[],columns={}, filter=None, load_first: bool = False):
self.columns = columns
if columns:
column = columns["name"]
self.dialog = DropdownDialog(load_first=load_first, data_set=column, on_click=self.on_click_text)
self.filter = filter
self.view_text = Text()
self._id_ = -1
super().__init__(
expand=True,
width=300,
border=ft.border.all(1, color[3]),
border_radius=ft.border_radius.all(5),
content=self.view_text,
on_click=self.open_dialog
)
def load_professions(self):
self.filter.dialog = DropdownDialog(
load_first=False,
data_set=Observer.Professions,
on_click=self.filter.on_click_text
)
def load_positions(self):
self.filter.dialog = DropdownDialog(
load_first=False,
data_set=Observer.Positions,
on_click=self.filter.on_click_text
)
def load_other_positions(self):
self.filter.dialog = DropdownDialog(
load_first=False,
data_set=[],
dismiss=self.filter.click_field
)
def update_filter_list(self):
if self.filter is None:
return
self.filter.view_text.value = ""
if self.view_text.value == "Профессии рабочих":
self.load_professions()
elif self.view_text.value == "Должности служащих, руководителей":
self.load_positions()
elif self.view_text.value == "Иные должности, не указанные в ОКПДТР":
self.load_other_positions()
self.page.update()
self.filter = self.filter
def click_field(self, e):
self.view_text.value = self.dialog.filter_text.value
self.page.close(self.dialog)
self.page.update()
def on_click_text(self, e):
if self.columns:
self._id_ = e.control.id
self.view_text.value = e.control.content.value
self.page.close(self.dialog)
self.update_filter_list()
self.page.update()
def open_dialog(self, e):
self.page.open(self.dialog)
def change_filter_text(self, e):
self.view_text.value = e.control.content.value
self.hidden_text.value = e.control.content.value
self.update()
class ObserverMethod:
@classmethod
def load_dropdown(cls, name_list: str = "", column: str = "", name_lists=[], columns=[]):
from user_data import User_data as ud
if ud.suggestions is None:
ud.suggestions = JsonJober.read_json(path_file=ud.path_json)
if columns:
return cls._load_columns(suggestions=ud.suggestions[0], columns=columns, name_list=name_list)
if name_list:
return cls._load_name_list(ud.suggestions[0], name_list, column)
return cls._load_name_lists(ud.suggestions[0], name_lists)
@staticmethod
def _load_columns(suggestions, columns, name_list=""):
lists = {}
if name_list in suggestions:
for col in columns:
lists[col] = [item for item in suggestions[name_list][col]]
return lists
@staticmethod
def _load_name_list(suggestions, name_list, column):
temp_data_list = []
if column:
if column in suggestions[name_list]:
temp_data_list = [item for item in suggestions[name_list][column]]
else:
temp_data_list = [item for item in suggestions[name_list]]
return temp_data_list
@staticmethod
def _load_name_lists(suggestions, name_lists):
temp_data_list = []
for col in name_lists:
if col in suggestions:
temp_data_list.extend([item for item in suggestions[col]])
return temp_data_list
class Observer:
inn_manufacturing_enterprises = ObserverMethod.load_dropdown(name_list="Manufacturing_enterprises", columns=["name", "inn"])
Manufacturing_enterprises = ObserverMethod.load_dropdown(name_list="Manufacturing_enterprises", column="name")
Classification_professions = ObserverMethod.load_dropdown(name_list="Classification_professions")
Professions = ObserverMethod.load_dropdown(name_list="Professions")
Positions = ObserverMethod.load_dropdown(name_list="Positions")
Specialization = ObserverMethod.load_dropdown(name_list="Specialization")
class DropdownDialog(ft.AlertDialog):
def __init__(self, data_set, dismiss: OptionalControlEventCallable = None, on_click: OptionalControlEventCallable = None, load_first: bool = False):
self.data_set = data_set
self.load_first = load_first
self.filter_text = ft.TextField(on_change=self.filter_data)
self.result = ft.Column(height=230, width=400, controls=[], scroll=True)
self.on_click = on_click
self.init_dialog()
super().__init__(
content=ft.Column(
height=300,
width=500,
controls=[
ft.Row(controls=[self.filter_text]),
self.result,
ft.Button(text="Закрыть", on_click=dismiss),
]
),
on_dismiss=dismiss
)
def init_dialog(self):
temp = []
if not self.load_first:
for itemx, item in enumerate(self.data_set):
if itemx >= 100:
self.result.controls = temp
return
temp.append(TextCellFilter(value=item, on_click=self.on_click,id=itemx))
else:
for iitem, item in enumerate(self.data_set):
temp.append(TextCellFilter(value=item, on_click=self.on_click,id=iitem))
self.result.controls = temp
def filter_data(self, e):
if len(e.control.value) < 3:
return
filter_text = e.control.value.lower()
filtered_data = []
for iitem,item in enumerate(self.data_set):
if filter_text in item.lower():
filtered_data.append(TextCellFilter(value=item, on_click=self.on_click,id=iitem))
self.result.controls = filtered_data
self.result.update()
class DataGrid(ft.DataTable):
def __init__(self, page: ft.Page):
self.page = page
self.column_name = None
self.load_column_names()
super().__init__(columns=self.add_list_column_names())
def return_data(self):
return self.rows
def load_column_names(self):
if not self.column_name:
url = "http://localhost:8001/api-data-base/get_name_column?table_name=%D0%A1ollection_form_opk"
try:
response = requests.post(url)
if response.status_code == 200:
self.column_name = response.json()
else:
print(f"Error: Received status code {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
def add_list_column_names(self):
temp_data = []
if self.column_name:
for col in self.column_name:
if col not in ["id", "ПОО", "Субъект_рф", "ИНН_ОПК", "время_создания", "указаноеремя"]:
temp_data.append(ft.DataColumn(label=Text(value=col)))
return temp_data
def load_new_row(self):
temp_data = []
temp = DownList(page=self.page, column=Observer.Professions)
for col in self.column_name:
if col in ["id", "ПОО", "Субъект_рф", "ИНН_ОПК", "время_создания", "указаноеремя"]:
continue
if col == "ОПК":
temp_data.append(ft.DataCell(content=DownList(load_first=True, page=self.page, columns=Observer.inn_manufacturing_enterprises)))
elif col == "Группа_профессий_должностей":
temp_data.append(ft.DataCell(content=DownList(filter=temp, load_first=True, page=self.page, column=Observer.Classification_professions)))
elif col == "Наименование_профессии":
temp_data.append(ft.DataCell(content=temp))
elif col == "Профессии_специальности_СПО":
temp_data.append(ft.DataCell(content=DownList(page=self.page, column=Observer.Specialization)))
else:
temp_data.append(ft.DataCell(content=Field()))
return temp_data
def add_new_row(self, e):
self.rows.append(ft.DataRow(cells=self.load_new_row()))
self.update()

39
app/dockerfile Normal file
View File

@@ -0,0 +1,39 @@
FROM python:3.11.8-slim AS build
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем файл с зависимостями в контейнер
COPY ./app/requirements.txt .
# Устанавливаем необходимые зависимости
RUN pip install -r requirements.txt
# Устанавливаем необходимые утилиты
RUN apt-get update && \
apt-get install -y wget unzip xz-utils git clang cmake ninja-build pkg-config curl libgtk-3-dev
# Устанавливаем Flutter SDK
RUN wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.24.0-stable.tar.xz && \
tar xf flutter_linux_3.24.0-stable.tar.xz && \
mv flutter /usr/local/flutter && \
git config --global --add safe.directory /usr/local/flutter && \
/usr/local/flutter/bin/flutter doctor
# Обновляем PATH для Flutter
ENV PATH="/usr/local/flutter/bin:${PATH}"
# Копируем файл с кодом в контейнер
COPY ./app/ .
# Выполняем сборку проекта
RUN flet build web
# Используем официальный образ Nginx для развертывания
FROM nginx:latest AS final
# Копируем собранные файлы в директорию Nginx
COPY --from=build /app/build/web /usr/share/nginx/html
# Копируем конфигурационный файл Nginx (если требуется)
COPY ./app/nginx.conf /etc/nginx/nginx.conf

View File

@@ -1,15 +1,17 @@
import flet as ft import flet as ft
import src.view as view from view import ViewsHendler
import designer as ds
def main(page: ft.Page): def main(page: ft.Page):
page.title = "COPP" page.title = "COPP"
def Pageloading(route): def PageLoading(route={}):
print(page.route) print(page.route)
page.views.clear() page.views.clear()
page.views.append(view.ViewsHendler(page=page)[page.route]) page.views.append(ViewsHendler(page=page)[page.route])
page.update() page.update()
page.on_route_change = Pageloading
page.on_route_change = PageLoading
page.go("/") page.go("/")
ft.app(target=main, port=80345 ,assets_dir="assets")
ft.app(main)

129
app/pages/add_new_data.py Normal file
View File

@@ -0,0 +1,129 @@
import flet as ft
from designer import color, Button, Text, DataPicer, DataGrid, DownList, Field, Loger,Observer
from user_data import User_data
import pydantic as pd
import datetime
import json
class Add_new_data(ft.View):
def __init__(self, page: ft.Page):
self.data_grid = DataGrid(page=page)
self.creation_time = datetime.datetime.today()
super().__init__(
route="/add_new_data",
bgcolor=color[2],
controls=[
ft.Column(
controls=[
ft.Container(
padding=ft.padding.all(20),
border_radius=ft.border_radius.all(10),
height=70,
expand=True,
bgcolor=color[3],
content=ft.Row(
expand=True,
controls=[
Button(text="профиль"),
Button(text="мои данные",on_click = self.go_to_my_data),
Button(text="заполнить данные", on_click=self.go_to_data),
]
)
),
ft.Container(
content=ft.Row(
scroll=True,
controls=[
ft.Column(
controls=[
self.data_grid,
Button(text="+", on_click=self.data_grid.add_new_row),
Button(text="сохранить", on_click=self.open_data_picer),
]
),
]
),
),
]
)
]
)
def send_data_to_api(self, save_data, api_url: str):
import requests
headers = {'Content-Type': 'application/json'}
response = requests.post(api_url, headers=headers, data=json.dumps(save_data))
return response
def validdate(self, item, column):
columns = {}
count_void = 0
icol = 1
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
columns[column[icol]] = current_time
icol += 1
columns[column[icol]] = str(self.creation_time.strftime("%Y-%m-%d"))
icol += 1
columns[column[icol]] = User_data.name_poo
icol += 1
columns[column[icol]] = "test"
icol += 1
for cell in item.cells:
print(f"{icol}")
if isinstance(cell.content, DownList):
if cell.content.view_text.value == "":
Loger(text="строку с пустыми выподающими списками")
break
elif cell.content._id_ != -1:
columns[column[icol]] = Observer.inn_manufacturing_enterprises["inn"][cell.content._id_]
icol += 1
columns[column[icol]] = cell.content.view_text.value
else:
columns[column[icol]] = cell.content.view_text.value
elif isinstance(cell.content, Field):
if cell.content.value == "":
columns[column[icol]] = "0"
else:
columns[column[icol]] = cell.content.value
icol += 1
return columns
def handle_change(self, e):
self.creation_time = e.control.value
self.save_data()
def handle_dismissal(self, e):
pass
def open_data_picer(self, e):
self.page.open(
DataPicer(creation_time=self.creation_time, handle_change=self.handle_change, handle_dismissal=self.handle_dismissal)
)
def save_data(self):
temp = self.data_grid.return_data()
column = self.data_grid.column_name
rows = []
for item in temp:
rows.append(self.validdate(item=item, column=column))
print(f"{rows=}")
table_name = "Сollection_form_opk"
api_url = f'http://localhost:8001/api-data-base/insert_table'
save_data_instance = {
"data": rows
}
response = self.send_data_to_api(save_data_instance, api_url)
if response.status_code == 200:
print("Данные успешно отправлены")
else:
print(f"Ошибка при отправке данных: {response.status_code}")
def go_to_info(self,e):
self.page.go("/user_info")
def go_to_my_data(self,e):
self.page.go("/my_data")
def go_to_data(self,e):
self.page.go("/add_new_data")

View File

@@ -0,0 +1,81 @@
import flet as ft
import designer as des
import requests
class Add_user_data(ft.View):
def load_table_column(self):
url = "http://localhost:8001/api-data-base/get_name_column?table_name=%D0%A1ollection_form_opk"
temp_column = []
try:
response = requests.post(url)
if response.status_code == 200:
temp = response.json()
for column in temp:
temp_column.append(ft.DataColumn(ft.Text(column)))
return temp_column
else:
print(f"Error: Received status code {response.status_code}")
return temp_column
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return temp_column
def load_return_rows(self):
from user_data import User_data as ud
url = f"http://localhost:8001/api-data-base/select_rows_with_filter?value={ud.inn}"
temp_row = []
try:
response = requests.post(url)
if response.status_code == 200:
temp = response.json()
for rows in temp:
for row in rows:
temp_row.append(ft.DataRow(
cells=[ft.DataCell(ft.Text(row[row]))]
))
return temp_row
else:
print(f"Error: Received status code {response.status_code}")
return temp_row
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return temp_row
def __init__(self,page:ft.Page,route:str=""):
self.data_grid = ft.DataTable(
columns=self.load_table_column(),
rows=self.load_return_rows()
)
super().__init__(
bgcolor=des.color[2],
route=route,
controls=[
ft.Column(
controls=[
ft.Row(
controls=[
des.Button(text="профиль"),
des.Button(text="мои данные",on_click = self.go_to_my_data),
des.Button(text="заполнить данные", on_click=self.go_to_data),
]
),
ft.Row(
expand=True,
controls=[
self.data_grid
]
)
]
)
]
)
def go_to_info(self,e):
self.page.go("/user_info")
def go_to_my_data(self,e):
self.page.go("/my_data")
def go_to_data(self,e):
self.page.go("/add_new_data")

89
app/pages/login.py Normal file
View File

@@ -0,0 +1,89 @@
import flet as ft
from designer import Field, color, Button, Loger, JsonJober
import requests
class Login(ft.Column):
def __init__(self, page: ft.Page = None):
self.page = page
self.login = Field(label="Login",width=300),
self.password = Field(label="Password", password=True,width=300)
super().__init__(
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
expand=True,
controls=[
ft.Row(
expand=True
),
ft.Row(
alignment=ft.MainAxisAlignment.CENTER,
controls=[
ft.Text(color=color[1], size=42, value="Copp")
]
),
ft.Row(
expand=True
),
ft.Row(
alignment=ft.MainAxisAlignment.CENTER,
controls=[
self.login[0]
]
),
ft.Row(
alignment=ft.MainAxisAlignment.CENTER,
controls=[
self.password
]
),
ft.Row(
height=12
),
ft.Row(
alignment=ft.MainAxisAlignment.CENTER,
controls=[
Button(text="Enter", on_click=self.enter_go_to_next_page)
]
),
ft.Row(
expand=True
),
]
)
def Load_user_inf(self, response):
from user_data import User_data as ud
if response is not None:
# Используем .get() для безопасного доступа к ключам
ud.id = response.get("id")
ud.inn = response.get("inn") # Если ключ 'inn' отсутствует, будет None
ud.name_poo = response.get("name_poo")
ud.mail = response.get("mail")
ud.suggestions = JsonJober.read_json(ud.path_json)
print(f"{ud.id=}, {ud.inn=}, {ud.name_poo=}, {ud.mail=}")
else:
print("Ошибка: Пустой ответ от сервера")
def enter_go_to_next_page(self, e):
url = "http://localhost:8001/api-data-base/login" # исправленный путь
data = {
"login": self.login[0].value,
"password": self.password.value
}
try:
response = requests.post(url, json=data)
if response.status_code == 200:
# Получаем JSON-данные из ответа
response_data = response.json() # Используем метод .json() для извлечения данных
# Проверяем сообщение в ответе
if response_data.get("id") != 0:
self.Load_user_inf(response=response_data)
self.page.go("/user_info")
else:
self.page.open(Loger(text="Неверные данные"))
else:
print("Ошибка:", response.status_code, response.text)
except requests.exceptions.RequestException as e:
print(f"Ошибка запроса: {e}")

87
app/pages/my_data.py Normal file
View File

@@ -0,0 +1,87 @@
import flet as ft
import designer as des
import requests
class My_data(ft.View):
def load_table_column(self):
url = "http://localhost:8001/api-data-base/get_name_column?table_name=%D0%A1ollection_form_opk"
temp_column = []
try:
response = requests.post(url)
if response.status_code == 200:
temp = response.json()
# Пропускаем первый столбец (id)
for column in temp[1:]:
temp_column.append(ft.DataColumn(ft.Text(column)))
return temp_column
else:
print(f"Error: Received status code {response.status_code}")
return temp_column
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return temp_column
def load_return_rows(self):
from user_data import User_data as ud
url = f"http://localhost:8001/api-data-base/select_rows_with_filter?value={str(ud.inn)}"
temp_row = []
try:
response = requests.post(url)
if response.status_code == 200:
temp = response.json()
for row in temp:
# Пропускаем первый столбец (id)
values = list(row.values())[1:]
temp_row.append(
ft.DataRow(
cells=[ft.DataCell(ft.Text(str(value))) for value in values]
)
)
return temp_row
else:
print(f"Error: Received status code {response.status_code}")
return temp_row
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return temp_row
def __init__(self,page:ft.Page,route:str=""):
self.data_grid = ft.DataTable(
columns=self.load_table_column(),
rows=self.load_return_rows()
)
super().__init__(
bgcolor=des.color[2],
route=route,
controls=[
ft.Column(
controls=[
ft.Row(
controls=[
des.Button(text="профиль"),
des.Button(text="мои данные",on_click = self.go_to_my_data),
des.Button(text="заполнить данные", on_click=self.go_to_data),
]
),
ft.Row(
expand=True,
scroll=True,
controls=[
self.data_grid
]
)
]
)
]
)
def go_to_info(self,e):
self.page.go("/user_info")
def go_to_my_data(self,e):
self.page.go("/my_data")
def go_to_data(self,e):
self.page.go("/add_new_data")

5
app/pages/test.py Normal file
View File

@@ -0,0 +1,5 @@
import flet as ft
import designer as ds
import requests
from user_data import User_data as ud

48
app/pages/user_info.py Normal file
View File

@@ -0,0 +1,48 @@
import flet as ft
from designer import color, Button,Text
from user_data import User_data
class User_info(ft.View):
def __init__(self, page:ft.Page):
super().__init__(
route="/user_info",
bgcolor=color[2],
controls=[
ft.Column(
controls=[
ft.Container(
padding=ft.padding.all(20),
border_radius=ft.border_radius.all(10),
height=70,
expand=True,
bgcolor=color[3],
content=ft.Row(
expand=True,
controls=[
Button(text="профиль"),
Button(text="мои данные",on_click = self.go_to_my_data),
Button(text="заполнить данные", on_click=self.go_to_data),
]
)
),
ft.Container(
expand=True,
content=ft.Column(
controls=[
ft.Row(controls=[Text(value="Имя ПОО"),Text(value=User_data.name_poo)]),
ft.Row(controls=[Text(value="ИНН"),Text(value=User_data.inn)]),
ft.Row(controls=[Text(value="Mail"),Text(value=User_data.mail)])
]
)
)
]
)
]
)
def go_to_info(self,e):
self.page.go("/user_info")
def go_to_my_data(self,e):
self.page.go("/my_data")
def go_to_data(self,e):
self.page.go("/add_new_data")

5
app/requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
flet
flet-core
flet-desktop
bcrypt
requests

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,14 +0,0 @@
import flet as ft
from src.base_models.colour import color
from flet.core.types import OptionalControlEventCallable
class Button(ft.ElevatedButton):
def __init__(self, width:int=150, height:int=40, text:str="",color:str=color[3], on_click: OptionalControlEventCallable = None,):
super().__init__(
width=width,
height=height,
text=text,
#color=color,
bgcolor=color,
on_click=on_click,
)

View File

@@ -1,7 +0,0 @@
color = [
"#f0ece2",
"#dfd3c3",
"#c7b198",
"#596e79",
"#7E93A0FF"
]

View File

@@ -1,13 +0,0 @@
import flet as ft
from src.base_models.colour import color
class TextField(ft.TextField):
def __init__(self, width:int=250,height:int=40,password:bool=False,bgcolor:str=color[3]):
super().__init__(
width=width,
height=height,
password=password
)

View File

@@ -1,71 +0,0 @@
import flet as ft
from src.base_models.text_fuild import TextField
from src.base_models.colour import color
from src.base_models.button import Button
#from src.storage.user_data import user_data
import requests
from requests.exceptions import RequestException
class LoginPage(ft.View):
def __init__(self,page:ft.Page,route:str,bgcolor:str):
self.page = page
self.login = TextField()
self.password = TextField(password=True)
next_button = Button(text="login",on_click=self.button_login,)
super().__init__(
route=route,
bgcolor=bgcolor,
vertical_alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
controls=[
ft.Row(height=5),
ft.Row(
height=30,
controls=[ft.Text(size=25,value="COPP",color=color[3])],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Row(controls=[self.login],alignment=ft.MainAxisAlignment.CENTER),
ft.Row(controls=[self.password],alignment=ft.MainAxisAlignment.CENTER),
ft.Row(controls=[next_button],alignment=ft.MainAxisAlignment.CENTER),
]
)
def button_login(self,e):
login_return = self.login_user(login=self.login.value,password=self.password.value)
print(f"{login_return.get("id")=}")
if login_return.get('id') == 0 or login_return.get('id') == None:
self.page.open(ft.SnackBar(ft.Text(f"Неверный логин или пароль")))
self.page.update
else:
# user_data.load_data(
# id=login_return.get('id'),
# name_poo=login_return.get('name_poo'),
# inn=login_return.get('inn'),
# mail=login_return.get('mail')
# )
self.page.go("/main")
def login_user(self,login:str,password:str) -> dict:
url = "http://localhost:8001/api-data-base/login"
headers = {
"accept": "application/json",
"Content-Type": "application/json"
}
payload = {
"login": login,
"password": password
}
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status() # Вызовет исключение при ошибке HTTP
return response.json()
except RequestException as e:
print(f"Ошибка запроса: {e}")
return {"error": str(e)}
except ValueError as e:
print(f"Ошибка парсинга JSON: {e}")
return {"error": "Invalid JSON response"}

View File

@@ -1,80 +0,0 @@
import flet as ft
from src.base_models.text_fuild import TextField
from src.base_models.colour import color
from src.base_models.button import Button
import requests
from requests.exceptions import RequestException
class MainPage(ft.View):
def __init__(self,page:ft.Page,route:str,bgcolor:str):
self.page = page
#table=ft.DataTable(
#
#)
dd = ft.Dropdown(
label="таблица",
options=[
ft.DropdownOption(text="Сollection_form_opk")
]
)
rail = ft.NavigationRail(
selected_index=0,
label_type=ft.NavigationRailLabelType.ALL,
#extended=True,
height=1000,
min_width=100,
min_extended_width=200,
bgcolor=color[3],
group_alignment=-0.4,
destinations=[
ft.NavigationRailDestination(
icon=ft.Icons.HOME_OUTLINED,
selected_icon=ft.Icons.HOME_SHARP,
label="Home"
),
ft.NavigationRailDestination(
icon=ft.Icon(ft.Icons.BOOKMARK_BORDER),
selected_icon=ft.Icon(ft.Icons.BOOKMARK),
label="Second",
),
ft.NavigationRailDestination(
icon=ft.Icons.SETTINGS_OUTLINED,
selected_icon=ft.Icon(ft.Icons.SETTINGS),
label="Second",
),
],
on_change=lambda e: print("Selected destination:", e.control.selected_index),
)
super().__init__(
route=route,
bgcolor=bgcolor,
controls=[
ft.Row(
expand=True,
controls=[
rail,
ft.VerticalDivider(width=1),
ft.Column(
alignment=ft.MainAxisAlignment.START,
expand=True,
controls=[
ft.Text("Добро пожаловать!"),
dd
],
),
]
)
]
)

View File

@@ -1,10 +0,0 @@
import flet as ft
from src.pages.login import LoginPage
from src.pages.main import MainPage
from src.base_models.colour import color
def ViewsHendler(page:ft.Page):
return {
"/":LoginPage(page=page,route='/',bgcolor=color[2]),
"/main":MainPage(page=page,route='/main',bgcolor=color[2]),
}

7
app/user_data.py Normal file
View File

@@ -0,0 +1,7 @@
class User_data():
id:int=0
name_poo:str=""
inn:str=""
mail:str=""
suggestions = None
path_json:str="./assets/test.json"

17
app/view.py Normal file
View File

@@ -0,0 +1,17 @@
import flet as ft
from designer import color
from pages import login,user_info,add_new_data,my_data
def ViewsHendler(page:ft.Page):
return {
"/":ft.View(
route="/",
controls=[
login.Login(page=page)
],
bgcolor=color[2]
),
"/user_info":user_info.User_info(page=page),
"/my_data":my_data.My_data(page=page),
"/add_new_data":add_new_data.Add_new_data(page=page)
}

View File

@@ -38,17 +38,16 @@ services:
# - NAVICAT_MONITOR_DB_USER=postgres # - NAVICAT_MONITOR_DB_USER=postgres
# - NAVICAT_MONITOR_DB_PASSWORD=postgres # - NAVICAT_MONITOR_DB_PASSWORD=postgres
# #
#api: api:
# image: copp:api-lasted image: copp:api-lasted
# container_name: api container_name: api
# #restart: always #restart: always
# ports: ports:
# - "8000:8000" - "8000:8000"
# networks:
# - backend build:
# build: context: .
# context: . dockerfile: ./server/api/dockerfile
# dockerfile: ./server/api/dockerfile
api_data_base: api_data_base:
image: copp:api-data-base-lasted image: copp:api-data-base-lasted
@@ -56,8 +55,7 @@ services:
#restart: always #restart: always
ports: ports:
- "8001:8001" - "8001:8001"
networks:
- backend
build: build:
context: . context: .
dockerfile: ./server/api-data-base/dockerfile dockerfile: ./server/api-data-base/dockerfile
@@ -90,16 +88,15 @@ services:
#restart: always #restart: always
ports: ports:
- "5432:5432" - "5432:5432"
networks:
- backend
environment: environment:
- POSTGRES_PASSWORD=postgres - POSTGRES_PASSWORD=postgres
- PGDATA=/var/lib/postgresql/data/pgdata - PGDATA=/var/lib/postgresql/data/pgdata
volumes: volumes:
- ./differen/data_base_hesh:/var/lib/postgresql/data - ./differen/data_base_hesh:/var/lib/postgresql/data
networks: #networks:
backend: # backend:
driver: bridge # driver: bridge
#docker compose -f "docker-compose.yml" up -d --build #docker compose -f "docker-compose.yml" up -d --build

6
main.py Normal file
View File

@@ -0,0 +1,6 @@
def main():
print("Hello from copp!")
if __name__ == "__main__":
main()

View File

@@ -6,9 +6,9 @@ readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"bcrypt>=4.3.0", "bcrypt>=4.3.0",
"flet-core>=0.24.1", "confluent-kafka>=2.10.0",
"flet-desktop>=0.27.6", "flet[all]>=0.28.2",
"flet[all]>=0.27.6", "kafka>=1.3.5",
"requests>=2.32.3", "redis>=6.0.0",
"sqlalchemy>=2.0.40", "sqlalchemy>=2.0.41",
] ]

View File

@@ -4,9 +4,9 @@ import re
class Settings(GenericModel): class Settings(GenericModel):
DB_HOST: str = "postgres"#"postgres" DB_HOST: str = "postgres"#"postgres"
DB_PORT: int = 5432 DB_PORT: int = 5432
DB_USER: str = "tester" DB_USER: str = "postgres"
DB_PASS: str = "tester" DB_PASS: str = "postgres"
DB_NAME: str = "test" DB_NAME: str = "postgres"
@classmethod @classmethod
def validate_db_port(cls, v): def validate_db_port(cls, v):

View File

@@ -4,6 +4,7 @@ import json
import model_messege as mm import model_messege as mm
from sqlalchemy.future import select from sqlalchemy.future import select
from sqlalchemy import MetaData, inspect, text, insert, Table from sqlalchemy import MetaData, inspect, text, insert, Table
import bcrypt
class JsonJober: class JsonJober:
data = [] data = []
@@ -51,6 +52,15 @@ class Request():
await cls.craete_all_table() await cls.craete_all_table()
mass_table = JsonJober.ReadJson(path_file="./data_base/preset/test.json") mass_table = JsonJober.ReadJson(path_file="./data_base/preset/test.json")
await cls.insert_mass_table(mass_table=mass_table) await cls.insert_mass_table(mass_table=mass_table)
user_data = {
"inn": "123",
"password": bcrypt.hashpw("123".encode("utf-8"), bcrypt.gensalt()).decode('utf-8'),
"mail": "123",
"name_poo": "123"
}
await cls.insert_table(data={"data": [user_data]}, table_name="users")
@classmethod @classmethod
async def insert_mass_table(cls, mass_table): async def insert_mass_table(cls, mass_table):

View File

@@ -5,4 +5,4 @@ sqlalchemy
asyncpg asyncpg
psycopg2-binary psycopg2-binary
bcrypt bcrypt
orjson orjson

View File

@@ -69,14 +69,7 @@ async def insert_table(data:dict):
"""Добавление записи""" """Добавление записи"""
return await Request.insert_table(data=data) return await Request.insert_table(data=data)
@router.post("select_rows_with_filter") @router.post("/select_rows_with_filter")
async def select_rows_with_filter(value:str): async def select_rows_with_filter(value:str):
"""Выборка с фильтром""" """Выборка с фильтром"""
return await Request.select_rows_with_filter(value=value) return await Request.select_rows_with_filter(value=value)
@router.get("get_table_name")
async def get_table_name(data:dict):
return await {
"name":"Сollection_form_opk",
"name_bd":"Сollection_form_opk"
}

2
server/api/README.md Normal file
View File

@@ -0,0 +1,2 @@
# api
api-copp

View File

15
server/api/dockerfile Normal file
View File

@@ -0,0 +1,15 @@
# Используем официальный образ Python
FROM python:3.11.8-slim AS build
# Устанавливаем рабочую директорию
WORKDIR /api
# Копируем файл с зависимостями в контейнер
COPY ./server/api/requirements.txt .
# Устанавливаем необходимые зависимости
RUN pip install -r requirements.txt
# Копируем файл с кодом в контейнер
COPY ./server/api/ .
FROM build AS final
WORKDIR /api
# Команда для запуска приложения
CMD ["python", "-u", "main.py"]

9
server/api/main.py Normal file
View File

@@ -0,0 +1,9 @@
from fastapi import FastAPI
from router import router
app = FastAPI(title="api")
app.include_router(router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@@ -0,0 +1,13 @@
from pydantic import BaseModel
class collection_form(BaseModel):
login:str
data:list
class Message(BaseModel):
key: str
value: str
class Login(BaseModel):
login:str
password:str

View File

@@ -0,0 +1,6 @@
fastapi
uvicorn
kafka-python
python-dotenv
redis
httpx

41
server/api/router.py Normal file
View File

@@ -0,0 +1,41 @@
import model_messege as mm
from fastapi import APIRouter
import httpx
router = APIRouter(prefix="/api")
@router.post("/api/validation_collection_form")
async def validation_collection_form():
return {
"message": "Ошибка валидации формы"
}
@router.post("/api/login")
async def login(auth_request: mm.Login):
login = auth_request.login
password = auth_request.password
print(f"login: {login}, password: {password}")
# Отправляем данные в другую API
async with httpx.AsyncClient() as client:
response = await client.post("http://api-data-base:8001/api-data-base/login", json={'login': login, 'password': password})
# Проверяем статус ответа
if response.status_code == 200:
user_data = response.json()
return {
"message": "Авторизация успешна.",
"user": user_data
}
else:
return {
"message": "Ошибка авторизации",
"user": None
}
@router.on_event("shutdown")
def shutdown_event():
producer.close()

873
uv.lock generated

File diff suppressed because it is too large Load Diff