Skip to content

Commit

Permalink
feat: Implement full GUI functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
georgedouzas committed Jan 10, 2025
1 parent a1676e5 commit a22261c
Show file tree
Hide file tree
Showing 7 changed files with 522 additions and 72 deletions.
8 changes: 8 additions & 0 deletions src/sportsbet/gui/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@
from .dataloader_creation import dataloader_creation_page
from .dataloader_loading import dataloader_loading_page
from .model_creation import model_creation_page
from .model_loading import model_loading_page


async def dataloader() -> FileResponse:
"""Dataloader endpoint."""
return FileResponse(filename='dataloader.pkl', path='dataloader.pkl', media_type='application/octet-stream')


async def model() -> FileResponse:
"""Modelr endpoint."""
return FileResponse(filename='model.pkl', path='model.pkl', media_type='application/octet-stream')


app = rx.App()
app.api.add_api_route("/dataloader", dataloader)
app.api.add_api_route("/model", model)
app.api.add_api_route("/dataloader/creation", dataloader_creation_page)
app.api.add_api_route("/dataloader/loading", dataloader_loading_page)
app.api.add_api_route("/model/creation", model_creation_page)
app.api.add_api_route("/model/loading", model_loading_page)
43 changes: 38 additions & 5 deletions src/sportsbet/gui/app/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,26 +72,59 @@ def dataloader(state: rx.State, visibility_level: int) -> rx.Component:
bg='white',
color='rgb(107,99,246)',
border='1px solid rgb(107,99,246)',
disabled=state.dataloader_serialized.bool(),
disabled=state.dataloader_serialized.bool() & ~state.dataloader_error,
),
id='dataloader',
multiple=False,
no_keyboard=True,
no_drag=True,
on_drop=state.handle_upload(rx.upload_files(upload_id='dataloader')),
on_drop=state.handle_dataloader_upload(rx.upload_files(upload_id='dataloader')),
padding='0px',
border=None,
),
margin_top='10px',
),
),
rx.cond(
state.dataloader_serialized,
state.dataloader_serialized & ~state.dataloader_error,
rx.text(f'Dataloader: {state.dataloader_filename}', size='1'),
),
)


def model(state: rx.State, visibility_level: int) -> rx.Component:
"""The model component."""
return rx.vstack(
rx.cond(
state.visibility_level > visibility_level,
rx.vstack(
title('Model', 'wand'),
rx.upload(
rx.button(
'Select File',
bg='white',
color='rgb(107,99,246)',
border='1px solid rgb(107,99,246)',
disabled=state.model_serialized.bool() & ~state.model_error,
),
id='model',
multiple=False,
no_keyboard=True,
no_drag=True,
on_drop=state.handle_model_upload(rx.upload_files(upload_id='model')),
padding='0px',
border=None,
),
margin_top='10px',
),
),
rx.cond(
state.model_serialized & ~state.model_error,
rx.text(f'Model: {state.model_filename}', size='1'),
),
)


