-
Notifications
You must be signed in to change notification settings - Fork 0
Activate/deactivate only #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e119804
bf4845f
745097b
81df237
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,7 @@ | |
| from sqlalchemy import func, desc, asc, delete | ||
|
|
||
| from src import config | ||
| from src.api.dependencies import get_account_from_bearer, get_client_account, PaginationParams | ||
| from src.api.dependencies import get_account_from_bearer, get_client_account, get_active_account, PaginationParams | ||
|
|
||
| #models | ||
| from src.api.roles.client.domain import ( | ||
|
|
@@ -39,8 +39,18 @@ | |
| from src.database.coach_client_relationship.models import ClientCoachRequest, ClientCoachRelationship | ||
| from src.database.account.models import Account, Availability, Notification | ||
| from src.database.client.models import Client, ClientAvailability, FitnessGoals | ||
| from src.database.telemetry.models import HealthMetrics, ClientTelemetry | ||
| from src.database.telemetry.models import ClientTelemetry | ||
| from src.database.telemetry.models import ( | ||
| HealthMetrics, | ||
| ClientTelemetry, | ||
| StepCount, | ||
| DailyMoodSurvey, | ||
| DailyWorkoutSurvey, | ||
| DailyBodyMetricsSurvey, | ||
| DailyStepsSurvey, | ||
| DailyMealSurvey, | ||
| CompletedMealActivity, | ||
| CompletedWorkout, | ||
| ) | ||
| from src.database.reports.models import CoachReport, CoachReviews | ||
| from src.database.payment.models import PaymentInformation, Invoice, BillingCycle, Subscription, PricingPlan | ||
|
|
||
|
|
@@ -189,11 +199,21 @@ def create_coach_request(coach_id: int, db = Depends(get_session), acc: Account | |
| client = db.get(Client, acc.client_id) | ||
| coach = db.get(Coach, coach_id) | ||
|
|
||
| if coach is None: | ||
| raise HTTPException(404, detail="Coach not found") | ||
|
|
||
| existing_request = db.query(ClientCoachRequest).filter_by( | ||
| client_id=client.id, coach_id=coach.id, is_accepted=None | ||
| if coach is None: | ||
| raise HTTPException(404, detail="Coach not found") | ||
|
|
||
| coach_account = db.exec( | ||
| select(Account).where( | ||
| Account.coach_id == coach.id, | ||
| Account.is_active == True, | ||
| ) | ||
| ).first() | ||
|
|
||
| if coach_account is None or not coach.verified: | ||
| raise HTTPException(404, detail="Coach not available") | ||
|
|
||
| existing_request = db.query(ClientCoachRequest).filter_by( | ||
| client_id=client.id, coach_id=coach.id, is_accepted=None | ||
| ).first() | ||
|
|
||
| if existing_request: | ||
|
|
@@ -207,10 +227,9 @@ def create_coach_request(coach_id: int, db = Depends(get_session), acc: Account | |
| db.refresh(request) | ||
|
|
||
| # notify the coach's account that a new request was created | ||
| coach_account = db.exec(select(Account).where(Account.coach_id == coach.id)).first() | ||
| if coach_account and coach_account.id is not None: | ||
| n = Notification( | ||
| account_id=coach_account.id, | ||
| if coach_account and coach_account.id is not None: | ||
| n = Notification( | ||
| account_id=coach_account.id, | ||
| fav_category="relationship_request_creation", | ||
| message=f"{acc.name} has requested to hire you.", | ||
| details=f"Request {request.id} from client {client.id} to coach {coach.id}.", | ||
|
|
@@ -515,12 +534,24 @@ def get_review(coach_id: int, db = Depends(get_session), acc: Account = Depends( | |
| if acc.id is None: | ||
| raise HTTPException(404, detail="Account not found") | ||
|
|
||
| if acc.client_id is None: | ||
| raise HTTPException(403, detail="You are not authorized to view this content") | ||
|
|
||
| reviews = db.query(CoachReviews).filter(CoachReviews.coach_id == coach_id).all() | ||
|
|
||
| return ReviewsResponse(reviews=reviews) | ||
| if acc.client_id is None: | ||
| raise HTTPException(403, detail="You are not authorized to view this content") | ||
|
|
||
| coach_account = db.exec( | ||
| select(Account).where( | ||
| Account.coach_id == coach_id, | ||
| Account.is_active == True, | ||
| ) | ||
| ).first() | ||
|
|
||
| if coach_account is None: | ||
| return ReviewsResponse(reviews=[]) | ||
|
|
||
| reviews = db.exec( | ||
| select(CoachReviews).where(CoachReviews.coach_id == coach_id) | ||
| ).all() | ||
|
|
||
| return ReviewsResponse(reviews=reviews) | ||
|
|
||
| @router.get("/my_coach", response_model=MyCoachResponse) | ||
| def get_my_coach(db = Depends(get_session), acc: Account = Depends(get_client_account)): | ||
|
|
@@ -533,8 +564,9 @@ def get_my_coach(db = Depends(get_session), acc: Account = Depends(get_client_ac | |
|
|
||
| coach_request = db.query(ClientCoachRequest).filter(ClientCoachRequest.client_id == acc.client_id).first() | ||
|
|
||
| if not coach_request.is_accepted: | ||
| raise HTTPException(403, detail="You are not authorized to see this coach until the request is accepted") | ||
| # If no request found or the request hasn't been accepted, surface as not found. | ||
| if coach_request is None or not getattr(coach_request, "is_accepted", False): | ||
| raise HTTPException(404, detail="No active coach relationship found") | ||
|
|
||
| relationship = db.query(ClientCoachRelationship).filter(ClientCoachRelationship.request_id == coach_request.id).first() | ||
|
|
||
|
|
@@ -557,3 +589,150 @@ def get_my_coach_requests(db = Depends(get_session), acc: Account = Depends(get_ | |
| requests = db.get(ClientCoachRequest).filter(ClientCoachRequest.client_id == acc.client_id).all() | ||
|
|
||
| return MyCoachRequestsResponse(requests = requests) | ||
|
|
||
| @router.get("/coach_profile/{coach_id}") | ||
| def get_coach_profile(coach_id: int, db = Depends(get_session), acc: Account = Depends(get_client_account)): | ||
| """ | ||
| Allows a client to view a coach's profile given their ID. | ||
| Returns account basics, specialties, certifications, experiences, | ||
| pricing/payment plan, availability, and rating summary. | ||
| """ | ||
|
|
||
| coach = db.get(Coach, coach_id) | ||
|
|
||
| if coach is None: | ||
| raise HTTPException(404, detail="Coach not found") | ||
|
|
||
| coach_account = db.exec( | ||
| select(Account).where(Account.coach_id == coach_id) | ||
| ).first() | ||
|
|
||
| if coach_account is None: | ||
| raise HTTPException(404, detail="Coach account not found") | ||
|
|
||
| if not coach_account.is_active or not coach.verified: | ||
| raise HTTPException(404, detail="Coach not available") | ||
|
|
||
| certifications = db.exec( | ||
| select(Certifications) | ||
| .join(CoachCertifications, CoachCertifications.certification_id == Certifications.id) | ||
| .where(CoachCertifications.coach_id == coach_id) | ||
| ).all() | ||
|
|
||
| experiences = db.exec( | ||
| select(Experience) | ||
| .join(CoachExperience, CoachExperience.experience_id == Experience.id) | ||
| .where(CoachExperience.coach_id == coach_id) | ||
| ).all() | ||
|
|
||
| availability = db.exec( | ||
| select(Availability).where( | ||
| Availability.coach_availability_id == coach.coach_availability | ||
| ) | ||
| ).all() | ||
|
|
||
| pricing_plan = db.exec( | ||
| select(PricingPlan).where(PricingPlan.coach_id == coach_id) | ||
| ).first() | ||
|
|
||
| rating_summary = db.exec( | ||
| select( | ||
| func.count(CoachReviews.id).label("rating_count"), | ||
| func.avg(CoachReviews.rating).label("avg_rating"), | ||
| ).where(CoachReviews.coach_id == coach_id) | ||
| ).first() | ||
|
|
||
| return { | ||
| "base_account": { | ||
| "id": coach_account.id, | ||
| "name": coach_account.name, | ||
| "email": coach_account.email, | ||
| "is_active": coach_account.is_active, | ||
| "gender": coach_account.gender, | ||
| "bio": coach_account.bio, | ||
| "age": coach_account.age, | ||
| "pfp_url": coach_account.pfp_url, | ||
| "client_id": coach_account.client_id, | ||
| "coach_id": coach_account.coach_id, | ||
| "admin_id": coach_account.admin_id, | ||
| "created_at": coach_account.created_at, | ||
| }, | ||
| "coach_account": coach, | ||
| "specialties": coach.specialties, | ||
| "certifications": certifications, | ||
| "experiences": experiences, | ||
| "pricing_plan": pricing_plan, | ||
| "availability": availability, | ||
| "rating_summary": { | ||
| "rating_count": int(rating_summary.rating_count or 0), | ||
| "avg_rating": float(rating_summary.avg_rating) if rating_summary.avg_rating is not None else None, | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| @router.get("/progress_pictures") | ||
| def get_progress_pictures(db = Depends(get_session), acc: Account = Depends(get_client_account)): | ||
| """ | ||
| Queries progress picture URLs for the logged-in client. | ||
| Progress pictures are stored in HealthMetrics.progress_pic_url. | ||
| """ | ||
|
|
||
| if acc.client_id is None: | ||
| raise HTTPException(403, detail="Client profile required") | ||
|
|
||
| pictures = db.exec( | ||
| select( | ||
| ClientTelemetry.date, | ||
| HealthMetrics.progress_pic_url, | ||
| ) | ||
| .join(HealthMetrics, HealthMetrics.client_telemetry_id == ClientTelemetry.id) | ||
| .where( | ||
| ClientTelemetry.client_id == acc.client_id, | ||
| HealthMetrics.progress_pic_url.is_not(None), | ||
| ) | ||
| .order_by(ClientTelemetry.date.desc()) | ||
| ).all() | ||
|
|
||
| return [ | ||
| { | ||
| "date": pic.date, | ||
| "progress_pic_url": pic.progress_pic_url, | ||
| } | ||
| for pic in pictures | ||
| ] | ||
|
|
||
|
|
||
| @router.get("/my_coach") | ||
| def get_my_coach(db = Depends(get_session), acc: Account = Depends(get_client_account)): | ||
| """ | ||
| Returns the active coach relationship for the logged-in client. | ||
| """ | ||
|
|
||
| if acc.client_id is None: | ||
| raise HTTPException(403, detail="Client profile required") | ||
|
|
||
| result = db.exec( | ||
| select(ClientCoachRequest, ClientCoachRelationship) | ||
| .join(ClientCoachRelationship, ClientCoachRelationship.request_id == ClientCoachRequest.id) | ||
| .where( | ||
| ClientCoachRequest.client_id == acc.client_id, | ||
| ClientCoachRequest.is_accepted == True, | ||
| ClientCoachRelationship.is_active == True, | ||
| ClientCoachRelationship.client_blocked == False, | ||
| ClientCoachRelationship.coach_blocked == False, | ||
| ) | ||
| ).first() | ||
|
|
||
| if result is None: | ||
| raise HTTPException(404, detail="No active coach relationship found") | ||
|
|
||
| request, relationship = result | ||
|
|
||
| return { | ||
| "relationship_id": relationship.id, | ||
| "request_id": request.id, | ||
| "client_id": request.client_id, | ||
| "coach_id": request.coach_id, | ||
| "created_at": relationship.created_at, | ||
| "is_active": relationship.is_active, | ||
| } | ||
|
Comment on lines
+705
to
+738
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate
Per the PR summary, the older handler was meant to be removed. Delete lines 556–578 so this new one is the only registration (or merge them into one), and ideally add a 🛠️ Suggested cleanupRemove the previous -@router.get("/my_coach", response_model=MyCoachResponse)
-def get_my_coach(db = Depends(get_session), acc: Account = Depends(get_client_account)):
- """
- Returns the coach of a specific client
- """
-
- if acc is None:
- raise HTTPException(404, detail="Account not found")
-
- coach_request = db.query(ClientCoachRequest).filter(ClientCoachRequest.client_id == acc.client_id).first()
-
- # If no request found or the request hasn't been accepted, surface as not found.
- if coach_request is None or not getattr(coach_request, "is_accepted", False):
- raise HTTPException(404, detail="No active coach relationship found")
-
- relationship = db.query(ClientCoachRelationship).filter(ClientCoachRelationship.request_id == coach_request.id).first()
-
- if relationship is None:
- raise HTTPException(404, detail="Relationship not Found")
-
- coach = db.query(Coach).filter(Coach.id == coach_request.coach_id).first()
-
- return MyCoachResponse(coach = coach)🧰 Tools🪛 Ruff (0.15.12)[warning] 706-706: Do not perform function call (B008) [warning] 706-706: Do not perform function call (B008) [error] 719-719: Avoid equality comparisons to Replace with (E712) [error] 720-720: Avoid equality comparisons to Replace with (E712) [error] 721-721: Avoid equality comparisons to Replace with (E712) [error] 722-722: Avoid equality comparisons to Replace with (E712) 🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misleading
coach_accountfield — returns aCoach, not anAccount.In the
get_coach_profileresponse,"base_account"holds theAccountrow while"coach_account"is set to theCoachmodel instance. The field naming is reversed from what consumers will expect and will likely cause integration bugs (e.g., a frontend readingcoach_account.emailwould get nothing becauseemaillives onAccount). Rename it (e.g.,"coach"or"coach_details") and consider aresponse_modelso the contract is enforced.🛠️ Suggested rename
return { "base_account": { ... }, - "coach_account": coach, + "coach": coach, "specialties": coach.specialties,🤖 Prompt for AI Agents