Skip to content

API Reference

User goals module for managing user fitness goals.

This module provides CRUD operations and data models for user goal tracking including activity type goals, intervals, and progress calculation.

Exports
  • CRUD: get_user_goals_by_user_id, get_user_goal_by_user_and_goal_id, create_user_goal, update_user_goal, delete_user_goal
  • Schemas: UsersGoalBase, UsersGoalCreate, UsersGoalUpdate, UsersGoalRead, UsersGoalProgress
  • Models: UsersGoal (ORM model)
  • Enums: Interval, ActivityType, GoalType
  • Utils: calculate_user_goals

ActivityType

Bases: str, Enum

Supported activity types for user goals.

Attributes:

Name Type Description
RUN

Running activities.

BIKE

Cycling activities.

SWIM

Swimming activities.

WALK

Walking or hiking activities.

STRENGTH

Strength or resistance training.

CARDIO

Cardiovascular training.

Source code in backend/app/users/users_goals/schema.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class ActivityType(str, Enum):
    """
    Supported activity types for user goals.

    Attributes:
        RUN: Running activities.
        BIKE: Cycling activities.
        SWIM: Swimming activities.
        WALK: Walking or hiking activities.
        STRENGTH: Strength or resistance training.
        CARDIO: Cardiovascular training.
    """

    RUN = "run"
    BIKE = "bike"
    SWIM = "swim"
    WALK = "walk"
    STRENGTH = "strength"
    CARDIO = "cardio"

GoalType

Bases: str, Enum

Types of measurable user goals.

Attributes:

Name Type Description
CALORIES

Target calories burned.

ACTIVITIES

Target count of completed activities.

DISTANCE

Target distance traveled.

ELEVATION

Target elevation gain.

DURATION

Target total duration.

Source code in backend/app/users/users_goals/schema.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class GoalType(str, Enum):
    """
    Types of measurable user goals.

    Attributes:
        CALORIES: Target calories burned.
        ACTIVITIES: Target count of completed activities.
        DISTANCE: Target distance traveled.
        ELEVATION: Target elevation gain.
        DURATION: Target total duration.
    """

    CALORIES = "calories"
    ACTIVITIES = "activities"
    DISTANCE = "distance"
    ELEVATION = "elevation"
    DURATION = "duration"

Interval

Bases: str, Enum

Recurrence intervals for user goals.

Attributes:

Name Type Description
DAILY

Daily recurrence interval.

WEEKLY

Weekly recurrence interval.

MONTHLY

Monthly recurrence interval.

YEARLY

Yearly recurrence interval.

Source code in backend/app/users/users_goals/schema.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Interval(str, Enum):
    """
    Recurrence intervals for user goals.

    Attributes:
        DAILY: Daily recurrence interval.
        WEEKLY: Weekly recurrence interval.
        MONTHLY: Monthly recurrence interval.
        YEARLY: Yearly recurrence interval.
    """

    DAILY = "daily"
    WEEKLY = "weekly"
    MONTHLY = "monthly"
    YEARLY = "yearly"

UserGoalModel

Bases: Base

User fitness goal tracking model.

Attributes:

Name Type Description
id Mapped[int]

Primary key.

user_id Mapped[int]

Foreign key to users table.

interval Mapped[str]

Goal time interval (daily, weekly, monthly, yearly).

activity_type Mapped[str]

Type of activity for the goal.

goal_type Mapped[str]

Type of goal metric.

goal_calories Mapped[int | None]

Target calories in kcal.

goal_activities_number Mapped[int | None]

Target number of activities.

goal_distance Mapped[int | None]

Target distance in meters.

goal_elevation Mapped[int | None]

Target elevation gain in meters.

goal_duration Mapped[int | None]

Target duration in seconds.

user Mapped[int | None]

Relationship to Users model.

