Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tigerpath/majors_and_certificates/scripts/university_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,12 @@
"PHI": "Philosophy",
"PHY": "Physics",
"POL": "Politics",
"POR": "Portuguese",
"PSY": "Psychology",
"REL": "Religion",
"SLA": "Slavic Languages and Literatures",
"SOC": "Sociology",
"SPA": "Spanish",
"SPI": "Public and International Affairs",
"SPO": "Spanish and Portuguese",
}
Expand Down
7 changes: 2 additions & 5 deletions tigerpath/majors_and_certificates/scripts/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@
import collections
import copy
import os
import pathlib
from functools import lru_cache

import requests
import yaml

from . import university_info

# Allow overriding the data repo base via env var for easy testing
# Must end with a trailing slash and point to a raw.githubusercontent.com base
import pathlib

_LOCAL_DATA_DIR = pathlib.Path(__file__).resolve().parent.parent.parent / "requirements_data"


@lru_cache(maxsize=256)
def _load_yaml(path: str):
"""Load and parse YAML from the local requirements data directory."""
Expand Down
98 changes: 98 additions & 0 deletions tigerpath/management/commands/clear_settled_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
Clear stale 'settled' paths from all users' schedules and add new Major records.

When the requirement YAML data is updated with new category names, existing users'
settled paths (stored in user_schedule JSON) no longer match the new requirement
tree structure. This command clears those stale paths and adds Major records for
newly available concentrations.

The verifier's auto-settle feature will re-assign courses that can only satisfy
one requirement; users only need to manually re-settle ambiguous courses.

Usage:
python manage.py clear_settled_paths # dry-run (default)
python manage.py clear_settled_paths --apply # actually write changes
"""

from django.core.management.base import BaseCommand

from tigerpath.models import Major, UserProfile

NEW_MAJORS = [
{"name": "Portuguese", "code": "POR", "degree": "AB"},
{"name": "Spanish", "code": "SPA", "degree": "AB"},
]


class Command(BaseCommand):
help = "Clear stale settled paths and add new Major records for updated requirement data"

def add_arguments(self, parser):
parser.add_argument(
"--apply",
action="store_true",
default=False,
help="Actually write changes to the database (default is dry-run)",
)

def handle(self, *args, **options):
apply = options["apply"]
mode = "APPLIED" if apply else "DRY-RUN"

# --- Step 1: Add new Major records ---
self.stdout.write(f"\n[{mode}] Step 1: Adding new Major records")
for m in NEW_MAJORS:
exists = Major.objects.filter(code=m["code"]).exists()
if exists:
self.stdout.write(f" {m['code']} already exists, skipping")
else:
self.stdout.write(f" {m['code']} ({m['name']}) - will create")
if apply:
Major.objects.create(
name=m["name"],
code=m["code"],
degree=m["degree"],
supported=True,
)

# --- Step 2: Clear settled paths ---
self.stdout.write(f"\n[{mode}] Step 2: Clearing settled paths from user schedules")
profiles = UserProfile.objects.exclude(user_schedule__isnull=True)
total = profiles.count()
affected = 0
courses_cleared = 0

for profile in profiles.iterator():
schedule = profile.user_schedule
if not schedule:
continue

modified = False
for semester in schedule:
if not isinstance(semester, list):
continue
for course in semester:
if not isinstance(course, dict):
continue
settled = course.get("settled")
if settled and isinstance(settled, list) and len(settled) > 0:
course["settled"] = []
modified = True
courses_cleared += 1

if modified:
affected += 1
if apply:
profile.user_schedule = schedule
profile.save(update_fields=["user_schedule"])

self.stdout.write(f"\n[{mode}] Results:")
self.stdout.write(f" Scanned {total} profiles")
self.stdout.write(f" {affected} profiles had settled paths")
self.stdout.write(f" {courses_cleared} course-requirement bindings cleared")
if not apply:
self.stdout.write(
self.style.WARNING("\nNo changes written. Pass --apply to commit changes.")
)
else:
self.stdout.write(self.style.SUCCESS("\nDone. Migration complete."))
Loading