Skip to content

Commit

Permalink
(Update v. 1.3.0) Add Courses Feature to the CSXL Application (#222)
Browse files Browse the repository at this point in the history
* Create Entities for Courses Feature (#183)

* Create Course Entity

This entity models course data in the CSXL Website.

* Complete the Term Entity

The Term Entity models terms / semesters in the database.

* Create Section Entity and Add Relationships

* Add Many-to-Many Relationships for User and Section

This commit adds many to many relationships to store students of course sections, as well as instructors for course sections.

* Clean Up Associations and Cascading

* Update section_entity.py

* Add Course Description Field to the Course Entity

* Remove Instructor Section Table

* Change Section Membership Type to Enum

* Modify Fields of Course, Section, and Term Entities

* Delete Section-User Relationship Fields

* Remove Course Entity Import from Entity __init__ File

* Rename Term shorthand Field to id

* Rename term_shorthand to term_id in Section Entity

* Rename User Section Enum to RosterRole

* Rename course_description to description in Course Entity

* Add Max String Lengths to Fields in Course Entity

* Create Models for Course Feature (#195)

* Create Term Model

* Add __init__ to Course Models Folder

* Add Course Model

* Add Section Model

* Add Detail Models with Relationships

* Add Term Conversion Functions

* Add all Conversion Functions

* Add SectionStaff Model

* Create Services for Course Feature (#196)

* Create the Term Service

* Fix Course Detail Import in Course Entity

* Fix Imports in Section Entity

* Fix Conventions in Term Service

* Fix Term Service Permissions

* Replace TermService.current() with TermService.get_by_date()

* Implement Course Service Functions

* Implement Section Service Functions

* Lay Foundations of Staff Querying

* Begin Writing Courses Tests, Create Fixtures

* Fix NUMEROUS Importing Headaches >_<

* Actually Fix Circular Imports with RosterRole

* Fix Permission Service Injection Issues

* Reach 100% Test Coverage for Term Service

* Update Term Test

* Reach 100% Test Coverage for Course Service

* Reach 100% Test Coverage for Section Service

* Add Staff Relationship Field to SectionEntity

* Fix Typing

* Implement Section Staff Relationships

* Rename SectionStaffUser to SectionMember

* Refactor Section Member Model Location

* Rename UserSectionEntity to SectionMemberEntity

* Move Section ID over User ID in Entity

* Rename member_type to member_role

* Create APIs for Course Feature (#213)

* Add Term APIs

* Add Course APIs

* Add Section APIs

* Remove Extraneous DateTime Imports

* Implement Course Catalog and Section Offerings Frontend (#217)

* Initialize Courses Module

* Add Course Service, Routing, and Resolvers

* Add Courses Home Component and Begin Offerings Component

* Initial Offerings Page

* Add Term Dropdown to Offerings Page

* Rename Components, Add Staff to Section Entity

* Move Room Entity out of Coworking; Establish 1-M Relationship Between Section and Room

* Add Lecture and Office Hours Room Relation to Course Sections

* Add Room Module to Courses

* Add Rooms to Offerings Frontend

* Fix Name of Section Room Table

* Fix Section Draft Status

* Rename OfferingsComponent to SectionOfferingsComponent

* Fix Section Offerings Import

* Implement Academics Home Page

* Update Title of Section Offerings Component

* Remove COMP From Component Card Titles

* Fix Comment

* Fix Comment in Room Assignment Enum

* Change Title of Academic Route in App Routing

* Add Credit Hours to Course Catalog Backend and Frontend

* Use Relative Import on Section Entity

* Refactor TimeRange to App Level on Frontend

* Refactor Courses into Academics on Backend and Frontend

* Add API Tags for the Academics Feature in FastAPI Docs

* Begin Writing Academics Feature Tech Spec Document

* Work on Frontend Section of Specs

* Complete Frontend Section of Specs

* Start Entity Design Section of Tech Specs

* Add Entity Table to Docs

* Add Academics Technical Specification Document (#221)

* Complete Pydantic Model Section

* Start API Section

* Upload API Images

* Add Permission Section

* Add Testing Section

* Add Future Considerations Section

* Add Table of Contents

* Implement Admin Features for the Academics Feature (#220)

* Add Preliminary Gear Icon

* Add showAdminSettingsNavigation to NavigationService

* Refactor to PermissionService, Fix Permission Naming

* Create Working Gear Prototype

* Implement Academics Admin Tabs

* Connect Gear to Academics Admin

* Implement Course Editor

* Fix Issues with Editing Credit Hours

* Refactor Delete APIs to Take IDs Instead of Objects

* Implement Term Editor

* Add Admin Course List

* Add Admin Term List

* Add Admin Section List

* Implement Reactive Updates with RxObjects in Course, Term, and Section Admin Lists

* Implement the Section Editor

* Create Admin Room Component

* Create Backend Room Service

* Implement the Room API

* Add Room Service Tests

* Add Admin Room List

* Implement the Room Editor

* Connect Section Editor UI to Room List

* Implement Room Selection in Section Editor

* Fix Delete API Method Not Allowed Error

* Add Documentation to Frontend Academics

* Fix Import in Reset Demo Script

* Remove Extraneous Comment from Room API

* Add Documentation for Lecture Room API Workaround

* Fix Room API Delete Description

* Make Section API Return Statement More Succinct

* Fix Section Create API

* Hide Term ID Field Upon Edit

* Tidying commit with minor changes

* Fix Failing Tests

* Refactor Roster Role to String Raw Value

* Refactor Term Get By Date Service Method

* Add Cancel Button to All Editors

* Move Admin Edit Functionality to Row

* Add Error Handling

* Move Admin Edit Functionality to Text and Remove from Row

* Minor changes for the academics feature (#224)

* Minor changes for the academics feature

* Revert enum

* Make staff more concise

* Add Room SQL To Entity

---------

Co-authored-by: Ajay Gandecha <[email protected]>

* Add migration for production

* Fix test warning about duplicate entities

* Cascade delete of SectionRoomEntity from SectionEntity

* Add Section Override Fields

* Clean Section Editor

* Add Override Section Title / Desc to Frontend

* Add Sorting to Course / Sections

* Fix Course Edit Code Issue

* Add New Columns to Migration Script

* Fix Issue with Creating New Sections without Any In Db

---------

Co-authored-by: Kris Jordan <[email protected]>
Co-authored-by: Kris Jordan <[email protected]>
  • Loading branch information
3 people authored Dec 31, 2023
1 parent b1ce118 commit 8358f7d
Show file tree
Hide file tree
Showing 137 changed files with 6,279 additions and 139 deletions.
Empty file.
99 changes: 99 additions & 0 deletions backend/api/academics/course.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Courses Course API
This API is used to access course data."""

from fastapi import APIRouter, Depends
from ..authentication import registered_user
from ...services.academics import CourseService
from ...models import User
from ...models.academics import Course, CourseDetails

__authors__ = ["Ajay Gandecha"]
__copyright__ = "Copyright 2023"
__license__ = "MIT"


api = APIRouter(prefix="/api/academics/course")
openapi_tags = {
"name": "Academics",
"description": "Academic and course information are managed via these endpoints.",
}


@api.get("", response_model=list[CourseDetails], tags=["Academics"])
def get_courses(course_service: CourseService = Depends()) -> list[CourseDetails]:
"""
Get all courses
Returns:
list[CourseDetails]: All `Course`s in the `Course` database table
"""
return course_service.all()


@api.get("/{id}", response_model=CourseDetails, tags=["Academics"])
def get_course_by_id(
id: str, course_service: CourseService = Depends()
) -> CourseDetails:
"""
Gets one course by its id
Returns:
CourseDetails: Course with the given ID
"""
return course_service.get_by_id(id)


@api.get("/{subject_code}/{number}", response_model=CourseDetails, tags=["Academics"])
def get_course_by_subject_code(
subject_code: str, number: str, course_service: CourseService = Depends()
) -> CourseDetails:
"""
Gets one course by its properties
Returns:
CourseDetails: Course with the given ID
"""
return course_service.get(subject_code, number)


@api.post("", response_model=CourseDetails, tags=["Academics"])
def new_course(
course: Course,
subject: User = Depends(registered_user),
course_service: CourseService = Depends(),
) -> CourseDetails:
"""
Adds a new course to the database
Returns:
CourseDetails: Course created
"""
return course_service.create(subject, course)


@api.put("", response_model=CourseDetails, tags=["Academics"])
def update_course(
course: Course,
subject: User = Depends(registered_user),
course_service: CourseService = Depends(),
) -> CourseDetails:
"""
Updates a course to the database
Returns:
CourseDetails: Course updated
"""
return course_service.update(subject, course)


@api.delete("/{course_id}", response_model=None, tags=["Academics"])
def delete_course(
course_id: str,
subject: User = Depends(registered_user),
course_service: CourseService = Depends(),
):
"""
Deletes a course from the database
"""
return course_service.delete(subject, course_id)
128 changes: 128 additions & 0 deletions backend/api/academics/section.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Section Course API
This API is used to access course data."""

from fastapi import APIRouter, Depends
from ..authentication import registered_user
from ...services.academics import SectionService
from ...models import User
from ...models.academics import Section, SectionDetails

__authors__ = ["Ajay Gandecha"]
__copyright__ = "Copyright 2023"
__license__ = "MIT"


api = APIRouter(prefix="/api/academics/section")


@api.get("", response_model=list[SectionDetails], tags=["Academics"])
def get_sections(section_service: SectionService = Depends()) -> list[SectionDetails]:
"""
Get all sections
Returns:
list[SectionDetails]: All `Section`s in the `Section` database table
"""
return section_service.all()


@api.get("/{id}", response_model=SectionDetails, tags=["Academics"])
def get_section_by_id(
id: int, section_service: SectionService = Depends()
) -> SectionDetails:
"""
Gets one section by its id
Returns:
SectionDetails: Section with the given ID
"""
return section_service.get_by_id(id)


@api.get("/term/{term_id}", response_model=list[SectionDetails], tags=["Academics"])
def get_section_by_term_id(
term_id: str, section_service: SectionService = Depends()
) -> list[SectionDetails]:
"""
Gets list of sections by term ID
Returns:
list[SectionDetails]: Sections with the given term
"""
return section_service.get_by_term(term_id)


@api.get("/subject/{subject}", response_model=list[SectionDetails], tags=["Academics"])
def get_section_by_subject(
subject: str, section_service: SectionService = Depends()
) -> list[SectionDetails]:
"""
Gets a list of sections by a subject
Returns:
list[SectionDetails]: Sections with the given section
"""
return section_service.get_by_subject(subject)


@api.get(
"/{subject_code}/{course_number}/{section_number}",
response_model=SectionDetails,
tags=["Academics"],
)
def get_section_by_subject_code(
subject_code: str,
course_number: str,
section_number: str,
section_service: SectionService = Depends(),
) -> SectionDetails:
"""
Gets one section by its properties
Returns:
SectionDetails: Course with the given properties
"""
return section_service.get(subject_code, course_number, section_number)


@api.post("", response_model=SectionDetails, tags=["Academics"])
def new_section(
section: Section,
subject: User = Depends(registered_user),
section_service: SectionService = Depends(),
) -> SectionDetails:
"""
Adds a new section to the database
Returns:
SectionDetails: Section created
"""
return section_service.create(subject, section)


@api.put("", response_model=SectionDetails, tags=["Academics"])
def update_section(
section: Section,
subject: User = Depends(registered_user),
section_service: SectionService = Depends(),
) -> SectionDetails:
"""
Updates a section to the database
Returns:
SectionDetails: Section updated
"""
return section_service.update(subject, section)


@api.delete("/{section_id}", response_model=None, tags=["Academics"])
def delete_section(
section_id: int,
subject: User = Depends(registered_user),
section_service: SectionService = Depends(),
):
"""
Deletes a section from the database
"""
return section_service.delete(subject, section_id)
92 changes: 92 additions & 0 deletions backend/api/academics/term.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Courses Term API
This API is used to access term data."""

from fastapi import APIRouter, Depends
from ..authentication import registered_user
from ...services.academics import TermService
from ...models import User
from ...models.academics import Term, TermDetails
from datetime import datetime

__authors__ = ["Ajay Gandecha"]
__copyright__ = "Copyright 2023"
__license__ = "MIT"


api = APIRouter(prefix="/api/academics/term")


@api.get("", response_model=list[TermDetails], tags=["Academics"])
def get_terms(term_service: TermService = Depends()) -> list[TermDetails]:
"""
Get all terms
Returns:
list[TermDetails]: All `Term`s in the `Term` database table
"""
return term_service.all()


@api.get("/current", response_model=TermDetails, tags=["Academics"])
def get_current_term(term_service: TermService = Depends()) -> TermDetails:
"""
Gets the current term based on the current date
Returns:
TermDetails: Currently active term
"""
return term_service.get_by_date(datetime.today())


@api.get("/{id}", response_model=TermDetails, tags=["Academics"])
def get_term_by_id(id: str, term_service: TermService = Depends()) -> TermDetails:
"""
Gets one term by its id
Returns:
TermDetails: Term with the given ID
"""
return term_service.get_by_id(id)


@api.post("", response_model=TermDetails, tags=["Academics"])
def new_term(
term: Term,
subject: User = Depends(registered_user),
term_service: TermService = Depends(),
) -> TermDetails:
"""
Adds a new term to the database
Returns:
TermDetails: Term created
"""
return term_service.create(subject, term)


@api.put("", response_model=TermDetails, tags=["Academics"])
def update_term(
term: Term,
subject: User = Depends(registered_user),
term_service: TermService = Depends(),
) -> TermDetails:
"""
Updates a term to the database
Returns:
TermDetails: Term updated
"""
return term_service.update(subject, term)


@api.delete("/{term_id}", response_model=None, tags=["Academics"])
def delete_term(
term_id: str,
subject: User = Depends(registered_user),
term_service: TermService = Depends(),
):
"""
Deletes a term from the database
"""
return term_service.delete(subject, term_id)
Loading

0 comments on commit 8358f7d

Please sign in to comment.