Skip to content

Commit

Permalink
More stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
day-mon committed Jan 16, 2024
1 parent 8934531 commit 210dbda
Show file tree
Hide file tree
Showing 37 changed files with 835 additions and 2,742 deletions.
4 changes: 2 additions & 2 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ FROM python:3.11.0-slim

WORKDIR /app

COPY ./api /app/api
COPY . .
COPY poetry.lock pyproject.toml /app/

RUN pip install poetry
RUN poetry config virtualenvs.create false
RUN poetry install --no-dev
RUN poetry install --no-dev --only api

CMD ["python", "/app/api/main.py"]
13 changes: 13 additions & 0 deletions api/Dockerfile-services
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Dockerfile-api
FROM python:3.11.0-slim

WORKDIR /app

COPY . .
COPY poetry.lock pyproject.toml /app/

RUN pip install poetry
RUN poetry config virtualenvs.create false
RUN poetry install --no-dev --without api

CMD ["python", "/app/services/main.py"]
4 changes: 2 additions & 2 deletions api/api/business/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async def get(self, key: str) -> Union[str, bytes, int, float, dict, list, tuple

@abstractmethod
async def set(
self, key: str, value: Union[str, bytes, int, float, dict, list, tuple]
self, key: str, value: Union[str, bytes, int, float, dict, list, tuple]
):
pass

Expand All @@ -44,7 +44,7 @@ async def get(self, key: str) -> Union[str, bytes, int, float, dict, list, tuple
return item

async def set(
self, key: str, value: Union[str, bytes, int, float, dict, list, tuple]
self, key: str, value: Union[str, bytes, int, float, dict, list, tuple]
):
return self.redis_client.set(key, value)

Expand Down
18 changes: 17 additions & 1 deletion api/api/business/daily_games.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from abc import ABC, abstractmethod
import httpx
from api.business.factory import AbstractFactory, FactoryItem
from api.model.games.daily_game import NBALiveData, DailyGame, TeamData
from api.model.games.daily_game import NBALiveData, DailyGame, TeamData, PlayerLeader


class DailyGamesSource(ABC):
Expand Down Expand Up @@ -50,6 +50,14 @@ async def fetch(self) -> list[DailyGame]:
wins=game.homeTeam.wins,
losses=game.homeTeam.losses,
abbreviation=game.homeTeam.teamTricode,
leader=None
if game.gameLeaders.home_is_empty()
else PlayerLeader(
name=game.gameLeaders.homeLeaders.name,
points=game.gameLeaders.homeLeaders.points,
rebounds=game.gameLeaders.homeLeaders.rebounds,
assists=game.gameLeaders.homeLeaders.assists,
),
),
away_team=TeamData(
id=game.awayTeam.teamId,
Expand All @@ -58,6 +66,14 @@ async def fetch(self) -> list[DailyGame]:
wins=game.awayTeam.wins,
losses=game.awayTeam.losses,
abbreviation=game.awayTeam.teamTricode,
leader=None
if game.gameLeaders.away_is_empty()
else PlayerLeader(
name=game.gameLeaders.awayLeaders.name,
points=game.gameLeaders.awayLeaders.points,
rebounds=game.gameLeaders.awayLeaders.rebounds,
assists=game.gameLeaders.awayLeaders.assists,
),
),
)
)
Expand Down
90 changes: 90 additions & 0 deletions api/api/business/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from abc import ABC, abstractmethod
from typing import Optional

import asyncpg
from asyncpg import Record

from api.business.factory import AbstractFactory, FactoryItem
from api.config.database import DatabaseSettings, get_database_settings

from loguru import logger

# from psycopg2 import pool
# from configs.settings import settings


class Database(ABC):
db_settings: DatabaseSettings

def __init__(self):
self.db_settings = get_database_settings()

@abstractmethod
async def query(self, query: str, values: Optional[list] = None):
pass

@abstractmethod
async def connect(self):
pass

@abstractmethod
async def close(self):
pass


class Postgres(Database):
def __init__(self):
super().__init__()
self.user = self.db_settings.DATABASE_USER
self.password = self.db_settings.DATABASE_PASSWORD
self.host = self.db_settings.DATABASE_HOST
self.port = self.db_settings.DATABASE_PORT
self.database = self.db_settings.DATABASE_NAME
self._cursor = None

self._connection_pool = None
self.con = None

def close(self):
if self._connection_pool:
self._connection_pool.close()

async def connect(self):
if not self._connection_pool:
try:
self._connection_pool = await asyncpg.create_pool(
min_size=1,
max_size=10,
command_timeout=60,
host=self.host,
port=self.port,
user=self.user,
password=self.password,
database=self.database,
)

except Exception as e:
print(e)

async def query(self, query: str, values: Optional[list] = None) -> list[Record]:
if not self._connection_pool:
await self.connect()

logger.debug(f"Query: {query}")

if values:
values = tuple(values)

self.con = await self._connection_pool.acquire()
try:
cursor = await self.con.fetch(query, *values)
logger.debug(f"Result: {cursor}")
return cursor
except Exception as e:
print(e)
finally:
await self._connection_pool.release(self.con)


class DatabaseFactory(AbstractFactory):
_values = {"postgres": FactoryItem(name="postgres", factory_item=Postgres)}
5 changes: 3 additions & 2 deletions api/api/business/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from loguru import logger
from pydantic import BaseModel


class FactoryItem(BaseModel):
name: str
factory_item: Any


class AbstractFactory:
_values: dict[str, FactoryItem] = {}

Expand All @@ -24,8 +26,7 @@ def compute_or_get(cls, name: str, **kwargs):
@classmethod
def keys(cls):
return cls._values.keys()

