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)
Example #9
0
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
Example #12
0
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()