Source code in backend/app/users/users_goals/models.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class UsersGoal(Base):
    """
    User fitness goal tracking model.

    Attributes:
        id: Primary key.
        user_id: Foreign key to users table.
        interval: Goal time interval (daily, weekly, monthly,
            yearly).
        activity_type: Type of activity for the goal.
        goal_type: Type of goal metric.
        goal_calories: Target calories in kcal.
        goal_activities_number: Target number of activities.
        goal_distance: Target distance in meters.
        goal_elevation: Target elevation gain in meters.
        goal_duration: Target duration in seconds.
        user: Relationship to Users model.
    """

    __tablename__ = "users_goals"

    id: Mapped[int] = mapped_column(
        primary_key=True,
        autoincrement=True,
    )
    user_id: Mapped[int] = mapped_column(
        ForeignKey("users.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="User ID that the goals belongs",
    )
    interval: Mapped[str] = mapped_column(
        String(length=250),
        nullable=False,
        comment=("Goal interval (e.g., 'daily', 'weekly', 'monthly', 'yearly')"),
    )
    activity_type: Mapped[str] = mapped_column(
        String(length=50),
        nullable=False,
        comment="Activity type (e.g., 'run', 'bike', 'swim')",
    )
    goal_type: Mapped[str] = mapped_column(
        String(length=50),
        nullable=False,
        comment="Goal type (e.g., 'calories', 'distance', 'duration')",
    )
    goal_calories: Mapped[int | None] = mapped_column(
        nullable=True,
        comment="Goal calories in kcal (e.g., 5000 for 5000 kcal)",
    )
    goal_activities_number: Mapped[int | None] = mapped_column(
        nullable=True,
        comment=("Goal activities number (e.g., 5 for 5 activities)"),
    )
    goal_distance: Mapped[int | None] = mapped_column(
        nullable=True,
        comment=("Goal distance in meters (e.g., 10000 for 10 km)"),
    )
    goal_elevation: Mapped[int | None] = mapped_column(
        nullable=True,
        comment=("Goal elevation in meters (e.g., 1000 for 1000 m)"),
    )
    goal_duration: Mapped[int | None] = mapped_column(
        nullable=True,
        comment=("Goal duration in seconds (e.g., 3600 for 1 hours)"),
    )

    # Relationship to User
    # TODO: Change to Mapped["User"] when all modules use mapped
    users = relationship("Users", back_populates="goals")

UsersGoalBase

Bases: BaseModel

Base schema for users fitness goals.

Attributes:

Name Type Description
interval Interval

Goal time interval (daily, weekly, monthly, yearly).

activity_type ActivityType

Type of activity for the goal.

goal_type GoalType

Type of goal metric being tracked.

goal_calories StrictInt | None

Target calories in kcal.

goal_activities_number StrictInt | None

Target number of activities.

goal_distance StrictInt | None

Target distance in meters.

goal_elevation StrictInt | None

Target elevation gain in meters.

goal_duration StrictInt | None

Target duration in seconds.

Source code in backend/app/users/users_goals/schema.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
class UsersGoalBase(BaseModel):
    """
    Base schema for users fitness goals.

    Attributes:
        interval: Goal time interval (daily, weekly, monthly,
            yearly).
        activity_type: Type of activity for the goal.
        goal_type: Type of goal metric being tracked.
        goal_calories: Target calories in kcal.
        goal_activities_number: Target number of activities.
        goal_distance: Target distance in meters.
        goal_elevation: Target elevation gain in meters.
        goal_duration: Target duration in seconds.
    """

    interval: Interval = Field(..., description="Goal time interval")
    activity_type: ActivityType = Field(
        ..., description="Type of activity for the goal"
    )
    goal_type: GoalType = Field(..., description="Type of goal metric being tracked")
    goal_calories: StrictInt | None = Field(
        default=None, ge=0, description="Target calories in kcal"
    )
    goal_activities_number: StrictInt | None = Field(
        default=None, ge=0, description="Target number of activities"
    )
    goal_distance: StrictInt | None = Field(
        default=None, ge=0, description="Target distance in meters"
    )
    goal_elevation: StrictInt | None = Field(
        default=None, ge=0, description="Target elevation gain in meters"
    )
    goal_duration: StrictInt | None = Field(
        default=None, ge=0, description="Target duration in seconds"
    )

    model_config = ConfigDict(
        from_attributes=True,
        extra="forbid",
        validate_assignment=True,
        use_enum_values=True,
    )

    @model_validator(mode="after")
    def ensure_correct_goal_field(self) -> "UsersGoalBase":
        """
        Validate exactly one goal field matches goal_type.

        Returns:
            Self with validated goal field.

        Raises:
            PydanticCustomError: If required field missing or
                extra fields set.
        """
        goal_type_value = (
            self.goal_type if isinstance(self.goal_type, str) else self.goal_type.value
        )

        required_field = TYPE_TO_FIELD.get(goal_type_value)
        if required_field is None:
            return self

        if getattr(self, required_field) is None:
            raise PydanticCustomError(
                "missing_goal_value",
                "{field} is required when goal_type={goal_type}",
                {
                    "field": required_field,
                    "goal_type": goal_type_value,
                },
            )

        for name in TYPE_TO_FIELD.values():
            if name != required_field and getattr(self, name) is not None:
                raise PydanticCustomError(
                    "exclusive_goal_value",
                    "Only {field} may be set when goal_type={goal_type}",
                    {
                        "field": required_field,
                        "goal_type": goal_type_value,
                    },
                )
        return self

ensure_correct_goal_field

ensure_correct_goal_field()

Validate exactly one goal field matches goal_type.

Returns:

Type Description
UsersGoalBase

Self with validated goal field.

Raises:

Type Description
PydanticCustomError

If required field missing or extra fields set.

Source code in backend/app/users/users_goals/schema.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
@model_validator(mode="after")
def ensure_correct_goal_field(self) -> "UsersGoalBase":
    """
    Validate exactly one goal field matches goal_type.

    Returns:
        Self with validated goal field.

    Raises:
        PydanticCustomError: If required field missing or
            extra fields set.
    """
    goal_type_value = (
        self.goal_type if isinstance(self.goal_type, str) else self.goal_type.value
    )

    required_field = TYPE_TO_FIELD.get(goal_type_value)
    if required_field is None:
        return self

    if getattr(self, required_field) is None:
        raise PydanticCustomError(
            "missing_goal_value",
            "{field} is required when goal_type={goal_type}",
            {
                "field": required_field,
                "goal_type": goal_type_value,
            },
        )

    for name in TYPE_TO_FIELD.values():
        if name != required_field and getattr(self, name) is not None:
            raise PydanticCustomError(
                "exclusive_goal_value",
                "Only {field} may be set when goal_type={goal_type}",
                {
                    "field": required_field,
                    "goal_type": goal_type_value,
                },
            )
    return self

UsersGoalCreate

Bases: UsersGoalBase

Schema for creating a new users goal.

Inherits all validation from UsersGoalBase.

Source code in backend/app/users/users_goals/schema.py
170
171
172
173
174
175
class UsersGoalCreate(UsersGoalBase):
    """
    Schema for creating a new users goal.

    Inherits all validation from UsersGoalBase.
    """

UsersGoalProgress

Bases: BaseModel

Schema for user goal progress tracking.

Attributes:

Name Type Description
goal_id StrictInt

Goal identifier.

interval Interval

Goal time interval.

activity_type ActivityType

Activity type for this goal.

goal_type GoalType

Type of goal metric.

start_date StrictStr

Period start date.

end_date StrictStr

Period end date.

percentage_completed StrictInt | None

Completion percentage.

total_calories StrictInt | None

Total calories achieved.

total_activities_number StrictInt | None

Total activities completed.

total_distance StrictInt | None

Total distance achieved.

total_elevation StrictInt | None

Total elevation gained.

total_duration StrictInt | None

Total duration achieved.

goal_calories StrictInt | None

Target calories.

goal_activities_number StrictInt | None

Target activities count.

goal_distance StrictInt | None

Target distance.

goal_elevation StrictInt | None

Target elevation.

goal_duration StrictInt | None

Target duration.

Source code in backend/app/users/users_goals/schema.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
class UsersGoalProgress(BaseModel):
    """
    Schema for user goal progress tracking.

    Attributes:
        goal_id: Goal identifier.
        interval: Goal time interval.
        activity_type: Activity type for this goal.
        goal_type: Type of goal metric.
        start_date: Period start date.
        end_date: Period end date.
        percentage_completed: Completion percentage.
        total_calories: Total calories achieved.
        total_activities_number: Total activities completed.
        total_distance: Total distance achieved.
        total_elevation: Total elevation gained.
        total_duration: Total duration achieved.
        goal_calories: Target calories.
        goal_activities_number: Target activities count.
        goal_distance: Target distance.
        goal_elevation: Target elevation.
        goal_duration: Target duration.
    """

    goal_id: StrictInt = Field(..., ge=1, description="Goal identifier")
    interval: Interval = Field(..., description="Goal time interval")
    activity_type: ActivityType = Field(..., description="Activity type for this goal")
    goal_type: GoalType
    start_date: StrictStr = Field(..., description="Period start date")
    end_date: StrictStr = Field(..., description="Period end date")
    percentage_completed: StrictInt | None = Field(
        default=None, ge=0, le=100, description="Completion percentage"
    )
    # total
    total_calories: StrictInt | None = Field(
        default=None, ge=0, description="Total calories achieved"
    )
    total_activities_number: StrictInt | None = Field(
        default=None, ge=0, description="Total activities completed"
    )
    total_distance: StrictInt | None = Field(
        default=None, ge=0, description="Total distance achieved"
    )
    total_elevation: StrictInt | None = Field(
        default=None, ge=0, description="Total elevation gained"
    )
    total_duration: StrictInt | None = Field(
        default=None, ge=0, description="Total duration achieved"
    )
    # goal
    goal_calories: StrictInt | None = Field(
        default=None, ge=0, description="Target calories"
    )
    goal_activities_number: StrictInt | None = Field(
        default=None, ge=0, description="Target activities count"
    )
    goal_distance: StrictInt | None = Field(
        default=None, ge=0, description="Target distance"
    )
    goal_elevation: StrictInt | None = Field(
        default=None, ge=0, description="Target elevation"
    )
    goal_duration: StrictInt | None = Field(
        default=None, ge=0, description="Target duration"
    )

    model_config = ConfigDict(
        from_attributes=True,
        extra="forbid",
        validate_assignment=True,
        use_enum_values=True,
    )

UsersGoalRead

Bases: UsersGoalBase

Schema for reading a user goal.

Attributes:

Name Type Description
id StrictInt

Unique identifier for the goal.

user_id StrictInt

User who owns this goal.

Source code in backend/app/users/users_goals/schema.py
178
179
180
181
182
183
184
185
186
187
188
class UsersGoalRead(UsersGoalBase):
    """
    Schema for reading a user goal.

    Attributes:
        id: Unique identifier for the goal.
        user_id: User who owns this goal.
    """

    id: StrictInt = Field(..., ge=1, description="Unique identifier for the goal")
    user_id: StrictInt = Field(..., ge=1, description="User who owns this goal")

UsersGoalUpdate

Bases: UsersGoalRead

Schema for updating an existing users goal.

Supports partial updates. Omitted fields remain unchanged.

Source code in backend/app/users/users_goals/schema.py
191
192
193
194
195
196
class UsersGoalUpdate(UsersGoalRead):
    """
    Schema for updating an existing users goal.

    Supports partial updates. Omitted fields remain unchanged.
    """

calculate_user_goals

calculate_user_goals(user_id, date, db)

Calculate progress for all user goals on a specified date.

Parameters:

Name Type Description Default
user_id int

The ID of the user.

required
date str | None

Date in YYYY-MM-DD format. If None, uses current date.

required
db Session

SQLAlchemy database session.

required

Returns:

Type Description
list[UsersGoalProgress] | None

List of UsersGoalProgress objects, or None if no goals found.

Raises:

Type Description
HTTPException

If database error occurs.

Source code in backend/app/users/users_goals/utils.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def calculate_user_goals(
    user_id: int, date: str | None, db: Session
) -> list[user_goals_schema.UsersGoalProgress] | None:
    """
    Calculate progress for all user goals on a specified date.

    Args:
        user_id: The ID of the user.
        date: Date in YYYY-MM-DD format. If None, uses
            current date.
        db: SQLAlchemy database session.

    Returns:
        List of UsersGoalProgress objects, or None if no
            goals found.

    Raises:
        HTTPException: If database error occurs.
    """
    if not date:
        date = datetime.now().strftime("%Y-%m-%d")
    try:
        goals = user_goals_crud.get_user_goals_by_user_id(user_id, db)

        if not goals:
            return None

        return [
            calculate_goal_progress_by_activity_type(goal, date, db) for goal in goals
        ]
    except HTTPException as http_err:
        raise http_err
    except (ValueError, TypeError) as err:
        # Log the exception
        core_logger.print_to_log(
            f"Error in calculate_user_goals: {err}",
            "error",
            exc=err,
        )
        # Raise an HTTPException with a 400 status code
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid data provided",
        ) from err
    except Exception as err:
        # Log unexpected exceptions
        core_logger.print_to_log(
            f"Unexpected error in calculate_user_goals: {err}",
            "error",
            exc=err,
        )
        # Raise an HTTPException with a 500 status code
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Internal Server Error",
        ) from err

