Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync with develop #698

Merged
merged 36 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4f7fa48
diff script
Sep 23, 2023
84fb031
Fixed typing issue
Sep 23, 2023
de8b0f2
Fixed typing issues
Sep 23, 2023
0f96dc0
refactor: move requirement map to Loader class to remove cyclic depen…
akevinge Sep 25, 2023
176f0de
fix: exclude free elective requirement from used core course check
akevinge Sep 25, 2023
733d9a6
fix: allow major to double dip but not core
akevinge Sep 25, 2023
8c7ee77
fix: type errors
akevinge Sep 25, 2023
2ecdaf0
fix: sync major.solver with degree_solver so tests are happy
akevinge Sep 25, 2023
737d268
Added working directory to workflow
Peytonbarre Sep 26, 2023
e40a1b3
fixed keyword highlighting in course description
enkyuan Sep 26, 2023
c0fb5be
Merge pull request #678 from UTDNebula/feat/diff
akevinge Sep 26, 2023
00443c5
Added lxml module requirement
Sep 26, 2023
22499f9
Merge pull request #687 from UTDNebula/fix/diff-requirements
akevinge Sep 26, 2023
33985e5
ran npm run format
enkyuan Sep 28, 2023
90d78c7
Workflow env fixes
Peytonbarre Sep 28, 2023
3315ee7
Added back used requirement
Peytonbarre Sep 28, 2023
00b6c75
Changed landing button colors
Peytonbarre Sep 28, 2023
824c334
Merge pull request #688 from UTDNebula/fix/diff
akevinge Sep 28, 2023
790cc2e
Added required fields for JIRA ticket
Sep 28, 2023
7fdbe39
Merge pull request #690 from UTDNebula/NP-92
akevinge Sep 28, 2023
43855cd
removed extra text
enkyuan Sep 28, 2023
9a73ed4
Merge pull request #689 from UTDNebula/NP-35
akevinge Sep 28, 2023
6efa6e8
Create state sharing to restrict sidebar scrolling
Peytonbarre Sep 28, 2023
9b1c7fe
Formatting!!
Peytonbarre Sep 28, 2023
751d300
Jira ticket file attachment for diff
Sep 30, 2023
746cb0b
Merge pull request #692 from UTDNebula/fix/NP-92
akevinge Oct 2, 2023
d14ac52
fixes np-128
saidarsht Oct 2, 2023
8a55ee7
Changing file to read binary
Oct 2, 2023
42aedc9
Changing variable name for typing issues
Oct 2, 2023
5c2c47e
Merge pull request #694 from UTDNebula/NP-92
akevinge Oct 2, 2023
fe27545
Merge pull request #691 from UTDNebula/NP-24
akevinge Oct 2, 2023
53069bc
Merge pull request #685 from UTDNebula/NP-43
akevinge Oct 2, 2023
bba9789
Merge pull request #693 from UTDNebula/og-ui
akevinge Oct 2, 2023
584835c
Merge pull request #682 from UTDNebula/fix-valid-major-double-dipping
cubetastic33 Oct 2, 2023
967ece3
Added major name to jira ticket title
Peytonbarre Oct 2, 2023
23acd81
Merge pull request #696 from UTDNebula/NP-92
cubetastic33 Oct 2, 2023
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
30 changes: 30 additions & 0 deletions .github/workflows/versioning.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Scheduled Versioning

on:
workflow_dispatch:
schedule:
- cron: '0 0 15 8 *'

jobs:
versioning:
name: Versioning System
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v3

- uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install dependencies
working-directory: validator
run: |
pip3 install -r requirements.txt

