Skip to content

Commit

Permalink
Merge branch 'master' into aug/search-panel-dnd
Browse files Browse the repository at this point in the history
  • Loading branch information
AaDalal authored Apr 9, 2024
2 parents 1ceceb6 + a379c99 commit bf2aaec
Show file tree
Hide file tree
Showing 39 changed files with 208 additions and 106 deletions.
18 changes: 9 additions & 9 deletions backend/degree/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,25 @@


# Register your models here.
@admin.register(Rule)
class RuleAdmin(admin.ModelAdmin):
search_fields = ["title", "id"]
list_display = ["title", "id", "parent"]
list_select_related = ["parent"]


admin.site.register(DegreePlan)
admin.site.register(SatisfactionStatus)


@admin.register(PDPBetaUser)
class PDPBetaUserAdmin(admin.ModelAdmin):
search_fields = ("person__username", "person__id")
autocomplete_fields = ("person",)


@admin.register(Fulfillment)
class FulfillmentAdmin(admin.ModelAdmin):
autocomplete_fields = ["rules"]


@admin.register(DoubleCountRestriction)
class DoubleCountRestrictionAdmin(admin.ModelAdmin):
autocomplete_fields = ["rule", "other_rule"]


@admin.register(Degree)
class DegreeAdmin(admin.ModelAdmin):
autocomplete_fields = ["rules"]
list_display = ["program", "degree", "major", "concentration", "year", "view_degree_editor"]
Expand All @@ -69,3 +60,12 @@ 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(Rule, RuleAdmin)
admin.site.register(DegreePlan)
admin.site.register(SatisfactionStatus)
admin.site.register(PDPBetaUser, PDPBetaUserAdmin)
admin.site.register(Fulfillment, FulfillmentAdmin)
admin.site.register(DoubleCountRestriction, DoubleCountRestrictionAdmin)
admin.site.register(Degree, DegreeAdmin)
8 changes: 2 additions & 6 deletions backend/degree/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,6 @@ class Meta:
model = Rule
fields = "__all__"

def to_representation(self, instance):
data = super(RuleSerializer, self).to_representation(instance)
data.q = ""
return data


# Allow recursive serialization of rules
RuleSerializer._declared_fields["rules"] = RuleSerializer(
Expand Down Expand Up @@ -189,7 +184,8 @@ class Meta:

class DockedCourseSerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField(help_text="The id of the docked course")
person = serializers.HiddenField(default=serializers.CurrentUserDefault())

class Meta:
model = DockedCourse
fields = ["full_code", "id"]
fields = ["full_code", "id", "person"]
2 changes: 0 additions & 2 deletions backend/degree/static/pdp/degree-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,10 @@ const LayoutFlow = () => {
const [edges, setEdges, onEdgesChange] = useEdgesState([]);

const onConnect = useCallback((params) => {
console.log("onConnect", params);
if (params.source === params.target) return;
const [sourceIsDegree, sourceId] = pkOfNodeId(params.source);
const [targetIsDegree, targetId] = pkOfNodeId(params.target);
if (sourceIsDegree || targetIsDegree) return;
console.log("HERE");
const redirect = `/admin/degree/doublecountrestriction/add/?rule=${sourceId}&other_rule=${targetId}`;
window.location.href = redirect;
}, []);
Expand Down
31 changes: 16 additions & 15 deletions backend/degree/utils/parse_degreeworks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,30 @@ def parse_coursearray(courseArray) -> Q:
case ("@", "@", end) | ("PSEUDO@", "@", end):
assert end is None
logging.info("ignoring @ course")
pass
case discipline, "@", end:
assert end is None
course_q &= Q(department__code=discipline)
case "@", number, None:
assert "@" not in number
course_q &= Q(code=number)
case discipline, number, None:
if number.isdigit():
if "@" not in number:
course_q &= Q(full_code=f"{discipline}-{number}")
elif number[:-1].isdigit() and number[-1] == "@":
course_q &= Q(full_code__startswith=f"{discipline}-{number[:-1]}")
else:
logging.warn(f"Non-integer course number: {number}")
case "@", number, end:
assert "@" not in number and "@" not in end
course_q &= Q(
code__gte=number.strip(),
code__lte=end.strip(),
)
case discipline, number, end:
if number.isdigit() and end.isdigit():
course_q &= Q(
department__code=discipline,
code__gte=number.strip(),
code__lte=end.strip(),
)
else:
logging.warn(
f"Non-integer course number or numberEnd: "
f"(number) {number} (numberEnd) {end}"
)
assert "@" not in number and "@" not in end
course_q &= Q(
department__code=discipline,
code__gte=number.strip(),
code__lte=end.strip(),
)

connector = "AND" # the connector to the next element; and by default
if "withArray" in course:
Expand Down
1 change: 0 additions & 1 deletion backend/degree/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ def create(self, request, *args, **kwargs):
if request.data.get("full_code") is None:
raise ValidationError({"full_code": "This field is required."})
self.kwargs["full_code"] = request.data["full_code"]
self.kwargs["person"] = self.request.user
try:
return self.partial_update(request, *args, **kwargs)
except Http404:
Expand Down
9 changes: 4 additions & 5 deletions backend/tests/degree/test_degreeworks_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ def test_course_range(self):
{"discipline": "BIBB", "number": "2000", "numberEnd": "2999"},
]
expected = Q(department__code="BIBB", code__gte="2000", code__lte="2999")
print("****TS")
print(expected, parse_degreeworks.parse_coursearray(course_array))
self.assertEqual(expected, parse_degreeworks.parse_coursearray(course_array))

Expand All @@ -223,16 +222,16 @@ def test_empty_course(self):

def test_non_int_course(self):
course_array = [
{"discipline": "CIS", "number": "not-a-number"},
{"discipline": "CIS", "number": "4999A"},
]
expected = Q()
expected = Q(full_code="CIS-4999A")
self.assertEqual(expected, parse_degreeworks.parse_coursearray(course_array))

def test_non_int_course_range(self):
course_array = [
{"discipline": "CIS", "number": "not-a-number", "numberEnd": "also-not-a-number"},
{"discipline": "CIS", "number": "4999A", "numberEnd": "4999B"},
]
expected = Q()
expected = Q(department__code="CIS", code__gte="4999A", code__lte="4999B")
self.assertEqual(expected, parse_degreeworks.parse_coursearray(course_array))


Expand Down
4 changes: 2 additions & 2 deletions frontend/degree-plan/components/Course/Course.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ReviewPanelTrigger } from "../Infobox/ReviewPanel";
import { Draggable } from "../common/DnD";
import Skeleton from "react-loading-skeleton"
import 'react-loading-skeleton/dist/skeleton.css'
import { TRANSFER_CREDIT_SEMESTER_KEY } from "@/constants";

