1234
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
data_base_hesh
|
||||
__pycache__
|
||||
.venv/
|
||||
.venv
|
||||
.nextcloudsync.log
|
||||
.sync_37d254c8843a.db
|
||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"python.testing.unittestArgs": [
|
||||
"-v",
|
||||
"-s",
|
||||
"./app",
|
||||
"-p",
|
||||
"*test*.py"
|
||||
],
|
||||
"python.testing.pytestEnabled": false,
|
||||
"python.testing.unittestEnabled": true
|
||||
}
|
||||
2
app/.gitignore
vendored
Normal file
2
app/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
data_base_hesh
|
||||
__pycache__
|
||||
2
app/README.md
Normal file
2
app/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# user_app
|
||||
user_app_copp
|
||||
8844
app/assets/test.json
Normal file
8844
app/assets/test.json
Normal file
File diff suppressed because it is too large
Load Diff
311
app/designer.py
Normal file
311
app/designer.py
Normal file
@@ -0,0 +1,311 @@
|
||||
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):
|
||||
super().__init__(
|
||||
max_lines=1,
|
||||
border_color=color[3],
|
||||
color=color[3],
|
||||
text_size=24,
|
||||
label=label,
|
||||
expand=True,
|
||||
password=password,
|
||||
can_reveal_password=password,
|
||||
label_style=ft.TextStyle(
|
||||
color=color[3],
|
||||
size=24
|
||||
),
|
||||
)
|
||||
|
||||
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
|
||||
16
app/main.py
Normal file
16
app/main.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import flet as ft
|
||||
from view import ViewsHendler
|
||||
import designer as ds
|
||||
|
||||
def main(page: ft.Page):
|
||||
page.title = "COPP"
|
||||
def PageLoading(route):
|
||||
print(page.route)
|
||||
page.views.clear()
|
||||
page.views.append(ViewsHendler(page=page)[page.route])
|
||||
page.update()
|
||||
page.on_route_change = PageLoading
|
||||
page.go("/")
|
||||
|
||||
ft.app(target=main, port=80345 ,assets_dir="assets")
|
||||
|
||||
121
app/pages/add_new_data.py
Normal file
121
app/pages/add_new_data.py
Normal file
@@ -0,0 +1,121 @@
|
||||
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="мои данные"),
|
||||
Button(text="заполнить данные"),
|
||||
]
|
||||
)
|
||||
),
|
||||
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}")
|
||||
20
app/pages/add_user_data.py
Normal file
20
app/pages/add_user_data.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import flet as ft
|
||||
import designer as des
|
||||
|
||||
class Add_user_data(ft.View):
|
||||
def __init__(self,page:ft.Page,route:str=""):
|
||||
super().__init__(
|
||||
bgcolor=des.color[2],
|
||||
route=route,
|
||||
controls=[
|
||||
ft.Column(
|
||||
controls=[
|
||||
ft.Row(
|
||||
controls=[
|
||||
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
80
app/pages/login.py
Normal file
80
app/pages/login.py
Normal file
@@ -0,0 +1,80 @@
|
||||
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"),
|
||||
self.password = Field(label="Password", password=True)
|
||||
super().__init__(
|
||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||
expand=True,
|
||||
controls=[
|
||||
ft.Row(
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
controls=[
|
||||
ft.Text(color=color[1], size=42, value="Copp")
|
||||
]
|
||||
),
|
||||
ft.Row(
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
controls=[
|
||||
self.login[0]
|
||||
]
|
||||
),
|
||||
ft.Row(
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
controls=[
|
||||
self.password
|
||||
]
|
||||
),
|
||||
ft.Row(
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
controls=[
|
||||
Button(text="Enter", on_click=self.enter_go_to_next_page)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
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:8000/api/api/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("message") == "Авторизация успешна.":
|
||||
user = response_data.get("user") # Достаем пользователя из ответа
|
||||
if user.get("id") == 0:
|
||||
self.page.open(Loger(text="Неверные данные"))
|
||||
else:
|
||||
self.Load_user_inf(response=user)
|
||||
self.page.go("/user_info")
|
||||
elif response_data.get("message") == "Неверные данные":
|
||||
self.page.open(Loger(text="Неверные данные"))
|
||||
else:
|
||||
print("Ошибка:", response.status_code, response.text)
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Ошибка запроса: {e}")
|
||||
32
app/pages/my_data.py
Normal file
32
app/pages/my_data.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import flet as ft
|
||||
import designer as des
|
||||
|
||||
class My_data(ft.View):
|
||||
def __init__(self,page:ft.Page,route:str="/my_data"):
|
||||
super().__init__(
|
||||
bgcolor=des.color[2],
|
||||
route=route,
|
||||
controls=[
|
||||
ft.Column(
|
||||
|
||||
controls=[
|
||||
ft.Container(
|
||||
padding=ft.padding.all(20),
|
||||
border_radius=ft.border_radius.all(10),
|
||||
height=70,
|
||||
expand=True,
|
||||
bgcolor=des.color[3],
|
||||
content=ft.Row(
|
||||
expand=True,
|
||||
controls=[
|
||||
des.Button(text="профиль"),
|
||||
des.Button(text="мои данные",on_click = self.go_to_my_data),
|
||||
des.Button(text="заполнить данные", on_click=self.go_to_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
|
||||
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)
|
||||
}
|
||||
1
differen/.gitignore
vendored
Normal file
1
differen/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
data_base_hesh
|
||||
2
differen/README.md
Normal file
2
differen/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# differen
|
||||
|
||||
18
differen/docker-compose-pastgres.yml
Normal file
18
differen/docker-compose-pastgres.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
container_name: postgres
|
||||
#restart: always
|
||||
ports:
|
||||
-5000:5432
|
||||
networks:
|
||||
- backend
|
||||
volumes:
|
||||
-C:/Users/Administrator/Documents/data-base-docker = /var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- PGDATA = /var/lib/postgresql/data/pgdata
|
||||
networks:
|
||||
backend:
|
||||
driver: bridge
|
||||
|
||||
23
differen/docker-compose_kafka+redis.yml
Normal file
23
differen/docker-compose_kafka+redis.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
kafka:
|
||||
image: 'bitnami/kafka:latest'
|
||||
networks:
|
||||
- backend
|
||||
environment:
|
||||
- KAFKA_CFG_NODE_ID=1
|
||||
- KAFKA_CFG_PROCESS_ROLES=controller,broker
|
||||
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
|
||||
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
||||
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
|
||||
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
|
||||
|
||||
redis:
|
||||
image: 'redis:latest'
|
||||
networks:
|
||||
- backend
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
networks:
|
||||
backend:
|
||||
driver: bridge
|
||||
2
differen/topics.txt
Normal file
2
differen/topics.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
auth-topic - возвращает авторизацию
|
||||
login-topic - отправка данных авторизации
|
||||
105
docker-compose.yml
Normal file
105
docker-compose.yml
Normal file
@@ -0,0 +1,105 @@
|
||||
services:
|
||||
|
||||
#app:
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ./app/dockerfile
|
||||
# container_name: app
|
||||
# ports:
|
||||
# - "80:80"
|
||||
# networks:
|
||||
# - backend
|
||||
# depends_on:
|
||||
# - redis
|
||||
# - postgres
|
||||
|
||||
#service:
|
||||
# image: copp:service-login-latest
|
||||
# container_name: login
|
||||
# #restart: always
|
||||
# networks:
|
||||
# - backend
|
||||
# depends_on:
|
||||
# - kafka
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ./server/service-login/dockerfile
|
||||
#
|
||||
#navicat-monitor:
|
||||
# image: navicat/navicatmonitor:3.1.2
|
||||
# container_name: navicat-monitor
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
# networks:
|
||||
# - backend
|
||||
# environment:
|
||||
# - NAVICAT_MONITOR_DB_HOST=postgres
|
||||
# - NAVICAT_MONITOR_DB_PORT=5432
|
||||
# - NAVICAT_MONITOR_DB_USER=postgres
|
||||
# - NAVICAT_MONITOR_DB_PASSWORD=postgres
|
||||
#
|
||||
api:
|
||||
image: copp:api-lasted
|
||||
container_name: api
|
||||
#restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
networks:
|
||||
- backend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./server/api/dockerfile
|
||||
|
||||
api_data_base:
|
||||
image: copp:api-data-base-lasted
|
||||
container_name: api-data-base
|
||||
#restart: always
|
||||
ports:
|
||||
- "8001:8001"
|
||||
networks:
|
||||
- backend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./server/api-data-base/dockerfile
|
||||
|
||||
#kafka:
|
||||
# image: 'bitnami/kafka:latest'
|
||||
# container_name: kafka
|
||||
# networks:
|
||||
# - backend
|
||||
# environment:
|
||||
# - KAFKA_CFG_NODE_ID=0
|
||||
# - KAFKA_CFG_PROCESS_ROLES=controller,broker
|
||||
# - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
|
||||
# - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
||||
# - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
|
||||
# - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
|
||||
#
|
||||
#
|
||||
#redis:
|
||||
# image: 'redis:latest'
|
||||
# container_name: redis
|
||||
# networks:
|
||||
# - backend
|
||||
# ports:
|
||||
# - "6379:6379"
|
||||
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
container_name: postgres
|
||||
#restart: always
|
||||
ports:
|
||||
- "5432:5432"
|
||||
networks:
|
||||
- backend
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- ./differen/data_base_hesh:/var/lib/postgresql/data
|
||||
|
||||
networks:
|
||||
backend:
|
||||
driver: bridge
|
||||
|
||||
#docker compose -f "docker-compose.yml" up -d --build
|
||||
2
server/api-data-base/.env
Normal file
2
server/api-data-base/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
DOCS_USERNAME=your_username
|
||||
DOCS_PASSWORD=your_password
|
||||
2
server/api-data-base/README.md
Normal file
2
server/api-data-base/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# api-data-base
|
||||
|
||||
32
server/api-data-base/data_base/config.py
Normal file
32
server/api-data-base/data_base/config.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from pydantic.generics import GenericModel
|
||||
import re
|
||||
|
||||
class Settings(GenericModel):
|
||||
DB_HOST: str = "postgres"#"postgres"
|
||||
DB_PORT: int = 5432
|
||||
DB_USER: str = "tester"
|
||||
DB_PASS: str = "tester"
|
||||
DB_NAME: str = "test"
|
||||
|
||||
@classmethod
|
||||
def validate_db_port(cls, v):
|
||||
# Проверка и преобразование DB_PORT
|
||||
if isinstance(v, str):
|
||||
# Используем регулярное выражение для извлечения целого числа
|
||||
match = re.search(r'\d+', v)
|
||||
if match:
|
||||
return int(match.group())
|
||||
else:
|
||||
raise ValueError(f"Invalid DB_PORT value: {v}")
|
||||
return v
|
||||
|
||||
@property
|
||||
def DATABASE_URL_asyncpg(self) -> str:
|
||||
return f"postgresql+asyncpg://{self.DB_USER}:{self.DB_PASS}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
|
||||
|
||||
@property
|
||||
def DATABASE_URL_syncpg(self) -> str:
|
||||
return f"postgresql+psycopg2://{self.DB_USER}:{self.DB_PASS}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
|
||||
|
||||
# Загрузка настроек из файла .env
|
||||
settings = Settings()
|
||||
18
server/api-data-base/data_base/data_base.py
Normal file
18
server/api-data-base/data_base/data_base.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.orm import sessionmaker, DeclarativeBase
|
||||
from sqlalchemy import create_engine
|
||||
from data_base.config import settings
|
||||
|
||||
# Создание асинхронного движка
|
||||
async_engine = create_async_engine(url=settings.DATABASE_URL_asyncpg, echo=True)
|
||||
|
||||
# Создание синхронного движка
|
||||
sync_engine = create_engine(url=settings.DATABASE_URL_syncpg, echo=True)
|
||||
|
||||
# Создание асинхронной и синхронной сессий
|
||||
async_session = async_sessionmaker(async_engine)
|
||||
sync_session = sessionmaker(sync_engine)
|
||||
|
||||
# Базовый класс для декларативных моделей
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
94
server/api-data-base/data_base/models/main_tables.py
Normal file
94
server/api-data-base/data_base/models/main_tables.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from sqlalchemy import Integer, String,PrimaryKeyConstraint,UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from data_base.data_base import Base
|
||||
|
||||
|
||||
|
||||
class users(Base):
|
||||
__tablename__ = "users"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
name_poo:Mapped[str]=mapped_column(String(50))
|
||||
inn:Mapped[str]=mapped_column(String(50))#login
|
||||
password:Mapped[str]=mapped_column(String(120))
|
||||
mail:Mapped[str]=mapped_column(String(50))
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint('id', name='user_pk'),
|
||||
UniqueConstraint('inn')
|
||||
)
|
||||
|
||||
class Classification_professions(Base):
|
||||
"""Классификация профессий"""
|
||||
__tablename__ = "Classification_professions"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
name:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint('id', name='id_pk_Classification_professions'),
|
||||
UniqueConstraint('name')
|
||||
)
|
||||
|
||||
class Professions(Base):
|
||||
"""Профессии"""
|
||||
__tablename__ = "Professions"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
name:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint('id', name='id_pk_Professions'),
|
||||
UniqueConstraint('name')
|
||||
)
|
||||
|
||||
class Positions(Base):
|
||||
"""Должность"""
|
||||
__tablename__ = "Positions"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
name:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint('id', name='id_pk_Positions'),
|
||||
UniqueConstraint('name')
|
||||
)
|
||||
|
||||
class Specialization(Base):
|
||||
"""Специализация"""
|
||||
__tablename__ = "Specialization"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
name:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint('id', name='id_pk_Specialization'),
|
||||
UniqueConstraint('name')
|
||||
)
|
||||
|
||||
class Manufacturing_enterprises(Base):
|
||||
"""
|
||||
производственные предприятия
|
||||
(id:int,name:str,INN_company:str)
|
||||
"""
|
||||
__tablename__ = "Manufacturing_enterprises"
|
||||
id:Mapped[int]=mapped_column(Integer(),primary_key=True)
|
||||
name:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
INN_company:Mapped[str]=mapped_column(String(), nullable=False)
|
||||
|
||||
|
||||
class Enterprises(Base):
|
||||
"""Предприятия"""
|
||||
__tablename__ = "Enterprises"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
name:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint('id', name='id_pk_Enterprises'),
|
||||
UniqueConstraint('name')
|
||||
)
|
||||
|
||||
class Сollection_form_opk(Base):
|
||||
"""Поо"""
|
||||
__tablename__="Сollection_form_opk"
|
||||
id:Mapped[int]=mapped_column(Integer(), primary_key=True)
|
||||
время_создания:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
указаное_время:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
ПОО:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
Субъект_рф:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
ИНН_ОПК:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
ОПК:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
Группа_профессий_должностей:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
Наименование_профессии:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
Разряд:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
Профессии_специальности_СПО:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
Количество_вакансий:Mapped[str]=mapped_column(String(),nullable=False)
|
||||
8841
server/api-data-base/data_base/preset/test.json
Normal file
8841
server/api-data-base/data_base/preset/test.json
Normal file
File diff suppressed because it is too large
Load Diff
154
server/api-data-base/data_base/requests.py
Normal file
154
server/api-data-base/data_base/requests.py
Normal file
@@ -0,0 +1,154 @@
|
||||
from data_base.data_base import async_session, Base, async_engine
|
||||
import data_base.models.main_tables as mt
|
||||
import json
|
||||
import model_messege as mm
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy import MetaData, inspect, text, insert, Table
|
||||
|
||||
class JsonJober:
|
||||
data = []
|
||||
|
||||
@classmethod
|
||||
def create_table(cls, table_name):
|
||||
""""Создание объектов таблицы"""
|
||||
temp_table = []
|
||||
if mt.Professions.__tablename__ == table_name:
|
||||
for row in JsonJober.data[table_name]:
|
||||
temp_table.append(mt.Professions(name=row))
|
||||
print(f"Added to Professions: {row}")
|
||||
elif mt.Positions.__tablename__ == table_name:
|
||||
for row in JsonJober.data[table_name]:
|
||||
temp_table.append(mt.Positions(name=row))
|
||||
print(f"Added to Positions: {row}")
|
||||
elif mt.Classification_professions.__tablename__ == table_name:
|
||||
for row in JsonJober.data[table_name]:
|
||||
temp_table.append(mt.Classification_professions(name=row))
|
||||
print(f"Added to Classification_professions: {row}")
|
||||
elif mt.Manufacturing_enterprises.__tablename__ == table_name:
|
||||
for i, name in enumerate(JsonJober.data[table_name]["name"]):
|
||||
inn = JsonJober.data[table_name]["inn"][i]
|
||||
temp_table.append(mt.Manufacturing_enterprises(name=name, INN_company=inn))
|
||||
print(f"Added to Manufacturing_enterprises: {name}, {inn}")
|
||||
else:
|
||||
print(f"Unknown table name: {table_name}")
|
||||
return temp_table
|
||||
|
||||
@classmethod
|
||||
def ReadJson(cls, path_file: str = ""):
|
||||
"""Чтение json файла"""
|
||||
JsonJober.data = []
|
||||
mass_table = []
|
||||
with open(path_file, "r", encoding="utf-8") as read_file:
|
||||
JsonJober.data = json.load(read_file)
|
||||
for table in JsonJober.data:
|
||||
mass_table.append(JsonJober.create_table(table_name=table))
|
||||
return mass_table
|
||||
|
||||
class Request():
|
||||
@classmethod
|
||||
async def load_preset(cls):
|
||||
""""Загрузка пресета"""
|
||||
await cls.craete_all_table()
|
||||
mass_table = JsonJober.ReadJson(path_file="./data_base/preset/test.json")
|
||||
await cls.insert_mass_table(mass_table=mass_table)
|
||||
|
||||
@classmethod
|
||||
async def insert_mass_table(cls, mass_table):
|
||||
"""Добавление множества таблиц"""
|
||||
async with async_session() as session:
|
||||
async with session.begin():
|
||||
for table in mass_table:
|
||||
session.add_all(table)
|
||||
await session.commit()
|
||||
|
||||
@classmethod
|
||||
async def select_drop_down_list(cls):
|
||||
"""Возвращает все выподающие списки"""
|
||||
async with async_session() as session:
|
||||
result = {}
|
||||
tables = [mt.Professions, mt.Positions, mt.Classification_professions, mt.Manufacturing_enterprises]
|
||||
for table in tables:
|
||||
query = select(table)
|
||||
result_proxy = await session.execute(query)
|
||||
result[table.__tablename__] = [row._asdict() for row in result_proxy.fetchall()]
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def craete_all_table():
|
||||
""""Создание всех таблиц"""
|
||||
async with async_engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
@classmethod
|
||||
async def login(cls, auth_request) -> mt.users:
|
||||
"""Авторизация"""
|
||||
async with async_session() as session:
|
||||
stmt = select(mt.users).where(
|
||||
mt.users.inn == auth_request.login,
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalars().first()
|
||||
|
||||
@classmethod
|
||||
async def add_column(cls, table_name: str, column_name: str, data_type: str):
|
||||
"""Добавление столбца в таблицу"""
|
||||
async with async_engine.connect() as connection:
|
||||
if data_type == 'String':
|
||||
data_type_sql = "VARCHAR"
|
||||
elif data_type == 'Integer':
|
||||
data_type_sql = "INTEGER"
|
||||
else:
|
||||
raise ValueError("Unsupported data type")
|
||||
|
||||
alter_stmt = text(f'ALTER TABLE {table_name} ADD COLUMN {column_name} {data_type_sql}')
|
||||
await connection.execute(alter_stmt)
|
||||
await connection.commit()
|
||||
|
||||
@classmethod
|
||||
async def drop_column(cls, table_name: str, column_name: str):
|
||||
"""Удаление столбца из таблицы"""
|
||||
async with async_engine.connect() as connection:
|
||||
alter_stmt = text(f'ALTER TABLE {table_name} DROP COLUMN {column_name}')
|
||||
await connection.execute(alter_stmt)
|
||||
await connection.commit()
|
||||
|
||||
@classmethod
|
||||
async def get_table_columns(cls, table_name: str):
|
||||
"""Получение заголовков (имен столбцов) указанной таблицы"""
|
||||
async with async_engine.connect() as connection:
|
||||
def sync_inspect(conn):
|
||||
inspector = inspect(conn)
|
||||
columns = inspector.get_columns(table_name)
|
||||
return [column['name'] for column in columns]
|
||||
|
||||
column_names = await connection.run_sync(sync_inspect)
|
||||
return column_names
|
||||
|
||||
@classmethod
|
||||
async def insert_table(cls, data: list, table_name:str = "Сollection_form_opk"):
|
||||
data = data["data"]
|
||||
metadata = MetaData()
|
||||
async with async_engine.connect() as connection:
|
||||
await connection.run_sync(metadata.reflect)
|
||||
table = Table(table_name, metadata, autoload_with=connection)
|
||||
async with async_session() as session:
|
||||
async with session.begin():
|
||||
for row in data:
|
||||
stmt = insert(table).values(**row)
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
|
||||
@classmethod
|
||||
async def select_rows_with_filter(cls, value:str, table_name: str = "Сollection_form_opk", column_name: str = "ПОО"):
|
||||
"""Выгрузка строк с фильтром по указанному столбцу и значению"""
|
||||
metadata = MetaData()
|
||||
async with async_engine.connect() as connection:
|
||||
await connection.run_sync(metadata.reflect)
|
||||
table = Table(table_name, metadata, autoload_with=connection)
|
||||
|
||||
async with async_session() as session:
|
||||
query = select(table).where(table.c[column_name] == value)
|
||||
result_proxy = await session.execute(query)
|
||||
result = [row._asdict() for row in result_proxy.fetchall()]
|
||||
return result
|
||||
15
server/api-data-base/dockerfile
Normal file
15
server/api-data-base/dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
# Используем официальный образ Python
|
||||
FROM python:3.11.8-slim AS build
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /api-data-base
|
||||
# Копируем файл с зависимостями в контейнер
|
||||
COPY ./server/api-data-base/requirements.txt .
|
||||
# Устанавливаем необходимые зависимости
|
||||
RUN pip install -r requirements.txt
|
||||
# Копируем файл с кодом в контейнер
|
||||
COPY ./server/api-data-base/ .
|
||||
|
||||
FROM build AS final
|
||||
WORKDIR /api-data-base
|
||||
# Команда для запуска приложения
|
||||
CMD ["python", "main.py"]
|
||||
57
server/api-data-base/main.py
Normal file
57
server/api-data-base/main.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from router import router
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Загрузите переменные окружения из файла .env
|
||||
load_dotenv()
|
||||
|
||||
app = FastAPI(title="api-data-base")
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
# Создайте функцию для проверки доступа
|
||||
def authenticate(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
correct_username = os.getenv("DOCS_USERNAME", "admin") # Укажите имя пользователя
|
||||
correct_password = os.getenv("DOCS_PASSWORD", "admin") # Укажите пароль
|
||||
if credentials.username == correct_username and credentials.password == correct_password:
|
||||
return True
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Неверное имя пользователя или пароль",
|
||||
headers={"WWW-Authenticate": "Basic"},
|
||||
)
|
||||
|
||||
# Переопределите маршруты для документации
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
def custom_swagger_ui(credentials: HTTPBasicCredentials = Depends(authenticate)):
|
||||
return get_swagger_ui_html(openapi_url=app.openapi_url, title="Документация API")
|
||||
|
||||
@app.get("/redoc", include_in_schema=False)
|
||||
def custom_redoc_html(credentials: HTTPBasicCredentials = Depends(authenticate)):
|
||||
return get_redoc_html(openapi_url=app.openapi_url, title="Документация API")
|
||||
|
||||
# Настройте кастомный OpenAPI (если требуется)
|
||||
def custom_openapi():
|
||||
if app.openapi_schema:
|
||||
return app.openapi_schema
|
||||
openapi_schema = get_openapi(
|
||||
title="Пример API",
|
||||
version="1.0.0",
|
||||
description="Документация защищена паролем",
|
||||
routes=app.routes,
|
||||
)
|
||||
app.openapi_schema = openapi_schema
|
||||
return app.openapi_schema
|
||||
|
||||
app.openapi = custom_openapi
|
||||
|
||||
|
||||
app.include_router(router)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8001)
|
||||
43
server/api-data-base/model_messege.py
Normal file
43
server/api-data-base/model_messege.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from pydantic import BaseModel
|
||||
from data_base.models.main_tables import users, Сollection_form_opk
|
||||
from enum import Enum
|
||||
|
||||
class Message(BaseModel):
|
||||
key: str
|
||||
value: str
|
||||
|
||||
class Login(BaseModel):
|
||||
login:str
|
||||
password:str
|
||||
|
||||
class TableName(str,Enum):
|
||||
users = "users"
|
||||
Сollection_form_opk = "Сollection_form_opk"
|
||||
|
||||
class DataType(str,Enum):
|
||||
String="String"
|
||||
str="String"
|
||||
Str="String"
|
||||
Integer="Integer"
|
||||
int="Integer"
|
||||
Int="Integer"
|
||||
|
||||
class TableModel():
|
||||
list={
|
||||
"users":users
|
||||
}
|
||||
|
||||
class column_table(BaseModel):
|
||||
table_name:TableName
|
||||
column_name:str
|
||||
data_type:DataType
|
||||
|
||||
class Table(str, Enum):
|
||||
Collection_form_opk = "Collection_form_opk"
|
||||
|
||||
class insert_table(BaseModel):
|
||||
creation_time:str
|
||||
inn:str
|
||||
table_name:Table
|
||||
data:dict
|
||||
|
||||
8
server/api-data-base/requirements.txt
Normal file
8
server/api-data-base/requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
fastapi
|
||||
uvicorn
|
||||
python-dotenv
|
||||
sqlalchemy
|
||||
asyncpg
|
||||
psycopg2-binary
|
||||
bcrypt
|
||||
orjson
|
||||
75
server/api-data-base/router.py
Normal file
75
server/api-data-base/router.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from fastapi import APIRouter, Query, HTTPException
|
||||
from data_base.requests import Request
|
||||
from fastapi.responses import FileResponse
|
||||
import model_messege as mm
|
||||
import bcrypt
|
||||
import asyncio
|
||||
|
||||
router = APIRouter(prefix="/api-data-base")
|
||||
|
||||
#table_name: mm.TableName = Query(...)
|
||||
|
||||
def check_password(input_password, hashed_password) -> bool:
|
||||
"""Проверка пароля"""
|
||||
return bcrypt.checkpw(input_password.encode('utf-8'), hashed_password.encode('utf-8'))
|
||||
|
||||
@router.get("/load_preset")
|
||||
async def load_preset():
|
||||
"""Загрузка пресета"""
|
||||
await Request.load_preset()
|
||||
return {"messege":"пресет загружен"}
|
||||
|
||||
@router.get("/start")
|
||||
async def create_tables():
|
||||
"""Создание таблиц"""
|
||||
await Request.craete_all_table()
|
||||
return {"message": f"все таблицы созданы"}
|
||||
|
||||
@router.post("/login")
|
||||
async def login(auth_request: mm.Login):#auth_request: mm.Login
|
||||
"""Авторизация"""
|
||||
user = await Request.login(auth_request=auth_request)
|
||||
if user == None:#если пользователь не найден
|
||||
return {
|
||||
"id":0,
|
||||
"name_poo":"",
|
||||
"inn":"",
|
||||
"mail":"",
|
||||
"drop_table":""
|
||||
}
|
||||
if check_password(input_password=auth_request.password, hashed_password=user.password):#если пароль верный
|
||||
#data = await Request.select_drop_down_list()
|
||||
return {
|
||||
"id":user.id,
|
||||
"name_poo":user.name_poo,
|
||||
"inn":user.inn,
|
||||
"mail":user.mail
|
||||
#"drop_table": data
|
||||
}
|
||||
@router.post("/add_column")
|
||||
async def add_column(new_column: mm.column_table):
|
||||
"""Добавление колонки"""
|
||||
await Request.add_column(table_name=new_column.table_name.name, column_name=new_column.column_name, data_type=new_column.data_type.name)
|
||||
return {"message":"колонка добавлена"}
|
||||
|
||||
@router.post("/drop_column")
|
||||
async def drop_column(new_column: mm.column_table):
|
||||
"""Удаление колонки"""
|
||||
await Request.drop_column(table_name=new_column.table_name.name, column_name=new_column.column_name)
|
||||
return {"message":"колонка удалена"}
|
||||
|
||||
@router.post("/get_name_column")
|
||||
async def get_name_column(table_name: mm.TableName):
|
||||
"""Получение имен колонок"""
|
||||
return await Request.get_table_columns(table_name=table_name.name)
|
||||
|
||||
|
||||
@router.post("/insert_table")
|
||||
async def insert_table(data:dict):
|
||||
"""Добавление записи"""
|
||||
return await Request.insert_table(data=data)
|
||||
|
||||
@router.post("select_rows_with_filter")
|
||||
async def select_rows_with_filter(value:str):
|
||||
"""Выборка с фильтром"""
|
||||
return await Request.select_rows_with_filter(value=value)
|
||||
2
server/api/README.md
Normal file
2
server/api/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# api
|
||||
api-copp
|
||||
20
server/api/config_kafka.py
Normal file
20
server/api/config_kafka.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from kafka import KafkaProducer,KafkaConsumer
|
||||
import redis
|
||||
import json
|
||||
|
||||
redis_client = redis.StrictRedis(host="redis", port=6379, db=0)
|
||||
|
||||
consumer = KafkaConsumer(
|
||||
'login-topic',
|
||||
bootstrap_servers='kafka:9092',
|
||||
value_deserializer=lambda x: json.loads(x.decode('utf-8')),
|
||||
api_version = (0,10,2),
|
||||
auto_offset_reset='earliest',
|
||||
group_id='0'
|
||||
)
|
||||
|
||||
producer = KafkaProducer(
|
||||
bootstrap_servers='kafka:9092',
|
||||
api_version = (0,10,2),
|
||||
value_serializer=lambda v: json.dumps(v).encode('utf-8')
|
||||
)
|
||||
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
|
||||
43
server/api/router.py
Normal file
43
server/api/router.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import model_messege as mm
|
||||
from config_kafka import producer, consumer, redis_client
|
||||
import threading
|
||||
import json
|
||||
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()
|
||||
2
server/service-login/README.md
Normal file
2
server/service-login/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# service-login
|
||||
service-copp
|
||||
18
server/service-login/config_kafka.py
Normal file
18
server/service-login/config_kafka.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from kafka import KafkaProducer,KafkaConsumer
|
||||
import json
|
||||
|
||||
consumer = KafkaConsumer(
|
||||
|
||||
'auth-topic',
|
||||
bootstrap_servers='kafka:9092',
|
||||
value_deserializer=lambda x: json.loads(x.decode('utf-8')),
|
||||
auto_offset_reset='earliest',
|
||||
api_version = (0,10,2),
|
||||
group_id='0'
|
||||
)
|
||||
#kafka
|
||||
producer = KafkaProducer(
|
||||
bootstrap_servers='kafka:9092',
|
||||
api_version = (0,10,2),
|
||||
value_serializer=lambda v: json.dumps(v).encode('utf-8')
|
||||
)
|
||||
16
server/service-login/dockerfile
Normal file
16
server/service-login/dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
# Используем официальный образ Python
|
||||
FROM python:3.11.8-slim AS build
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /service-login
|
||||
# Копируем файл с зависимостями в контейнер
|
||||
COPY ./server/service-login/requirements.txt .
|
||||
# Устанавливаем необходимые зависимости
|
||||
RUN pip install -r requirements.txt
|
||||
# Копируем файл с кодом в контейнер
|
||||
COPY ./server/service-login/ .
|
||||
|
||||
FROM build AS final
|
||||
WORKDIR /service-login
|
||||
# Команда для запуска приложения
|
||||
CMD ["python", "-u", "./main.py"]
|
||||
|
||||
23
server/service-login/main.py
Normal file
23
server/service-login/main.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from config_kafka import consumer,producer
|
||||
import requests
|
||||
|
||||
def process_message(message):
|
||||
url = 'http://api-data-base:8001/api-data-base/login'
|
||||
auth_request = {
|
||||
'login': message.value['login'],
|
||||
'password': message.value['password']
|
||||
}
|
||||
response = requests.post(url, json=auth_request)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
for message in consumer:
|
||||
result = process_message(message)
|
||||
producer.send("login-topic", {'login': message.value['login'], 'success': result})
|
||||
producer.flush()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
3
server/service-login/requirements.txt
Normal file
3
server/service-login/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
kafka-python
|
||||
python-dotenv
|
||||
requests
|
||||
47
server/validation_collection_form/main.py
Normal file
47
server/validation_collection_form/main.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import threading
|
||||
from confluent_kafka import Consumer, KafkaException, KafkaError
|
||||
|
||||
def consume_messages():
|
||||
# Настройка конфигурации потребителя
|
||||
conf = {
|
||||
'bootstrap.servers': 'localhost:9092',
|
||||
'group.id': 'my_group',
|
||||
'auto.offset.reset': 'earliest'
|
||||
}
|
||||
|
||||
# Создание потребителя
|
||||
consumer = Consumer(conf)
|
||||
|
||||
# Подписка на топик
|
||||
consumer.subscribe(['my_topic'])
|
||||
|
||||
try:
|
||||
while True:
|
||||
# Получение сообщения
|
||||
msg = consumer.poll(timeout=1.0)
|
||||
if msg is None:
|
||||
continue
|
||||
if msg.error():
|
||||
if msg.error().code() == KafkaError._PARTITION_EOF:
|
||||
# Достигнут конец раздела
|
||||
continue
|
||||
else:
|
||||
raise KafkaException(msg.error())
|
||||
# Обработка сообщения
|
||||
print(f'Received message: {msg.value().decode("utf-8")}')
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
# Закрытие потребителя
|
||||
consumer.close()
|
||||
|
||||
def start_consumer_thread():
|
||||
consumer_thread = threading.Thread(target=consume_messages)
|
||||
consumer_thread.daemon = True
|
||||
consumer_thread.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_consumer_thread()
|
||||
# Основной поток может выполнять другие задачи
|
||||
while True:
|
||||
pass
|
||||
30
test.py
Normal file
30
test.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import bcrypt
|
||||
|
||||
def hash_password(password):
|
||||
salt = bcrypt.gensalt()
|
||||
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
|
||||
return hashed_password
|
||||
def hesh():
|
||||
while True:
|
||||
password = input("Введите пароль для хеширования: ")
|
||||
hashed_password = hash_password(password)
|
||||
print(f"Хешированный пароль: {hashed_password.decode('utf-8')}")
|
||||
|
||||
import json
|
||||
import itertools
|
||||
|
||||
hesh()
|
||||
|
||||
## Генерация всех возможных комбинаций чисел от 1 до 18
|
||||
#combinations = list(itertools.permutations(range(1, 19)))
|
||||
#
|
||||
## Чтение существующего файла JSON
|
||||
#with open('/home/jze9/Desktop/git-lab/copp/app/assets/test.json', 'r', encoding='utf-8') as file:
|
||||
# data = json.load(file)
|
||||
#
|
||||
## Запись комбинаций в категорию Category
|
||||
#data['Category'] = combinations
|
||||
#
|
||||
## Запись обновленного содержимого обратно в файл JSON
|
||||
#with open('/home/jze9/Desktop/git-lab/copp/app/assets/test.json', 'w', encoding='utf-8') as file:
|
||||
# json.dump(data, file, ensure_ascii=False, indent=4)
|
||||
Reference in New Issue
Block a user