def save_config_and_update_page(self): context = self.view.context io = IO(self.view.current_user) config = io.read_user_config() config.activity_config.charts_to_plot = context['switches'] io.save_user_config(config) return ActivityPresenter(io, context).make_layout(), config
def test_get_hardio_activity_by_id(self, test_user_id, test_client, mock_activity): io = IO(test_user_id, token_refresh=False) io.save_activity(mock_activity) activity = io.get_hardio_activity_by_id(5806863104) assert isinstance(activity, Activity) assert activity.id == 5806863104 assert isinstance(activity.dataframe, pd.DataFrame)
def test_build_activity(self, test_user_id, test_client): with open('tests/testing_data/detailed_activity.dill', 'rb') as f: strava_activity = dill.load(f) io = IO(test_user_id, token_refresh=False) activity = io.make_hardio_activity_from_strava_activity(strava_activity=strava_activity.to_dict(), get_streams=False) assert isinstance(activity, Activity) assert isinstance(activity.details, dict)
def test_save_activity(self, test_user_id, test_client, mock_activity, create_db_connection): io = IO(test_user_id, token_refresh=False) io.save_activity(mock_activity) with create_db_connection as connection: select = connection.execute(f"SELECT * FROM db_activity").fetchall() assert select[0].activity_id == 5806863104 assert select[0].user_id == test_user_id assert select[0].athlete_id == 21932478 assert "All" in select[0].intervals[0]
def update_user_config_in_db(config: UserConfig, io: IO, month_year: tuple[int, int]) -> None: """ Updates UserConfig based on latest calendar Month/Year view preference. Saves UserConfig to db :param config: instance of UserConfig :param io: instance of IO object :param month_year: tuple[int, int] Month, Year :return: None """ config.user_calendar_date_preference = month_year io.save_user_config(config)
def get_activities(_): activities = IO(current_user.id).get_activities_from_strava() if activities: return test_strava_methods_page.make_table(activities), False else: return dash_external_redirect.redirect( url_for('users.strava_login')), True
def render_page_content(pathname, user_config) -> dash.Dash.layout: """ Callback defines general structure of an multi-page Dash app. :param pathname: string :param user_config: json containing user configuration :return: layout, username, user_config """ io = IO(current_user.id) config = io.read_user_config() if pathname == "/application/": return [dash_app.presenter.get_calendar()], [current_user.username] elif pathname == "/power/": return [html.H2(f"Not yet implemented")], [current_user.username] elif pathname == "/fitness/": return [html.H2(f"Not yet implemented")], [current_user.username] elif pathname == "/application/activity": dash_app.context = {'activity': None} return [dash_app.presenter.get_activity()], [current_user.username] elif "/application/activity/" in pathname: activity_id = pathname.split("/")[-1] dash_app.context = {'activity': activity_id} return [dash_app.presenter.get_activity()], [current_user.username] elif pathname == "/application/test_strava": return [ html.H1("Activity", style={"textAlign": "center"}), html.H2(f"Current user id: {current_user.id}"), test_strava_methods_page.make_layout() ], [current_user.username] # If the user tries to reach a different page, return a 404 message return dbc.Card([ html.H1("404: Not Found", className="text-danger"), html.Hr(), html.P(f"The pathname {pathname} was not recognized...") ]), [current_user.username]
def activity_delete_intervals_and_refresh_view(self): context = self.view.context activity_id = int(context['activity']) io = IO(self.view.current_user) config = io.read_user_config() activity = io.get_hardio_activity_by_id(activity_id) activity.delete_intervals() io.save_activity(activity) return ActivityPresenter(io, context).make_figure(activity, config)
def make_layout(user_id: int = None, activity_id: int = None, config: UserConfig = None) -> dash.Dash.layout: if not user_id: return _make_layout(activity=IO(0).build_mock_up_ride()) if not activity_id: io = IO(user_id=user_id) last_activity = io.get_last_activity() io.save_activity(last_activity) return _make_layout(last_activity, config) return _make_layout( IO(user_id=user_id).get_hardio_activity_by_id(int(activity_id)), config)
def activity_find_intervals_and_refresh_view(self): context = self.view.context activity_id = int(context['activity']) io = IO(self.view.current_user) config = io.read_user_config() activity = io.get_hardio_activity_by_id(int(activity_id)) interval_finder_params = context['interval_finder_prams'] found_intervals = activity.find_intervals(**interval_finder_params) activity.add_intervals(found_intervals) io.save_activity(activity) return ActivityPresenter(io, context).make_figure(activity, config)
def set_month_year(io: IO, user_selected_moth_year: str) -> tuple[int, int]: """ Produce Month/Year combination to filter calendar by :param io: instance of IO object :param user_selected_moth_year: string containing user-selected combination of Month/Year :return: tuple[int, int] Month, Year """ config = io.read_user_config() if user_selected_moth_year: month_year = _month_year_to_tuple(user_selected_moth_year) update_user_config_in_db(config, io, month_year) month, year = month_year elif config.user_calendar_date_preference: month, year = config.user_calendar_date_preference else: month: int = datetime.now().month year: int = datetime.now().year return month, year
def home() -> str: """ Landing page includes logic to check if user logged in and if app is authorized in Strava to produce relevant experience: - Advise to create account for new users - Ask to authorize app in strava for existing user (in case not authorized previously) - Link to proceed to application for logged-in users with app authorized in strava :return: rendered template """ content = [] try: user_id = current_user.id return render_template( 'index.html', content=content, title='HARDIO Home', strava_authorized=IO(user_id).is_strava_authorized()) except AttributeError: return render_template('index.html', content=content)
def make_month_selector(user_id: int, year: int, month: int) -> dbc.Select: earliest, _ = IO(user_id).get_user_activity_date_range() _today = datetime.now() next_year = [_today.year + 1] next_months = [d for d in range(_today.month + 1, 13)][::-1] today = [_today.month] prev_months = [d for d in range(1, _today.month)][::-1] prev_years = list(range(earliest.year, _today.year))[::-1] selector = _make_selector( { 'next_year': next_year, 'next_months': next_months, 'today': today, 'prev_months': prev_months, 'prev_years': prev_years }, year, month) return selector
def activity_create_intervals_and_refresh_view(self): context = self.view.context intervals_range = self._relayout_data_to_range( context['intervals_range']) activity_id = int(context['activity']) io = IO(self.view.current_user) config = io.read_user_config() if intervals_range: activity = io.get_hardio_activity_by_id(activity_id) activity.add_interval(*intervals_range) io.save_activity(activity) return ActivityPresenter(io, context).make_figure(activity, config) else: return dash.no_update
def refresh_activities_from_strava(user_id: int, month_year: str) -> None: month, year = _month_year_to_tuple(month_year) io = IO(user_id) io.refresh_user_activities_from_strava(month, year)
def get_calendar(self) -> dash.Dash.layout: context = self.view.context io = IO(self.view.current_user) formatter = CalendarFormatter() calendar = AppCalendar(io, formatter) return calendar.make_layout(context)
def make_layout(user_id: int, user_selected_moth_year: str = None) -> Dash.layout: io = IO(user_id) month, year = set_month_year(io, user_selected_moth_year) cal = calendar.Calendar().monthdatescalendar(year, month) activities_list = io.get_list_of_hardio_activities_in_range( cal[0][0], cal[-1][-1]) formatted_cal = HardioCalendar().format_month(cal, activities_list) columns = [{ 'id': d, 'name': d, 'editable': False, 'type': 'text', 'presentation': 'markdown', } for d in WEEK_DAYS] table = dash_table.DataTable( id="calendar", data=formatted_cal, columns=columns, # page_size=3, # page_current=0, # virtualization=True, fixed_rows={ 'headers': True, 'data': 0 }, style_cell={ 'minWidth': 95, 'width': 95, 'maxWidth': 95, }, style_data={ 'font_family': 'cursive', 'font_size': '10px', 'text_align': 'center', 'white-space': 'normal', 'height': 'auto', 'overflow': 'hidden', 'textOverflow': 'ellipsis', 'maxWidth': 0, }, # style_table={'height': '500px'}, # default is 500, # style_table={ # 'minHeight': '100vh', 'height': '100vh', 'maxHeight': '100vh', # 'minWidth': '900px', 'width': '900px', 'maxWidth': '900px' # }, markdown_options={"link_target": "_self"}, css=[ { "selector": ".dash-spreadsheet tr th", "rule": "height: 15px;" }, # set height of header # {"selector": ".dash-spreadsheet tr td", "rule": "height: 75px;"}, # set height of body rows # {"selector": ".dash-spreadsheet-container", "rule": "max-height: 1000px;"}, # {"selector": "table", "rule": "width: 100%;"}, # {"selector": "cell cell-1-1 dash-fixed-content", "rule": "height: 100px;"}, { "selector": "dash-spreadsheet-container dash-spreadsheet dash-virtualized dash-freeze-top dash-no-filter dash-fill-width", "rule": "max-height: 1200px; height: 1200px" }, # {"selector": ".dash-table-container tr", "rule": 'max-height: "150px"; height: "150px"; '}, # {"selector": "dash-spreadsheet dash-freeze-top dash-spreadsheet dash-virtualized", "rule": "max-height: inherit !important;"}, # {"selector": "dash-table-container", "rule": "max-height: calc(100vh - 225px);"} ], ) alert = html.Div([ dbc.Alert( "Refresh may take up to a minute or so, depending on number of activities", id="alert-calendar-refresh", is_open=False, duration=4000, ), ]) layout = html.Div(id="calendar_view", children=[ html.Div([ make_month_selector(user_id, month, year), dbc.Button('Refresh', id='btn_refresh_activities', n_clicks=0, color="link"), ], style={'width': '10rem'}), html.Br(), alert, dbc.Spinner( html.Div(id="refresh_spinner", children='calendar')), table ]) return layout
def get_activity(self) -> dash.Dash.layout: context = self.view.context io = IO(self.view.current_user) if Config.CYCLOMETRY: return Cyclometry(io, context).make_layout() return ActivityPresenter(io, context).make_layout()
def update_cyclometry_view(self): context = self.view.context io = IO(self.view.current_user) c = Cyclometry(io, context) c.api_request_update()