Compare commits
3 Commits
new-web-in
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b024ea50b2 | |||
| cedb00bb4c | |||
|
|
e8343313a9 |
165
app/.gitignore
vendored
165
app/.gitignore
vendored
@@ -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/
|
|
||||||
@@ -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
8844
app/assets/test.json
Normal file
File diff suppressed because it is too large
Load Diff
317
app/designer.py
Normal file
317
app/designer.py
Normal 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
39
app/dockerfile
Normal 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
|
||||||
14
app/main.py
14
app/main.py
@@ -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
129
app/pages/add_new_data.py
Normal 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")
|
||||||
81
app/pages/add_user_data.py
Normal file
81
app/pages/add_user_data.py
Normal 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
89
app/pages/login.py
Normal 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
87
app/pages/my_data.py
Normal 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
5
app/pages/test.py
Normal 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
48
app/pages/user_info.py
Normal 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
5
app/requirements.txt
Normal 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 |
@@ -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,
|
|
||||||
)
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
color = [
|
|
||||||
"#f0ece2",
|
|
||||||
"#dfd3c3",
|
|
||||||
"#c7b198",
|
|
||||||
"#596e79",
|
|
||||||
"#7E93A0FF"
|
|
||||||
]
|
|
||||||
@@ -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
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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"}
|
|
||||||
@@ -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
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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
7
app/user_data.py
Normal 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
17
app/view.py
Normal 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)
|
||||||
|
}
|
||||||
@@ -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
6
main.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def main():
|
||||||
|
print("Hello from copp!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -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",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ sqlalchemy
|
|||||||
asyncpg
|
asyncpg
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
bcrypt
|
bcrypt
|
||||||
orjson
|
orjson
|
||||||
|
|||||||
@@ -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
2
server/api/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# api
|
||||||
|
api-copp
|
||||||
0
server/api/config_kafka.py
Normal file
0
server/api/config_kafka.py
Normal file
15
server/api/dockerfile
Normal file
15
server/api/dockerfile
Normal 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
9
server/api/main.py
Normal 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)
|
||||||
13
server/api/model_messege.py
Normal file
13
server/api/model_messege.py
Normal 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
|
||||||
6
server/api/requirements.txt
Normal file
6
server/api/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
kafka-python
|
||||||
|
python-dotenv
|
||||||
|
redis
|
||||||
|
httpx
|
||||||
41
server/api/router.py
Normal file
41
server/api/router.py
Normal 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()
|
||||||
Reference in New Issue
Block a user