Skip to content

Commit ac548f0

Browse files
NickGuerreroarsalaerfansezavalasrmk-2005
authored
Version 1.1.0 (#78)
* Update process attendance logic to use gspread instead of CSV export link (#64) * Replace CSV functionality with gspread in attendance log processing * Update test fixtures to mock gspread for tests * Production Fix 2 (#67) * Fix canvas url issue and alembic set-up script * Withdrawal Form Processing (#61) * Student withdrawal form endpoint logic * Updated to use a schema to accept JSON body * Accelerate Attendance Bug (#73) * Fix incorrect field used in accelerate/process-attendance * Check Actvity (#69) * Database alembic update and router logic for check activity endpoint route * Logic to check active status using Attendance and Canvas activity * Change Alembic config to use command line argument for environment * Create alias for fetching threshold values from environment vars * Removes direct env access from env.py and keeps runtime DB selection logic intact. (#70) Co-authored-by: Nicolas Guerrero <Guerreronicolas1872@gmail.com> Co-authored-by: Erfan <41974204+arsalaerfan@users.noreply.github.com> Co-authored-by: Sergio Zavala <106635253+sezavala@users.noreply.github.com> Co-authored-by: srmk-2005 <130954718+srmk-2005@users.noreply.github.com> Co-authored-by: RAMAMUTHUKUMARAN <130954718+RAMAMUTHUKUMARAN@users.noreply.github.com>
1 parent 2ed8556 commit ac548f0

File tree

13 files changed

+875
-11
lines changed

13 files changed

+875
-11
lines changed

migrations/env.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
from logging.config import fileConfig
2-
import os
3-
42
from sqlalchemy import engine_from_config, create_engine
53
from sqlalchemy import pool
6-
4+
from src.config import settings
75
from alembic import context
86

97
# this is the Alembic Config object, which provides
@@ -22,14 +20,14 @@
2220

2321
# map Alembic environment names to DB URLs
2422
DB_URLS = {
25-
"development": os.getenv("DEV_DATABASE_URL"),
26-
"production": os.getenv("PROD_DATABASE_URL"),
27-
"custom": os.getenv("CUSTOM_DATABASE_URL"),
23+
"development": settings.dev_database_url,
24+
"production": settings.prod_database_url,
25+
"custom": settings.custom_database_url,
2826
}
2927

3028
def get_url() -> str:
3129
# Alembic Command line: alembic -x environment=development
32-
env_name = config.get_main_option("environment") or "development"
30+
env_name = context.get_x_argument(as_dictionary=True).get("environment") or "development"
3331
url = DB_URLS.get(env_name)
3432
if not url:
3533
raise RuntimeError(f"No database URL configured for environment '{env_name}'")
@@ -39,9 +37,8 @@ def get_url() -> str:
3937
# can be acquired:
4038
# my_important_option = config.get_main_option("my_important_option")
4139
# ... etc.
42-
from os import environ as env
4340
def env_url():
44-
return env.get("CTI_POSTGRES_URL")
41+
return settings.cti_postgres_url
4542

4643
def run_migrations_offline() -> None:
4744
"""Run migrations in 'offline' mode.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Add last_canvas_access
2+
3+
Revision ID: ab2e5d7e05e8
4+
Revises: c0025f85a371
5+
Create Date: 2025-12-04 11:24:44.987577
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = 'ab2e5d7e05e8'
16+
down_revision: Union[str, None] = 'c0025f85a371'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
# Add last_canvas_access column to accelerate_course_progress
23+
op.add_column(
24+
'accelerate_course_progress',
25+
sa.Column('last_canvas_access', sa.DateTime(timezone=False), nullable=True)
26+
)
27+
28+
29+
def downgrade() -> None:
30+
# Drop column
31+
op.drop_column('accelerate_course_progress', 'last_canvas_access')
32+

src/api.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
from src.students.alternate_emails.router import router as student_alternate_emails_router
77
from src.students.attendance_log.router import router as student_attendance_log_router
88
from src.students.accelerate.process_attendance.router import router as accelerate_attendance_record_router
9+
from src.students.accelerate.check_activity.router import router as accelerate_activity_check_router
910
from src.students.missing_students.router import router as student_recover_attendance_router
1011
from src.students.attendance_entry.router import router as student_attendance_entry_router
12+
from src.students.withdrawal_processing.router import router as student_withdrawal_router
13+
1114
from src.gsheet.refresh.router import router as gsheet_refresh_router
1215
from src.utils.authorization import verify_api_key
1316

@@ -55,6 +58,13 @@
5558
tags=["Accelerate"],
5659
)
5760

61+
# /api/students/accelerate/check-activity
62+
api_router.include_router(
63+
accelerate_activity_check_router,
64+
prefix="/students/accelerate/check-activity",
65+
tags=["Accelerate"],
66+
)
67+
5868
# /api/students/recover-attendance
5969
api_router.include_router(
6070
student_recover_attendance_router,
@@ -69,9 +79,16 @@
6979
tags=["Students"],
7080
)
7181

82+
# /api/students/process-withdrawal
83+
api_router.include_router(
84+
student_withdrawal_router,
85+
prefix="/students/process-withdrawal",
86+
tags=["Students"],
87+
)
88+
7289
# /api/gsheet/refresh/...
7390
api_router.include_router(
7491
gsheet_refresh_router,
7592
prefix="/gsheet/refresh",
7693
tags=["GSheet"],
77-
)
94+
)

src/config.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ class Settings(BaseSettings):
2020
cti_mongo_url: Optional[str] = Field(validation_alias="CTI_MONGO_URL", default=None)
2121
cti_postgres_url: Optional[str] = Field(validation_alias="CTI_POSTGRES_URL", default=None)
2222

23+
# Alembic / Migration Database URLs
24+
dev_database_url: Optional[str] = Field(validation_alias="DEV_DATABASE_URL", default=None)
25+
prod_database_url: Optional[str] = Field(validation_alias="PROD_DATABASE_URL", default=None)
26+
custom_database_url: Optional[str] = Field(validation_alias="CUSTOM_DATABASE_URL", default=None)
27+
2328
# Canvas Variables, only required for Canvas specific-operations
2429
# NOTE: SIS ID's are needed for SIS operations, must be manually defined ahead of time
2530
# TODO: Rename CTI_ACCESS_TOKEN to CTI_CANVAS_TOKEN, improve clarity on token purpose and differentiate from server token
@@ -46,6 +51,14 @@ class Settings(BaseSettings):
4651
gs_private_key_id: Optional[str] = Field(validation_alias="GS_PRIVATE_KEY_ID", default=None)
4752
gs_project_id: Optional[str] = Field(validation_alias="GS_PROJECT_ID", default=None)
4853

54+
# Thresholds in weeks for determining if a student is active
55+
activity_attendance_threshold_weeks: int = Field(validation_alias="ACTIVITY_ATTENDANCE_THRESHOLD_WEEKS",
56+
default=2,
57+
description="Number of weeks to check for attendance activity")
58+
activity_canvas_threshold_weeks: int = Field(validation_alias="ACTIVITY_CANVAS_THRESHOLD_WEEKS",
59+
default=2,
60+
description="Number of weeks to check for Canvas activity")
61+
4962
# Application Constants
5063
canvas_api_url: str = "https://cti-courses.instructure.com"
5164
canvas_api_test_url: str = "https://cti-courses.test.instructure.com"
@@ -60,4 +73,4 @@ class Settings(BaseSettings):
6073
APPLICATIONS_COLLECTION = "applications"
6174
ACCELERATE_FLEX_COLLECTION = "accelerate_flex"
6275
PATHWAY_GOALS_COLLECTION = "pathway_goals"
63-
COURSES_COLLECTION = "courses"
76+
COURSES_COLLECTION = "courses"

src/database/postgres/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ class AccelerateCourseProgress(Base):
171171
latest_milestone: Mapped[Optional[str]] = mapped_column(String)
172172
pathway_score: Mapped[Optional[float]] = mapped_column(Float(3))
173173
pathway_difference: Mapped[Optional[int]] = mapped_column(Integer)
174+
last_canvas_access: Mapped[Optional[datetime]] = mapped_column(DateTime)
174175
owner: Mapped["Accelerate"] = relationship(back_populates="progress_record")
175176

176177
class AccountabilityGroup(Base):
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Any, Dict, Optional
2+
3+
from fastapi import APIRouter, Depends, Query, status
4+
from sqlalchemy.orm import Session
5+
6+
from src.config import settings
7+
from src.database.postgres.core import make_session
8+
from src.utils.exceptions import handle_db_exceptions
9+
import src.students.accelerate.check_activity.service as service
10+
11+
router = APIRouter()
12+
13+
@router.post("", status_code=status.HTTP_200_OK)
14+
def check_all_students_activity(
15+
db: Session = Depends(make_session),
16+
) -> Dict[str, Any]:
17+
"""Check and update activity for all active Accelerate students."""
18+
try:
19+
att_threshold = settings.activity_attendance_threshold_weeks
20+
canvas_threshold = settings.activity_canvas_threshold_weeks
21+
22+
results = service.check_all_students(db, att_threshold, canvas_threshold)
23+
24+
if settings.app_env == "production":
25+
results.pop("details", None)
26+
27+
return results
28+
29+
except Exception as exc:
30+
handle_db_exceptions(db, exc)

0 commit comments

Comments
 (0)