create_user_goal

create_user_goal(user_id, user_goal, db)

Create a new user goal.

Parameters:

Name Type Description Default
user_id int

The ID of the user.

required
user_goal UsersGoalCreate

Goal data to create.

required
db Session

SQLAlchemy database session.

required

Returns:

Type Description
UsersGoal

The created UsersGoal model.

Raises:

Type Description
HTTPException

If goal already exists (409) or database error occurs (500).

Source code in backend/app/users/users_goals/crud.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
@core_decorators.handle_db_errors
def create_user_goal(
    user_id: int,
    user_goal: user_goals_schema.UsersGoalCreate,
    db: Session,
) -> user_goals_models.UsersGoal:
    """
    Create a new user goal.

    Args:
        user_id: The ID of the user.
        user_goal: Goal data to create.
        db: SQLAlchemy database session.

    Returns:
        The created UsersGoal model.

    Raises:
        HTTPException: If goal already exists (409) or database
            error occurs (500).
    """
    try:
        # Check if goal already exists
        stmt = select(user_goals_models.UsersGoal).where(
            user_goals_models.UsersGoal.user_id == user_id,
            user_goals_models.UsersGoal.interval == user_goal.interval,
            user_goals_models.UsersGoal.activity_type == user_goal.activity_type,
            user_goals_models.UsersGoal.goal_type == user_goal.goal_type,
        )
        existing_goal = db.execute(stmt).scalar_one_or_none()

        if existing_goal:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=(
                    "User already has a goal for this activity type, "
                    "interval, and goal type."
                ),
            )

        db_user_goal = user_goals_models.UsersGoal(
            user_id=user_id,
            interval=user_goal.interval,
            activity_type=user_goal.activity_type,
            goal_type=user_goal.goal_type,
            goal_calories=user_goal.goal_calories,
            goal_activities_number=user_goal.goal_activities_number,
            goal_distance=user_goal.goal_distance,
            goal_elevation=user_goal.goal_elevation,
            goal_duration=user_goal.goal_duration,
        )
        db.add(db_user_goal)
        db.commit()
        db.refresh(db_user_goal)
        return db_user_goal
    except HTTPException:
        raise