@classmethod
def values(cls):
return cls._values


6 changes: 1 addition & 5 deletions api/api/business/injury.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

class PlayerInjurySource(ABC):
source_url: str
client: httpx.AsyncClient = httpx.AsyncClient(
timeout=60
)
client: httpx.AsyncClient = httpx.AsyncClient(timeout=60)

def __init__(self, source_url: str):
self.source_url = source_url
Expand Down Expand Up @@ -41,6 +39,4 @@ class PlayerInjuryFactory(AbstractFactory):
name="rotowire",
factory_item=RotowireInjurySource,
),


}
40 changes: 35 additions & 5 deletions api/api/business/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ async def predict(self, data: DataFrame) -> list[Prediction]:
def fetch_stats(self, **kwargs) -> DataFrame:
pass

@abstractmethod
def accuracy_statement(self) -> str:
pass


class TFPredictionModel(PredictionModel):
_data_dir: str
Expand Down Expand Up @@ -125,7 +129,7 @@ def fetch_stats(self, **kwargs) -> DataFrame:

def _write_stats_to_csv(
self, response: TeamStats, matches: list[DailyGame], date: str
) -> DataFrame:
) -> Optional[DataFrame]:
result_set_name = "LeagueDashTeamStats"
csv_str = ""
correct_result_set = next(
Expand All @@ -139,7 +143,8 @@ def _write_stats_to_csv(
logger.debug(f"Correct Result Set: {correct_result_set}")
if not correct_result_set:
logger.error(f"Unable to find result set: {result_set_name}")
return pd.DataFrame()
# this is a placeholder for now
raise Exception(f"Unable to find result set: {result_set_name}")

for i in range(2):
if i == 1:
Expand All @@ -158,9 +163,18 @@ def _write_stats_to_csv(

return pd.read_csv(os.path.join(self._data_dir, f"{date}.csv"))

def accuracy_statement(self) -> str:
return f"""
SELECT
model_name,
(COUNT(winner) FILTER (WHERE (winner = home_team_name AND prediction = home_team_name) OR (winner = away_team_name AND prediction = away_team_name))) * 100.0 / COUNT(winner) AS win_rate
FROM
saved_games
WHERE
model_name = $1
GROUP BY
model_name"""

class OpenRouterLLMBackedPrediction(PredictionModel):
pass

class FortyTwoDPModel(TFPredictionModel):
def __init__(self, model_name: str, model_dir: str):
Expand Down Expand Up @@ -222,20 +236,36 @@ async def predict(self, data: DataFrame) -> list[Prediction]:
)
return predicts

def accuracy_statement(self) -> str:
return f"""SELECT
CAST(AVG(ABS((home_team_score::int + away_team_score::int) - prediction::int)) AS FLOAT) AS win_rate,
model_name
FROM
saved_games
WHERE
prediction ~ '^\d+$'
AND model_name = $1
GROUP BY
model_name"""


class ModelFactoryItem(FactoryItem):
is_enabled: bool = True


class PredictionModelFactory(AbstractFactory):
_values = {
"v2": ModelFactoryItem(name="v2", factory_item=FortyTwoDPModel),
"v1": ModelFactoryItem(name="v1", factory_item=FortyEightDPModel, is_enabled=False),
"v1": ModelFactoryItem(
name="v1", factory_item=FortyEightDPModel, is_enabled=False
),
"ou": ModelFactoryItem(name="ou", factory_item=OUPredictionModel),
}

@classmethod
def keys(cls):
return [key for key, value in cls._values.items() if value.is_enabled]

@classmethod
def values(cls):
return {key: value for key, value in cls._values.items() if value.is_enabled}
17 changes: 13 additions & 4 deletions api/api/business/service.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
from abc import abstractmethod
from multiprocessing import Event

from api.main import BASE_PATH
import httpx
import os

class Service:
@abstractmethod
async def start(self):
raise NotImplementedError

class Service:
client: httpx.AsyncClient

def __init__(self):
host = os.getenv("API_HOST", "http://localhost:8000")
if host.endswith("/"):
host = host[:-1]

self.client = httpx.AsyncClient(base_url=f"{host}{BASE_PATH}", timeout=60)

@abstractmethod
async def start(self):
raise NotImplementedError
19 changes: 11 additions & 8 deletions api/api/config/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@
import os


class AppSettings(BaseSettings):
ENVIRONMENT: Literal["development", "production"] = "development"
DAILY_GAMES_SOURCE: Literal["nba"] = "nba"
PLAYER_INJURY_SOURCE: Literal["rotowire"] = "rotowire"
ODDS_SOURCE: Literal["actionnetwork"] = "actionnetwork"
MODEL_DIR: str
DATA_DIR: str
CF_WORKER_URL: str
class AppBaseSettings(BaseSettings):
model_config = SettingsConfigDict(
env_file=os.getenv(
"ENV_FILE_OVERRIDE",
Expand All @@ -21,6 +14,16 @@ class AppSettings(BaseSettings):
)


class AppSettings(AppBaseSettings):
ENVIRONMENT: Literal["development", "production"] = "development"
DAILY_GAMES_SOURCE: Literal["nba"] = "nba"
PLAYER_INJURY_SOURCE: Literal["rotowire"] = "rotowire"
ODDS_SOURCE: Literal["actionnetwork"] = "actionnetwork"
MODEL_DIR: str
DATA_DIR: str
CF_WORKER_URL: str


@lru_cache
def get_settings():
return AppSettings()
Loading

0 comments on commit 210dbda

Please sign in to comment.