diff --git a/backend/config.yaml b/backend/config.yaml index ca8d03a..b6ae0d2 100644 --- a/backend/config.yaml +++ b/backend/config.yaml @@ -3,7 +3,7 @@ server: host: 0.0.0.0 redis: - port: 6379 + port: 6479 host: localhost admin: diff --git a/backend/db/rate_limit.py b/backend/db/rate_limit.py index 652f460..d44f3df 100644 --- a/backend/db/rate_limit.py +++ b/backend/db/rate_limit.py @@ -12,5 +12,6 @@ def team_secret(request: Request): return param -limiter = Limiter(key_func=team_secret, default_limits=["20/second"], +# TODO, staviti na 20 ako se breaka +limiter = Limiter(key_func=team_secret, default_limits=["50/second"], storage_uri=f"redis://localhost:{config['redis']['port']}/0") diff --git a/backend/db/run_redis.py b/backend/db/run_redis.py index 964e89c..cbab248 100644 --- a/backend/db/run_redis.py +++ b/backend/db/run_redis.py @@ -63,14 +63,14 @@ def create_teams_and_games(): total_ticks=2300, tick_time=3000 ), - Game( - game_name="Natjecanje", - is_contest=int(True), - dataset_id=datasets[1].dataset_id, - start_time=datetime.now() + timedelta(milliseconds=5000), - total_ticks=1800, - tick_time=1000, - ) + # Game( + # game_name="Natjecanje", + # is_contest=int(True), + # dataset_id=datasets[1].dataset_id, + # start_time=datetime.now() + timedelta(milliseconds=5000), + # total_ticks=1800, + # tick_time=1000, + # ) ] for game in games: game.save() diff --git a/backend/fill_real_games.py b/backend/fill_real_games.py new file mode 100644 index 0000000..4a12cbf --- /dev/null +++ b/backend/fill_real_games.py @@ -0,0 +1,61 @@ +from model.game import Game +from model import Game, Team, Order, Player, DatasetData, Datasets +from operator import attrgetter +from pprint import pprint +from typing import List +from logger import logger +from datetime import datetime, timedelta +from config import config + + +if __name__ == "__main__": + logger.info("Creating games") + datasets: List[Datasets] = Datasets.find().all() + datasets.sort(key=attrgetter("dataset_name")) + assert len(datasets) > 0 + # pprint(datasets) + # print("Creating First testing round") + + # for i, dataset in enumerate(datasets): + # dataset_id = dataset.dataset_id + # # print(dataset_id) + # dataset_data = DatasetData.find(DatasetData.dataset_id == dataset_id).all() + # print(i, dataset_id, len(dataset_data)) + + + + games = [ + # Game( + # game_name="First contest round", + # is_contest=int(True), + # dataset_id=datasets[6].dataset_id, + # start_time=datetime.now() + timedelta(milliseconds=15*1000*60), + # total_ticks=1800, + # tick_time=1000 + # ), + Game( + game_name="Fifth normal round", + is_contest=int(False), + dataset_id=datasets[13].dataset_id, + start_time=datetime.now() + timedelta(milliseconds=5000), + total_ticks=5000, + tick_time=3000 + ), + ] + for game in games: + game.save() + print(game.pk) + + # bots_team_name = config["bots"]["team_name"] + # bots_team_secret = config["bots"]["team_secret"] + # if Team.find( + # Team.team_name == bots_team_name, + # Team.team_secret == bots_team_secret, + # ).count() > 0: + # logger.info("Bots team already created") + # else: + # logger.info("Creating bots team") + # Team( + # team_name=bots_team_name, + # team_secret=bots_team_secret, + # ).save() diff --git a/backend/fill_real_teams.py b/backend/fill_real_teams.py index 5a8d32f..4e2deeb 100644 --- a/backend/fill_real_teams.py +++ b/backend/fill_real_teams.py @@ -11,31 +11,32 @@ def id_generator(size=8, chars=string.ascii_uppercase + string.digits): team_names = [ - ("); DROP TABLE players; --", "R2KHWG3WNM"), - ("brokeRS", "63TX9AH00B"), - ("Cekmi", "GTPZGOP0YC"), - ("Data Diggers", "FRVFO9ANAP"), - ("Guta", "W7Z8WJ8T97"), - ("INAI", "K2758VIHOE"), - ("Kako mislis mene nema?", "UFVOQKGR64"), - ("Kodirani Kapital", "5MN5ATVJDY"), - ("LIMA", "SWANHGVS3C"), - ("Maas", "PXUVRKRQAV"), - ("Održavanje dalekovoda", "CWO9LH6DH9"), - ("OptiMinds", "AH9L64F6JT"), - ("Pip install v2", "J31LWN865I"), - ("Šampioni Hackathona", "4TPTGJQ1AQ"), - ("Three and a half men", "FWOOMZ5M30"), - ("Between exams", "0F85SNN717"), - ("Evolutionary Enigmas", "61PGJMGECE"), - ("green48", "4QUYXUSLII"), - ("Jerry & Totally Spies", "QHNILUTVS0"), - ("Pigeons", "ZALW5495N6"), - ("Polacy Robacy", "V8D3O7HH70"), - ("si_intl", "JE673V0R3Q"), - ("UW2", "FPY5E4V872"), - ("Vanjo", "60IMBZPLWE"), - ("Warsaw Mesh Trade AI", "9EJ6MAV3N5"), + # -("); DROP TABLE players; --", "R2KHWG3WNM"), + # -("brokeRS", "63TX9AH00B"), + # -("Cekmi", "GTPZGOP0YC"), + # -("Data Diggers", "FRVFO9ANAP"), + # -("Guta", "W7Z8WJ8T97"), + # -("INAI", "K2758VIHOE"), + # -("Kako mislis mene nema?", "UFVOQKGR64"), + # -("Kodirani Kapital", "5MN5ATVJDY"), + # -("LIMA", "SWANHGVS3C"), + # -("Maas", "PXUVRKRQAV"), + # -("Održavanje dalekovoda", "CWO9LH6DH9"), + # -("OptiMinds", "AH9L64F6JT"), + # -("Pip install v2", "J31LWN865I"), + # -("Šampioni Hackathona", "4TPTGJQ1AQ"), + # -("Three and a half men", "FWOOMZ5M30"), + # -("Between exams", "0F85SNN717"), + # -("Evolutionary Enigmas", "61PGJMGECE"), + # -("green48", "4QUYXUSLII"), + # -("Jerry & Totally Spies", "QHNILUTVS0"), + # -("Pigeons", "ZALW5495N6"), + # -("Polacy Robacy", "V8D3O7HH70"), + # -("si_intl", "JE673V0R3Q"), + # -("UW2", "FPY5E4V872"), + # -("Vanjo", "60IMBZPLWE"), + # -("Warsaw Mesh Trade AI", "9EJ6MAV3N5"), + # ("Kruno", "QRO5QE8T5D") ] @@ -47,5 +48,5 @@ def id_generator(size=8, chars=string.ascii_uppercase + string.digits): except Exception: team = Team(team_name=team_name, team_secret=team_secret) team.save() - print(f"Created team {team_name}") + print(f"Created team {team_name} with team secret {team_secret}") print(len(team_names)) diff --git a/backend/get_scores.py b/backend/get_scores.py new file mode 100644 index 0000000..3380f9e --- /dev/null +++ b/backend/get_scores.py @@ -0,0 +1,91 @@ +team_names = [ + "); DROP TABLE players; --", + "brokeRS", + "Cekmi", + "Data Diggers", + "Guta", + "INAI", + "Kako mislis mene nema?", + "Kodirani Kapital", + "LIMA", + "Maas", + "Održavanje dalekovoda", + "OptiMinds", + "Pip install v2", + "Šampioni Hackathona", + "Three and a half men", + "Between exams", + "Evolutionary Enigmas", + "green48", + "Jerry & Totally Spies", + "Pigeons", + "Polacy Robacy", + "si_intl", + "UW2", + "Vanjo", + "Warsaw Mesh Trade AI", +] + +leaderboard_scores = [200, 182, 170, 158, 146, 134, 124, 114, 104, 94, 84, 78, 72, 66, 60, 54, 48, 42, 36, 30, 24, 18, 12, 6, 0] + + +lines = [] +with open('scores/01HVAB7VVCN6DYYAYHM8142WGB_First contest round.txt', "r") as f: + for line in f: + lines.append(line) + last_line = line + +# text = "1196, [(Polacy Robacy/Polacy_Robacy, 107251400), (Jerry & Totally Spies/Botko, 94323634), (Pip install v2/pom, 37574900), (Održavanje dalekovoda/, 136683600), (si_intl/dfg, 53925395), (Evolutionary Enigmas/ttt, 224211978), (Data Diggers/diggy, 220143262), (Kako mislis mene nema?/ElonMusk Kukulele, 167075037), (OptiMinds/OptiMindsPlayer, 62426452), (Kruno/Casey Jones, 163297233), (Maas/Maas, 39749469), (green48/test2, 100015214), (Cekmi/ivkalu_bot, 43196913), (brokeRS/brrsbot, 270338065), (Between exams/string, 322312528), (Kodirani Kapital/Kodirani Kapital, 1196722622), (Warsaw Mesh Trade AI/croatia_on_steroids, 310802358), (Three and a half men/test, 326151613), (UW2/mati, 320454914), (LIMA/LIMA_1, 56961157), (Šampioni Hackathona/testko3, 392445174)]" +def parse_one(text): + team_scores = [] + for team_name in team_names: + try: + ind = text.index(team_name) + except Exception: + continue + # try: + ind_1 = text.index(", ", ind) + try: + ind_2 = text.index("), ", ind_1) + except Exception: + ind_2 = text.index(")]", ind_1) + score = int(text[ind_1+2: ind_2]) + team_scores.append((score, team_name)) + # except Exception: + # print(f"Error parsing score for team {team_name}") + return team_scores + +def print_last(): + team_scores = parse_one(last_line) + team_scores.sort(reverse=True) + for i, value in enumerate(team_scores): + score, team_name = value + print(f"{i+1}) {leaderboard_scores[i]} - {score}$ - {team_name}, {score/50_000_000}") + + +def one_to_dict(team_scores): + scores_dict = {} + for score, team_name in team_scores: + scores_dict[team_name] = score + return scores_dict + +# all_scores_dict = {team_name: [] for team_name in team_names} + +# scores = [] +# for line in lines: +# tick_dict = one_to_dict(parse_one(line)) +# for team_name in team_names: +# try: +# score = tick_dict[team_name] +# except Exception: +# score = 50_000_000 +# all_scores_dict[team_name].append(score) + + +# print(len(scores)) + +print_last() + +# print(last_line) + +# DataFrame \ No newline at end of file diff --git a/backend/logger.py b/backend/logger.py index 56598f6..3cecad8 100644 --- a/backend/logger.py +++ b/backend/logger.py @@ -31,7 +31,7 @@ file_handler.setFormatter(file_formatter) logger.addHandler(console_handler) -# logger.addHandler(file_handler) +logger.addHandler(file_handler) class GameLogger(logging.Logger): def game_log(self, game_id: str, msg: object, *args, **kwargs): diff --git a/backend/main.py b/backend/main.py index 38186fa..c747684 100644 --- a/backend/main.py +++ b/backend/main.py @@ -56,10 +56,11 @@ async def lifespan(app: FastAPI): app.add_middleware( CORSMiddleware, - allow_origins="Access-Control-Allow-Origin", + allow_origins=["*"], allow_credentials=True, allow_methods=["*"], - allow_headers=["*"] + allow_headers=["*"], + expose_headers=["*"] ) diff --git a/backend/model/market.py b/backend/model/market.py index bb8aa89..8c79d36 100644 --- a/backend/model/market.py +++ b/backend/model/market.py @@ -1,13 +1,13 @@ from db.db import get_my_redis_connection from model.enum_type import get_enum -from .resource import Resource, Energy from redis_om import JsonModel, Field +from model.resource import ResourceOrEnergy, Resource, Energy class Market(JsonModel): game_id: str = Field(index=True) tick: int = Field(index=True) - resource: Resource | Energy + resource: ResourceOrEnergy = Field(index=True) low: int high: int open: int diff --git a/backend/routers/admin/game.py b/backend/routers/admin/game.py index 55814d1..878dfa9 100644 --- a/backend/routers/admin/game.py +++ b/backend/routers/admin/game.py @@ -13,8 +13,10 @@ from model.team import Team from routers.model import SuccessfulResponse from db import limiter +from model.resource import Energy, ResourceOrEnergy import asyncio from logger import logger +from operator import attrgetter router = APIRouter() @@ -237,18 +239,31 @@ async def dashboard_orderbooks(websocket: WebSocket, game_id: str): while True: game = Game.get(game_id) - # orders = await Order.list(game_id=game_id, order_status=OrderStatus.ACTIVE) - orders = Order.find( - (Order.game_id == game.game_id) & - (Order.order_status == OrderStatus.ACTIVE.value) + bots = Player.find(Player.is_bot == int(True)).all() + bot_ids = set(map(attrgetter("pk"), bots)) + + active_orders = Order.find( + Order.game_id == game.game_id, + Order.order_status == OrderStatus.ACTIVE.value + ).all() + pending_orders = Order.find( + Order.game_id == game.game_id, + Order.order_status == OrderStatus.PENDING.value ).all() - # orders = [dataclasses.asdict(order) for order in orders] - orders = [order.dict() for order in orders] + def is_bot_order(order: Order): + return order.player_id in bot_ids - orders_by_resource = defaultdict( - lambda: {str(OrderSide.BUY): [], str(OrderSide.SELL): []}) + all_orders = active_orders + list(filter(is_bot_order, pending_orders)) + # orders = [dataclasses.asdict(order) for order in orders] + orders = [order.dict() for order in all_orders] + # orders_by_resource = defaultdict( + # lambda: {str(OrderSide.BUY): [], str(OrderSide.SELL): []}) + orders_by_resource = dict() + for resource in ResourceOrEnergy: + orders_by_resource[str(resource)] = {str(OrderSide.BUY): [], str(OrderSide.SELL): []} + for order in orders: orders_by_resource[str(order["resource"]) ][str(order["order_side"])].append(order) @@ -260,4 +275,4 @@ async def dashboard_orderbooks(websocket: WebSocket, game_id: str): pass except Exception: logger.warning("Error in websocets") - return + return \ No newline at end of file diff --git a/backend/routers/users/game.py b/backend/routers/users/game.py index 4f3020d..541e6d8 100644 --- a/backend/routers/users/game.py +++ b/backend/routers/users/game.py @@ -6,7 +6,8 @@ from fastapi import APIRouter, Depends from pydantic import BaseModel, Field -from model import Game +from model import Game, Trade +from model.resource import ResourceOrEnergy from model.dataset_data import DatasetData from model.power_plant_model import PowerPlantsApiModel, ResourcesApiModel from routers.users.dependencies import game_dep, start_end_tick_dep @@ -114,3 +115,31 @@ def dataset_list( for entry in all_entries: all_entries_dict[entry.tick] = entry return all_entries_dict + + +@router.get( + "/game/{game_id}/energy_demand", + summary="Get total sold energy for last ticks", +) +def get_energy_trades( + start_end=Depends(start_end_tick_dep), +) -> Dict[int, List[Trade]]: + """ + Dictionary where keys are ticks and values are fullfiled demand in that tick + Total price is combined money spent by our resource market for energy for this tick + """ + start_tick, end_tick = start_end + + energy_trades: List[Trade] = Trade.find( + Trade.tick <= end_tick, + Trade.tick >= start_tick, + Trade.resource == ResourceOrEnergy.ENERGY.value + ).all() + + energy_trades_by_tick = dict() + for tick in range(start_tick, end_tick+1): + energy_trades_by_tick[tick] = [] + for trade in energy_trades: + if trade.tick in energy_trades_by_tick: + energy_trades_by_tick[trade.tick].append(trade) + return energy_trades_by_tick \ No newline at end of file diff --git a/backend/routers/users/market.py b/backend/routers/users/market.py index 4b10556..7764a99 100644 --- a/backend/routers/users/market.py +++ b/backend/routers/users/market.py @@ -47,9 +47,9 @@ class MarketPricesResponse(BaseModel): ) def market_prices( start_end=Depends(start_end_tick_dep), - resource: Resource | Energy = Query(default=None), + resource: Resource = Query(default=None), game: Game = Depends(game_dep), -) -> Dict[Resource | Energy, List[MarketPricesResponse]]: +) -> Dict[Resource, List[MarketPricesResponse]]: """ Returns market data for resources for queried ticks. If no trades were made in this tick, data from previous tick is taken. @@ -67,7 +67,8 @@ def market_prices( all_prices: List[Market] = Market.find(*query).all() all_prices_dict = defaultdict(list) for price in all_prices: - all_prices_dict[price.resource].append(price) + if price.resource.value != ResourceOrEnergy.ENERGY.value: + all_prices_dict[price.resource.value].append(price) return all_prices_dict @@ -346,10 +347,10 @@ def order_cancel_player( raise HTTPException( status_code=400, detail="You can only cancel your own orders" ) - elif order_to_cancel.order_status == OrderStatus.PENDING.value: + elif order_to_cancel.order_status.value == OrderStatus.PENDING.value: order.order_status=OrderStatus.CANCELLED.value order.save() - elif order_to_cancel.order_status == OrderStatus.ACTIVE.value: + elif order_to_cancel.order_status.value == OrderStatus.ACTIVE.value: order.order_status=OrderStatus.USER_CANCELLED.value order.save() else: @@ -407,3 +408,6 @@ def get_trades_player( OrderSide.BUY: buy_trades, OrderSide.SELL: sell_trades, } + + + diff --git a/backend/start.sh b/backend/start.sh index 324123a..76e8858 100644 --- a/backend/start.sh +++ b/backend/start.sh @@ -20,8 +20,8 @@ python run_migrations.py if [[ "$TESTING" == 1 ]]; then echo "Running main with reload" export WATCHFILES_FORCE_POLLING=1 - uvicorn main:app --reload --host=0.0.0.0 --port=3000 --log-level critical + uvicorn main:app --reload --host=0.0.0.0 --port=8000 --log-level critical else echo "Running uvicorn main with 4 workers" - uvicorn main:app --workers 4 --host=0.0.0.0 --port=3000 --log-level warning + uvicorn main:app --workers 4 --host=0.0.0.0 --port=8000 fi \ No newline at end of file