delete_user_goal

delete_user_goal(user_id, goal_id, db)

Delete a user's goal from the database.

Parameters:

Name Type Description Default
user_id int

ID of the user who owns the goal.

required
goal_id int

ID of the goal to delete.

required
db Session

SQLAlchemy database session.

required

Raises:

Type Description
HTTPException

If goal not found (404) or database error occurs (500).

Source code in backend/app/users/users_goals/crud.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
@core_decorators.handle_db_errors
def delete_user_goal(user_id: int, goal_id: int, db: Session) -> None:
    """
    Delete a user's goal from the database.

    Args:
        user_id: ID of the user who owns the goal.
        goal_id: ID of the goal to delete.
        db: SQLAlchemy database session.

    Raises:
        HTTPException: If goal not found (404) or database
            error occurs (500).
    """
    db_user_goal = get_user_goal_by_user_and_goal_id(user_id, goal_id, db)

    if not db_user_goal:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User goal not found",
        )

    db.delete(db_user_goal)
    db.commit()

get_user_goal_by_user_and_goal_id

get_user_goal_by_user_and_goal_id(user_id, goal_id, db)

Retrieve a specific goal by user ID and goal ID.

Parameters:

Name Type Description Default
user_id int

The ID of the user.

required
goal_id int

The ID of the goal.

