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