Skip to content

Commit

Permalink
Everything is a rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Clue88 committed Oct 6, 2023
1 parent e91929c commit 950b47c
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 76 deletions.
2 changes: 1 addition & 1 deletion backend/degree/management/commands/import_degreeworks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import tqdm
from django.core.management.base import BaseCommand

from degree.models import DegreePlan, Requirement, Rule
from degree.models import DegreePlan, Rule
from degree.utils.request_degreeworks import audit, degree_plans_of, get_programs, write_dp


Expand Down
84 changes: 84 additions & 0 deletions backend/degree/migrations/0004_auto_20231006_1754.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Generated by Django 3.2.20 on 2023-10-06 21:54

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


class Migration(migrations.Migration):

dependencies = [
("degree", "0003_auto_20231001_1145"),
]

operations = [
migrations.RemoveField(
model_name="rule",
name="max_cus",
),
migrations.RemoveField(
model_name="rule",
name="max_num",
),
migrations.RemoveField(
model_name="rule",
name="min_cus",
),
migrations.RemoveField(
model_name="rule",
name="min_num",
),
migrations.RemoveField(
model_name="rule",
name="requirement",
),
migrations.AddField(
model_name="rule",
name="cus",
field=models.DecimalField(
decimal_places=1,
help_text="\nThe minimum number of CUs required for this rule. Only non-null\nif this is a Rule leaf.\n",
max_digits=4,
null=True,
),
),
migrations.AddField(
model_name="rule",
name="degree_plan",
field=models.ForeignKey(
default=0,
help_text="\nThe degree plan that has this rule.\n",
on_delete=django.db.models.deletion.CASCADE,
to="degree.degreeplan",
),
preserve_default=False,
),
migrations.AddField(
model_name="rule",
name="num",
field=models.IntegerField(
help_text="\nThe minimum number of courses or subrules required for this rule.\n",
null=True,
),
),
migrations.AddField(
model_name="rule",
name="parent",
field=models.ForeignKey(
help_text="\nThis rule's parent Rule if it has one.\n",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="degree.rule",
),
),
migrations.AlterField(
model_name="rule",
name="q",
field=models.TextField(
help_text="\nString representing a Q() object that returns the set of courses\nsatisfying this rule. Only non-empty if this is a Rule leaf.\n",
max_length=1000,
),
),
migrations.DeleteModel(
name="Requirement",
),
]
91 changes: 20 additions & 71 deletions backend/degree/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from django.db import models
from textwrap import dedent
from django.contrib.auth import get_user_model

from courses.models import Topic, Course, string_dict_to_html


class DegreePlan(models.Model):
Expand Down Expand Up @@ -61,110 +58,62 @@ def __str__(self) -> str:
return f"{self.program} {self.degree} in {self.major} with conc. {self.concentration} ({self.year})"


class Requirement(models.Model):
class Rule(models.Model):
"""
This model represents a degree requirement.
This model represents a degree requirement rule.
"""

name = models.CharField(
max_length=256,
help_text=dedent(
"""
The name of this requirement, e.g., General Education, Foundations
"""
),
)
code = models.CharField(
max_length=32,
num = models.IntegerField(
null=True,
help_text=dedent(
"""
The canonical code for this requirement, e.g., U-GE-FND
The minimum number of courses or subrules required for this rule.
"""
),
)
min_cus = models.DecimalField(

cus = models.DecimalField(
decimal_places=1,
max_digits=4,
null=True,
help_text=dedent(
"""
The minimum number of CUs required to qualify for this degree requirement
The minimum number of CUs required for this rule. Only non-null
if this is a Rule leaf.
"""
),
)
degree_plan = models.ManyToManyField(

degree_plan = models.ForeignKey(
DegreePlan,
on_delete=models.CASCADE,
help_text=dedent(
"""
The degree plan(s) that have this requirement.
The degree plan that has this rule.
"""
),
)

def __str__(self) -> str:
return f"{self.name} ({self.code}), min_cus={self.min_cus}, degree_plan={self.degree_plan}"


class Rule(models.Model):
"""
This model represents a degree requirement rule. A rule has a Q object
representing courses that can fulfill this rule and a number of required
courses, number of required CUs, or both.
"""

q = models.TextField(
max_length=1000,
help_text=dedent(
"""
String representing a Q() object that returns the set of courses satisfying this rule.
String representing a Q() object that returns the set of courses
satisfying this rule. Only non-empty if this is a Rule leaf.
"""
),
)
min_num = models.IntegerField(
null=True,
help_text=dedent(
"""
The minimum number of courses required for this rule.
"""
),
)
max_num = models.IntegerField(
null=True,
help_text=dedent(
"""
The maximum number of courses required for this rule.
"""
),
)
min_cus = models.DecimalField(
decimal_places=1,
max_digits=4,
null=True,
help_text=dedent(
"""
The minimum number of CUs required for this rule.
"""
),
)
max_cus = models.DecimalField(
decimal_places=1,
max_digits=4,

parent = models.ForeignKey(
"self",
null=True,
help_text=dedent(
"""
The maximum number of CUs required for this rule.
"""
),
)
requirement = models.ForeignKey(
Requirement,
on_delete=models.CASCADE,
help_text=dedent(
"""
The degree requirement that has this rule.
This rule's parent Rule if it has one.
"""
),
)

def __str__(self) -> str:
return f"{self.q}, min_num={self.min_num}, max_num={self.max_num}, min_cus={self.min_cus}, max_cus={self.max_cus}, requirement={self.requirement}"
return f"{self.q}, num={self.num}, cus={self.cus}, degree_plan={self.degree_plan}"
8 changes: 4 additions & 4 deletions backend/degree/utils/parse_degreeworks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db.models import Q

from degree.models import DegreePlan, Requirement, Rule
from degree.models import DegreePlan, Rule

# TODO: these should not be hardcoded, but rather added to the database
E_DEPTS = ["BE", "CIS", "CMPE", "EAS", "ESE", "MEAM", "MSE", "NETS", "ROBO"] # SEAS
Expand Down Expand Up @@ -354,17 +354,17 @@ def parse_rulearray(ruleArray, degree_plan, parent_rule=None) -> list[Rule]:


# TODO: Make the function names more descriptive
def parse_degreeworks(json: str, degree_plan: DegreePlan) -> list[Requirement]:
def parse_degreeworks(json: str, degree_plan: DegreePlan) -> list[Rule]:
"""
Returns a list of Requirements given a DegreeWorks JSON audit and a DegreePlan.
Returns a list of Rules given a DegreeWorks JSON audit and a DegreePlan.
"""
blockArray = json.get("blockArray")

# TODO: Instead of creating a list assign each Requirement to DegreePlan
degree_reqs = []

for requirement in blockArray:
degree_req = Requirement(
degree_req = Rule(
name=requirement["title"],
code=requirement["requirementValue"],
# TODO: parse min_cus
Expand Down

0 comments on commit 950b47c

Please sign in to comment.