Skip to content

Commit

Permalink
implemented degreeprofile, coursetaken
Browse files Browse the repository at this point in the history
  • Loading branch information
ashleymyan committed Mar 28, 2024
1 parent 3dcb85e commit 2fed696
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 75 deletions.
2 changes: 1 addition & 1 deletion backend/courses/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class CourseAdmin(admin.ModelAdmin):
readonly_fields = ("topic", "crosslistings", "course_attributes")
exclude = ("attributes",)
list_filter = ("semester",)
list_display = ("full_code", "semester", "title")
list_display = ("id", "full_code", "semester", "title")

list_select_related = ("department",)

Expand Down
11 changes: 9 additions & 2 deletions backend/degree/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.urls import reverse
from django.utils.html import format_html

from degree.models import Degree, DegreePlan, DoubleCountRestriction, Rule, SatisfactionStatus, Fulfillment, DegreeProfile, CourseTaken
from degree.models import Degree, DegreePlan, DoubleCountRestriction, Rule, SatisfactionStatus, Fulfillment, DegreeProfile, CourseTaken, UserProfile


# Register your models here.
Expand Down Expand Up @@ -54,6 +54,13 @@ def get_urls(self):
def degree_editor(self, request):
context = dict(self.admin_site.each_context(request))
return TemplateResponse(request, "degree-editor.html", context)


admin.site.register(DegreeProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('id', 'user')
admin.site.register(UserProfile, UserProfileAdmin)

class DegreeProfileAdmin(admin.ModelAdmin):
list_display = ('id', 'user_profile', 'graduation_date')
admin.site.register(DegreeProfile, DegreeProfileAdmin)
admin.site.register(CourseTaken)
2 changes: 1 addition & 1 deletion backend/degree/management/commands/fetch_degrees.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from courses.util import get_current_semester
from degree.management.commands.deduplicate_rules import deduplicate_rules
from degree.models import Degree, program_code_to_name
from degree.models import Degree, program_code_to_name, add_course,
from degree.utils.degreeworks_client import DegreeworksClient
from degree.utils.parse_degreeworks import parse_and_save_degreeworks

Expand Down
20 changes: 20 additions & 0 deletions backend/degree/migrations/0012_alter_coursetaken_course.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 3.2.24 on 2024-03-28 05:54

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('courses', '0066_course_credits'),
('degree', '0011_alter_coursetaken_course'),
]

operations = [
migrations.AlterField(
model_name='coursetaken',
name='course',
field=models.ForeignKey(help_text='\nThe full-code (e.g. CIS-1210) of the course taken\n', on_delete=django.db.models.deletion.CASCADE, to='courses.course'),
),
]
20 changes: 20 additions & 0 deletions backend/degree/migrations/0013_alter_coursetaken_course.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 3.2.24 on 2024-03-28 06:33

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('courses', '0066_course_credits'),
('degree', '0012_alter_coursetaken_course'),
]

operations = [
migrations.AlterField(
model_name='coursetaken',
name='course',
field=models.ForeignKey(help_text='\nThe course ID of the course taken\n', on_delete=django.db.models.deletion.CASCADE, to='courses.course'),
),
]
69 changes: 33 additions & 36 deletions backend/degree/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,6 @@
program_code_to_name = dict(program_choices)


"""
Transcript Model
==================
The user optionally uploads a pdf of transcript and we create a Transcript object based on the scraped transcript info.
"""

class Transcript(models.Model):
program = models.CharField(
max_length=255,
db_index=True,
help_text=dedent(
"""
The user's current program (e.g. SEAS B.S.)
"""
),
)
courses = models.ForeignKey(
Course,
null=True,
blank=True,
on_delete=models.SET_NULL,
help_text=dedent(
"""
Courses already taken, prob a list of Course objects
"""
),
)