def submit_reset(state: rx.State, disabled: bool, url: str | None = None) -> rx.Component:
"""Submit and reset buttons of UI."""
return rx.vstack(
Expand All @@ -101,7 +134,7 @@ def submit_reset(state: rx.State, disabled: bool, url: str | None = None) -> rx.
rx.button(
'Submit',
on_click=state.submit_state,
disabled=disabled,
disabled=disabled | state.dataloader_error,
loading=state.loading,
position='fixed',
top='620px',
Expand Down Expand Up @@ -259,7 +292,7 @@ def bot(state: rx.State, visibility_level: int) -> rx.Component:
left='600px',
top='100px',
width='500px',
background_color=rx.color('gray', 3),
background_color=rx.color('gray', 2),
padding='30px',
),
)
23 changes: 22 additions & 1 deletion src/sportsbet/gui/app/dataloader_creation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Dataloader creation page."""

from collections.abc import Callable
from typing import cast

import reflex as rx

Expand Down Expand Up @@ -100,6 +101,18 @@ def _dialog(name: str, icon_name: str, rows: list[list[str]], on_submit: Callabl
_dialog('Years', 'calendar', state.all_years, state.handle_submit_years),
_dialog('Divisions', 'gauge', state.all_divisions, state.handle_submit_divisions),
),
rx.cond(
~cast(rx.Var, DataloaderCreationState).leagues.bool(),
rx.text('Leagues are empty. Please select at least one.', size='1'),
),
rx.cond(
~cast(rx.Var, DataloaderCreationState).years.bool(),
rx.text('Years are empty. Please select at least one.', size='1'),
),
rx.cond(
~cast(rx.Var, DataloaderCreationState).divisions.bool(),
rx.text('Divisions are empty. Please select at least one.', size='1'),
),
margin_top='10px',
),
)
Expand Down Expand Up @@ -154,7 +167,15 @@ def dataloader_creation_page() -> rx.Component:
training_parameters(DataloaderCreationState),
submit_reset(
DataloaderCreationState,
DataloaderCreationState.visibility_level == VL['control'],
(DataloaderCreationState.visibility_level == VL['control'])
| (
(DataloaderCreationState.visibility_level == VL['parameters'])
& (
(~cast(rx.Var, DataloaderCreationState).leagues.bool())
| (~cast(rx.Var, DataloaderCreationState).years.bool())
| (~cast(rx.Var, DataloaderCreationState).divisions.bool())
)
),
),
save_dataloader(DataloaderCreationState, VL['training_parameters']),
**SIDEBAR_OPTIONS,
Expand Down
41 changes: 20 additions & 21 deletions src/sportsbet/gui/app/model_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .components import SIDEBAR_OPTIONS, bot, dataloader, home, mode, model_data, submit_reset, title
from .states import VISIBILITY_LEVELS_MODEL_CREATION as VL
from .states import ModelState
from .states import ModelCreationState


def model(state: rx.State) -> rx.Component:
Expand All @@ -15,8 +15,8 @@ def model(state: rx.State) -> rx.Component:
title('Model', 'wand'),
rx.text('Select a model', size='1'),
rx.select(
items=['Odds Comparison'],
value='Odds Comparison',
items=['Odds Comparison', 'Logistic Regression', 'Gradient Boosting'],
value=state.model_selection,
disabled=state.visibility_level > VL['model'],
on_change=state.set_model_selection,
width='160px',
Expand All @@ -25,14 +25,13 @@ def model(state: rx.State) -> rx.Component:
)


def evaluation(state: rx.State) -> rx.Component:
"""The evaluation component."""

def run(state: rx.State) -> rx.Component:
"""The usage component."""
return rx.cond(
ModelState.visibility_level > VL['dataloader'],
state.visibility_level > VL['dataloader'],
rx.vstack(
title('Evaluation', 'hand-coins'),
rx.text('Evaluate the model', size='1'),
title('Run', 'play'),
rx.text('Run the model', size='1'),
rx.select(
items=['Backtesting', 'Value bets'],
value=state.evaluation_selection,
Expand Down Expand Up @@ -60,27 +59,27 @@ def save_model(state: rx.State, visibility_level: int) -> rx.Component:
)


@rx.page(route="/model/creation", on_load=ModelState.on_load)
@rx.page(route="/model/creation", on_load=ModelCreationState.on_load)
def model_creation_page() -> rx.Component:
"""Main page."""
return rx.container(
rx.vstack(
home(),
mode(ModelState, 'Create a model'),
model(ModelState),
dataloader(ModelState, VL['model']),
evaluation(ModelState),
mode(ModelCreationState, 'Create a model'),
model(ModelCreationState),
dataloader(ModelCreationState, VL['model']),
run(ModelCreationState),
submit_reset(
ModelState,
ModelCreationState,
(
(~cast(rx.Var, ModelState.dataloader_serialized).bool())
& (ModelState.visibility_level == VL['dataloader'])
(~cast(rx.Var, ModelCreationState.dataloader_serialized).bool())
& (ModelCreationState.visibility_level == VL['dataloader'])
)
| (ModelState.visibility_level > VL['evaluation']),
| (ModelCreationState.visibility_level > VL['evaluation']),
),
save_model(ModelState, VL['evaluation']),
save_model(ModelCreationState, VL['evaluation']),
**SIDEBAR_OPTIONS,
),
model_data(ModelState, VL['control']),
bot(ModelState, VL['control']),
model_data(ModelCreationState, VL['control']),
bot(ModelCreationState, VL['control']),
)
67 changes: 67 additions & 0 deletions src/sportsbet/gui/app/model_loading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Model creation page."""

from typing import cast

import reflex as rx

from .components import SIDEBAR_OPTIONS, bot, dataloader, home, mode, model, model_data, submit_reset, title
from .states import VISIBILITY_LEVELS_MODEL_LOADING as VL
from .states import ModelLoadingState


def run(state: rx.State) -> rx.Component:
"""The usage component."""
return rx.cond(
state.visibility_level > VL['dataloader_model'],
rx.vstack(
title('Run', 'play'),
rx.text('Run the model', size='1'),
rx.select(
items=['Backtesting', 'Value bets'],
value=state.evaluation_selection,
disabled=state.visibility_level > VL['evaluation'],
on_change=state.set_evaluation_selection,
width='160px',
),
margin_top='10px',
),
)


def save_model(state: rx.State, visibility_level: int) -> rx.Component:
"""The save component."""
return rx.cond(
state.visibility_level > visibility_level,
rx.button(
'Save',
position='fixed',
top='620px',
left='275px',
width='70px',
on_click=state.download_model,
),
)


@rx.page(route="/model/loading", on_load=ModelLoadingState.on_load)
def model_loading_page() -> rx.Component:
"""Main page."""
return rx.container(
rx.vstack(
home(),
mode(ModelLoadingState, 'Load a model'),
dataloader(ModelLoadingState, 1),
model(ModelLoadingState, 1),
run(ModelLoadingState),
submit_reset(
ModelLoadingState,
(~cast(rx.Var, ModelLoadingState.dataloader_serialized).bool())
| (~cast(rx.Var, ModelLoadingState.model_serialized).bool())
| (ModelLoadingState.visibility_level > VL['evaluation']),
),
save_model(ModelLoadingState, VL['evaluation']),
**SIDEBAR_OPTIONS,
),
model_data(ModelLoadingState, VL['control']),
bot(ModelLoadingState, VL['control']),
)
Loading

0 comments on commit a22261c

Please sign in to comment.