78 lines
2.0 KiB
Python
78 lines
2.0 KiB
Python
from fastapi import FastAPI
|
|
from pydantic import BaseModel
|
|
from typing import List
|
|
from collections import defaultdict
|
|
|
|
app = FastAPI(openapi_url="/optimizetka/openapi.json", docs_url="/optimizetka/docs")
|
|
|
|
|
|
class Participant(BaseModel):
|
|
id: int
|
|
|
|
|
|
class Position(BaseModel):
|
|
price: float
|
|
assigned_to_ids: List[int]
|
|
|
|
|
|
class Transaction(BaseModel):
|
|
owner_id: int
|
|
positions: List[Position]
|
|
|
|
|
|
class InputData(BaseModel):
|
|
participants: List[Participant]
|
|
transactions: List[Transaction]
|
|
|
|
|
|
class Debt(BaseModel):
|
|
from_user_id: int
|
|
to_user_id: int
|
|
amount: float
|
|
|
|
|
|
@app.post("/optimizetka/api/calculate-debts", response_model=List[Debt])
|
|
def calculate_debts(data: InputData) -> List[Debt]:
|
|
balances = defaultdict(float)
|
|
|
|
for transaction in data.transactions:
|
|
owner_id = transaction.owner_id
|
|
for position in transaction.positions:
|
|
price = position.price
|
|
assigned_to_ids = position.assigned_to_ids
|
|
share = price / len(assigned_to_ids)
|
|
|
|
for participant_id in assigned_to_ids:
|
|
if participant_id != owner_id:
|
|
balances[participant_id] -= share
|
|
balances[owner_id] += share
|
|
|
|
debts = []
|
|
creditors = [(p, amount) for p, amount in balances.items() if amount > 0]
|
|
debtors = [(p, -amount) for p, amount in balances.items() if amount < 0]
|
|
|
|
i, j = 0, 0
|
|
while i < len(creditors) and j < len(debtors):
|
|
creditor_id, credit_amount = creditors[i]
|
|
debtor_id, debt_amount = debtors[j]
|
|
|
|
payment = min(credit_amount, debt_amount)
|
|
|
|
debts.append(
|
|
{
|
|
"from_user_id": debtor_id,
|
|
"to_user_id": creditor_id,
|
|
"amount": round(payment, 2),
|
|
}
|
|
)
|
|
|
|
creditors[i] = (creditor_id, credit_amount - payment)
|
|
debtors[j] = (debtor_id, debt_amount - payment)
|
|
|
|
if creditors[i][1] == 0:
|
|
i += 1
|
|
if debtors[j][1] == 0:
|
|
j += 1
|
|
|
|
return debts
|