mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-10 08:17:59 -05:00
Backend revamp
[README] Added new FRONTEND_HOST to backend env table [README] Added link to Endurain Mastodon profile [docker-compose] Added new env variable [backend] (Re)Added scheduler to refresh strava tokens periodically [backend] Strava activities flow finished [backend] Strava gear id in activity and gear DB tables changed from int to str (Strava uses a string for gear id) [frontend] Fixed some issues related to new backend logic [frontend] It is not possible to delete a Strava activity from UI [frontend] Link to Strava activity replaced with a logo
This commit is contained in:
@@ -91,7 +91,7 @@ JAEGER_PROTOCOL | http | Yes
|
||||
JAEGER_HOST | jaeger | Yes
|
||||
JAGGER_PORT | 4317 | Yes
|
||||
STRAVA_DAYS_ACTIVITIES_ONLINK | 30 | Yes
|
||||
API_ENDPOINT* | changeme | Yes
|
||||
API_ENDPOINT* | backend | Yes
|
||||
FRONTEND_HOST* | frontend | Yes
|
||||
GEOCODES_MAPS_API** | changeme | `No`
|
||||
|
||||
|
||||
@@ -15,6 +15,6 @@ JAEGER_PROTOCOL=http
|
||||
JAEGER_HOST=jaeger
|
||||
JAGGER_PORT=4317
|
||||
STRAVA_DAYS_ACTIVITIES_ONLINK=30
|
||||
API_ENDPOINT=changeme
|
||||
API_ENDPOINT=backend
|
||||
FRONTEND_HOST=frontend
|
||||
GEOCODES_MAPS_API=changeme
|
||||
@@ -44,6 +44,20 @@ def authenticate_user(username: str, password: str, db: Session):
|
||||
) from err
|
||||
|
||||
|
||||
def get_all_users(db: Session):
|
||||
try:
|
||||
# Get the number of users from the database
|
||||
return db.query(models.User).all()
|
||||
except Exception as err:
|
||||
# Log the exception
|
||||
logger.error(f"Error in get_all_number: {err}", exc_info=True)
|
||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
|
||||
|
||||
def get_users_number(db: Session):
|
||||
try:
|
||||
# Get the number of users from the database
|
||||
|
||||
@@ -19,12 +19,13 @@ from routers import (
|
||||
router_activity_streams,
|
||||
router_gear,
|
||||
router_followers,
|
||||
router_strava
|
||||
router_strava,
|
||||
)
|
||||
from constants import API_VERSION
|
||||
from database import engine
|
||||
from schemas import schema_access_tokens
|
||||
from dependencies import dependencies_database
|
||||
from processors import strava_processor
|
||||
import models
|
||||
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
@@ -40,6 +41,8 @@ def startup_event():
|
||||
# Job to remove expired tokens every 5 minutes
|
||||
logger.info("Added scheduler job to remove expired tokens every 5 minutes")
|
||||
scheduler.add_job(remove_expired_tokens_job, "interval", minutes=5)
|
||||
logger.info("Added scheduler job to refresh strava user tokens every 60 minutes")
|
||||
scheduler.add_job(remove_expired_tokens_job, "interval", minutes=60)
|
||||
|
||||
|
||||
def shutdown_event():
|
||||
@@ -61,6 +64,17 @@ def remove_expired_tokens_job():
|
||||
db.close()
|
||||
|
||||
|
||||
def refresh_strava_tokens_job():
|
||||
# Get the first (and only) item from the generator
|
||||
db = next(dependencies_database.get_db())
|
||||
try:
|
||||
# Refresh Strava tokens
|
||||
strava_processor.refresh_strava_tokens(db=db)
|
||||
finally:
|
||||
# Ensure the session is closed after use
|
||||
db.close()
|
||||
|
||||
|
||||
# Create loggger
|
||||
logger = logging.getLogger("myLogger")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
@@ -153,4 +167,4 @@ FastAPIInstrumentor.instrument_app(app)
|
||||
app.add_event_handler("startup", startup_event)
|
||||
|
||||
# Register the shutdown event handler
|
||||
app.add_event_handler("shutdown", shutdown_event)
|
||||
app.add_event_handler("shutdown", shutdown_event)
|
||||
|
||||
@@ -217,7 +217,7 @@ class Gear(Base):
|
||||
is_active = Column(
|
||||
Integer, nullable=False, comment="Is gear active (0 - not active, 1 - active)"
|
||||
)
|
||||
strava_gear_id = Column(BigInteger, nullable=True, comment="Strava gear ID")
|
||||
strava_gear_id = Column(String(length=45), nullable=True, comment="Strava gear ID")
|
||||
|
||||
# Define a relationship to the User model
|
||||
user = relationship("User", back_populates="gear")
|
||||
@@ -295,7 +295,7 @@ class Activity(Base):
|
||||
index=True,
|
||||
comment="Gear ID associated with this activity",
|
||||
)
|
||||
strava_gear_id = Column(BigInteger, nullable=True, comment="Strava gear ID")
|
||||
strava_gear_id = Column(String(length=45), nullable=True, comment="Strava gear ID")
|
||||
strava_activity_id = Column(BigInteger, nullable=True, comment="Strava activity ID")
|
||||
|
||||
# Define a relationship to the User model
|
||||
|
||||
@@ -215,9 +215,10 @@ def define_activity_type(activity_type):
|
||||
"cycling": 4,
|
||||
"Ride": 4,
|
||||
"GravelRide": 5,
|
||||
"EBikeRide": 6,
|
||||
"MountainBikeRide": 6,
|
||||
"VirtualRide": 7,
|
||||
"virtual_ride": 7,
|
||||
"Swim": 8,
|
||||
"swimming": 8,
|
||||
"open_water_swimming": 8,
|
||||
"Walk": 9,
|
||||
|
||||
@@ -1,21 +1,78 @@
|
||||
import logging
|
||||
import os
|
||||
import requests
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from stravalib.client import Client
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pint import Quantity
|
||||
|
||||
from schemas import schema_activities, schema_activity_streams
|
||||
from crud import crud_user_integrations, crud_activities, crud_activity_streams
|
||||
from schemas import schema_activities, schema_activity_streams, schema_user_integrations
|
||||
from crud import crud_user_integrations, crud_activities, crud_activity_streams, crud_users
|
||||
from dependencies import dependencies_database
|
||||
from processors import activity_processor
|
||||
|
||||
# Define a loggger created on main.py
|
||||
logger = logging.getLogger("myLogger")
|
||||
|
||||
|
||||
def get_user_strava_activities_by_days(start_date: datetime, user_id: int, db: Session):
|
||||
def refresh_strava_token(db: Session):
|
||||
# Get all users
|
||||
users = crud_users.get_all_users(db)
|
||||
|
||||
# Iterate through all users
|
||||
for user in users:
|
||||
# Get the user integrations by user ID
|
||||
user_integrations = crud_user_integrations.get_user_integrations_by_user_id(user.id, db)
|
||||
|
||||
# Check if user_integrations strava token is not None
|
||||
if user_integrations.strava_token is not None:
|
||||
refresh_time = user_integrations.strava_token_expires_at - timedelta(
|
||||
minutes=60
|
||||
)
|
||||
|
||||
if datetime.utcnow() > refresh_time:
|
||||
# Strava token refresh endpoint
|
||||
token_url = "https://www.strava.com/oauth/token"
|
||||
# Parameters for the token refresh request
|
||||
payload = {
|
||||
"client_id": os.environ.get("STRAVA_CLIENT_ID"),
|
||||
"client_secret": os.environ.get("STRAVA_CLIENT_SECRET"),
|
||||
"refresh_token": user_integrations.strava_refresh_token,
|
||||
"grant_type": "refresh_token",
|
||||
}
|
||||
|
||||
try:
|
||||
# Send a POST request to the token URL
|
||||
response = requests.post(token_url, data=payload)
|
||||
|
||||
# Check if the response status code is not 200
|
||||
if response.status_code != 200:
|
||||
# Raise an HTTPException with a 424 Failed Dependency status code
|
||||
logger.error("Unable to retrieve tokens for refresh process from Strava")
|
||||
|
||||
tokens = response.json()
|
||||
except Exception as err:
|
||||
# Log the exception
|
||||
logger.error(f"Error in refresh_strava_token: {err}", exc_info=True)
|
||||
|
||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Internal Server Error",
|
||||
) from err
|
||||
finally:
|
||||
# Update the user integrations with the tokens
|
||||
crud_user_integrations.link_strava_account(user_integrations, tokens, db)
|
||||
#else:
|
||||
# Log an informational event if the Strava access token is not found
|
||||
#logger.info(f"User {user.id}: Strava access token not found")
|
||||
|
||||
|
||||
def fetch_user_integrations_and_validate_token(
|
||||
user_id: int, db: Session
|
||||
) -> schema_user_integrations.UserIntegrations:
|
||||
# Get the user integrations by user ID
|
||||
user_integrations = crud_user_integrations.get_user_integrations_by_user_id(
|
||||
user_id, db
|
||||
@@ -35,12 +92,20 @@ def get_user_strava_activities_by_days(start_date: datetime, user_id: int, db: S
|
||||
detail="Strava access token not found",
|
||||
)
|
||||
|
||||
# Log the start of the activities processing
|
||||
logger.info(f"User {user_id}: Started activities processing")
|
||||
# Return the user integrations
|
||||
return user_integrations
|
||||
|
||||
# Create a Strava client with the user's access token
|
||||
strava_client = Client(access_token=user_integrations.strava_token)
|
||||
|
||||
def create_strava_client(
|
||||
user_integrations: schema_user_integrations.UserIntegrations,
|
||||
) -> Client:
|
||||
# Create a Strava client with the user's access token and return it
|
||||
return Client(access_token=user_integrations.strava_token)
|
||||
|
||||
|
||||
def fetch_and_process_activities(
|
||||
strava_client: Client, start_date: datetime, user_id: int, db: Session
|
||||
) -> int:
|
||||
# Fetch Strava activities after the specified start date
|
||||
strava_activities = list(strava_client.get_activities(after=start_date))
|
||||
|
||||
@@ -50,32 +115,39 @@ def get_user_strava_activities_by_days(start_date: datetime, user_id: int, db: S
|
||||
f"User {user_id}: No new activities found after {start_date}: strava_activities is None"
|
||||
)
|
||||
|
||||
# Use ThreadPoolExecutor for parallel processing of activities
|
||||
with ThreadPoolExecutor() as executor:
|
||||
executor.map(
|
||||
lambda activity: process_activity(activity, user_id, strava_client, db),
|
||||
strava_activities,
|
||||
)
|
||||
# Return 0 to indicate no activities were processed
|
||||
return 0
|
||||
|
||||
# Log an informational event for tracing
|
||||
logger.info(f"User {user_id}: {len(strava_activities)} activities processed")
|
||||
# Process the activities
|
||||
for activity in strava_activities:
|
||||
process_activity(activity, user_id, strava_client, db)
|
||||
|
||||
# Return the number of activities processed
|
||||
return len(strava_activities)
|
||||
|
||||
|
||||
def process_activity(activity, user_id, strava_client, db: Session):
|
||||
def fetch_and_validate_activity(
|
||||
activity_id: int, user_id: int, db: Session
|
||||
) -> schema_activities.Activity | None:
|
||||
# Get the activity by Strava ID from the user
|
||||
activity = crud_activities.get_activity_by_strava_id_from_user_id(
|
||||
activity.id, user_id, db
|
||||
activity_db = crud_activities.get_activity_by_strava_id_from_user_id(
|
||||
activity_id, user_id, db
|
||||
)
|
||||
|
||||
# Check if activity is None
|
||||
if activity:
|
||||
if activity_db:
|
||||
# Log an informational event if the activity already exists
|
||||
logger.info(
|
||||
f"User {user_id}: Activity {activity.id} already exists. Will skip processing"
|
||||
f"User {user_id}: Activity {activity_id} already exists. Will skip processing"
|
||||
)
|
||||
|
||||
# Return None
|
||||
return activity_db
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def parse_activity(activity, user_id: int, strava_client: Client) -> dict:
|
||||
# Parse start and end dates
|
||||
start_date_parsed = activity.start_date
|
||||
|
||||
@@ -183,8 +255,15 @@ def process_activity(activity, user_id, strava_client, db: Session):
|
||||
is_power_set = True
|
||||
|
||||
for i in range(len(vel)):
|
||||
# Append velocity to the velocity waypoints
|
||||
vel_waypoints.append({"time": time[i], "vel": vel[i]})
|
||||
pace_calculation = 1 / vel[i]
|
||||
|
||||
# Calculate pace on-the-fly. If velocity is 0, pace is 0
|
||||
pace_calculation = 0
|
||||
if vel[i] != 0:
|
||||
pace_calculation = 1 / vel[i]
|
||||
|
||||
# Append pace to the pace waypoints
|
||||
pace_waypoints.append({"time": time[i], "pace": pace_calculation})
|
||||
is_velocity_set = True
|
||||
|
||||
@@ -203,37 +282,6 @@ def process_activity(activity, user_id, strava_client, db: Session):
|
||||
if activity.average_watts is not None:
|
||||
average_watts = activity.average_watts
|
||||
|
||||
# Create the activity in the database
|
||||
created_activity = crud_activities.create_activity(
|
||||
schema_activities.Activity(
|
||||
user_id=user_id,
|
||||
name=activity.name,
|
||||
distance=(
|
||||
round(float(activity.distance))
|
||||
if isinstance(activity.distance, Quantity)
|
||||
else round(activity.distance)
|
||||
),
|
||||
activity_type=activity_processor.define_activity_type(activity.sport_type),
|
||||
start_time=start_date_parsed,
|
||||
end_time=end_date_parsed,
|
||||
city=city,
|
||||
town=town,
|
||||
country=country,
|
||||
waypoints=waypoints,
|
||||
elevation_gain=elevation_gain,
|
||||
elevation_loss=elevation_loss,
|
||||
pace=average_pace,
|
||||
average_speed=average_speed,
|
||||
average_power=average_watts,
|
||||
calories=activity.calories,
|
||||
strava_gear_id=activity.gear_id,
|
||||
strava_activity_id=activity.id,
|
||||
),
|
||||
db,
|
||||
)
|
||||
|
||||
activity_streams = []
|
||||
|
||||
# List of conditions, stream types, and corresponding waypoints
|
||||
stream_data = [
|
||||
(is_heart_rate_set, 1, hr_waypoints),
|
||||
@@ -245,8 +293,48 @@ def process_activity(activity, user_id, strava_client, db: Session):
|
||||
(latitude is not None and longitude is not None, 7, lat_lon_waypoints),
|
||||
]
|
||||
|
||||
for condition, stream_type, waypoints in stream_data:
|
||||
if condition:
|
||||
# Create the activity object
|
||||
activity_to_store = schema_activities.Activity(
|
||||
user_id=user_id,
|
||||
name=activity.name,
|
||||
distance=(
|
||||
round(float(activity.distance))
|
||||
if isinstance(activity.distance, Quantity)
|
||||
else round(activity.distance)
|
||||
),
|
||||
activity_type=activity_processor.define_activity_type(activity.sport_type),
|
||||
start_time=start_date_parsed.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||
end_time=end_date_parsed.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||
city=city,
|
||||
town=town,
|
||||
country=country,
|
||||
waypoints=waypoints,
|
||||
elevation_gain=elevation_gain,
|
||||
elevation_loss=elevation_loss,
|
||||
pace=average_pace,
|
||||
average_speed=average_speed,
|
||||
average_power=average_watts,
|
||||
calories=activity.calories,
|
||||
strava_gear_id=activity.gear_id,
|
||||
strava_activity_id=int(activity.id),
|
||||
)
|
||||
|
||||
# Return the activity and stream data
|
||||
return {"activity_to_store": activity_to_store, "stream_data": stream_data}
|
||||
|
||||
|
||||
def save_activity_and_streams(
|
||||
activity: schema_activities.Activity, stream_data: list, db: Session
|
||||
):
|
||||
# Create the activity and get the ID
|
||||
created_activity = crud_activities.create_activity(activity, db)
|
||||
|
||||
# Create the empty array of activity streams
|
||||
activity_streams = []
|
||||
|
||||
# Create the activity streams objects
|
||||
for is_set, stream_type, waypoints in stream_data:
|
||||
if is_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
@@ -256,77 +344,53 @@ def process_activity(activity, user_id, strava_client, db: Session):
|
||||
)
|
||||
)
|
||||
|
||||
# Create the activity streams in the database
|
||||
crud_activity_streams.create_activity_streams(activity_streams, db)
|
||||
|
||||
""" activity_streams = []
|
||||
|
||||
if is_heart_rate_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=1,
|
||||
stream_waypoints=hr_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
)
|
||||
|
||||
if is_power_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=2,
|
||||
stream_waypoints=power_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
def process_activity(activity, user_id: int, strava_client: Client, db: Session):
|
||||
# Get the activity by Strava ID from the user
|
||||
activity_db = fetch_and_validate_activity(activity.id, user_id, db)
|
||||
|
||||
# Check if activity is None and return None if it is
|
||||
if activity_db is not None:
|
||||
return None
|
||||
|
||||
# Log an informational event for activity processing
|
||||
logger.info(f"User {user_id}: Activity {activity.id} will be processed")
|
||||
|
||||
# Parse the activity and streams
|
||||
parsed_activity = parse_activity(activity, user_id, strava_client)
|
||||
|
||||
# Save the activity and streams to the database
|
||||
save_activity_and_streams(
|
||||
parsed_activity["activity_to_store"], parsed_activity["stream_data"], db
|
||||
)
|
||||
|
||||
|
||||
def get_user_strava_activities_by_days(start_date: datetime, user_id: int):
|
||||
# Get the first (and only) item from the generator
|
||||
db = next(dependencies_database.get_db())
|
||||
|
||||
try:
|
||||
# Get the user integrations by user ID
|
||||
user_integrations = fetch_user_integrations_and_validate_token(user_id, db)
|
||||
|
||||
# Log the start of the activities processing
|
||||
logger.info(f"User {user_id}: Started activities processing")
|
||||
|
||||
# Create a Strava client with the user's access token
|
||||
strava_client = create_strava_client(user_integrations)
|
||||
|
||||
# Fetch Strava activities after the specified start date
|
||||
num_strava_activities_processed = fetch_and_process_activities(
|
||||
strava_client, start_date, user_id, db
|
||||
)
|
||||
|
||||
if is_cadence_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=3,
|
||||
stream_waypoints=cad_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
# Log an informational event for tracing
|
||||
logger.info(
|
||||
f"User {user_id}: {num_strava_activities_processed} activities processed"
|
||||
)
|
||||
|
||||
if is_elevation_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=4,
|
||||
stream_waypoints=ele_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
)
|
||||
|
||||
if is_velocity_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=5,
|
||||
stream_waypoints=vel_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
)
|
||||
|
||||
if is_velocity_set:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=6,
|
||||
stream_waypoints=pace_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
)
|
||||
|
||||
if latitude is not None and longitude is not None:
|
||||
activity_streams.append(
|
||||
schema_activity_streams.ActivityStreams(
|
||||
activity_id=created_activity.id,
|
||||
stream_type=7,
|
||||
stream_waypoints=lat_lon_waypoints,
|
||||
strava_activity_stream_id=None,
|
||||
)
|
||||
) """
|
||||
|
||||
finally:
|
||||
# Ensure the session is closed after use
|
||||
db.close()
|
||||
|
||||
@@ -3,7 +3,7 @@ import requests
|
||||
import os
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Annotated, Callable
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from fastapi.responses import RedirectResponse
|
||||
@@ -80,7 +80,7 @@ async def strava_link(
|
||||
redirect_url = (
|
||||
"https://"
|
||||
+ os.environ.get("FRONTEND_HOST")
|
||||
+ "/settings/settings.php?profileSettings=1&stravaLinked=1"
|
||||
+ "/settings/settings.php?integrationsSettings=1&stravaLinked=1"
|
||||
)
|
||||
|
||||
# Return a RedirectResponse to the redirect URL
|
||||
@@ -106,7 +106,7 @@ async def strava_retrieve_activities_days(
|
||||
user_id: Annotated[
|
||||
int, Depends(dependencies_session.validate_token_and_get_authenticated_user_id)
|
||||
],
|
||||
db: Annotated[Session, Depends(dependencies_database.get_db)],
|
||||
#db: Annotated[Session, Depends(dependencies_database.get_db)],
|
||||
background_tasks: BackgroundTasks,
|
||||
):
|
||||
# Process strava activities in the background
|
||||
@@ -114,13 +114,12 @@ async def strava_retrieve_activities_days(
|
||||
strava_processor.get_user_strava_activities_by_days,
|
||||
(datetime.utcnow() - timedelta(days=days)).strftime("%Y-%m-%dT%H:%M:%S"),
|
||||
user_id,
|
||||
db,
|
||||
)
|
||||
|
||||
# Return success message and status code 202
|
||||
logger.info(f"Strava activities will be processed in the background for {user_id}")
|
||||
logger.info(f"Strava activities will be processed in the background for user {user_id}")
|
||||
return {
|
||||
"detail": f"Strava activities will be processed in the background for {user_id}"
|
||||
"detail": f"Strava activities will be processed in the background for for {user_id}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class Activity(BaseModel):
|
||||
calories: int | None = None
|
||||
visibility: int | None = None
|
||||
gear_id: int | None = None
|
||||
strava_gear_id: int | None = None
|
||||
strava_gear_id: str | None = None
|
||||
strava_activity_id: int | None = None
|
||||
|
||||
class Config:
|
||||
|
||||
@@ -9,7 +9,7 @@ class Gear(BaseModel):
|
||||
user_id: int | None = None
|
||||
created_at: str
|
||||
is_active: int | None = None
|
||||
strava_gear_id: int | None = None
|
||||
strava_gear_id: str | None = None
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
@@ -240,19 +240,28 @@ if($activityUser["id"] == $_SESSION["id"]){
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown d-flex">
|
||||
<?php if (isset($activity['strava_activity_id'])) { ?>
|
||||
<a class="btn btn-link btn-lg mt-1" href="https://www.strava.com/activities/<?php echo $activity['strava_activity_id']; ?>" role="button">
|
||||
<i class="fa-brands fa-strava"></i>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<button class="btn btn-link btn-lg" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-ellipsis-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<?php if ($activity['strava_activity_id'] != null) { ?>
|
||||
<li><a class="dropdown-item"
|
||||
<!--<?php if (isset($activity['strava_activity_id'])) { ?>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="https://www.strava.com/activities/<?php echo $activity['strava_activity_id']; ?>">
|
||||
<?php echo $translationsActivitiesActivity['activity_title_dropdown_seeItOnStrava']; ?>
|
||||
</a></li>
|
||||
<?php } ?>
|
||||
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#deleteActivityModal">
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>-->
|
||||
<li>
|
||||
<a class="dropdown-item <?php if(isset($activity["strava_activity_id"])){ echo "disabled"; } ?>" href="#" data-bs-toggle="modal" data-bs-target="#deleteActivityModal">
|
||||
<?php echo $translationsActivitiesActivity['activity_title_dropdown_deleteActivity']; ?>
|
||||
</a></li>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -415,6 +424,15 @@ if($activityUser["id"] == $_SESSION["id"]){
|
||||
<div class="mt-3 mb-3" id="map" style="height: 500px"></div>
|
||||
<?php } ?>
|
||||
|
||||
<!--<?php if (isset($activity['strava_activity_id'])) { ?>
|
||||
<?php if (!isset($latlonStream)) { ?>
|
||||
<br>
|
||||
<?php } ?>
|
||||
<a href="https://www.strava.com/activities/<?php echo $activity['strava_activity_id']; ?>">
|
||||
<?php echo $translationsActivitiesActivity['activity_title_dropdown_seeItOnStrava']; ?>
|
||||
</a>
|
||||
<?php } ?>-->
|
||||
|
||||
|
||||
<script>
|
||||
// JavaScript code to create the map for this activity
|
||||
|
||||
@@ -33,23 +33,26 @@ function unsetUniqueUserStateStravaLink()
|
||||
|
||||
function linkStrava($state)
|
||||
{
|
||||
// Example PHP code for the authentication link
|
||||
$client_id = '115321';
|
||||
$redirect_uri = urlencode(getenv('BACKEND_PROTOCOL').'://'.getenv('BACKEND_URL').'/strava/link');
|
||||
$scope = 'read,read_all,profile:read_all,activity:read,activity:read_all'; // Set your required scope
|
||||
|
||||
$redirect_uri = urlencode(getenv('BACKEND_PROTOCOL').'://api-gearguardian.jvslab.pt/strava/link');
|
||||
$scope = 'read,read_all,profile:read_all,activity:read,activity:read_all';
|
||||
|
||||
$strava_auth_url = "http://www.strava.com/oauth/authorize?client_id={$client_id}&response_type=code&redirect_uri={$redirect_uri}&approval_prompt=force&scope={$scope}&state={$state}";
|
||||
|
||||
header("Location: " . $strava_auth_url);
|
||||
|
||||
#header("Location: " . $strava_auth_url);
|
||||
echo "<script>location.href = '$strava_auth_url';</script>";
|
||||
#<meta http-equiv="Location" content=$strava_auth_url>
|
||||
}
|
||||
|
||||
function getStravaActivitiesLastDays($days)
|
||||
{
|
||||
$response = callAPIRoute("/strava/activities/days/$days", 0, 0, NULL);
|
||||
$response = callAPIRoute("/strava/activities/days/$days", 1, 0, NULL);
|
||||
if ($response[0] === false) {
|
||||
return -1;
|
||||
} else {
|
||||
if ($response[1] === 200) {
|
||||
if ($response[1] === 202) {
|
||||
return 0;
|
||||
} else {
|
||||
return -2;
|
||||
|
||||
@@ -341,66 +341,79 @@ $thisMonthDistances = getUserActivitiesThisMonthDistances($_SESSION["id"]);
|
||||
<?php if (isset($userActivities)) { ?>
|
||||
<?php foreach ($userActivities as $activity) { ?>
|
||||
<?php
|
||||
$activityStream = getActivityActivitiesStreamByStreamType($activity["id"],7);
|
||||
if($activityStream["stream_type"] == 7){
|
||||
$latlonStream = $activityStream["stream_waypoints"];
|
||||
$activityStream = getActivityActivitiesStreamByStreamType($activity["id"],7);
|
||||
if(isset($activityStream)){
|
||||
if($activityStream["stream_type"] == 7){
|
||||
$latlonStream = $activityStream["stream_waypoints"];
|
||||
}
|
||||
}else{
|
||||
$latlonStream = NULL;
|
||||
}
|
||||
?>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src=<?php if (is_null($_SESSION["photo_path"])) {
|
||||
if ($_SESSION["gender"] == 1) {
|
||||
echo ("../img/avatar/male1.png");
|
||||
} else {
|
||||
echo ("../img/avatar/female1.png");
|
||||
}
|
||||
} else {
|
||||
echo ($_SESSION["photo_path"]);
|
||||
} ?> alt="userPicture" class="rounded-circle" width="55" height="55">
|
||||
<div class="ms-3 me-3">
|
||||
<div class="fw-bold">
|
||||
<a href="activities/activity.php?activityID=<?php echo ($activity["id"]); ?>"
|
||||
class="link-underline-opacity-25 link-underline-opacity-100-hover">
|
||||
<?php echo ($activity["name"]); ?>
|
||||
</a>
|
||||
</div>
|
||||
<h7>
|
||||
<?php if ($activity["activity_type"] == 1 || $activity["activity_type"] == 2) {
|
||||
echo '<i class="fa-solid fa-person-running"></i>';
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex align-items-center">
|
||||
<img src=<?php if (is_null($_SESSION["photo_path"])) {
|
||||
if ($_SESSION["gender"] == 1) {
|
||||
echo ("../img/avatar/male1.png");
|
||||
} else {
|
||||
if ($activity["activity_type"] == 3) {
|
||||
echo '<i class="fa-solid fa-person-running"></i> (Virtual)';
|
||||
echo ("../img/avatar/female1.png");
|
||||
}
|
||||
} else {
|
||||
echo ($_SESSION["photo_path"]);
|
||||
} ?> alt="userPicture" class="rounded-circle" width="55" height="55">
|
||||
<div class="ms-3 me-3">
|
||||
<div class="fw-bold">
|
||||
<a href="activities/activity.php?activityID=<?php echo ($activity["id"]); ?>"
|
||||
class="link-underline-opacity-25 link-underline-opacity-100-hover">
|
||||
<?php echo ($activity["name"]); ?>
|
||||
</a>
|
||||
</div>
|
||||
<h7>
|
||||
<?php if ($activity["activity_type"] == 1 || $activity["activity_type"] == 2) {
|
||||
echo '<i class="fa-solid fa-person-running"></i>';
|
||||
} else {
|
||||
if ($activity["activity_type"] == 4 || $activity["activity_type"] == 5 || $activity["activity_type"] == 6) {
|
||||
echo '<i class="fa-solid fa-person-biking"></i>';
|
||||
if ($activity["activity_type"] == 3) {
|
||||
echo '<i class="fa-solid fa-person-running"></i> (Virtual)';
|
||||
} else {
|
||||
if ($activity["activity_type"] == 7) {
|
||||
echo '<i class="fa-solid fa-person-biking"></i> (Virtual)';
|
||||
if ($activity["activity_type"] == 4 || $activity["activity_type"] == 5 || $activity["activity_type"] == 6) {
|
||||
echo '<i class="fa-solid fa-person-biking"></i>';
|
||||
} else {
|
||||
if ($activity["activity_type"] == 8 || $activity["activity_type"] == 9) {
|
||||
echo '<i class="fa-solid fa-person-swimming"></i>';
|
||||
if ($activity["activity_type"] == 7) {
|
||||
echo '<i class="fa-solid fa-person-biking"></i> (Virtual)';
|
||||
} else {
|
||||
if ($activity["activity_type"] == 10) {
|
||||
echo '<i class="fa-solid fa-dumbbell"></i>';
|
||||
if ($activity["activity_type"] == 8 || $activity["activity_type"] == 9) {
|
||||
echo '<i class="fa-solid fa-person-swimming"></i>';
|
||||
} else {
|
||||
if ($activity["activity_type"] == 10) {
|
||||
echo '<i class="fa-solid fa-dumbbell"></i>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?>
|
||||
<?php echo (new DateTime($activity["start_time"]))->format("d/m/y"); ?>@
|
||||
<?php echo (new DateTime($activity["start_time"]))->format("H:i"); ?>
|
||||
<?php if (isset($activity["city"]) || isset($activity["country"])) {
|
||||
echo " - ";
|
||||
} ?>
|
||||
<?php if (isset($activity["city"]) && !empty($activity["city"])) {
|
||||
echo $activity["city"] . ", ";
|
||||
} ?>
|
||||
<?php if (isset($activity["country"]) && !empty($activity["country"])) {
|
||||
echo $activity["country"];
|
||||
} ?>
|
||||
</h7>
|
||||
} ?>
|
||||
<?php echo (new DateTime($activity["start_time"]))->format("d/m/y"); ?>@
|
||||
<?php echo (new DateTime($activity["start_time"]))->format("H:i"); ?>
|
||||
<?php if (isset($activity["city"]) || isset($activity["country"])) {
|
||||
echo " - ";
|
||||
} ?>
|
||||
<?php if (isset($activity["city"]) && !empty($activity["city"])) {
|
||||
echo $activity["city"] . ", ";
|
||||
} ?>
|
||||
<?php if (isset($activity["country"]) && !empty($activity["country"])) {
|
||||
echo $activity["country"];
|
||||
} ?>
|
||||
</h7>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown d-flex">
|
||||
<?php if (isset($activity['strava_activity_id'])) { ?>
|
||||
<a class="btn btn-link btn-lg mt-1" href="https://www.strava.com/activities/<?php echo $activity['strava_activity_id']; ?>" role="button">
|
||||
<i class="fa-brands fa-strava"></i>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex mt-3">
|
||||
@@ -468,7 +481,7 @@ $thisMonthDistances = getUserActivitiesThisMonthDistances($_SESSION["id"]);
|
||||
echo "mb-3";
|
||||
} ?>" id="map_<?php echo $activity['id']; ?>" style="height: 300px"></div>
|
||||
<?php } ?>
|
||||
<?php if ($activity['strava_activity_id'] != null) { ?>
|
||||
<!--<?php if ($activity['strava_activity_id'] != null) { ?>
|
||||
<div class="mb-3">
|
||||
<span class="fw-lighter ms-3 me-3">
|
||||
<?php echo $translationsIndex['index_activities_stravaText1']; ?><a
|
||||
@@ -478,7 +491,7 @@ $thisMonthDistances = getUserActivitiesThisMonthDistances($_SESSION["id"]);
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php } ?>-->
|
||||
</div>
|
||||
<br>
|
||||
<script>
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
// success banner zone
|
||||
"settings_integration_settings_success_stravaLinked" => "Strava linked with success",
|
||||
"settings_integration_settings_success_stravaGear" => "Strava gear retrieved",
|
||||
"settings_integration_settings_success_stravaActivities" => "Strava activities retrieved",
|
||||
"settings_integration_settings_success_stravaActivities" => "Strava activities retrieval background process started",
|
||||
// card zone
|
||||
"settings_integration_settings_strava_title" => "Strava",
|
||||
"settings_integration_settings_strava_body" => "Strava is an American internet service for tracking physical exercise which incorporates social network features.",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
$strava_gear_result = getStravaGear();
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- Error banners -->
|
||||
<?php if ($strava_gear_result == -1 || $strava_gear_result == -2 || $strava_activities_result == -1 || $strava_activities_result == -2) { ?>
|
||||
<div class="alert alert-danger alert-dismissible d-flex align-items-center" role="alert">
|
||||
@@ -84,12 +83,12 @@
|
||||
<div class="card-body">
|
||||
<h4 class="card-title"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_strava_title']; ?></h4>
|
||||
<p class="card-text"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_strava_body']; ?></p>
|
||||
<a href="../settings/settings.php?integrationSettings=1&linkStrava=1" class="btn btn-primary <?php if ($_SESSION["is_strava_linked"] == 1) { echo "disabled"; } ?>"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_connect_button']; ?></a>
|
||||
<a href="../settings/settings.php?integrationsSettings=1&linkStrava=1" class="btn btn-primary <?php if ($_SESSION["is_strava_linked"] == 1) { echo "disabled"; } ?>"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_connect_button']; ?></a>
|
||||
<?php if ($_SESSION["is_strava_linked"] == 1) { ?>
|
||||
<hr>
|
||||
<a href="../settings/settings.php?integrationSettings=1&getUserStravaActivities=1" class="btn btn-primary"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_retrieve_last_week_activities_button']; ?></a>
|
||||
<a href="../settings/settings.php?integrationsSettings=1&getUserStravaActivities=1" class="btn btn-primary"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_retrieve_last_week_activities_button']; ?></a>
|
||||
|
||||
<a href="../settings/settings.php?integrationSettings=1&getUserStravaGear=1" class="btn btn-primary mt-3"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_retrieve_gear_button']; ?></a>
|
||||
<a href="../settings/settings.php?integrationsSettings=1&getUserStravaGear=1" class="btn btn-primary mt-3"><?php echo $translationsSettingsIntegrationSettings['settings_integration_settings_retrieve_gear_button']; ?></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,6 @@ switch ($_SESSION["preferred_language"]) {
|
||||
default:
|
||||
$translationsSettings = include $_SERVER['DOCUMENT_ROOT'] . '/lang/settings/en.php';
|
||||
}
|
||||
|
||||
?>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/inc/Template-Top.php" ?>
|
||||
<div class="container mt-4">
|
||||
|
||||
@@ -531,8 +531,12 @@ $userFollowingAll = getUserFollowingAll($_GET["userID"]);
|
||||
<?php } else { ?>
|
||||
<?php foreach ($weekActivities as $activity) { ?>
|
||||
<?php $activityStream = getActivityActivitiesStreamByStreamType($activity["id"],7);
|
||||
if($activityStream["stream_type"] == 7){
|
||||
$latlonStream = $activityStream["stream_waypoints"];
|
||||
if (isset($activityStream)){
|
||||
if($activityStream["stream_type"] == 7){
|
||||
$latlonStream = $activityStream["stream_waypoints"];
|
||||
}
|
||||
}else{
|
||||
$latlonStream = null;
|
||||
}
|
||||
?>
|
||||
<div class="card">
|
||||
|
||||
Reference in New Issue
Block a user