Initial
commit
7690d9e1b7
|
@ -0,0 +1,160 @@
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__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
|
||||||
|
#poetry.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/
|
|
@ -0,0 +1,11 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
# Import Routers
|
||||||
|
from .office import office_router
|
||||||
|
from .profile import profile_router
|
||||||
|
|
||||||
|
router = APIRouter(prefix='/api/v2')
|
||||||
|
|
||||||
|
# Including Routers
|
||||||
|
router.include_router(office_router)
|
||||||
|
router.include_router(profile_router)
|
|
@ -0,0 +1 @@
|
||||||
|
from .handlers import router as office_router
|
|
@ -0,0 +1,18 @@
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
|
from core.models.office.requests import UpdateOfficeRequest
|
||||||
|
from core.models.office.responses import JdeOfficeDetailResponse
|
||||||
|
from core.services.office import services
|
||||||
|
|
||||||
|
router = APIRouter(prefix='/offices')
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/offices')
|
||||||
|
async def list_offices(offices: list[JdeOfficeDetailResponse] = Depends(services.list_offices_service)) -> list[
|
||||||
|
JdeOfficeDetailResponse]:
|
||||||
|
return offices
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/offices')
|
||||||
|
async def update_office(data: UpdateOfficeRequest):
|
||||||
|
await services.update_office(data)
|
|
@ -0,0 +1 @@
|
||||||
|
from .handlers import router as profile_router
|
|
@ -0,0 +1,30 @@
|
||||||
|
from fastapi import APIRouter, Path
|
||||||
|
|
||||||
|
router = APIRouter(prefix='/profiles')
|
||||||
|
|
||||||
|
|
||||||
|
# todo implement this handlers in services and storages. Then use fastapi.Depends()
|
||||||
|
|
||||||
|
@router.post('/')
|
||||||
|
async def create_profile():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/')
|
||||||
|
async def list_profiles():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/{profile_id}')
|
||||||
|
async def get_profile(profile_id: int = Path()):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete('/{profile_id}')
|
||||||
|
async def delete_profile(profile_id: int = Path()):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@router.put('/{profile_id}')
|
||||||
|
async def update_profile(profile_id: int = Path()):
|
||||||
|
pass
|
|
@ -0,0 +1,38 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class JdeOfficeDB(BaseModel):
|
||||||
|
code: str
|
||||||
|
title: str
|
||||||
|
kladr_code: str
|
||||||
|
aex_only: str
|
||||||
|
mst_pr_aex: str
|
||||||
|
mst_pr_virt: str
|
||||||
|
addr: str
|
||||||
|
features: str
|
||||||
|
coords: dict[str, str]
|
||||||
|
city: str
|
||||||
|
country_code: str
|
||||||
|
contry_name: str
|
||||||
|
max_ves: str
|
||||||
|
max_obyom: str
|
||||||
|
max_ves_gm: str
|
||||||
|
max_obyom_gm: str
|
||||||
|
max_l_gm: str
|
||||||
|
max_w_gm: str
|
||||||
|
max_h_gm: str
|
||||||
|
|
||||||
|
|
||||||
|
class JdeOfficeLocalDB(BaseModel):
|
||||||
|
code: str
|
||||||
|
features: str | None = None
|
||||||
|
contact_person_id: int | None = None
|
||||||
|
person_count: int | None = None
|
||||||
|
rating: int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileDB(BaseModel):
|
||||||
|
id: int
|
||||||
|
full_name: str | None
|
||||||
|
phone: str | None
|
||||||
|
email: str | None
|
|
@ -0,0 +1,16 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateOfficeRequest(BaseModel):
|
||||||
|
code: int
|
||||||
|
features: str | None
|
||||||
|
contact_person_id: int | None
|
||||||
|
person_count: int | None
|
||||||
|
rating: int | None
|
||||||
|
|
||||||
|
|
||||||
|
class CreateOrUpdateProfileRequest(BaseModel):
|
||||||
|
id: int | None
|
||||||
|
full_name: str
|
||||||
|
phone: str | None
|
||||||
|
email: str | None
|
|
@ -0,0 +1,17 @@
|
||||||
|
from pydantic import BaseModel, EmailStr
|
||||||
|
from .db import ProfileDB, JdeOfficeDB
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileResponse(ProfileDB):
|
||||||
|
email: EmailStr | None
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedResponse(BaseModel):
|
||||||
|
features: str | None
|
||||||
|
contact_person: ProfileResponse | None
|
||||||
|
person_count: int | None
|
||||||
|
rating: int | None
|
||||||
|
|
||||||
|
|
||||||
|
class JdeOfficeDetailResponse(JdeOfficeDB):
|
||||||
|
changeable_info: ExtendedResponse | None
|
|
@ -0,0 +1,9 @@
|
||||||
|
from database import Database
|
||||||
|
from .storages.profile_storage import ProfileStorage
|
||||||
|
from .storages.office_storage import OfficeStorage
|
||||||
|
|
||||||
|
|
||||||
|
db = Database()
|
||||||
|
|
||||||
|
profile_storage = ProfileStorage(db)
|
||||||
|
office_storage = OfficeStorage(db)
|
|
@ -0,0 +1,9 @@
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from core.models.office.db import JdeOfficeDB
|
||||||
|
|
||||||
|
|
||||||
|
async def grab_data() -> list[JdeOfficeDB]:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.get('http://localhost/api/v1/vD/geo/search?mode=2')
|
||||||
|
return [JdeOfficeDB.model_validate(x) for x in response.json()]
|
|
@ -0,0 +1,19 @@
|
||||||
|
from .helpers import grab_data
|
||||||
|
from core.registry import office_storage
|
||||||
|
from core.models.office.responses import JdeOfficeDetailResponse
|
||||||
|
from ...models.office.requests import UpdateOfficeRequest
|
||||||
|
|
||||||
|
|
||||||
|
async def list_offices_service() -> list[JdeOfficeDetailResponse]:
|
||||||
|
offices = await grab_data()
|
||||||
|
offices.sort(key=lambda x: x.code)
|
||||||
|
offices_additional_data = {data.code: data for data in office_storage.list_all_offices()}
|
||||||
|
response_data = [
|
||||||
|
JdeOfficeDetailResponse(**office.model_dump(), changeable_info=None or offices_additional_data.get(office.code)) for office in offices
|
||||||
|
]
|
||||||
|
|
||||||
|
return response_data
|
||||||
|
|
||||||
|
|
||||||
|
async def update_office(data: UpdateOfficeRequest):
|
||||||
|
office_storage.update_office(data)
|
|
@ -0,0 +1,28 @@
|
||||||
|
from database import Database
|
||||||
|
|
||||||
|
|
||||||
|
class BaseStorage:
|
||||||
|
|
||||||
|
def __init__(self, db: Database):
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
def __fetch_all(self, sql, *args):
|
||||||
|
return self.db.fetch_all(sql, *args)
|
||||||
|
|
||||||
|
def __fetch_one(self, sql, *args):
|
||||||
|
return self.db.fetch_one(sql, *args)
|
||||||
|
|
||||||
|
def __form_query(self, table_name: str, kwargs) -> str:
|
||||||
|
sql = f'select * from {table_name}'
|
||||||
|
|
||||||
|
if len(kwargs) != 0:
|
||||||
|
filters = [f"{key}='{val}'" for key, val in kwargs.items()]
|
||||||
|
sql = f"select * from {table_name} where {' and '.join(filters)}"
|
||||||
|
|
||||||
|
return sql
|
||||||
|
|
||||||
|
def get_by(self, table_name: str, **kwargs):
|
||||||
|
return self.__fetch_one(self.__form_query(table_name, kwargs))
|
||||||
|
|
||||||
|
def list_by(self, table_name: str, **kwargs):
|
||||||
|
return self.__fetch_all(self.__form_query(table_name, kwargs))
|
|
@ -0,0 +1,34 @@
|
||||||
|
from fastapi.exceptions import HTTPException
|
||||||
|
from .base_storage import BaseStorage
|
||||||
|
from core.models.office.db import JdeOfficeLocalDB
|
||||||
|
from core.models.office.requests import UpdateOfficeRequest
|
||||||
|
|
||||||
|
|
||||||
|
class OfficeStorage(BaseStorage):
|
||||||
|
|
||||||
|
def get_by_id(self, idx: int) -> JdeOfficeLocalDB:
|
||||||
|
row = self.get_by('office', code=idx)
|
||||||
|
if row is None:
|
||||||
|
raise HTTPException(status_code=404, detail='Office not found')
|
||||||
|
office = JdeOfficeLocalDB(code=row[0], features=row[1], contact_person_id=row[2], person_count=row[3],
|
||||||
|
rating=row[4])
|
||||||
|
return office
|
||||||
|
|
||||||
|
def list_all_offices(self):
|
||||||
|
rows = self.list_by('office')
|
||||||
|
return [
|
||||||
|
JdeOfficeLocalDB(code=row[0],
|
||||||
|
features=row[1],
|
||||||
|
contact_person_id=row[2],
|
||||||
|
person_count=row[3],
|
||||||
|
rating=row[4])
|
||||||
|
for row in rows]
|
||||||
|
|
||||||
|
def update_office(self, data: UpdateOfficeRequest):
|
||||||
|
try:
|
||||||
|
self.get_by_id(data.code)
|
||||||
|
sql = f"update office set features = %s, contact_person_id = %s, person_count = %s, rating = %s where code = %s"
|
||||||
|
self.db.execute(sql, (data.features, data.contact_person_id, data.person_count, data.rating, data.code))
|
||||||
|
except HTTPException as _:
|
||||||
|
sql = f"insert into office (code, features, contact_person_id, person_count, rating) values (%s, %s, %s, %s, %s)"
|
||||||
|
self.db.execute(sql, (data.code, data.features, data.contact_person_id, data.person_count, data.rating))
|
|
@ -0,0 +1,30 @@
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
|
||||||
|
from .base_storage import BaseStorage
|
||||||
|
from core.models.office.db import ProfileDB
|
||||||
|
from core.models.office.requests import CreateOrUpdateProfileRequest
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileStorage(BaseStorage):
|
||||||
|
|
||||||
|
def get_by_id(self, id: int):
|
||||||
|
row = self.get_by('profile', id=id)
|
||||||
|
if row is None:
|
||||||
|
raise HTTPException(status_code=404, detail='Profile not found')
|
||||||
|
return ProfileDB(id=row[0], full_name=row[1], phone=row[2], email=row[3])
|
||||||
|
|
||||||
|
def create_or_update_profile(self, data: CreateOrUpdateProfileRequest):
|
||||||
|
try:
|
||||||
|
if not data.id:
|
||||||
|
raise KeyError('No id provided')
|
||||||
|
self.get_by_id(data.id)
|
||||||
|
sql = f"update profile set full_name = %s, phone = %s, email = %s where id = %s"
|
||||||
|
self.db.execute(sql, (data.full_name, data.phone, data.email, data.id))
|
||||||
|
except (HTTPException, KeyError) as _:
|
||||||
|
sql = f"insert into profile (full_name, phone, email) values (%s, %s, %s)"
|
||||||
|
self.db.execute(sql, (data.full_name, data.phone, data.email))
|
||||||
|
|
||||||
|
def list_all_profiles(self):
|
||||||
|
rows = self.list_by('profile')
|
||||||
|
return [ProfileDB(id=row[0], full_name=row[1], phone=row[2], email=row[3]) for row in rows]
|
|
@ -0,0 +1,59 @@
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
__instance: 'Database' = None
|
||||||
|
__client: pymysql.Connection = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if cls.__instance is None:
|
||||||
|
cls.__instance = super().__new__(cls)
|
||||||
|
return cls.__instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__create_con()
|
||||||
|
|
||||||
|
def __create_con(self):
|
||||||
|
# TODO Set to environment variables. Use pydantic-settings
|
||||||
|
self.__client = pymysql.connect(
|
||||||
|
host='127.0.0.1',
|
||||||
|
port=3306,
|
||||||
|
user='root',
|
||||||
|
password='root',
|
||||||
|
db='jde',
|
||||||
|
charset='utf8'
|
||||||
|
)
|
||||||
|
|
||||||
|
def __execute_cmd(self, wrapper, *args, **kwargs):
|
||||||
|
with self.__client.cursor() as cursor:
|
||||||
|
data = wrapper(cursor, *args, **kwargs)
|
||||||
|
self.__client.commit()
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _fetch_all(cls, cursor: pymysql.cursors.Cursor, sql: str, *args) -> list:
|
||||||
|
#todo research formatting input values in sql queries. Which type of args?
|
||||||
|
cursor.execute(sql, *args)
|
||||||
|
return cursor.fetchall()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _fetch_one(cls, cursor: pymysql.cursors.Cursor, sql: str, *args) -> typing.Any:
|
||||||
|
#todo research formatting input values in sql queries. Which type of args?
|
||||||
|
cursor.execute(sql, *args)
|
||||||
|
return cursor.fetchone()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _execute(cls, cursor: pymysql.cursors.Cursor, sql: str, *args) -> None:
|
||||||
|
#todo research formatting input values in sql queries. Which type of args?
|
||||||
|
cursor.execute(sql, *args)
|
||||||
|
|
||||||
|
def fetch_all(self, sql: str, *args) -> list:
|
||||||
|
return self.__execute_cmd(self._fetch_all, sql, *args)
|
||||||
|
|
||||||
|
def fetch_one(self, sql: str, *args) -> typing.Any | None:
|
||||||
|
return self.__execute_cmd(self._fetch_one, sql, *args)
|
||||||
|
|
||||||
|
def execute(self, sql: str, *args) -> None:
|
||||||
|
return self.__execute_cmd(self._execute, sql, *args)
|
|
@ -0,0 +1 @@
|
||||||
|
<mxfile host="drawio-plugin" modified="2023-10-29T21:12:08.379Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36" version="20.5.3" etag="JcmW1FTD10G-E-Wj7tJR" type="embed"><diagram id="bhLovqJHFxc52hLkd4M8" name="Page-1"><mxGraphModel dx="461" dy="366" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="Office" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;" vertex="1" parent="1"><mxGeometry x="50" y="60" width="140" height="156" as="geometry"/></mxCell><mxCell id="3" value="code: varchar(64)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="2"><mxGeometry y="26" width="140" height="26" as="geometry"/></mxCell><mxCell id="4" value="features: text" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="2"><mxGeometry y="52" width="140" height="26" as="geometry"/></mxCell><mxCell id="5" value="contact_person: int8 FK" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="2"><mxGeometry y="78" width="140" height="26" as="geometry"/></mxCell><mxCell id="10" value="personal_count: int8" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="2"><mxGeometry y="104" width="140" height="26" as="geometry"/></mxCell><mxCell id="11" value="rating: int8 <=5 " style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="2"><mxGeometry y="130" width="140" height="26" as="geometry"/></mxCell><mxCell id="12" value="Profile" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;" vertex="1" parent="1"><mxGeometry x="270" y="60" width="140" height="130" as="geometry"/></mxCell><mxCell id="14" value="id: int8" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="12"><mxGeometry y="26" width="140" height="26" as="geometry"/></mxCell><mxCell id="13" value="full_name: varchar(64)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="12"><mxGeometry y="52" width="140" height="26" as="geometry"/></mxCell><mxCell id="15" value="phone: varchar(30)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="12"><mxGeometry y="78" width="140" height="26" as="geometry"/></mxCell><mxCell id="17" value="email: varchar(64)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="12"><mxGeometry y="104" width="140" height="26" as="geometry"/></mxCell><mxCell id="18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="14" target="5"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile>
|
|
@ -0,0 +1,23 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
import uvicorn
|
||||||
|
from core.handlers import router
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=['*']
|
||||||
|
)
|
||||||
|
|
||||||
|
app.include_router(router)
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def startup():
|
||||||
|
print("startup")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
uvicorn.run('main:app', host='0.0.0.0', port=8000, reload=True)
|
|
@ -0,0 +1,21 @@
|
||||||
|
BEGIN;
|
||||||
|
create database if not exists jde;
|
||||||
|
use jde;
|
||||||
|
|
||||||
|
|
||||||
|
create table if not exists profile (
|
||||||
|
id int8 primary key auto_increment,
|
||||||
|
full_name varchar(64),
|
||||||
|
phone varchar(30),
|
||||||
|
email varchar(64)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists office (
|
||||||
|
code int8 primary key auto_increment,
|
||||||
|
features text,
|
||||||
|
contact_person_id int8 references profile(id),
|
||||||
|
person_count int8 default 0,
|
||||||
|
rating int8 default 0
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
|
@ -0,0 +1,19 @@
|
||||||
|
annotated-types==0.6.0
|
||||||
|
anyio==3.7.1
|
||||||
|
certifi==2023.7.22
|
||||||
|
click==8.1.7
|
||||||
|
dnspython==2.4.2
|
||||||
|
email-validator==2.1.0.post1
|
||||||
|
fastapi==0.104.0
|
||||||
|
h11==0.14.0
|
||||||
|
httpcore==0.18.0
|
||||||
|
httpx==0.25.0
|
||||||
|
idna==3.4
|
||||||
|
pydantic==2.4.2
|
||||||
|
pydantic-to-typescript==1.0.10
|
||||||
|
pydantic_core==2.10.1
|
||||||
|
PyMySQL==1.1.0
|
||||||
|
sniffio==1.3.0
|
||||||
|
starlette==0.27.0
|
||||||
|
typing_extensions==4.8.0
|
||||||
|
uvicorn==0.23.2
|
Loading…
Reference in New Issue