const COURSE_BORDER_RADIUS = "9px";

Expand Down Expand Up @@ -103,10 +104,9 @@ const IconBadge = styled.div`
`


const SemesterIcon = ({semester}:{semester: string | null}) => {
if (!semester) return <div></div>;
const year = semester.substring(2,4);
const year = semester === TRANSFER_CREDIT_SEMESTER_KEY ? "AP" : semester.substring(2,4);
const sem = semester.substring(4);

return (
Expand Down
37 changes: 37 additions & 0 deletions frontend/degree-plan/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import styled from "@emotion/styled";

const Wrapper = styled.div`
color: #999999;
font-size: 0.8rem;
text-align: center;
bottom: 15px;
width: 100%;
padding-bottom: 1rem;
line-height: 1.5;
`;

const Link = styled.a`
color: rgb(50, 115, 220);
`

const Footer = () => (
<Wrapper>
Made with{" "}
<span className="icon is-small">
<i className="fa fa-heart" style={{ color: "red" }} />
</span>{" "}
by{" "}
<Link href="http://pennlabs.org" rel="noopener noreferrer" target="_blank">
Penn Labs
</Link>
.
Have feedback about Penn Degree Plan? Let us know: {" "}
<Link href="mailto:[email protected]">[email protected]</Link>
{
// TODO: uncomment once out of beta
// <Link href="https://airtable.com/appFRa4NQvNMEbWsA/shrzXeuiEFF8OD89P">here!</Link>
}
</Wrapper>
);

export default Footer;
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const CoursesPlanned = ({fulfillments, removeCourse, className, semester, isLoad
{fulfillments.map(fulfillment =>
<CourseInPlan key={fulfillment.full_code} semester={semester} course={fulfillment} removeCourse={removeCourse} isDisabled={false}/>
)}
{/* <PlannedCourseContainer $isDepressed={true}/> */}
</PlannedCoursesContainer>
)
}
Expand Down
20 changes: 10 additions & 10 deletions frontend/degree-plan/components/FourYearPlan/DegreeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ const DegreeAddInterior = styled.div`
padding: 1.2rem 2rem;
`;

/** Create label for major listings */
export const createMajorLabel = (degree: DegreeListing) => {
const concentration =
degree.concentration && degree.concentration !== "NONE"
? ` - ${degree.concentration_name}`
: "";
return `${degree.major_name}${concentration} (${degree.year})`;
};

interface RemoveDegreeProps {
degreeplanId: number;
degreeId: number;
Expand Down Expand Up @@ -169,15 +178,6 @@ const ModalInterior = ({
const { data: degrees, isLoading: isLoadingDegrees } =
useSWR<DegreeListing[]>(`/api/degree/degrees`);

/** Create label for major listings */
const createMajorLabel = (degree: DegreeListing) => {
const concentration =
degree.concentration && degree.concentration !== "NONE"
? ` - ${degree.concentration_name}`
: "";
return `${degree.major_name}${concentration}`;
};

const getMajorOptions = React.useCallback(() => {
/** Filter major options based on selected schools/degrees */
const majorOptions =
Expand Down Expand Up @@ -301,7 +301,7 @@ const ModalInterior = ({
placeholder={
isLoadingDegrees
? "loading programs..."
: "Major - Concentration"
: "Major - Concentration (Starting Year)"
}
isLoading={isLoadingDegrees}
/>
Expand Down
6 changes: 2 additions & 4 deletions frontend/degree-plan/components/FourYearPlan/PanelCommon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Icon } from "../common/bulma_derived_components";
export const PanelTopBarIcon = styled(Icon)`
width: 1rem;
height: 1rem;
color: #FFFFFF;
flex-shrink: 0;
`;

Expand All @@ -28,10 +27,8 @@ export const PanelTopBarButton = styled.button`
font-size: 1rem;
font-weight: 500;
background: var(--primary-color-xx-dark);
border-radius: 5px;
color: #FFFFFF;
color: var(--primary-color-xxx-dark);
`;
export const DarkBlueBackgroundSkeleton: React.FC<{ width?: string; }> = (props) => (
<Skeleton
Expand All @@ -44,6 +41,7 @@ export const PanelHeader = styled.div`
justify-content: space-between;
align-items: center;
background-color: var(--primary-color);
color: var(--primary-color-ultra-dark);
padding: 0.5rem 1rem;
flex-grow: 0;
font-weight: 300;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const SatisfiedCheck = () => (
<i className="fas fa-check-circle" style={{color: '#5EA872'}} />
)
export default SatisfiedCheck;
25 changes: 20 additions & 5 deletions frontend/degree-plan/components/FourYearPlan/Semester.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,24 @@ import Skeleton from "react-loading-skeleton"
import 'react-loading-skeleton/dist/skeleton.css'
import { mutate } from "swr";
import { ModalKey } from "./DegreeModal";
import { TRANSFER_CREDIT_SEMESTER_KEY } from "@/constants";

const SEMESTER_REGEX = /\d{4}[ABC]/

const translateSemester = (semester: Course["semester"]) => {
if (semester === TRANSFER_CREDIT_SEMESTER_KEY) return "AP & Transfer Credit";
const year = semester.slice(0, 4);
const term = semester.slice(4);
return `${term === "A" ? "Spring" : term === "B" ? "Summer" : "Fall"} ${year}`
}

export const SemesterCard = styled.div<{$isDroppable:boolean, $isOver: boolean}>`
background: #FFFFFF;
export const SemesterCard = styled.div<{
$isDroppable:boolean,
$isOver: boolean,
$semesterComparison: number // -1 if currentSemester is less than this semester...
}>`
background: ${props => props.$semesterComparison < 0 ? "#F8F8F8" : props.$semesterComparison === 0 ? "var(--primary-color)" : "#FFFFFF"};
color: ${props => props.$semesterComparison === 0 ? "var(--primary-color-ultra-dark)" : "inherit"};
box-shadow: 0px 0px 4px 2px ${props => props.$isOver ? 'var(--selected-color);' : props.$isDroppable ? 'var(--primary-color-dark);' : 'rgba(0, 0, 0, 0.05);'}
border-radius: 10px;
border-width: 0px;
Expand All @@ -38,7 +46,7 @@ const SemesterHeader = styled.div`
`

const SemesterLabel = styled.div`
font-weight: 500;
font-weight: 600;
`;

const SemesterContent = styled.div`
Expand Down Expand Up @@ -76,7 +84,7 @@ export const SkeletonSemester = ({
showStats,
} : { showStats: boolean }) => {
return (
<SemesterCard $isDroppable={false} $isOver={false}>
<SemesterCard $isDroppable={false} $isOver={false} $semesterComparison={1}>
<SemesterHeader>
<SemesterLabel>
<Skeleton width="5em" />
Expand All @@ -103,6 +111,7 @@ interface SemesterProps {
setModalKey: (arg0: ModalKey) => void;
setModalObject: (obj: any) => void;
removeSemester: (sem: string) => void;
currentSemester?: Course["semester"];
isLoading?: boolean
}

Expand All @@ -115,6 +124,7 @@ const FlexSemester = ({
setModalKey,
setModalObject,
removeSemester,
currentSemester,
isLoading = false
} : SemesterProps) => {
const credits = fulfillments.reduce((acc, curr) => acc + (curr.course?.credits || 1), 0)
Expand Down Expand Up @@ -166,7 +176,12 @@ const FlexSemester = ({
}

return (
<SemesterCard $isDroppable={canDrop} $isOver={isOver} ref={drop}>
<SemesterCard
$isDroppable={canDrop}
$isOver={isOver}
ref={drop}
$semesterComparison={currentSemester ? semester.localeCompare(currentSemester) : 1}
>
<SemesterHeader>
<SemesterLabel>
{translateSemester(semester)}
Expand Down
Loading

0 comments on commit bf2aaec

Please sign in to comment.