- name: Execute versioning script
env:
JIRA_API_KEY: ${{ secrets.JIRA_API_KEY }}
working-directory: ./validator/scripts
run: python diff.py
6 changes: 3 additions & 3 deletions public/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ header a:hover {
*/

.login {
background-color: #3772ff;
border-color: #3772ff;
background-color: #6266F9;
border-color: #6266F9;
color: #fff !important;
}

.login:hover {
background-color: #2956c5;
background-color: #474bb6;
color: #fff;
}

Expand Down
8 changes: 6 additions & 2 deletions src/components/planner/CourseInfoHoverCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const CourseInfoHoverCard: FC<CourseInfoHoverCardProps> = ({
</Link>
</h3>
<CourseDescription description={description} />

<HoverCard.Arrow className="fill-primary" />
</HoverCard.Content>
</HoverCard.Portal>
Expand All @@ -60,10 +59,15 @@ export default CourseInfoHoverCard;
*/
const CourseDescription = ({ description }: { description: string }) => {
const [showMore, setShowMore] = useState(false);
const boldDescription = description.replaceAll(/(\b[A-Z]{2,4} \d{4}\b)/gi, '<b>$1</b>');
return (
<span>
<p className="text-xs">
{!showMore ? `${description.substring(0, 200)}... ` : `${description} `}
<span
dangerouslySetInnerHTML={{
__html: !showMore ? boldDescription.substring(0, 200) + '...' : boldDescription,
}}
/>{' '}
<button
className={`${showMore ? '' : 'inline'} font-medium text-primary`}
onClick={(e) => {
Expand Down
10 changes: 8 additions & 2 deletions src/components/planner/Planner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ export default function Planner({
// Course that is currently being dragged
const [activeCourse, setActiveCourse] = useState<ActiveDragData | null>(null);

// Controls drag locking for the sidebar
const [isCourseDragging, setIsCourseDragging] = useState(false);

// Delay necessary so events inside draggables propagate
// valid sensors: https://github.com/clauderic/dnd-kit/discussions/82#discussioncomment-347608
const sensors = useSensors(
Expand All @@ -116,6 +119,7 @@ export default function Planner({
const handleOnDragStart = ({ active }: { active: Active }) => {
const originData = active.data.current as DragEventOriginData;
setActiveCourse({ from: originData.from, course: originData.course });
setIsCourseDragging(true);
};

const handleOnDragEnd = ({ active, over }: { active: Active; over: Over | null }) => {
Expand Down Expand Up @@ -143,15 +147,16 @@ export default function Planner({
);
}
}

setIsCourseDragging(false);
};

const ref = useRef<HTMLDivElement>(null);
// TODO: Use resizeobserver to change column count based on screen size

return (
<DndContext
// Enabling autoScroll causes odd behavior when dragging outside of a scrollable container (eg. Sidebar)
autoScroll={false}
autoScroll={true}
sensors={sensors}
collisionDetection={pointerWithin}
onDragStart={handleOnDragStart}
Expand Down Expand Up @@ -218,6 +223,7 @@ export default function Planner({
transferCredits={transferCredits}
getSearchedDragId={(course) => `course-list-searched-${course.id}`}
getRequirementDragId={(course) => `course-list-requirement-${course.id}`}
courseDragged={isCourseDragging}
/>
</div>
</DndContext>
Expand Down
6 changes: 5 additions & 1 deletion src/components/planner/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface CourseSelectorContainerProps {
transferCredits: string[];
getSearchedDragId: GetDragIdByCourse;
getRequirementDragId: GetDragIdByCourse;
courseDragged: boolean;
}

function CourseSelectorContainer({
Expand All @@ -33,6 +34,7 @@ function CourseSelectorContainer({
transferCredits,
getSearchedDragId,
getRequirementDragId,
courseDragged,
}: CourseSelectorContainerProps) {
const {
data: validationData,
Expand Down Expand Up @@ -119,7 +121,9 @@ function CourseSelectorContainer({
{open ? (
<div
id="tutorial-editor-1"
className="z-0 h-screen w-[30%] min-w-[30%] overflow-x-hidden overflow-y-scroll"
className={`z-0 h-screen w-[30%] min-w-[30%] overflow-x-hidden ${
courseDragged ? 'overflow-y-hidden' : 'overflow-y-scroll'
}`}
>
<div className="flex h-fit min-h-screen w-full flex-col gap-y-4 bg-white p-4">
<div className="flex flex-col">
Expand Down
1 change: 1 addition & 0 deletions src/components/planner/Tiles/SemesterTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ const DroppableSemesterTile: FC<DroppableSemesterTileProps> = ({
const { setNodeRef } = useDroppable({
id: dropId,
data: { to: 'semester-tile', semester } as DragDataToSemesterTile,
disabled: semester.locked,
});

return (
Expand Down
37 changes: 21 additions & 16 deletions validator/degree_solver.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from __future__ import annotations
from enum import Enum
from glob import glob
from collections import Counter

from pydantic import Json

from typing import Any

import core
from major.requirements import AbstractRequirement
from major.requirements import AbstractRequirement, FreeElectiveRequirement
from dataclasses import dataclass

from major.requirements.map import REQUIREMENTS_MAP
from major.requirements import loader
import json

from major.requirements.shared import (
Expand All @@ -19,6 +20,7 @@
)
from course import Course

LOADER = loader.Loader()

# Read all degree plan JSON files and store their contents in a hashmap
# This is so that we can avoid reading all the files each time we want to get the data for a certain course
Expand Down Expand Up @@ -144,7 +146,7 @@ def __init__(
requirements: DegreeRequirementsInput,
bypasses: BypassInput,
) -> None:
self.courses = set(courses)
self.courses = set([Course.from_name(course) for course in courses])
self.degree_requirements = self.load_requirements(requirements)
self.solved_core: core.store.AssignmentStore | None = None
self.bypasses = bypasses
Expand Down Expand Up @@ -187,9 +189,7 @@ def load_requirements(

# Add requirements
for req_data in requirements_data:
major_req.requirements.append(
REQUIREMENTS_MAP[req_data["matcher"]].from_json(req_data)
)
major_req.requirements.append(LOADER.requirement_from_json(req_data))
degree_requirements.append(major_req)
# We don't need to check the other JSON files
break
Expand All @@ -201,23 +201,28 @@ def load_requirements(
def solve(self) -> DegreeRequirementsSolver:
# Run for core
core_solver = self.load_core()
self.solved_core = core_solver.solve(
[Course.from_name(course) for course in self.courses], []
)
# Set of the core courses that are fulfilled, so they won't be considered as free electives
used_core_courses = set()
self.solved_core = core_solver.solve(list(self.courses), [])

# Counter of the core courses and their used hours, so they won't be considered as free electives.
used_core_courses: Counter[Course] = Counter()
if self.solved_core is not None:
for req_fill in self.solved_core.reqs_to_courses.values():
used_core_courses.update([course.name for course in req_fill.keys()])
used_core_courses.update(req_fill)

# Run for major
for degree_req in self.degree_requirements:
for course in self.courses:
# Make sure it's not a core course
if course in used_core_courses:
continue
for requirement in degree_req.requirements:
if requirement.attempt_fulfill(course):
# Free elective requirements are special, since they can take left over hours from core courses.
if type(requirement) == FreeElectiveRequirement:
if requirement.attempt_fulfill(
course.name,
available_hours=(
int(course.hours) - used_core_courses[course]
),
):
break
elif requirement.attempt_fulfill(course.name):
break

# Handle requirements bypasses for major
Expand Down
7 changes: 4 additions & 3 deletions validator/gen_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from types import GenericAlias
from typing import Any, ForwardRef
from jsonschema import Draft7Validator
from major.requirements import REQUIREMENTS_MAP
from major.requirements import loader

schema: dict[str, Any] = {
"$schema": Draft7Validator.META_SCHEMA["$id"],
Expand Down Expand Up @@ -89,8 +89,9 @@ def forward_ref_to_schema(ref: ForwardRef) -> dict[str, Any]:
raise Exception("Expected type, got", type(ref_type), ref_type)


for req_name in REQUIREMENTS_MAP:
req = REQUIREMENTS_MAP[req_name]
req_loader = loader.Loader()
for req_name in req_loader.REQUIREMENTS_MAP:
req = req_loader.REQUIREMENTS_MAP[req_name]
requirement_schema_props: dict[str, Any] = {"matcher": {"const": req_name}}

for prop_name, prop_type in req.JSON.__annotations__.items():
Expand Down
2 changes: 1 addition & 1 deletion validator/major/requirements/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .base import AbstractRequirement
from .shared import *
from .map import *
from . import loader
2 changes: 1 addition & 1 deletion validator/major/requirements/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations
from abc import abstractmethod, ABC
from dataclasses import dataclass

from typing import Any

from pydantic import Json
Expand All @@ -19,6 +18,7 @@ class AbstractRequirement(ABC):
def attempt_fulfill(
self,
course: str,
available_hours: int = 0,
) -> bool:
pass

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
from typing import Any, TypedDict
from typing import Any

from pydantic import Json
from major.requirements import loader
from major.requirements.base import AbstractRequirement
from major.requirements.shared import MultiGroupElectiveRequirement
import utils
Expand Down Expand Up @@ -45,13 +46,9 @@ def __init__(

@classmethod
def from_json(cls, json: JSON) -> MultiGroupElectiveRequirement: # type: ignore[override]
from ..map import REQUIREMENTS_MAP

requirements: list[AbstractRequirement] = []
for requirement_data in json["requirements"]:
requirement = REQUIREMENTS_MAP[requirement_data["matcher"]].from_json(
requirement_data
)
requirement = loader.Loader().requirement_from_json(requirement_data)
requirements.append(requirement)

return cls(
Expand Down Expand Up @@ -87,7 +84,7 @@ def to_json(self) -> Json[Any]:
}
)

def attempt_fulfill(self, course: str) -> bool:
def attempt_fulfill(self, course: str, _: int = 0) -> bool:
fulfilled = super().attempt_fulfill(course)
if fulfilled:
if utils.get_level_from_course(course) == 4:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import json

from pydantic import Json
from major.requirements import AbstractRequirement, map
from major.requirements import AbstractRequirement

from functools import reduce
from typing import Any, TypedDict
from major.requirements.shared import OrRequirement

import utils
from major.requirements import loader

"""
Note: assuming BA 4V90 & BA 4090 cover one of the groups
Expand All @@ -21,7 +21,7 @@ class SomeRequirement(OrRequirement):
NOTE: Allows attempt_filled to work even if is_fulfilled() is true
"""

def attempt_fulfill(self, course: str) -> bool:
def attempt_fulfill(self, course: str, _: int = 0) -> bool:
for requirement in self.requirements:
if requirement.attempt_fulfill(course):
return True
Expand Down Expand Up @@ -66,7 +66,7 @@ def __init__(
self.metadata = metadata
self.override_filled = False

def attempt_fulfill(self, course: str) -> bool:
def attempt_fulfill(self, course: str, _: int = 0) -> bool:
if self.is_fulfilled():
return False

Expand Down Expand Up @@ -146,9 +146,7 @@ def from_json(cls, json: JSON) -> BusinessAdministrationElectiveRequirement:

requirements: list[AbstractRequirement] = []
for requirement_data in json["prefix_groups"]:
requirement = map.REQUIREMENTS_MAP[requirement_data["matcher"]].from_json(
requirement_data
)
requirement = loader.Loader().requirement_from_json(requirement_data)
requirements.append(requirement)

return cls(
Expand Down Expand Up @@ -182,4 +180,4 @@ def __str__(self) -> str:
_______________
Required fulfilled: {self.is_fulfilled()}
"""
return s
return s
Loading
Loading