required
db Session

SQLAlchemy database session.

required

Returns:

Type Description
UsersGoal | None

UsersGoal model if found, None otherwise.

Raises:

Type Description
HTTPException

If database error occurs.

Source code in backend/app/users/users_goals/crud.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@core_decorators.handle_db_errors
def get_user_goal_by_user_and_goal_id(
    user_id: int, goal_id: int, db: Session
) -> user_goals_models.UsersGoal | None:
    """
    Retrieve a specific goal by user ID and goal ID.

    Args:
        user_id: The ID of the user.
        goal_id: The ID of the goal.
        db: SQLAlchemy database session.

    Returns:
        UsersGoal model if found, None otherwise.

    Raises:
        HTTPException: If database error occurs.
    """
    stmt = select(user_goals_models.UsersGoal).where(
        user_goals_models.UsersGoal.user_id == user_id,
        user_goals_models.UsersGoal.id == goal_id,
    )
    return db.execute(stmt).scalar_one_or_none()

get_user_goals_by_user_id

get_user_goals_by_user_id(user_id, db, interval=None, activity_type=None, goal_type=None)

Retrieve goals for a specific user with optional filters.

Parameters:

Name Type Description Default
user_id int

The ID of the user.

required
db Session

SQLAlchemy database session.

required
interval Interval | None

