init commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,68 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi import HTTPException
|
||||
from fastapi import Request
|
||||
from fastapi.security import HTTPAuthorizationCredentials
|
||||
from fastapi.security import HTTPBearer
|
||||
import jwt
|
||||
|
||||
from app.core.config import config
|
||||
from app.models.user import User
|
||||
|
||||
from .utils import decode_jwt
|
||||
|
||||
|
||||
class BearerAuth(HTTPBearer):
|
||||
def __init__(self, auto_error: bool = True):
|
||||
super().__init__(auto_error=auto_error)
|
||||
|
||||
async def __call__(self, request: Request):
|
||||
credentials: HTTPAuthorizationCredentials = await super().__call__(
|
||||
request
|
||||
)
|
||||
|
||||
if credentials:
|
||||
if not credentials.scheme == 'Bearer':
|
||||
raise HTTPException(
|
||||
status_code=403, detail='Invalid authentication scheme.'
|
||||
)
|
||||
if not self.verify_jwt(credentials.credentials):
|
||||
raise HTTPException(
|
||||
status_code=403, detail='Invalid token or expired token.'
|
||||
)
|
||||
return credentials.credentials
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=403, detail='Invalid authorization code.'
|
||||
)
|
||||
|
||||
def verify_jwt(self, token: str) -> bool:
|
||||
payload = decode_jwt(token)
|
||||
is_valid = bool(payload)
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
oauth2_scheme = BearerAuth()
|
||||
|
||||
|
||||
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token, config.JWT_SECRET_KEY, algorithms=['HS256']
|
||||
)
|
||||
user = await User.get_or_create_user(
|
||||
User(
|
||||
id=payload['user_id'],
|
||||
username=payload['username'],
|
||||
events=[],
|
||||
items=[],
|
||||
transactions=[],
|
||||
)
|
||||
)
|
||||
return user
|
||||
except jwt.ExpiredSignatureError:
|
||||
return {'error': 'Token is expired'}
|
||||
except jwt.InvalidTokenError:
|
||||
return {'error': 'Invalid token'}
|
||||
@@ -0,0 +1,7 @@
|
||||
import fastapi
|
||||
|
||||
credentials_exception = fastapi.HTTPException(
|
||||
status_code=fastapi.status.HTTP_401_UNAUTHORIZED,
|
||||
detail='Could not validate credentials',
|
||||
headers={'WWW-Authenticate': 'Bearer'},
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
import fastapi
|
||||
|
||||
auth_router = fastapi.APIRouter()
|
||||
@@ -0,0 +1,60 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import fastapi
|
||||
|
||||
from app.api.auth.routers import auth_router
|
||||
import app.core.security.tokens
|
||||
from app.models.base import BasicResponse
|
||||
from app.models.telegram import TelegramInputData
|
||||
from app.models.tokens import Token
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
@auth_router.post(
|
||||
'/token',
|
||||
responses={
|
||||
fastapi.status.HTTP_401_UNAUTHORIZED: {
|
||||
'description': 'Unauthorized',
|
||||
'model': BasicResponse,
|
||||
},
|
||||
},
|
||||
)
|
||||
async def authenticate(init_data: TelegramInputData) -> Token:
|
||||
# if not config.DEBUG:
|
||||
# fields = init_data.model_dump()
|
||||
# sorted_fields = sorted(fields.items())
|
||||
# formatted = [f'{key}={value}' for key, value in sorted_fields]
|
||||
# data_check_string = '\n'.join(formatted)
|
||||
|
||||
# secret_key = hmac.new(
|
||||
# config.TOKEN_TELEGRAM_API.encode(), b'WebAppData', sha256
|
||||
# ).digest()
|
||||
|
||||
# if (
|
||||
# hmac.new(
|
||||
# data_check_string.encode(), secret_key, sha256
|
||||
# ).hexdigest()
|
||||
# != init_data.hash
|
||||
# ):
|
||||
# print(hmac.new(
|
||||
# data_check_string.encode(), secret_key, sha256
|
||||
# ).hexdigest())
|
||||
# print(init_data.hash)
|
||||
# raise HTTPException(status_code=403, detail='Unauthorized')
|
||||
|
||||
user = await User.get_or_create_user(
|
||||
User(id=init_data.user.id, username=init_data.user.username)
|
||||
)
|
||||
|
||||
return Token(
|
||||
access_token=app.core.security.tokens.generate_token(
|
||||
{'user_id': user.id, 'username': user.username},
|
||||
expires_delta=timedelta(days=7),
|
||||
),
|
||||
token_type='bearer',
|
||||
)
|
||||
|
||||
|
||||
@auth_router.get('/ping')
|
||||
def ping() -> str:
|
||||
return 'pong'
|
||||
@@ -0,0 +1,14 @@
|
||||
import jwt
|
||||
|
||||
from app.core.config import config
|
||||
|
||||
|
||||
def decode_jwt(token: str) -> dict:
|
||||
try:
|
||||
decoded_token = jwt.decode(
|
||||
token, config.JWT_SECRET_KEY, algorithms=['HS256']
|
||||
)
|
||||
return decoded_token
|
||||
|
||||
except (jwt.InvalidTokenError, jwt.ExpiredSignatureError):
|
||||
return {}
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,3 @@
|
||||
import fastapi
|
||||
|
||||
calculate_debits_router = fastapi.APIRouter()
|
||||
@@ -0,0 +1,106 @@
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi import HTTPException
|
||||
import pydantic
|
||||
import requests
|
||||
from sqlmodel import select
|
||||
from sqlmodel import Session
|
||||
|
||||
from app.api.auth.deps import BearerAuth
|
||||
from app.api.auth.deps import get_current_user
|
||||
from app.core.db import engine
|
||||
from app.models.event import Event
|
||||
from app.models.user import User
|
||||
from app.utils.telegram import send_telegram_message
|
||||
|
||||
from .routers import calculate_debits_router
|
||||
|
||||
|
||||
class Debt(pydantic.BaseModel):
|
||||
from_user_id: int
|
||||
to_user_id: int
|
||||
amount: float
|
||||
|
||||
|
||||
@calculate_debits_router.post(
|
||||
'/events/{event_id}',
|
||||
description='Get debts smeta',
|
||||
dependencies=[Depends(BearerAuth())],
|
||||
)
|
||||
def calculate_event_debts(
|
||||
event_id: UUID, user: User = Depends(get_current_user)
|
||||
) -> list[Debt]:
|
||||
with Session(engine) as session:
|
||||
result = session.execute(select(Event).where(Event.id == event_id))
|
||||
event = result.scalars().first()
|
||||
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail='Event not found')
|
||||
|
||||
if user.id != event.owner_id:
|
||||
raise HTTPException(status_code=403, detail='Permission denied')
|
||||
|
||||
transactions = []
|
||||
for transaction in event.transactions:
|
||||
if not transaction.closed:
|
||||
positions = [
|
||||
{
|
||||
'price': item.price,
|
||||
# 'assigned_to_ids': [
|
||||
# user.id for user in item.assigned_to
|
||||
# ],
|
||||
'assigned_to_ids': [user.id for user in event.users],
|
||||
}
|
||||
for item in transaction.items
|
||||
]
|
||||
transactions.append(
|
||||
{'owner_id': transaction.payer_id, 'positions': positions}
|
||||
)
|
||||
|
||||
transaction.closed = True
|
||||
|
||||
if not transactions:
|
||||
return []
|
||||
|
||||
participants = [{'id': user.id} for user in event.users]
|
||||
input_data = {
|
||||
'participants': participants,
|
||||
'transactions': transactions,
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
'http://optimizetka:8000/optimizetka/api/calculate-debts',
|
||||
json=input_data,
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise HTTPException(
|
||||
status_code=response.status_code,
|
||||
detail=f'Failed to calculate debts: {response.text}',
|
||||
)
|
||||
|
||||
debts = response.json()
|
||||
|
||||
session.commit()
|
||||
|
||||
messages = {user.id: [] for user in event.users}
|
||||
print(debts)
|
||||
for debt in debts:
|
||||
from_user = next(
|
||||
u for u in event.users if u.id == debt['from_user_id']
|
||||
)
|
||||
to_user = next(
|
||||
u for u in event.users if u.id == debt['to_user_id']
|
||||
)
|
||||
print(from_user, to_user)
|
||||
amount = debt['amount']
|
||||
messages[from_user].append(f'You owe {amount} to {to_user.name}.')
|
||||
messages[to_user.id].append(f'{from_user.name} owes you {amount}.')
|
||||
|
||||
for user in event.users:
|
||||
message_text = '\n'.join(messages[user.id])
|
||||
if message_text:
|
||||
send_telegram_message(user.chat_id, message_text)
|
||||
|
||||
return debts
|
||||
@@ -0,0 +1,5 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .invites import *
|
||||
from .routes import *
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,3 @@
|
||||
import fastapi
|
||||
|
||||
invites_router = fastapi.APIRouter()
|
||||
@@ -0,0 +1,44 @@
|
||||
import typing
|
||||
import uuid
|
||||
|
||||
import fastapi
|
||||
from fastapi import HTTPException
|
||||
import sqlmodel
|
||||
|
||||
import app.api.auth.deps
|
||||
from app.api.auth.deps import BearerAuth
|
||||
from app.api.events.invites.routers import invites_router
|
||||
import app.core.db
|
||||
from app.models import Event
|
||||
import app.models.base
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
@invites_router.get(
|
||||
'/{invite_id}',
|
||||
responses={
|
||||
fastapi.status.HTTP_400_BAD_REQUEST: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': 'Invalid invite',
|
||||
}
|
||||
},
|
||||
dependencies=[fastapi.Depends(BearerAuth())],
|
||||
)
|
||||
def join_by_invite(
|
||||
invite_id: uuid.UUID,
|
||||
user: typing.Annotated[
|
||||
User, fastapi.Depends(app.api.auth.deps.get_current_user)
|
||||
],
|
||||
) -> app.models.event.Event:
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
user = session.get(User, user.id)
|
||||
event = session.query(Event).filter_by(invite=invite_id).first()
|
||||
if not event:
|
||||
raise HTTPException(
|
||||
fastapi.status.HTTP_404_NOT_FOUND, detail='Invite not found'
|
||||
)
|
||||
|
||||
event.users.append(user)
|
||||
session.commit()
|
||||
|
||||
return event
|
||||
@@ -0,0 +1,9 @@
|
||||
import fastapi
|
||||
|
||||
# from .invites.routers import invites_router
|
||||
|
||||
events_router = fastapi.APIRouter()
|
||||
|
||||
# events_router.include_router(
|
||||
# invites_router, prefix='/invites', tags=['invites']
|
||||
# )
|
||||
@@ -0,0 +1,151 @@
|
||||
import typing
|
||||
import uuid
|
||||
|
||||
import fastapi
|
||||
import sqlmodel
|
||||
|
||||
import app.api.auth.deps
|
||||
from app.api.auth.deps import BearerAuth
|
||||
from app.api.events.routers import events_router
|
||||
import app.core.db
|
||||
from app.models import Event
|
||||
from app.models import OutputEvent
|
||||
import app.models.event
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
@events_router.get(
|
||||
'/',
|
||||
response_model=list[app.models.event.OutputEvent],
|
||||
description='Return events containing given user (by token)',
|
||||
dependencies=[fastapi.Depends(BearerAuth())],
|
||||
)
|
||||
def list_events(
|
||||
user: typing.Annotated[
|
||||
User, fastapi.Depends(app.api.auth.deps.get_current_user)
|
||||
],
|
||||
):
|
||||
output = []
|
||||
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
user = session.get(User, user.id)
|
||||
|
||||
for event in user.events:
|
||||
new_output = app.models.event.OutputEvent(
|
||||
id=event.id,
|
||||
owner=event.owner_id,
|
||||
users=event.users,
|
||||
invite=event.invite,
|
||||
name=event.name,
|
||||
)
|
||||
output.append(new_output)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
@events_router.post(
|
||||
'/add/{event_id}',
|
||||
response_model=Event,
|
||||
description='Add user to event',
|
||||
dependencies=[fastapi.Depends(BearerAuth())],
|
||||
responses={
|
||||
fastapi.status.HTTP_404_NOT_FOUND: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': r'Event \ user not found',
|
||||
},
|
||||
},
|
||||
)
|
||||
async def add_to_event(
|
||||
user: typing.Annotated[
|
||||
User, fastapi.Depends(app.api.auth.deps.get_current_user)
|
||||
],
|
||||
event_id: uuid.UUID,
|
||||
):
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
event = session.get(Event, event_id)
|
||||
|
||||
if not event:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Event not found', status_code=404
|
||||
)
|
||||
|
||||
user = session.get(User, user.id)
|
||||
|
||||
if not user:
|
||||
raise fastapi.HTTPException(
|
||||
detail='User not found', status_code=404
|
||||
)
|
||||
|
||||
event.users.append(user)
|
||||
|
||||
session.add(event)
|
||||
session.commit()
|
||||
session.refresh(event)
|
||||
|
||||
return event
|
||||
|
||||
|
||||
@events_router.post(
|
||||
'/',
|
||||
description='Create event',
|
||||
dependencies=[fastapi.Depends(BearerAuth())],
|
||||
)
|
||||
def create_event(
|
||||
event: app.models.event.BaseEvent,
|
||||
user: typing.Annotated[
|
||||
User, fastapi.Depends(app.api.auth.deps.get_current_user)
|
||||
],
|
||||
) -> app.models.event.Event:
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
new_event = app.models.event.Event(
|
||||
name=event.name, owner_id=user.id, users=[user]
|
||||
)
|
||||
session.add(new_event)
|
||||
session.commit()
|
||||
session.refresh(new_event)
|
||||
|
||||
return new_event
|
||||
|
||||
|
||||
# @events_router.delete(
|
||||
# '/{event_id}',
|
||||
# dependencies=[fastapi.Depends(app.api.auth.deps.get_current_user)],
|
||||
# description='Delete an event',
|
||||
# )
|
||||
# def delete_event(
|
||||
# event_id: uuid.UUID,
|
||||
# ):
|
||||
# with sqlmodel.Session(app.core.db.engine) as session:
|
||||
# session.delete(session.get(app.models.event.Event, event_id))
|
||||
# session.commit()
|
||||
|
||||
|
||||
@events_router.get(
|
||||
'/{event_id}',
|
||||
response_model=app.models.event.OutputEvent,
|
||||
dependencies=[fastapi.Depends(app.api.auth.deps.get_current_user)],
|
||||
description='Get info for an event by the id',
|
||||
responses={
|
||||
fastapi.status.HTTP_404_NOT_FOUND: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': r'Event \ user not found',
|
||||
},
|
||||
},
|
||||
)
|
||||
def event_by_id(event_id: uuid.UUID):
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
event = session.get(app.models.event.Event, event_id)
|
||||
|
||||
if not event:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Event not found', status_code=404
|
||||
)
|
||||
|
||||
new_event = app.models.event.OutputEvent(
|
||||
id=event.id,
|
||||
owner=event.owner_id,
|
||||
users=event.users,
|
||||
invite=event.invite,
|
||||
name=event.name,
|
||||
)
|
||||
return new_event
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi import Depends
|
||||
|
||||
from app.api.auth.deps import BearerAuth
|
||||
|
||||
items_router = APIRouter(
|
||||
dependencies=[Depends(BearerAuth())],
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
from uuid import UUID
|
||||
|
||||
import fastapi
|
||||
import sqlmodel
|
||||
|
||||
import app.core.db
|
||||
from app.models.event import Event
|
||||
from app.models.item import Item
|
||||
from app.models.item import ItemRequest
|
||||
from app.models.item import OutputItem
|
||||
from app.models.transactions import Transaction
|
||||
|
||||
from ...models import BasicResponse
|
||||
from ..auth.deps import BearerAuth
|
||||
from .routers import items_router
|
||||
|
||||
|
||||
@items_router.get(
|
||||
'/{transaction_id}',
|
||||
description='Get items from transaction',
|
||||
responses={
|
||||
404: {
|
||||
'model': BasicResponse,
|
||||
'description': 'Transaction by this ID is not found',
|
||||
},
|
||||
403: {'model': BasicResponse, 'description': 'Unauthorized'},
|
||||
},
|
||||
)
|
||||
async def get_items(transaction_id: UUID) -> list[OutputItem]:
|
||||
with sqlmodel.Session(bind=app.core.db.engine) as session:
|
||||
transaction = session.get(Transaction, transaction_id)
|
||||
if not transaction:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Transaction not found', status_code=404
|
||||
)
|
||||
|
||||
out = []
|
||||
for ti in transaction.items:
|
||||
out.append(OutputItem(
|
||||
title=ti.title,
|
||||
id=ti.id,
|
||||
price=ti.price,
|
||||
assigned_to=ti.assigned_to,
|
||||
transaction_id=ti.transaction_id
|
||||
))
|
||||
return out
|
||||
|
||||
|
||||
@items_router.post(
|
||||
'/{transaction_id}',
|
||||
description='Create item in transaction',
|
||||
responses={
|
||||
400: {
|
||||
'model': BasicResponse,
|
||||
'description': 'Invalid data format (ex. negative price)',
|
||||
},
|
||||
404: {
|
||||
'model': BasicResponse,
|
||||
'description': 'Transaction with this ID is not found',
|
||||
},
|
||||
403: {'model': BasicResponse, 'description': 'Unauthorized'},
|
||||
},
|
||||
)
|
||||
async def create_item(item: ItemRequest, transaction_id: UUID) -> OutputItem:
|
||||
if item.price <= 0:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Price cannot be null or negative', status_code=400
|
||||
)
|
||||
|
||||
with sqlmodel.Session(bind=app.core.db.engine) as session:
|
||||
transaction = session.get(Transaction, transaction_id)
|
||||
if not transaction:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Transaction not found', status_code=404
|
||||
)
|
||||
event = session.get(Event, transaction.event_id)
|
||||
|
||||
new_item = Item(
|
||||
title=item.title,
|
||||
price=item.price,
|
||||
assigned_to=[] if not item.all_users_selected else event.users,
|
||||
transaction_id=transaction.id,
|
||||
)
|
||||
session.add(new_item)
|
||||
session.commit()
|
||||
session.refresh(new_item)
|
||||
|
||||
return OutputItem(
|
||||
title=item.title,
|
||||
id=item.id,
|
||||
price=item.price,
|
||||
assigned_to=item.assigned_to,
|
||||
transaction_id=item.transaction_id
|
||||
)
|
||||
|
||||
|
||||
@items_router.get(
|
||||
path='/{transaction_id}/{item_id}',
|
||||
description='Get item by itself ID',
|
||||
dependencies=[fastapi.Depends(BearerAuth())],
|
||||
responses={
|
||||
404: {
|
||||
'model': BasicResponse,
|
||||
'description': 'Item with this ID is not found',
|
||||
},
|
||||
403: {'model': BasicResponse, 'description': 'Unauthorized'},
|
||||
},
|
||||
)
|
||||
async def get_item(transaction_id: UUID, item_id: UUID) -> OutputItem:
|
||||
with sqlmodel.Session(bind=app.core.db.engine) as session:
|
||||
item = session.get(Item, item_id)
|
||||
if not item:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Item not found', status_code=404
|
||||
)
|
||||
|
||||
return OutputItem(
|
||||
id=item.id,
|
||||
title=item.title,
|
||||
price=item.price,
|
||||
assigned_to=item.assigned_to,
|
||||
transaction_id=transaction_id,
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
import fastapi
|
||||
|
||||
import app.api.auth.routers
|
||||
import app.api.calculate_debits.routers
|
||||
import app.api.events.routers
|
||||
import app.api.items.routers
|
||||
import app.api.transactions.routers
|
||||
import app.api.utils.routers
|
||||
|
||||
api_router = fastapi.APIRouter()
|
||||
|
||||
api_router.include_router(
|
||||
app.api.auth.routers.auth_router, prefix='/auth', tags=['auth']
|
||||
)
|
||||
api_router.include_router(
|
||||
app.api.utils.routers.utils_router, prefix='/utils', tags=['utils']
|
||||
)
|
||||
api_router.include_router(
|
||||
app.api.events.routers.events_router, prefix='/events', tags=['events']
|
||||
)
|
||||
api_router.include_router(
|
||||
app.api.transactions.routers.transactions_router,
|
||||
prefix='/transaction',
|
||||
tags=['transactions'],
|
||||
)
|
||||
api_router.include_router(
|
||||
app.api.calculate_debits.routers.calculate_debits_router,
|
||||
prefix='/calculate_debits',
|
||||
tags=['calculate_debits'],
|
||||
)
|
||||
api_router.include_router(
|
||||
app.api.items.routers.items_router, prefix='/items', tags=['items']
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,3 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
transactions_router = APIRouter()
|
||||
@@ -0,0 +1,208 @@
|
||||
import typing
|
||||
import uuid
|
||||
|
||||
import fastapi
|
||||
import sqlmodel
|
||||
|
||||
import app.api.auth.deps
|
||||
from app.api.transactions.routers import transactions_router
|
||||
import app.core.db
|
||||
import app.models.item
|
||||
|
||||
|
||||
@transactions_router.get(
|
||||
'/{event_id}/{transaction_id}',
|
||||
response_model=app.models.transactions.OutputTransaction,
|
||||
description='Get a transaction by its event and transaction id',
|
||||
dependencies=[fastapi.Depends(app.api.auth.deps.get_current_user)],
|
||||
responses={
|
||||
fastapi.status.HTTP_404_NOT_FOUND: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': r'Event \ transaction not found',
|
||||
},
|
||||
},
|
||||
)
|
||||
async def get_transaction(
|
||||
event_id: uuid.UUID,
|
||||
transaction_id: uuid.UUID,
|
||||
):
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
event = session.get(app.models.event.Event, event_id)
|
||||
|
||||
if not event:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404, detail='Event not found'
|
||||
)
|
||||
|
||||
transaction = session.get(
|
||||
app.models.transactions.Transaction, transaction_id
|
||||
)
|
||||
output_transaction = app.models.transactions.OutputTransaction(
|
||||
title=transaction.title,
|
||||
payer=transaction.payer,
|
||||
event_id=transaction.event_id,
|
||||
closed=transaction.closed,
|
||||
items=transaction.items,
|
||||
id=transaction.id,
|
||||
)
|
||||
|
||||
return output_transaction
|
||||
|
||||
|
||||
@transactions_router.get(
|
||||
'/{event_id}',
|
||||
description='Get all transactions of an event',
|
||||
dependencies=[fastapi.Depends(app.api.auth.deps.get_current_user)],
|
||||
responses={
|
||||
fastapi.status.HTTP_404_NOT_FOUND: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': r'Event not found',
|
||||
},
|
||||
},
|
||||
)
|
||||
async def list_transactions(
|
||||
event_id: uuid.UUID,
|
||||
) -> list[app.models.transactions.OutputTransaction]:
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
event = session.get(app.models.event.Event, event_id)
|
||||
|
||||
if not event:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404, detail='Event not found'
|
||||
)
|
||||
|
||||
output = []
|
||||
|
||||
for transaction in event.transactions:
|
||||
output.append(
|
||||
app.models.transactions.OutputTransaction(
|
||||
title=transaction.title,
|
||||
payer=transaction.payer,
|
||||
event_id=transaction.event_id,
|
||||
closed=transaction.closed,
|
||||
items=transaction.items,
|
||||
id=transaction.id,
|
||||
)
|
||||
)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
@transactions_router.post(
|
||||
'/{event_id}',
|
||||
response_model=app.models.transactions.OutputTransaction,
|
||||
description='Create a transaction by a event id',
|
||||
dependencies=[fastapi.Depends(app.api.auth.deps.get_current_user)],
|
||||
responses={
|
||||
fastapi.status.HTTP_404_NOT_FOUND: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': 'Event not found',
|
||||
},
|
||||
},
|
||||
)
|
||||
async def create_transaction(
|
||||
title: typing.Annotated[
|
||||
str, fastapi.Body(description='Title of transaction')
|
||||
],
|
||||
event_id: uuid.UUID,
|
||||
user: typing.Annotated[
|
||||
app.models.user.User,
|
||||
fastapi.Depends(app.api.auth.deps.get_current_user),
|
||||
],
|
||||
):
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
event = session.get(app.models.event.Event, event_id)
|
||||
|
||||
if not event:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404, detail='Event not found'
|
||||
)
|
||||
|
||||
transaction = app.models.transactions.Transaction(
|
||||
title=title,
|
||||
payer_id=user.id,
|
||||
event_id=event.id,
|
||||
items=[],
|
||||
)
|
||||
|
||||
session.add(transaction)
|
||||
session.commit()
|
||||
session.refresh(transaction)
|
||||
|
||||
output_transaction = app.models.transactions.OutputTransaction(
|
||||
id=transaction.id,
|
||||
title=transaction.title,
|
||||
payer=transaction.payer,
|
||||
event_id=transaction.event_id,
|
||||
closed=transaction.closed,
|
||||
items=transaction.items,
|
||||
)
|
||||
|
||||
return output_transaction
|
||||
|
||||
|
||||
@transactions_router.post(
|
||||
'/{event_id}/{transaction_id}/items',
|
||||
response_model=app.models.item.Item,
|
||||
description='Create a transaction by a event id',
|
||||
dependencies=[fastapi.Depends(app.api.auth.deps.get_current_user)],
|
||||
responses={
|
||||
fastapi.status.HTTP_404_NOT_FOUND: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'detail': r'Event \ transaction not found',
|
||||
},
|
||||
400: {
|
||||
'model': app.models.base.BasicResponse,
|
||||
'description': 'Invalid data format (ex. negative price)',
|
||||
},
|
||||
},
|
||||
)
|
||||
async def add_item_to_transaction(
|
||||
event_id: uuid.UUID,
|
||||
transaction_id: uuid.UUID,
|
||||
title: str = fastapi.Body(description='Title of item'),
|
||||
price: float = fastapi.Body(description='Price of item'),
|
||||
add_all_users: bool = fastapi.Body(
|
||||
description='Assign all users to this item', default=False
|
||||
),
|
||||
):
|
||||
with sqlmodel.Session(app.core.db.engine) as session:
|
||||
event = session.get(app.models.event.Event, event_id)
|
||||
|
||||
if not event:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404, detail='Event not found'
|
||||
)
|
||||
|
||||
transaction = session.get(
|
||||
app.models.transactions.Transaction, transaction_id
|
||||
)
|
||||
|
||||
if not transaction:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404, detail='Transaction not found'
|
||||
)
|
||||
|
||||
if transaction.event_id != event.id:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404,
|
||||
detail='This transaction is not a child of this event',
|
||||
)
|
||||
|
||||
item = app.models.item.Item(
|
||||
title=title,
|
||||
price=price,
|
||||
assigned_to=[] if not add_all_users else event.users,
|
||||
transaction_id=transaction_id,
|
||||
)
|
||||
|
||||
if item.price <= 0:
|
||||
raise fastapi.HTTPException(
|
||||
detail='Price cannot be null or negative', status_code=400
|
||||
)
|
||||
|
||||
session.add(item)
|
||||
session.commit()
|
||||
session.refresh(item)
|
||||
|
||||
return item
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__: list[str] = []
|
||||
|
||||
# Initialize all routes
|
||||
from .routes import *
|
||||
@@ -0,0 +1,3 @@
|
||||
import fastapi
|
||||
|
||||
utils_router = fastapi.APIRouter()
|
||||
@@ -0,0 +1,78 @@
|
||||
import fastapi
|
||||
from fastapi import HTTPException
|
||||
import sqlmodel
|
||||
|
||||
from app.api.auth.deps import BearerAuth
|
||||
from app.api.auth.deps import get_current_user
|
||||
from app.api.utils.routers import utils_router
|
||||
import app.core.db
|
||||
from app.models import BasicResponse
|
||||
from app.models import User
|
||||
from app.models.base import OfdRequest
|
||||
from app.models.event import Event
|
||||
from app.models.item import Item
|
||||
from app.models.transactions import Transaction
|
||||
from app.utils.nalog import get_nalog_data
|
||||
|
||||
|
||||
@utils_router.get('/health-check')
|
||||
def health_check() -> dict[str, str]:
|
||||
return {'msg': 'healthy'}
|
||||
|
||||
|
||||
@utils_router.post(
|
||||
'/ofd',
|
||||
description='Get items info from OFD bare string',
|
||||
dependencies=[fastapi.Depends(BearerAuth())],
|
||||
responses={
|
||||
400: {'model': BasicResponse, 'description': 'Bad OFD data'},
|
||||
403: {'model': BasicResponse, 'description': 'Unauthorized'},
|
||||
404: {'model': BasicResponse, 'description': 'Event not found'},
|
||||
500: {
|
||||
'model': BasicResponse,
|
||||
'description': 'Error while processing OFD data',
|
||||
},
|
||||
},
|
||||
)
|
||||
async def ofd(
|
||||
ofd: OfdRequest, user: User = fastapi.Depends(get_current_user)
|
||||
) -> Transaction:
|
||||
with sqlmodel.Session(bind=app.core.db.engine) as session:
|
||||
event = session.get(Event, ofd.event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail='Event not found')
|
||||
|
||||
data = await get_nalog_data(ofd.ofd_string)
|
||||
data = data['data']['json']
|
||||
if not data:
|
||||
raise HTTPException(status_code=400, detail='Bad OFD data')
|
||||
|
||||
transaction = Transaction(
|
||||
payer_id=user.id, event_id=event.id, title=data['retailPlace']
|
||||
)
|
||||
session.add(transaction)
|
||||
session.commit()
|
||||
items = data['items']
|
||||
|
||||
for item in items:
|
||||
new_item = Item(
|
||||
title=item['name'],
|
||||
price=item['sum'] / 100,
|
||||
transaction_id=transaction.id,
|
||||
)
|
||||
session.add(new_item)
|
||||
session.commit()
|
||||
|
||||
transaction.items.append(new_item)
|
||||
session.add(transaction)
|
||||
session.refresh(transaction)
|
||||
session.commit()
|
||||
|
||||
try:
|
||||
return transaction
|
||||
|
||||
except Exception:
|
||||
raise HTTPException(
|
||||
detail='Error while getting information about check',
|
||||
status_code=500,
|
||||
)
|
||||
Reference in New Issue
Block a user