class Degree(models.Model):
"""
This model represents a degree for a specific year.
Expand Down Expand Up @@ -557,6 +527,32 @@ class Meta:
)
]


class Transcript(models.Model):
"""
Not currently implemented
"""
program = models.CharField(
max_length=255,
db_index=True,
help_text=dedent(
"""
The user's current program (e.g. SEAS B.S.)
"""
),
)
courses = models.ForeignKey(
Course,
null=True,
blank=True,
on_delete=models.SET_NULL,
help_text=dedent(
"""
Courses already taken, prob a list of Course objects
"""
),
)

class DegreeProfile(models.Model):
user_profile = models.OneToOneField(
UserProfile,
Expand Down Expand Up @@ -627,27 +623,28 @@ def calculate_total_credits(self):

return total_credits

def add_course(self, course, semester, grade):
def add_course(self, course_id, semester, grade):
"""
Adds a course to courses taken
"""
course_instance = Course.objects.get(id=course_id)
CourseTaken.objects.create(
degree_profile=self,
course=course,
course=course_instance,
semester=semester,
grade=grade
)

def remove_course(self, course, semester):
def remove_course(self, course_id, semester):
"""
Removes a course taken by a specific person
"""
course_taken = CourseTaken.objects.filter(degree_profile=self, course=course, semester=semester)
course_taken = CourseTaken.objects.filter(degree_profile=self, course=course_id, semester=semester)

if course_taken.exists():
course_taken.delete()
else:
print(f"Course not found for course code {course} in semester {semester}.")
print(f"Course not found for course id {course_id} in semester {semester}.")

class Meta:
constraints = [
Expand Down Expand Up @@ -677,7 +674,7 @@ class CourseTaken(models.Model):
on_delete=models.CASCADE,
help_text=dedent(
"""
The course code (e.g. CIS-1210) of the course taken
The course object of the course taken
"""
),
)
Expand Down
53 changes: 34 additions & 19 deletions backend/degree/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from courses.models import Course
from courses.serializers import CourseListSerializer, CourseDetailSerializer
from degree.models import Degree, DegreePlan, DoubleCountRestriction, Fulfillment, Rule, DockedCourse, CourseTaken, DegreeProfile
from degree.models import Degree, DegreePlan, DoubleCountRestriction, Fulfillment, Rule, DockedCourse, CourseTaken, DegreeProfile, UserProfile
from courses.util import get_current_semester

class DegreeListSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -188,7 +188,7 @@ class Meta:


class CourseTakenSerializer(serializers.ModelSerializer):
course = SimpleCourseSerializer(read_only=True)
course = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all())

class Meta:
model = CourseTaken
Expand All @@ -198,27 +198,42 @@ class DegreeSerializer(serializers.ModelSerializer):
class Meta:
model = Degree
fields = "__all__"

def validate_degrees(self, degrees):
if not all(Degree.objects.filter(id=degree_id).exists() for degree_id in degrees):
raise serializers.ValidationError("Degree(s) not valid")
return degrees

class DegreeProfileSerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField(help_text="The id of the user profile")
courses_taken = CourseTakenSerializer(many=True)
degrees = DegreeSerializer(many=True)
id = serializers.ReadOnlyField(help_text="The id of the degree profile")
courses_taken = CourseTakenSerializer(source='coursetaken_set', many=True, required=False)
degrees = DegreeSerializer(many=True, required=False)

class Meta:
model = DegreeProfile
fields = '__all__'
fields = ['id', 'user_profile', 'graduation_date', 'degrees', 'courses_taken']

def create(self, data):
degrees = data.pop('degrees')
profile = DegreeProfile.objects.create(**data)
for degree in degrees:
Degree.objects.create(degree_profile=profile, **degree)
return profile

def update(self, instance, data):
degrees = data.pop('degrees')
instance.degrees.clear()
for degree in degrees:
Degree.objects.create(degree_profile=instance, **degree)
return super().update(instance, data)

degrees_data = data.pop('degrees', [])
courses_taken_data = data.pop('courses_taken', [])

user_profile_id = data.pop('user_profile')
user_profile = UserProfile.objects.get(id=user_profile_id)

degree_profile = DegreeProfile.objects.create(user_profile=user_profile, **data)

if degrees_data:
degrees = Degree.objects.filter(id__in=degrees_data)
degree_profile.degrees.set(degrees)

for course_taken_data in courses_taken_data:
CourseTaken.objects.create(degree_profile=degree_profile, **course_taken_data)

return degree_profile

class DegreeProfilePatchSerializer(serializers.ModelSerializer):
degrees = serializers.PrimaryKeyRelatedField(queryset=Degree.objects.all(), many=True)

class Meta:
model = DegreeProfile
fields = ['degrees', 'graduation_date']
57 changes: 53 additions & 4 deletions backend/degree/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@

from courses.models import Course
from courses.serializers import CourseListSerializer
from degree.models import Degree, DegreePlan, Fulfillment, Rule, DockedCourse, DegreeProfile
from degree.models import Degree, DegreePlan, Fulfillment, Rule, DockedCourse, DegreeProfile, UserProfile
from degree.serializers import (
DegreeDetailSerializer,
DegreeListSerializer,
DegreePlanDetailSerializer,
DegreePlanListSerializer,
FulfillmentSerializer,
DockedCourseSerializer,
DegreeProfileSerializer
DegreeProfileSerializer,
CourseTakenSerializer,
DegreeProfilePatchSerializer
)


Expand Down Expand Up @@ -224,15 +226,62 @@ class DegreeProfileViewset(viewsets.ModelViewSet):

def get_queryset(self):
return DegreeProfile.objects.filter(user_profile__user=self.request.user)

def get_serializer_class(self):
if self.request.method == 'PATCH':
return DegreeProfilePatchSerializer
return super().get_serializer_class()

def perform_create(self, serializer):
return serializer.save(user_profile=self.request.user.user_profile)
user_profile, _ = UserProfile.objects.get_or_create(user=self.request.user)
return serializer.save(user_profile=user_profile.id)

def update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)

def destroy(self, request, *args, **kwargs):
instance = self.get_object()

if instance.user_profile.user != request.user:
raise ValidationError({"user_profile": "Unable to delete profile"})
return Response(
{"detail": "Unable to delete profile."},
status=status.HTTP_403_FORBIDDEN
)

return super().destroy(request, *args, **kwargs)


@action(detail=True, methods=['post'], url_path='add-course', url_name='add_course')
def add_course(self, request, pk=None):
degree_profile = self.get_object()
serializer = CourseTakenSerializer(data=request.data)
if serializer.is_valid():
degree_profile = self.get_object()
degree_profile.add_course(
course_id=serializer.validated_data['course'].id,
semester=serializer.validated_data['semester'],
grade=serializer.validated_data['grade']
)
return Response({'status': 'course added'}, status=status.HTTP_200_OK)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@action(detail=True, methods=['post'], url_path='remove-course', url_name='remove_course')
def remove_course(self, request, pk=None):
degree_profile = self.get_object()
course_id = request.data.get('course')
semester = request.data.get('semester')

if not course_id or not semester:
return Response({'error': 'missing course id or semester'}, status=status.HTTP_400_BAD_REQUEST)

try:
degree_profile.remove_course(course_id=course_id, semester=semester)
return Response({'status': 'course removed'}, status=status.HTTP_204_NO_CONTENT)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)

Loading

0 comments on commit 2fed696

Please sign in to comment.