Optional filter by goal interval.

None
activity_type ActivityType | None

Optional filter by activity type.

None
goal_type GoalType | None

Optional filter by goal type.

None

Returns:

Type Description
list[UsersGoal]

List of UsersGoal models matching the filters.

Raises:

Type Description
HTTPException

If database error occurs.

Source code in backend/app/users/users_goals/crud.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@core_decorators.handle_db_errors
def get_user_goals_by_user_id(
    user_id: int,
    db: Session,
    interval: user_goals_schema.Interval | None = None,
    activity_type: user_goals_schema.ActivityType | None = None,
    goal_type: user_goals_schema.GoalType | None = None,
) -> list[user_goals_models.UsersGoal]:
    """
    Retrieve goals for a specific user with optional filters.

    Args:
        user_id: The ID of the user.
        db: SQLAlchemy database session.
        interval: Optional filter by goal interval.
        activity_type: Optional filter by activity type.
        goal_type: Optional filter by goal type.

    Returns:
        List of UsersGoal models matching the filters.

    Raises:
        HTTPException: If database error occurs.
    """
    stmt = select(user_goals_models.UsersGoal).where(
        user_goals_models.UsersGoal.user_id == user_id
    )

    if interval is not None:
        stmt = stmt.where(user_goals_models.UsersGoal.interval == interval.value)
    if activity_type is not None:
        stmt = stmt.where(
            user_goals_models.UsersGoal.activity_type == activity_type.value
        )
    if goal_type is not None:
        stmt = stmt.where(user_goals_models.UsersGoal.goal_type == goal_type.value)

    return db.execute(stmt).scalars().all()

update_user_goal

update_user_goal(user_id, user_goal, db)

Update a user's goal.

Parameters:

Name Type Description Default
user_id int

ID of the user.

required
user_goal UsersGoalUpdate

Schema with fields to update.

required
db Session

SQLAlchemy database session.

required

Returns:

Type Description
UsersGoal

The updated UsersGoal model.

Raises:

Type Description
HTTPException

If goal not found (404) or database error occurs (500).

Source code in backend/app/users/users_goals/crud.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
@core_decorators.handle_db_errors
def update_user_goal(
    user_id: int,
    user_goal: user_goals_schema.UsersGoalUpdate,
    db: Session,
) -> user_goals_models.UsersGoal:
    """
    Update a user's goal.

    Args:
        user_id: ID of the user.
        user_goal: Schema with fields to update.
        db: SQLAlchemy database session.

    Returns:
        The updated UsersGoal model.

    Raises:
        HTTPException: If goal not found (404) or database
            error occurs (500).
    """
    if user_goal.user_id != user_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Cannot edit user goal for another user",
        )

    db_user_goal = get_user_goal_by_user_and_goal_id(user_id, user_goal.id, db)

    if not db_user_goal:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User goal not found",
        )

    # Update only provided fields
    update_data = user_goal.model_dump(exclude_unset=True, exclude={"user_id", "id"})
    for field, value in update_data.items():
        setattr(db_user_goal, field, value)

    db.commit()
    db.refresh(db_user_goal)
    return db_user_goal