From 3304b6453b3a05d694aabd7b8e6082c95c5efbcc Mon Sep 17 00:00:00 2001 From: martig7 Date: Fri, 2 Feb 2024 16:51:56 -0500 Subject: [PATCH 01/18] Started post request script file. --- rpi_data/modules/post_test.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 rpi_data/modules/post_test.py diff --git a/rpi_data/modules/post_test.py b/rpi_data/modules/post_test.py new file mode 100644 index 000000000..96191a494 --- /dev/null +++ b/rpi_data/modules/post_test.py @@ -0,0 +1,20 @@ +import requests +import os + +url = os.environ.get('yacs_url') +url = "http://localhost:5000" +api_location = url + "/api/bulkCourseUpload" +__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + +def csvUpload(fileName): + headers = {'Content-Type': 'text/csv'} + endpath = os.path.join(__location__, fileName) + endpath = os.path.dirname(os.path.dirname(endpath)) + "\\" + fileName + print(endpath) + data = {'name': "file", 'filename': fileName} + file = {'filename': open(endpath, 'rb')} + r = requests.post(api_location, headers=headers, data=data, files=file) + print(r.reason, r.status_code) + +if __name__ == "__main__": + csvUpload("spring-2021.csv") \ No newline at end of file From fff79976f2f6fc4dff5ae1521fdc34960e35fd2c Mon Sep 17 00:00:00 2001 From: Tevetron Date: Tue, 6 Feb 2024 16:56:08 -0500 Subject: [PATCH 02/18] Update .gitignore --- src/web/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web/.gitignore b/src/web/.gitignore index b047fbdd1..d0c0395d8 100644 --- a/src/web/.gitignore +++ b/src/web/.gitignore @@ -2,7 +2,8 @@ node_modules /dist docs - +/.venv +.venv # local env files .env.local .env.*.local From 57cb6afbfab9dabca2d35d568d44485e3c7fe590 Mon Sep 17 00:00:00 2001 From: Tevetron Date: Tue, 6 Feb 2024 16:58:52 -0500 Subject: [PATCH 03/18] Update .gitignore --- src/web/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web/.gitignore b/src/web/.gitignore index d0c0395d8..238dccda9 100644 --- a/src/web/.gitignore +++ b/src/web/.gitignore @@ -2,8 +2,9 @@ node_modules /dist docs -/.venv .venv +.venv/ +/.venv/ # local env files .env.local .env.*.local From 3ef70b882f4a30d36e26e9a2929f2508d70656cb Mon Sep 17 00:00:00 2001 From: Tevetron Date: Tue, 6 Feb 2024 16:59:16 -0500 Subject: [PATCH 04/18] Update post_test.py --- rpi_data/modules/post_test.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/rpi_data/modules/post_test.py b/rpi_data/modules/post_test.py index 96191a494..beef8f2d6 100644 --- a/rpi_data/modules/post_test.py +++ b/rpi_data/modules/post_test.py @@ -1,20 +1,25 @@ +from fastapi import FastAPI, UploadFile, Form, File import requests +import base64 import os url = os.environ.get('yacs_url') url = "http://localhost:5000" api_location = url + "/api/bulkCourseUpload" -__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) +__location__ = os.path.realpath(os.path.join( + os.getcwd(), os.path.dirname(__file__))) + def csvUpload(fileName): - headers = {'Content-Type': 'text/csv'} endpath = os.path.join(__location__, fileName) endpath = os.path.dirname(os.path.dirname(endpath)) + "\\" + fileName print(endpath) - data = {'name': "file", 'filename': fileName} - file = {'filename': open(endpath, 'rb')} - r = requests.post(api_location, headers=headers, data=data, files=file) + uploadFile = {'file': open(endpath, 'rb')} + data = {'isPubliclyVisible': 'on'} + + r = requests.post(api_location, files=uploadFile, data=data) print(r.reason, r.status_code) + if __name__ == "__main__": - csvUpload("spring-2021.csv") \ No newline at end of file + csvUpload("spring-2022.csv") From 959950ae24720df29464046ff9194e4ab307c14d Mon Sep 17 00:00:00 2001 From: Tevetron Date: Tue, 6 Feb 2024 17:06:36 -0500 Subject: [PATCH 05/18] Update .gitignore --- src/web/.gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/.gitignore b/src/web/.gitignore index 238dccda9..18610ef23 100644 --- a/src/web/.gitignore +++ b/src/web/.gitignore @@ -2,9 +2,9 @@ node_modules /dist docs -.venv + .venv/ -/.venv/ + # local env files .env.local .env.*.local From d27280b1991f52c4942a06fe9195e6c6f1ec4832 Mon Sep 17 00:00:00 2001 From: Tevetron Date: Tue, 6 Feb 2024 17:16:35 -0500 Subject: [PATCH 06/18] gitignore --- .gitignore | 1 + src/web/.gitignore | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6b2baa272..744f0fde0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ courses20.xml compose-dev.yaml rpi_data/get-summer-2023-2.sh rpi_data/summer-20232.csv +.venv/ \ No newline at end of file diff --git a/src/web/.gitignore b/src/web/.gitignore index 18610ef23..b047fbdd1 100644 --- a/src/web/.gitignore +++ b/src/web/.gitignore @@ -3,8 +3,6 @@ node_modules /dist docs -.venv/ - # local env files .env.local .env.*.local From 45b392927c1ea27b2f7861c91aa61c91115fb6cf Mon Sep 17 00:00:00 2001 From: Tevetron Date: Tue, 6 Feb 2024 17:20:24 -0500 Subject: [PATCH 07/18] Update post_test.py --- rpi_data/modules/post_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rpi_data/modules/post_test.py b/rpi_data/modules/post_test.py index beef8f2d6..2c1314ce3 100644 --- a/rpi_data/modules/post_test.py +++ b/rpi_data/modules/post_test.py @@ -1,4 +1,3 @@ -from fastapi import FastAPI, UploadFile, Form, File import requests import base64 import os From 2c159c1191a26ed5a28b0e1908b6d1dbb33c3932 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Tue, 24 Sep 2024 17:27:51 -0400 Subject: [PATCH 08/18] fix semester deletion --- src/api/app.py | 7 ++++++- src/api/db/semester_info.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/api/app.py b/src/api/app.py index 31cf7fe4a..cae6f2c35 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -46,7 +46,7 @@ date_range_map = DateMapping.semester_date_mapping(db_conn) admin_info = AdminInfo.Admin(db_conn) course_select = CourseSelect.student_course_selection(db_conn) -semester_info = SemesterInfo.semester_info(db_conn) +semester_info = SemesterInfo.semester_info(db_conn, FastAPICache) professor_info = All_professors.Professor(db_conn, FastAPICache) users = UserModel.User() @@ -213,6 +213,11 @@ async def uploadJSON( print(error) return Response(error.__str__(), status_code=500) +@app.delete('/api/semester/{semester_id}') +async def remove_semester(semester_id: str): + print(semester_id) + semester, error = semester_info.delete_semester(semester=semester_id) + return Response(status_code=200) if not error else Response(str(error), status_code=500) @app.post('/api/mapDateRangeToSemesterPart') async def map_date_range_to_semester_part_handler(request: Request): diff --git a/src/api/db/semester_info.py b/src/api/db/semester_info.py index 5d4222ee7..b01f90da8 100644 --- a/src/api/db/semester_info.py +++ b/src/api/db/semester_info.py @@ -1,7 +1,20 @@ -class semester_info: +import asyncio - def __init__(self, db_wrapper): +class semester_info: + def __init__(self, db_wrapper, cache): self.db = db_wrapper + self.cache = cache + + def clear_cache(self): + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = None + + if loop and loop.is_running(): + loop.create_task(self.cache.clear(namespace="API_CACHE")) + else: + asyncio.run(self.cache.clear("API_CACHE")) def upsert(self, semester, isPublic): self.db.execute(""" @@ -28,3 +41,15 @@ def is_public(self, semester): if data is not None and len(data) > 0: return data[0]['public'] return False + + def delete_semester(self, semester): + # clear cache so this semester does not come up again + self.clear_cache() + return self.db.execute(""" + BEGIN TRANSACTION; + DELETE FROM semester_info + WHERE semester=%(Semester)s; + COMMIT; + """, { + "Semester": semester + }, isSELECT=False) \ No newline at end of file From f4ce837950c3d350c0d63d101ab3128c004f1f63 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 27 Sep 2024 16:22:32 -0400 Subject: [PATCH 09/18] link tables --- src/api/app.py | 17 +++++++---------- src/api/db/admin.py | 2 +- src/api/tables/course.py | 8 ++++++-- src/api/tables/semester_info.py | 3 +++ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/api/app.py b/src/api/app.py index cae6f2c35..4007292ff 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -27,11 +27,10 @@ import pandas as pd from constants import Constants -""" -NOTE: on caching -on add of semester of change of data from GET -do a cache.clear() to ensure data integrity -""" + +# NOTE: on caching +# on add of semester of change of data from GET +# do a cache.clear() to ensure data integrity app = FastAPI() app.add_middleware(SessionMiddleware, @@ -176,9 +175,8 @@ async def uploadHandler( isSuccess, error = courses.populate_from_csv(csv_file) if (isSuccess): return Response(status_code=200) - else: - print(error) - return Response(error.__str__(), status_code=500) + print(error) + return Response(error.__str__(), status_code=500) @app.post('/api/bulkProfessorUpload') async def uploadJSON( @@ -239,8 +237,7 @@ async def map_date_range_to_semester_part_handler(request: Request): semester_info.upsert(semester_title, is_publicly_visible) if (not error): return Response(status_code=200) - else: - return Response(error, status_code=500) + return Response(error, status_code=500) return Response("Did not receive proper form data", status_code=500) @app.get('/api/user/course') diff --git a/src/api/db/admin.py b/src/api/db/admin.py index 32d8cd988..f95c8d0f5 100644 --- a/src/api/db/admin.py +++ b/src/api/db/admin.py @@ -5,7 +5,7 @@ def __init__(self, db_conn): self.interface_name = 'admin_info' def get_semester_default(self): - # NOTE: COALESCE takes first non-null vaue from the list + # NOTE: COALESCE takes first non-null value from the list result, error = self.db_conn.execute(""" SELECT admin.semester FROM admin_settings admin UNION ALL diff --git a/src/api/tables/course.py b/src/api/tables/course.py index a249dfbaa..60c235e57 100644 --- a/src/api/tables/course.py +++ b/src/api/tables/course.py @@ -1,4 +1,5 @@ -from sqlalchemy import Column +from sqlalchemy import Column, ForeignKey +from sqlalchemy.orm import relationship, backref from sqlalchemy.dialects.postgresql import TEXT, INTEGER, VARCHAR, DATE, TSVECTOR from .database import Base @@ -8,7 +9,10 @@ class Course(Base): crn = Column(VARCHAR(length=255), primary_key=True) section = Column(VARCHAR(length=255)) - semester = Column(VARCHAR(length=255)) + + semester = Column(VARCHAR(length=255), ForeignKey("semester_info.semester", ondelete="CASCADE")) + professor = Column(VARCHAR(length=255)) + min_credits = Column(INTEGER) max_credits = Column(INTEGER) date_start = Column(DATE) diff --git a/src/api/tables/semester_info.py b/src/api/tables/semester_info.py index 74d49678a..5f647b2f9 100644 --- a/src/api/tables/semester_info.py +++ b/src/api/tables/semester_info.py @@ -1,4 +1,5 @@ from sqlalchemy import Column, PrimaryKeyConstraint +from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import VARCHAR, BOOLEAN from .database import Base @@ -8,3 +9,5 @@ class SemesterInfo(Base): semester = Column(VARCHAR(length=255), primary_key=True) public = Column(BOOLEAN) + + courses = relationship("Course", backref="semester_info", passive_deletes=True) \ No newline at end of file From b42176a631c928649eabd196e831f4b4a5f03a90 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 27 Sep 2024 16:25:50 -0400 Subject: [PATCH 10/18] make codefactor less upset --- src/api/app.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/api/app.py b/src/api/app.py index 4007292ff..d951e1950 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -27,7 +27,6 @@ import pandas as pd from constants import Constants - # NOTE: on caching # on add of semester of change of data from GET # do a cache.clear() to ensure data integrity @@ -142,9 +141,8 @@ def set_defaultSemester(semester_set: DefaultSemesterSetPydantic): success, error = admin_info.set_semester_default(semester_set.default) if success: return Response(status_code=200) - else: - print(error) - return Response(error.__str__(), status_code=500) + print(error) + return Response(error.__str__(), status_code=500) #Parses the data from the .csv data files @app.post('/api/bulkCourseUpload') @@ -206,10 +204,9 @@ async def uploadJSON( if isSuccess: print("SUCCESS") return Response(status_code=200) - else: - print("NOT WORKING") - print(error) - return Response(error.__str__(), status_code=500) + print("NOT WORKING") + print(error) + return Response(error.__str__(), status_code=500) @app.delete('/api/semester/{semester_id}') async def remove_semester(semester_id: str): From 34fb1963cb073bbdc64f3f4050f4d5cf2b1d9216 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 27 Sep 2024 16:27:00 -0400 Subject: [PATCH 11/18] codefactor^2 --- src/api/db/admin.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/db/admin.py b/src/api/db/admin.py index f95c8d0f5..1dc1c6b64 100644 --- a/src/api/db/admin.py +++ b/src/api/db/admin.py @@ -21,8 +21,7 @@ def get_semester_default(self): if error: return (None, error) - else: - return (default_semester, error) + return (default_semester, error) def set_semester_default(self, semester): try: @@ -40,5 +39,4 @@ def set_semester_default(self, semester): if response != None: return(True, None) - else: - return (False, error) + return (False, error) From 0fadc4e3f38de3568802659da872c357e5eee38e Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 11 Oct 2024 17:06:49 -0400 Subject: [PATCH 12/18] initial commit --- docker-compose.development.yml | 6 +- rpi_data/out.csv | 431 +++++++++++++++++++++++++++++++++ src/api/app.py | 24 +- src/api/db/finals.py | 240 ++++++++++++++++++ src/api/tables/final.py | 21 ++ 5 files changed, 716 insertions(+), 6 deletions(-) create mode 100644 rpi_data/out.csv create mode 100644 src/api/db/finals.py create mode 100644 src/api/tables/final.py diff --git a/docker-compose.development.yml b/docker-compose.development.yml index 038529a8b..083bae28e 100644 --- a/docker-compose.development.yml +++ b/docker-compose.development.yml @@ -28,12 +28,12 @@ services: - ./src/web:/app - web_node_modules:/app/node_modules/ environment: - - YACS_API_HOST=http://yacs_api:5000 + - YACS_API_HOST=http://yacs_api:4000 yacs_api: - command: /bin/bash -c "python tables/database_session.py && PYTHONPATH=. alembic upgrade head && uvicorn app:app --reload --host 0.0.0.0 --port 5000" + command: /bin/bash -c "python tables/database_session.py && PYTHONPATH=. alembic upgrade head && uvicorn app:app --reload --host 0.0.0.0 --port 4000" ports: - - 5000:5000 + - 4000:4000 volumes: - ./src/api:/usr/src environment: diff --git a/rpi_data/out.csv b/rpi_data/out.csv new file mode 100644 index 000000000..9eece6687 --- /dev/null +++ b/rpi_data/out.csv @@ -0,0 +1,431 @@ +,Season,Year,Major,Course,Section,Start,End,Building,Room_Number +0,FALL,2024,ARCH,2150,1,2024-12-16 11:30:00,2024-12-16 14:30:00,ONLINE,NA +1,FALL,2024,ARCH,2160,1,2024-12-17 08:00:00,2024-12-17 11:00:00,CARNEGIE,113 +2,FALL,2024,ARCH,2160,2,2024-12-17 08:00:00,2024-12-17 11:00:00,CARNEGIE,113 +3,FALL,2024,ARCH,2330,1,2024-12-16 08:00:00,2024-12-16 11:00:00,ACADEMY,AUD +4,FALL,2024,ARCH,2330,3,2024-12-16 08:00:00,2024-12-16 11:00:00,ACADEMY,AUD +5,FALL,2024,ARCH,2350,1,2024-12-17 08:00:00,2024-12-17 11:00:00,SAGE,3510 +6,FALL,2024,ARCH,2350,2,2024-12-17 08:00:00,2024-12-17 11:00:00,SAGE,3510 +7,FALL,2024,ARCH,2350,3,2024-12-17 08:00:00,2024-12-17 11:00:00,SAGE,3510 +8,FALL,2024,ARCH,2350,4,2024-12-17 08:00:00,2024-12-17 11:00:00,SAGE,3510 +9,FALL,2024,ARCH,2510,1,2024-12-16 08:00:00,2024-12-16 11:00:00,LOW,4050 +10,FALL,2024,ARCH,2510,3,2024-12-16 08:00:00,2024-12-16 11:00:00,LOW,4050 +11,FALL,2024,ARCH,4050,1,2024-12-18 08:00:00,2024-12-18 11:00:00,SAGE,3510 +12,FALL,2024,ARCH,4050,2,2024-12-18 08:00:00,2024-12-18 11:00:00,SAGE,3510 +13,FALL,2024,ARCH,4050,3,2024-12-18 08:00:00,2024-12-18 11:00:00,SAGE,3510 +14,FALL,2024,ARCH,4065,1,2024-12-17 18:30:00,2024-12-17 21:30:00,LOW,4034 +15,FALL,2024,ARCH,4170,80,2024-12-16 18:30:00,2024-12-16 21:30:00,LOW,3112 +16,FALL,2024,ARCH,4320,1,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,4101 +17,FALL,2024,ARCH,4320,2,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,4101 +18,FALL,2024,ARCH,4320,3,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,4101 +19,FALL,2024,ARCH,4320,4,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,4101 +20,FALL,2024,ARCH,4330,1,2024-12-17 08:00:00,2024-12-17 11:00:00,ACADEMY,AUD +21,FALL,2024,ARCH,4580,80,2024-12-18 11:30:00,2024-12-18 14:30:00,LOW,3112 +22,FALL,2024,ARCH,4770,5,2024-12-18 18:30:00,2024-12-18 21:30:00,ONLINE,NA +23,FALL,2024,ARCH,4780,5,2024-12-18 18:30:00,2024-12-18 21:30:00,ONLINE,NA +24,FALL,2024,ARCH,4790,6,2024-12-18 18:30:00,2024-12-18 21:30:00,ONLINE,NA +25,FALL,2024,ARCH,4820,2,2024-12-18 08:00:00,2024-12-18 11:00:00,LOW,3051 +26,FALL,2024,ARCH,4820,3,2024-12-18 08:00:00,2024-12-18 11:00:00,LOW,3051 +27,FALL,2024,ARCH,4820,6,2024-12-18 08:00:00,2024-12-18 11:00:00,LOW,3051 +28,FALL,2024,ARCH,4820,7,2024-12-18 08:00:00,2024-12-18 11:00:00,LOW,3051 +29,FALL,2024,ARCH,4890,1,2024-12-13 18:30:00,2024-12-13 21:30:00,GREENE,120 +30,FALL,2024,ARCH,4910,1,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +31,FALL,2024,ARCH,4910,2,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +32,FALL,2024,ARCH,4910,3,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +33,FALL,2024,ARCH,4910,4,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +34,FALL,2024,ARCH,4910,5,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +35,FALL,2024,ARCH,4956,80,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,4034 +36,FALL,2024,ARCH,4958,80,2024-12-16 15:00:00,2024-12-16 18:00:00,TROY,2015 +37,FALL,2024,ARCH,4962,1,2024-12-19 18:30:00,2024-12-19 21:30:00,LOW,3112 +38,FALL,2024,ARCH,4966,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3116 +39,FALL,2024,ARCH,5100,1,2024-12-16 11:30:00,2024-12-16 14:30:00,ONLINE,NA +40,FALL,2024,ARCH,5140,1,2024-12-16 08:00:00,2024-12-16 11:00:00,ACADEMY,AUD +41,FALL,2024,ARCH,5150,1,2024-12-17 08:00:00,2024-12-17 11:00:00,ACADEMY,AUD +42,FALL,2024,ARCH,6065,1,2024-12-17 18:30:00,2024-12-17 21:30:00,LOW,4034 +43,FALL,2024,ARCH,6340,80,2024-12-18 11:30:00,2024-12-18 14:30:00,LOW,3112 +44,FALL,2024,ARCH,6350,1,2024-12-19 15:00:00,2024-12-19 18:00:00,LOW,3116 +45,FALL,2024,ARCH,6380,80,2024-12-16 18:30:00,2024-12-16 21:30:00,LOW,3112 +46,FALL,2024,ARCH,6810,80,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,4034 +47,FALL,2024,ARCH,6840,1,2024-12-13 18:30:00,2024-12-13 21:30:00,GREENE,120 +48,FALL,2024,ARCH,6910,1,2024-12-19 11:30:00,2024-12-19 14:30:00,LOW,3112 +49,FALL,2024,ARCH,6966,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3116 +50,FALL,2024,ARTS,2610,1,2024-12-17 11:30:00,2024-12-17 14:30:00,TROY,2018 +51,FALL,2024,ARTS,4120,1,2024-12-17 18:30:00,2024-12-17 21:30:00,WEST,214 +52,FALL,2024,ARTS,6120,1,2024-12-17 18:30:00,2024-12-17 21:30:00,WEST,214 +53,FALL,2024,ARTS,6900,1,2024-12-16 11:30:00,2024-12-16 14:30:00,WEST,113 +54,FALL,2024,ASTR,1540,1,2024-12-17 18:30:00,2024-12-17 21:30:00,LOW,4050 +55,FALL,2024,ASTR,2050,1,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,337 +56,FALL,2024,BCBP,4345,1,2024-12-16 18:30:00,2024-12-16 21:30:00,LOW,4034 +57,FALL,2024,BCBP,6345,1,2024-12-16 18:30:00,2024-12-16 21:30:00,LOW,4034 +58,FALL,2024,BIOL,2120,1,2024-12-17 11:30:00,2024-12-17 14:30:00,DCC,330 +59,FALL,2024,BIOL,4345,1,2024-12-16 18:30:00,2024-12-16 21:30:00,LOW,4034 +60,FALL,2024,BIOL,4720,1,2024-12-17 18:30:00,2024-12-17 21:30:00,JEC,4309 +61,FALL,2024,BIOL,4720,2,2024-12-17 18:30:00,2024-12-17 21:30:00,JEC,4309 +62,FALL,2024,BIOL,6345,1,2024-12-16 18:30:00,2024-12-16 21:30:00,LOW,4034 +63,FALL,2024,BMED,2050,1,2024-12-16 11:30:00,2024-12-16 14:30:00,DCC,337 +64,FALL,2024,BMED,2050,2,2024-12-16 11:30:00,2024-12-16 14:30:00,DCC,337 +65,FALL,2024,BMED,2100,1,2024-12-17 11:30:00,2024-12-17 14:30:00,CARNEGIE,113 +66,FALL,2024,BMED,2540,1,2024-12-18 15:00:00,2024-12-18 18:00:00,DCC,324 +67,FALL,2024,BMED,4200,1,2024-12-17 11:30:00,2024-12-17 14:30:00,LOW,4050 +68,FALL,2024,BMED,4250,1,2024-12-16 15:00:00,2024-12-16 18:00:00,TROY,2012 +69,FALL,2024,BMED,4540,1,2024-12-17 11:30:00,2024-12-17 14:30:00,PITTS,5114 +70,FALL,2024,BMED,6420,1,2024-12-16 15:00:00,2024-12-16 18:00:00,SAGE,4203 +71,FALL,2024,BMED,6961,1,2024-12-13 18:30:00,2024-12-13 21:30:00,ONLINE,NA +72,FALL,2024,CHEM,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,DCC,308 +73,FALL,2024,CHEM,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,DCC,318 +74,FALL,2024,CHEM,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,DCC,324 +75,FALL,2024,CHEM,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,DCC,330 +76,FALL,2024,CHEM,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,DCC,337 +77,FALL,2024,CHEM,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,WALKER,5113 +78,FALL,2024,CHEM/ENGR/ISCI/PHYS,6961,,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3510 +79,FALL,2024,CHEM,2250,1,2024-12-17 11:30:00,2024-12-17 14:30:00,DCC,318 +80,FALL,2024,CHEM,4300,1,2024-12-17 15:00:00,2024-12-17 18:00:00,TROY,2015 +81,FALL,2024,CHEM,6300,1,2024-12-17 15:00:00,2024-12-17 18:00:00,TROY,2015 +82,FALL,2024,CHME,2010,1,2024-12-18 11:30:00,2024-12-18 14:30:00,CARNEGIE,113 +83,FALL,2024,CHME,2050,1,2024-12-17 15:00:00,2024-12-17 18:00:00,LOW,3039 +84,FALL,2024,CHME,4010,1,2024-12-18 15:00:00,2024-12-18 18:00:00,LOW,3130 +85,FALL,2024,CHME,4020,1,2024-12-18 11:30:00,2024-12-18 14:30:00,PITTS,5114 +86,FALL,2024,CHME,4030,1,2024-12-16 15:00:00,2024-12-16 18:00:00,TROY,2018 +87,FALL,2024,CHME,4040,1,2024-12-17 18:30:00,2024-12-17 21:30:00,PITTS,4114 +88,FALL,2024,CHME,4050,1,2024-12-18 18:30:00,2024-12-18 21:30:00,LOW,4050 +89,FALL,2024,CHME,4050,2,2024-12-18 18:30:00,2024-12-18 21:30:00,LOW,4050 +90,FALL,2024,CHME,4430,1,2024-12-16 11:30:00,2024-12-16 14:30:00,TROY,2015 +91,FALL,2024,CHME,4500,1,2024-12-17 11:30:00,2024-12-17 14:30:00,PITTS,4114 +92,FALL,2024,CHME,4600,1,2024-12-16 15:00:00,2024-12-16 18:00:00,LOW,3130 +93,FALL,2024,CHME,6610,1,2024-12-18 15:00:00,2024-12-18 18:00:00,JEC,4309 +94,FALL,2024,CIVL,1200,1,2024-12-19 18:30:00,2024-12-19 21:30:00,DCC,324 +95,FALL,2024,CIVL,2050,1,2024-12-16 08:00:00,2024-12-16 11:00:00,WEST,220 +96,FALL,2024,CIVL,2050,2,2024-12-16 08:00:00,2024-12-16 11:00:00,WEST,220 +97,FALL,2024,CIVL,2050,3,2024-12-16 08:00:00,2024-12-16 11:00:00,WEST,220 +98,FALL,2024,CIVL,4010,1,2024-12-16 15:00:00,2024-12-16 18:00:00,PITTS,4114 +99,FALL,2024,CIVL,4070,1,2024-12-18 15:00:00,2024-12-18 18:00:00,ACADEMY,AUD +100,FALL,2024,CIVL,4140,1,2024-12-16 11:30:00,2024-12-16 14:30:00,JEC,3207 +101,FALL,2024,CIVL,4940,1,2024-12-17 15:00:00,2024-12-17 18:00:00,JEC,4309 +102,FALL,2024,CIVL,4960,1,2024-12-16 18:30:00,2024-12-16 21:30:00,PITTS,5114 +103,FALL,2024,CIVL,6170,1,2024-12-13 15:00:00,2024-12-13 18:00:00,LOW,3130 +104,FALL,2024,CIVL,6250,1,2024-12-19 11:30:00,2024-12-19 14:30:00,TROY,2015 +105,FALL,2024,CIVL,6260,1,2024-12-17 15:00:00,2024-12-17 18:00:00,JEC,3210 +106,FALL,2024,CIVL,6310,1,2024-12-18 11:30:00,2024-12-18 14:30:00,LOW,4040 +107,FALL,2024,CIVL,6360,1,2024-12-19 18:30:00,2024-12-19 21:30:00,JEC,5119 +108,FALL,2024,CIVL,6390,1,2024-12-16 15:00:00,2024-12-16 18:00:00,PITTS,4206 +109,FALL,2024,CIVL,6450,1,2024-12-17 11:30:00,2024-12-17 14:30:00,JEC,5119 +110,FALL,2024,CIVL,6510,1,2024-12-17 15:00:00,2024-12-17 18:00:00,JEC,3207 +111,FALL,2024,CIVL,6550,1,2024-12-16 11:30:00,2024-12-16 14:30:00,JEC,3207 +112,FALL,2024,CIVL,6550,,2024-12-13 11:30:00,2024-12-13 14:30:00,LOW,3112 +113,FALL,2024,CIVL,6960,1,2024-12-16 18:30:00,2024-12-16 21:30:00,PITTS,5114 +114,FALL,2024,CIVL,6962,1,2024-12-17 18:30:00,2024-12-17 21:30:00,LOW,3112 +115,FALL,2024,COMM,4420,1,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,337 +116,FALL,2024,COMM,6420,1,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,337 +117,FALL,2024,CSCI,6420,,2024-12-13 11:30:00,2024-12-13 14:30:00,DCC,308 +118,FALL,2024,CSCI,6420,,2024-12-13 11:30:00,2024-12-13 14:30:00,DCC,318 +119,FALL,2024,CSCI,6420,,2024-12-13 11:30:00,2024-12-13 14:30:00,DCC,324 +120,FALL,2024,CSCI,6420,,2024-12-13 11:30:00,2024-12-13 14:30:00,DCC,330 +121,FALL,2024,CSCI,6420,,2024-12-13 11:30:00,2024-12-13 14:30:00,DCC,337 +122,FALL,2024,CSCI,6420,,2024-12-18 15:00:00,2024-12-18 18:00:00,DCC,308 +123,FALL,2024,CSCI,6420,,2024-12-16 18:30:00,2024-12-16 21:30:00,DCC,308 +124,FALL,2024,CSCI,6420,,2024-12-16 18:30:00,2024-12-16 21:30:00,DCC,318 +125,FALL,2024,CSCI,6420,,2024-12-16 15:00:00,2024-12-16 18:00:00,DCC,308 +126,FALL,2024,CSCI,6420,,2024-12-16 15:00:00,2024-12-16 18:00:00,DCC,318 +127,FALL,2024,CSCI,6420,,2024-12-13 18:30:00,2024-12-13 21:30:00,DCC,308 +128,FALL,2024,CSCI,6420,,2024-12-13 18:30:00,2024-12-13 21:30:00,DCC,318 +129,FALL,2024,CSCI,4220,1,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,318 +130,FALL,2024,CSCI,4380,1,2024-12-13 08:00:00,2024-12-13 11:00:00,SAGE,3303 +131,FALL,2024,CSCI,4380,,2024-12-19 08:00:00,2024-12-19 11:00:00,SAGE,3303 +132,FALL,2024,CSCI,4480,1,2024-12-17 08:00:00,2024-12-17 11:00:00,WEST,220 +133,FALL,2024,CSCI,4480,,2024-12-17 08:00:00,2024-12-17 11:00:00,LOW,4050 +134,FALL,2024,CSCI,4961,1,2024-12-16 15:00:00,2024-12-16 18:00:00,SAGE,3510 +135,FALL,2024,CSCI,4961,,2024-12-17 08:00:00,2024-12-17 11:00:00,LOW,4050 +136,FALL,2024,CSCI,6800,1,2024-12-16 15:00:00,2024-12-16 18:00:00,SAGE,3101 +137,FALL,2024,ECON,2010,3,2024-12-18 15:00:00,2024-12-18 18:00:00,LOW,4050 +138,FALL,2024,ECON,2010,4,2024-12-16 15:00:00,2024-12-16 18:00:00,JEC,3210 +139,FALL,2024,ECON,4360,1,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +140,FALL,2024,ECON,4960,1,2024-12-18 11:30:00,2024-12-18 14:30:00,JEC,4309 +141,FALL,2024,ECON,6360,1,2024-12-16 18:30:00,2024-12-16 21:30:00,ONLINE,NA +142,FALL,2024,ECON,6730,1,2024-12-18 15:00:00,2024-12-18 18:00:00,JEC,3207 +143,FALL,2024,ECON,6770,1,2024-12-18 11:30:00,2024-12-18 14:30:00,JEC,3207 +144,FALL,2024,ECSE,1010,1,2024-12-17 11:30:00,2024-12-17 14:30:00,SAGE,3303 +145,FALL,2024,ECSE,1010,2,2024-12-17 11:30:00,2024-12-17 14:30:00,SAGE,3303 +146,FALL,2024,ECSE,2010,1,2024-12-18 11:30:00,2024-12-18 14:30:00,ACADEMY,AUD +147,FALL,2024,ECSE,2010,2,2024-12-18 11:30:00,2024-12-18 14:30:00,ACADEMY,AUD +148,FALL,2024,ECSE,2050,1,2024-12-17 11:30:00,2024-12-17 14:30:00,ACADEMY,AUD +149,FALL,2024,ECSE,2050,2,2024-12-17 11:30:00,2024-12-17 14:30:00,ACADEMY,AUD +150,FALL,2024,ECSE,2100,1,2024-12-16 15:00:00,2024-12-16 18:00:00,LOW,4050 +151,FALL,2024,ECSE,2110,1,2024-12-17 15:00:00,2024-12-17 18:00:00,TROY,2012 +152,FALL,2024,ECSE,2210,1,2024-12-19 15:00:00,2024-12-19 18:00:00,LOW,4050 +153,FALL,2024,ECSE,2410,1,2024-12-13 15:00:00,2024-12-13 18:00:00,LOW,4050 +154,FALL,2024,ECSE,2610,1,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,4114 +155,FALL,2024,ECSE,2610,2,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,4114 +156,FALL,2024,ECSE,2610,1,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,4206 +157,FALL,2024,ECSE,2610,2,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,4206 +158,FALL,2024,ECSE,2610,1,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,5114 +159,FALL,2024,ECSE,2610,2,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,5114 +160,FALL,2024,ECSE,2610,1,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,5216 +161,FALL,2024,ECSE,2610,2,2024-12-16 08:00:00,2024-12-16 11:00:00,PITTS,5216 +162,FALL,2024,ECSE,2660,1,2024-12-18 15:00:00,2024-12-18 18:00:00,DCC,330 +163,FALL,2024,ECSE,4030,1,2024-12-16 15:00:00,2024-12-16 18:00:00,PITTS,5216 +164,FALL,2024,ECSE,4080,1,2024-12-18 18:30:00,2024-12-18 21:30:00,PITTS,4114 +165,FALL,2024,ECSE,4220,1,2024-12-17 18:30:00,2024-12-17 21:30:00,LOW,3051 +166,FALL,2024,ECSE,4250,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3130 +167,FALL,2024,ECSE,4480,1,2024-12-17 08:00:00,2024-12-17 11:00:00,WEST,220 +168,FALL,2024,ECSE,4670,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3039 +169,FALL,2024,ECSE,4790,1,2024-12-19 11:30:00,2024-12-19 14:30:00,LOW,4050 +170,FALL,2024,ECSE,4810,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3045 +171,FALL,2024,ECSE,4900,1,2024-12-19 08:00:00,2024-12-19 11:00:00,JEC,3232 +172,FALL,2024,ECSE,4900,2,2024-12-19 11:30:00,2024-12-19 14:30:00,JEC,3232 +173,FALL,2024,ECSE,4900,3,2024-12-19 15:00:00,2024-12-19 18:00:00,JEC,3232 +174,FALL,2024,ECSE,4900,4,2024-12-19 18:30:00,2024-12-19 21:30:00,JEC,3232 +175,FALL,2024,ECSE,4961,1,2024-12-19 08:00:00,2024-12-19 11:00:00,ACADEMY,AUD +176,FALL,2024,ECSE,4962,1,2024-12-16 18:30:00,2024-12-16 21:30:00,JEC,4309 +177,FALL,2024,ECSE,6230,1,2024-12-18 15:00:00,2024-12-18 18:00:00,LOW,3112 +178,FALL,2024,ECSE,6470,1,2024-12-17 08:00:00,2024-12-17 11:00:00,WEST,220 +179,FALL,2024,ECSE,6810,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3045 +180,FALL,2024,ECSE,6961,1,2024-12-19 08:00:00,2024-12-19 11:00:00,ACADEMY,AUD +181,FALL,2024,ECSE,6962,1,2024-12-16 18:30:00,2024-12-16 21:30:00,JEC,4309 +182,FALL,2024,ENGR,6962,,2024-12-13 08:00:00,2024-12-13 11:00:00,DCC,308 +183,FALL,2024,ENGR,6962,,2024-12-13 08:00:00,2024-12-13 11:00:00,DCC,318 +184,FALL,2024,ENGR,6962,,2024-12-13 08:00:00,2024-12-13 11:00:00,DCC,324 +185,FALL,2024,ENGR,6962,,2024-12-13 08:00:00,2024-12-13 11:00:00,DCC,330 +186,FALL,2024,ENGR,6962,,2024-12-13 08:00:00,2024-12-13 11:00:00,DCC,337 +187,FALL,2024,ENGR,6962,,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,308 +188,FALL,2024,ENGR,6962,,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,318 +189,FALL,2024,ENGR,6962,,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,324 +190,FALL,2024,ENGR,2090,1,2024-12-18 11:30:00,2024-12-18 14:30:00,LOW,4050 +191,FALL,2024,ENGR,2090,2,2024-12-18 15:00:00,2024-12-18 18:00:00,SAGE,3303 +192,FALL,2024,ENGR,2090,3,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,330 +193,FALL,2024,ENGR,2300,1,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,330 +194,FALL,2024,ENGR,2300,2,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,330 +195,FALL,2024,ENGR,2300,1,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,337 +196,FALL,2024,ENGR,2300,2,2024-12-19 08:00:00,2024-12-19 11:00:00,DCC,337 +197,FALL,2024,ENGR,2300,,2024-12-13 08:00:00,2024-12-13 11:00:00,WEST,220 +198,FALL,2024,ENGR,2300,,2024-12-17 11:30:00,2024-12-17 14:30:00,DCC,308 +199,FALL,2024,ENGR,2710,1,2024-12-18 11:30:00,2024-12-18 14:30:00,LOW,3045 +200,FALL,2024,ENGR,4710,1,2024-12-13 11:30:00,2024-12-13 14:30:00,JEC,3232 +201,FALL,2024,ENGR,4710,2,2024-12-13 11:30:00,2024-12-13 14:30:00,JEC,3232 +202,FALL,2024,ENGR,4961,1,2024-12-19 08:00:00,2024-12-19 11:00:00,ACADEMY,AUD +203,FALL,2024,ENGR,6961,1,2024-12-19 08:00:00,2024-12-19 11:00:00,ACADEMY,AUD +204,FALL,2024,ENVE,4220,1,2024-12-19 18:30:00,2024-12-19 21:30:00,JEC,4309 +205,FALL,2024,ENVE,4240,1,2024-12-16 11:30:00,2024-12-16 14:30:00,MATLS,136 +206,FALL,2024,ENVE,4330,1,2024-12-16 15:00:00,2024-12-16 18:00:00,PITTS,5114 +207,FALL,2024,ENVE,4350,1,2024-12-17 11:30:00,2024-12-17 14:30:00,JEC,4309 +208,FALL,2024,ENVE,4370,1,2024-12-16 18:30:00,2024-12-16 21:30:00,DCC,337 +209,FALL,2024,ENVE,4370,2,2024-12-16 18:30:00,2024-12-16 21:30:00,DCC,337 +210,FALL,2024,ENVE,4370,3,2024-12-16 18:30:00,2024-12-16 21:30:00,DCC,337 +211,FALL,2024,ENVE,4710,1,2024-12-18 08:00:00,2024-12-18 11:00:00,TROY,2012 +212,FALL,2024,ENVE,6710,1,2024-12-18 08:00:00,2024-12-18 11:00:00,TROY,2012 +213,FALL,2024,ERTH,1100,1,2024-12-13 18:30:00,2024-12-13 21:30:00,SAGE,3303 +214,FALL,2024,ERTH,1100,2,2024-12-13 18:30:00,2024-12-13 21:30:00,SAGE,3303 +215,FALL,2024,ERTH,2120,1,2024-12-17 18:30:00,2024-12-17 21:30:00,LOW,3039 +216,FALL,2024,ERTH,2330,1,2024-12-16 18:30:00,2024-12-16 21:30:00,JEC,3207 +217,FALL,2024,ERTH,4710,1,2024-12-18 08:00:00,2024-12-18 11:00:00,TROY,2012 +218,FALL,2024,ERTH,6710,1,2024-12-18 08:00:00,2024-12-18 11:00:00,TROY,2012 +219,FALL,2024,GSAS,1600,2,2024-12-13 15:00:00,2024-12-13 18:00:00,PITTS,4114 +220,FALL,2024,GSAS,2520,1,2024-12-16 15:00:00,2024-12-16 18:00:00,LOW,3039 +221,FALL,2024,ISCI,4961,1,2024-12-19 08:00:00,2024-12-19 11:00:00,ACADEMY,AUD +222,FALL,2024,ISCI,6961,1,2024-12-19 08:00:00,2024-12-19 11:00:00,ACADEMY,AUD +223,FALL,2024,ISYE,4210,1,2024-12-13 11:30:00,2024-12-13 14:30:00,JEC,3210 +224,FALL,2024,ISYE,4270,1,2024-12-19 08:00:00,2024-12-19 11:00:00,JEC,3232 +225,FALL,2024,ISYE,4270,2,2024-12-19 11:30:00,2024-12-19 14:30:00,JEC,3232 +226,FALL,2024,ISYE,4270,3,2024-12-19 15:00:00,2024-12-19 18:00:00,JEC,3232 +227,FALL,2024,ISYE,4270,4,2024-12-19 18:30:00,2024-12-19 21:30:00,JEC,3232 +228,FALL,2024,ISYE,4600,1,2024-12-19 11:30:00,2024-12-19 14:30:00,TROY,2012 +229,FALL,2024,ISYE,6600,1,2024-12-13 11:30:00,2024-12-13 14:30:00,JEC,3210 +230,FALL,2024,ISYE,6610,1,2024-12-19 11:30:00,2024-12-19 14:30:00,TROY,2012 +231,FALL,2024,ISYE,6780,1,2024-12-17 15:00:00,2024-12-17 18:00:00,LOW,3051 +232,FALL,2024,ISYE,6900,1,2024-12-16 15:00:00,2024-12-16 18:00:00,LALLY,02 +233,FALL,2024,MANE,2710,1,2024-12-13 15:00:00,2024-12-13 18:00:00,DCC,308 +234,FALL,2024,MANE,2710,1,2024-12-13 15:00:00,2024-12-13 18:00:00,DCC,308 +235,FALL,2024,MANE,2710,1,2024-12-13 15:00:00,2024-12-13 18:00:00,DCC,330 +236,FALL,2024,MANE,2710,2,2024-12-13 15:00:00,2024-12-13 18:00:00,DCC,330 +237,FALL,2024,MANE,2720,1,2024-12-19 11:30:00,2024-12-19 14:30:00,SAGE,3303 +238,FALL,2024,MANE,2960,1,2024-12-13 08:00:00,2024-12-13 11:00:00,LOW,4040 +239,FALL,2024,MANE,4030,1,2024-12-13 15:00:00,2024-12-13 18:00:00,DCC,318 +240,FALL,2024,MANE,4030,2,2024-12-13 15:00:00,2024-12-13 18:00:00,DCC,318 +241,FALL,2024,MANE,4060,1,2024-12-16 15:00:00,2024-12-16 18:00:00,ACADEMY,AUD +242,FALL,2024,MANE,4070,1,2024-12-17 11:30:00,2024-12-17 14:30:00,DCC,324 +243,FALL,2024,MANE,4090,1,2024-12-18 11:30:00,2024-12-18 14:30:00,SAGE,3510 +244,FALL,2024,MANE,4250,1,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,324 +245,FALL,2024,MANE,4250,2,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,324 +246,FALL,2024,MANE,4260,1,2024-12-19 08:00:00,2024-12-19 11:00:00,JEC,3232 +247,FALL,2024,MANE,4260,2,2024-12-19 11:30:00,2024-12-19 14:30:00,JEC,3232 +248,FALL,2024,MANE,4260,3,2024-12-19 15:00:00,2024-12-19 18:00:00,JEC,3232 +249,FALL,2024,MANE,4260,4,2024-12-19 18:30:00,2024-12-19 21:30:00,JEC,3232 +250,FALL,2024,MANE,4290,1,2024-12-17 15:00:00,2024-12-17 18:00:00,PITTS,5114 +251,FALL,2024,MANE,4500,1,2024-12-18 08:00:00,2024-12-18 11:00:00,SAGE,3303 +252,FALL,2024,MANE,4500,2,2024-12-18 08:00:00,2024-12-18 11:00:00,SAGE,3303 +253,FALL,2024,MANE,4560,1,2024-12-17 08:00:00,2024-12-17 11:00:00,WEST,220 +254,FALL,2024,MANE,4610,1,2024-12-13 11:30:00,2024-12-13 14:30:00,JEC,3232 +255,FALL,2024,MANE,4610,2,2024-12-13 11:30:00,2024-12-13 14:30:00,JEC,3232 +256,FALL,2024,MANE,4900,1,2024-12-13 15:00:00,2024-12-13 18:00:00,ACADEMY,AUD +257,FALL,2024,MANE,4900,,2024-12-13 08:00:00,2024-12-13 11:00:00,WALKER,5113 +258,FALL,2024,MANE,4900,,2024-12-13 08:00:00,2024-12-13 11:00:00,WALKER,6113 +259,FALL,2024,MANE,4964,1,2024-12-13 18:30:00,2024-12-13 21:30:00,LOW,3116 +260,FALL,2024,MANE,4965,1,2024-12-18 18:30:00,2024-12-18 21:30:00,SAGE,3101 +261,FALL,2024,MANE,6120,1,2024-12-17 08:00:00,2024-12-17 11:00:00,WEST,220 +262,FALL,2024,MANE,6170,1,2024-12-13 15:00:00,2024-12-13 18:00:00,PITTS,5114 +263,FALL,2024,MANE,6290,1,2024-12-17 15:00:00,2024-12-17 18:00:00,PITTS,5114 +264,FALL,2024,MANE,6290,,2024-12-13 11:30:00,2024-12-13 14:30:00,LOW,3112 +265,FALL,2024,MANE,6730,1,2024-12-16 11:30:00,2024-12-16 14:30:00,VCC,208 +266,FALL,2024,MANE,6963,1,2024-12-13 18:30:00,2024-12-13 21:30:00,LOW,3116 +267,FALL,2024,MANE,6965,1,2024-12-18 18:30:00,2024-12-18 21:30:00,SAGE,3101 +268,FALL,2024,MANE,6966,1,2024-12-18 11:30:00,2024-12-18 14:30:00,LOW,3116 +269,FALL,2024,MATH,1010,1,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +270,FALL,2024,MATH,1010,2,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +271,FALL,2024,MATH,1010,3,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +272,FALL,2024,MATH,1010,4,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +273,FALL,2024,MATH,1010,5,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +274,FALL,2024,MATH,1010,6,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +275,FALL,2024,MATH,1010,7,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +276,FALL,2024,MATH,1010,8,2024-12-17 18:30:00,2024-12-17 21:30:00,DCC,308 +277,FALL,2024,MATH,1010,9,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +278,FALL,2024,MATH,1010,10,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +279,FALL,2024,MATH,1010,11,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +280,FALL,2024,MATH,1010,12,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +281,FALL,2024,MATH,1010,13,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +282,FALL,2024,MATH,1010,14,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +283,FALL,2024,MATH,1010,15,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +284,FALL,2024,MATH,1010,16,2024-12-18 18:30:00,2024-12-18 21:30:00,DCC,308 +285,FALL,2024,MATH,1010,17,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +286,FALL,2024,MATH,1010,18,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +287,FALL,2024,MATH,1010,19,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +288,FALL,2024,MATH,1010,20,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +289,FALL,2024,MATH,1010,21,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +290,FALL,2024,MATH,1010,22,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +291,FALL,2024,MATH,1010,23,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +292,FALL,2024,MATH,1010,24,2024-12-19 15:00:00,2024-12-19 18:00:00,SAGE,3303 +293,FALL,2024,MATH,1010,25,2024-12-13 15:00:00,2024-12-13 18:00:00,EATON,214 +294,FALL,2024,MATH,1010,26,2024-12-13 15:00:00,2024-12-13 18:00:00,EATON,214 +295,FALL,2024,MATH,1010,27,2024-12-13 15:00:00,2024-12-13 18:00:00,EATON,214 +296,FALL,2024,MATH,1010,28,2024-12-13 15:00:00,2024-12-13 18:00:00,EATON,214 +297,FALL,2024,MATH,1010,29,2024-12-13 18:30:00,2024-12-13 21:30:00,DCC,324 +298,FALL,2024,MATH,1010,30,2024-12-13 18:30:00,2024-12-13 21:30:00,DCC,324 +299,FALL,2024,MATH,1010,31,2024-12-13 18:30:00,2024-12-13 21:30:00,DCC,324 +300,FALL,2024,MATH,1010,32,2024-12-13 18:30:00,2024-12-13 21:30:00,DCC,324 +301,FALL,2024,MATH,1020,9,2024-12-16 11:30:00,2024-12-16 14:30:00,SAGE,3303 +302,FALL,2024,MATH,1020,10,2024-12-16 11:30:00,2024-12-16 14:30:00,SAGE,3303 +303,FALL,2024,MATH,1020,11,2024-12-16 11:30:00,2024-12-16 14:30:00,SAGE,3303 +304,FALL,2024,MATH,1020,12,2024-12-16 11:30:00,2024-12-16 14:30:00,SAGE,3303 +305,FALL,2024,MATH,2010,9,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +306,FALL,2024,MATH,2010,10,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +307,FALL,2024,MATH,2010,11,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +308,FALL,2024,MATH,2010,12,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +309,FALL,2024,MATH,2010,13,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +310,FALL,2024,MATH,2010,14,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +311,FALL,2024,MATH,2010,15,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +312,FALL,2024,MATH,2010,16,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,308 +313,FALL,2024,MATH,2010,9,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +314,FALL,2024,MATH,2010,10,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +315,FALL,2024,MATH,2010,11,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +316,FALL,2024,MATH,2010,12,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +317,FALL,2024,MATH,2010,13,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +318,FALL,2024,MATH,2010,14,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +319,FALL,2024,MATH,2010,15,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +320,FALL,2024,MATH,2010,16,2024-12-19 11:30:00,2024-12-19 14:30:00,DCC,318 +321,FALL,2024,MATH,2010,17,2024-12-16 08:00:00,2024-12-16 11:00:00,EATON,214 +322,FALL,2024,MATH,2010,18,2024-12-16 08:00:00,2024-12-16 11:00:00,EATON,214 +323,FALL,2024,MATH,2010,19,2024-12-16 08:00:00,2024-12-16 11:00:00,EATON,214 +324,FALL,2024,MATH,2010,20,2024-12-16 08:00:00,2024-12-16 11:00:00,EATON,214 +325,FALL,2024,MATH,2010,21,2024-12-17 15:00:00,2024-12-17 18:00:00,SAGE,3303 +326,FALL,2024,MATH,2010,22,2024-12-17 15:00:00,2024-12-17 18:00:00,SAGE,3303 +327,FALL,2024,MATH,2010,23,2024-12-17 15:00:00,2024-12-17 18:00:00,SAGE,3303 +328,FALL,2024,MATH,2010,24,2024-12-17 15:00:00,2024-12-17 18:00:00,SAGE,3303 +329,FALL,2024,MATH,2400,1,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +330,FALL,2024,MATH,2400,2,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +331,FALL,2024,MATH,2400,3,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +332,FALL,2024,MATH,2400,4,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +333,FALL,2024,MATH,2400,5,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +334,FALL,2024,MATH,2400,6,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +335,FALL,2024,MATH,2400,7,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +336,FALL,2024,MATH,2400,8,2024-12-16 08:00:00,2024-12-16 11:00:00,SAGE,3303 +337,FALL,2024,MATH,2400,9,2024-12-16 18:30:00,2024-12-16 21:30:00,SAGE,3303 +338,FALL,2024,MATH,2400,10,2024-12-16 18:30:00,2024-12-16 21:30:00,SAGE,3303 +339,FALL,2024,MATH,2400,11,2024-12-16 18:30:00,2024-12-16 21:30:00,SAGE,3303 +340,FALL,2024,MATH,2400,12,2024-12-16 18:30:00,2024-12-16 21:30:00,SAGE,3303 +341,FALL,2024,MATH,2400,17,2024-12-13 15:00:00,2024-12-13 18:00:00,SAGE,3303 +342,FALL,2024,MATH,2400,18,2024-12-13 15:00:00,2024-12-13 18:00:00,SAGE,3303 +343,FALL,2024,MATH,2400,19,2024-12-13 15:00:00,2024-12-13 18:00:00,SAGE,3303 +344,FALL,2024,MATH,2400,20,2024-12-13 15:00:00,2024-12-13 18:00:00,SAGE,3303 +345,FALL,2024,MATH,4040,1,2024-12-18 15:00:00,2024-12-18 18:00:00,DCC,337 +346,FALL,2024,MATH,4090,1,2024-12-16 15:00:00,2024-12-16 18:00:00,CARNEGIE,113 +347,FALL,2024,MATH,4200,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,3051 +348,FALL,2024,MATH,4400,1,2024-12-13 15:00:00,2024-12-13 18:00:00,SAGE,3303 +349,FALL,2024,MATH,4600,1,2024-12-16 11:30:00,2024-12-16 14:30:00,LOW,4050 +350,FALL,2024,MATH,4700,1,2024-12-18 11:30:00,2024-12-18 14:30:00,TROY,2012 +351,FALL,2024,MATH,6500,1,2024-12-17 11:30:00,2024-12-17 14:30:00,EATON,216 +352,FALL,2024,MATH,6790,1,2024-12-18 15:00:00,2024-12-18 18:00:00,TROY,2012 +353,FALL,2024,MATH,6800,1,2024-12-16 15:00:00,2024-12-16 18:00:00,SAGE,3101 +354,FALL,2024,MATP,4910,1,2024-12-13 11:30:00,2024-12-13 14:30:00,AE,217 +355,FALL,2024,MATP,6600,1,2024-12-17 15:00:00,2024-12-17 18:00:00,LOW,3051 +356,FALL,2024,MATP,6910,1,2024-12-13 11:30:00,2024-12-13 14:30:00,AE,217 +357,FALL,2024,MGMT,1100,2,2024-12-18 11:30:00,2024-12-18 14:30:00,TROY,2018 +358,FALL,2024,MGMT,1100,3,2024-12-17 15:00:00,2024-12-17 18:00:00,ACADEMY,AUD +359,FALL,2024,MGMT,1100,4,2024-12-17 15:00:00,2024-12-17 18:00:00,ACADEMY,AUD +360,FALL,2024,MGMT,1260,1,2024-12-16 15:00:00,2024-12-16 18:00:00,DCC,330 +361,FALL,2024,MGMT,2100,1,2024-12-17 11:30:00,2024-12-17 14:30:00,DCC,337 +362,FALL,2024,MGMT,2100,2,2024-12-18 15:00:00,2024-12-18 18:00:00,CARNEGIE,113 +363,FALL,2024,MGMT,2300,1,2024-12-16 15:00:00,2024-12-16 18:00:00,EATON,214 +364,FALL,2024,MGMT,2300,2,2024-12-16 15:00:00,2024-12-16 18:00:00,EATON,214 +365,FALL,2024,MGMT,2320,1,2024-12-16 11:30:00,2024-12-16 14:30:00,SAGE,3510 +366,FALL,2024,MGMT,2320,2,2024-12-19 11:30:00,2024-12-19 14:30:00,PITTS,4114 +367,FALL,2024,MGMT,2430,1,2024-12-16 11:30:00,2024-12-16 14:30:00,ONLINE,NA +368,FALL,2024,MGMT,2430,2,2024-12-17 11:30:00,2024-12-17 14:30:00,ONLINE,NA +369,FALL,2024,MGMT,2510,1,2024-12-16 11:30:00,2024-12-16 14:30:00,CARNEGIE,113 +370,FALL,2024,MGMT,2510,2,2024-12-18 15:00:00,2024-12-18 18:00:00,PITTS,5216 +371,FALL,2024,MGMT,4140,1,2024-12-17 15:00:00,2024-12-17 18:00:00,ONLINE,NA +372,FALL,2024,MGMT,4170,1,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,330 +373,FALL,2024,MGMT,4170,2,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,330 +374,FALL,2024,MGMT,4190,1,2024-12-13 11:30:00,2024-12-13 14:30:00,LOW,4050 +375,FALL,2024,MGMT,4190,2,2024-12-13 11:30:00,2024-12-13 14:30:00,LOW,4050 +376,FALL,2024,MGMT,4240,1,2024-12-18 15:00:00,2024-12-18 18:00:00,ONLINE,NA +377,FALL,2024,MGMT,4310,1,2024-12-18 11:30:00,2024-12-18 14:30:00,PITTS,4114 +378,FALL,2024,MGMT,4320,1,2024-12-16 11:30:00,2024-12-16 14:30:00,PITTS,4114 +379,FALL,2024,MGMT,4340,1,2024-12-17 11:30:00,2024-12-17 14:30:00,LOW,3112 +380,FALL,2024,MGMT,4540,1,2024-12-17 11:30:00,2024-12-17 14:30:00,LOW,3130 +381,FALL,2024,MGMT,4600,1,2024-12-18 15:00:00,2024-12-18 18:00:00,TROY,2018 +382,FALL,2024,MGMT,4600,2,2024-12-18 15:00:00,2024-12-18 18:00:00,TROY,2018 +383,FALL,2024,MGMT,4850,1,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,337 +384,FALL,2024,MGMT,4860,1,2024-12-17 11:30:00,2024-12-17 14:30:00,SAGE,3101 +385,FALL,2024,MGMT,4860,2,2024-12-18 11:30:00,2024-12-18 14:30:00,PITTS,5114 +386,FALL,2024,MGMT,4870,1,2024-12-16 15:00:00,2024-12-16 18:00:00,LOW,3051 +387,FALL,2024,MGMT,4870,2,2024-12-17 15:00:00,2024-12-17 18:00:00,LOW,3116 +388,FALL,2024,MGMT,6020,1,2024-12-13 11:30:00,2024-12-13 14:30:00,PITTS,4206 +389,FALL,2024,MGMT,6040,1,2024-12-18 15:00:00,2024-12-18 18:00:00,LOW,3051 +390,FALL,2024,MGMT,6060,1,2024-12-19 15:00:00,2024-12-19 18:00:00,TROY,2012 +391,FALL,2024,MGMT,6140,1,2024-12-16 11:30:00,2024-12-16 14:30:00,SAGE,5510 +392,FALL,2024,MGMT,6190,1,2024-12-19 18:30:00,2024-12-19 21:30:00,DCC,330 +393,FALL,2024,MGMT,6240,1,2024-12-18 11:30:00,2024-12-18 14:30:00,PITTS,4114 +394,FALL,2024,MGMT,6260,1,2024-12-17 11:30:00,2024-12-17 14:30:00,LOW,3130 +395,FALL,2024,MGMT,6370,1,2024-12-18 18:30:00,2024-12-18 21:30:00,ONLINE,NA +396,FALL,2024,MGMT,6380,1,2024-12-17 11:30:00,2024-12-17 14:30:00,LOW,3112 +397,FALL,2024,MGMT,6490,1,2024-12-17 15:00:00,2024-12-17 18:00:00,ONLINE,NA +398,FALL,2024,MGMT,6520,1,2024-12-19 18:30:00,2024-12-19 21:30:00,ONLINE,NA +399,FALL,2024,MGMT,6560,1,2024-12-13 11:30:00,2024-12-13 14:30:00,LOW,4050 +400,FALL,2024,MGMT,6560,2,2024-12-13 11:30:00,2024-12-13 14:30:00,LOW,4050 +401,FALL,2024,MGMT,6570,1,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,330 +402,FALL,2024,MGMT,6570,2,2024-12-17 15:00:00,2024-12-17 18:00:00,DCC,330 +403,FALL,2024,MGMT,6600,1,2024-12-18 15:00:00,2024-12-18 18:00:00,TROY,2018 +404,FALL,2024,MGMT,6600,2,2024-12-18 15:00:00,2024-12-18 18:00:00,TROY,2018 +405,FALL,2024,MGMT,6840,1,2024-12-19 18:30:00,2024-12-19 21:30:00,ACADEMY,AUD +406,FALL,2024,MGMT,6960,1,2024-12-13 15:00:00,2024-12-13 18:00:00,PITTS,5114 +407,FALL,2024,MGMT,6961,1,2024-12-13 18:30:00,2024-12-13 21:30:00,ONLINE,NA +408,FALL,2024,MGMT,6962,1,2024-12-19 11:30:00,2024-12-19 14:30:00,PITTS,5114 +409,FALL,2024,MGMT,7730,1,2024-12-19 11:30:00,2024-12-19 14:30:00,PITTS,5216 +410,FALL,2024,MGMT,7740,1,2024-12-13 15:00:00,2024-12-13 18:00:00,TROY,2012 +411,FALL,2024,MTLE,4050,1,2024-12-17 15:00:00,2024-12-17 18:00:00,PITTS,4114 +412,FALL,2024,MTLE,4920,2,2024-12-19 11:30:00,2024-12-19 14:30:00,JEC,3232 +413,FALL,2024,MTLE,4920,3,2024-12-19 15:00:00,2024-12-19 18:00:00,JEC,3232 +414,FALL,2024,MTLE,4920,4,2024-12-19 18:30:00,2024-12-19 21:30:00,JEC,3232 +415,FALL,2024,MTLE,6050,1,2024-12-17 15:00:00,2024-12-17 18:00:00,PITTS,4114 +416,FALL,2024,PHYS,6050,,2024-12-17 08:00:00,2024-12-17 11:00:00,DCC,308 +417,FALL,2024,PHYS,6050,,2024-12-17 08:00:00,2024-12-17 11:00:00,DCC,318 +418,FALL,2024,PHYS,6050,,2024-12-17 08:00:00,2024-12-17 11:00:00,DCC,324 +419,FALL,2024,PHYS,6050,,2024-12-17 08:00:00,2024-12-17 11:00:00,DCC,330 +420,FALL,2024,PHYS,6050,,2024-12-17 08:00:00,2024-12-17 11:00:00,DCC,337 +421,FALL,2024,PHYS,6050,,2024-12-17 08:00:00,2024-12-17 11:00:00,SAGE,3303 +422,FALL,2024,PHYS,6050,,2024-12-18 08:00:00,2024-12-18 11:00:00,DCC,308 +423,FALL,2024,PHYS,6050,,2024-12-18 08:00:00,2024-12-18 11:00:00,DCC,318 +424,FALL,2024,PHYS,6050,,2024-12-18 08:00:00,2024-12-18 11:00:00,DCC,324 +425,FALL,2024,PHYS,6050,,2024-12-18 08:00:00,2024-12-18 11:00:00,DCC,330 +426,FALL,2024,PHYS,6050,,2024-12-18 08:00:00,2024-12-18 11:00:00,LOW,4050 +427,FALL,2024,PHYS,1250,1,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 +428,FALL,2024,PHYS,1250,2,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 +429,FALL,2024,PHYS,1250,3,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 diff --git a/src/api/app.py b/src/api/app.py index 31cf7fe4a..b2efdbf6b 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -11,6 +11,7 @@ import db.connection as connection import db.classinfo as ClassInfo import db.courses as Courses +import db.finals as Finals import db.professor as All_professors import db.semester_info as SemesterInfo import db.semester_date_mapping as DateMapping @@ -21,10 +22,8 @@ import controller.session as session_controller import controller.userevent as event_controller from io import StringIO -from sqlalchemy.orm import Session import json import os -import pandas as pd from constants import Constants """ @@ -43,6 +42,7 @@ db_conn = connection.db class_info = ClassInfo.ClassInfo(db_conn) courses = Courses.Courses(db_conn, FastAPICache) +finals = Finals.Finals(db_conn) date_range_map = DateMapping.semester_date_mapping(db_conn) admin_info = AdminInfo.Admin(db_conn) course_select = CourseSelect.student_course_selection(db_conn) @@ -182,7 +182,6 @@ async def uploadHandler( @app.post('/api/bulkProfessorUpload') async def uploadJSON( - isPubliclyVisible: str = Form(...), file: UploadFile = File(...)): # Check to make sure the user has sent a file if not file: @@ -213,6 +212,25 @@ async def uploadJSON( print(error) return Response(error.__str__(), status_code=500) +@app.post('/api/bulkFinalUpload') +async def uploadHandler( + file: UploadFile = File(...)): + # check for user files + print("in process") + if not file: + return Response("No file received", 400) + if file.filename.find('.') == -1 or file.filename.rsplit('.', 1)[1].lower() != 'csv': + return Response("File must have csv extension", 400) + # get file + contents = await file.read() + csv_file = StringIO(contents.decode()) + # Populate DB from CSV + isSuccess, error = finals.populate_from_csv(csv_file) + if (isSuccess): + return Response(status_code=200) + else: + print(error) + return Response(error.__str__(), status_code=500) @app.post('/api/mapDateRangeToSemesterPart') async def map_date_range_to_semester_part_handler(request: Request): diff --git a/src/api/db/finals.py b/src/api/db/finals.py new file mode 100644 index 000000000..0fb58461b --- /dev/null +++ b/src/api/db/finals.py @@ -0,0 +1,240 @@ +import csv +import re +from psycopg2.extras import RealDictCursor +from ast import literal_eval +import asyncio + +# https://stackoverflow.com/questions/54839933/importerror-with-from-import-x-on-simple-python-files +if __name__ == "__main__": + import connection +else: + from . import connection + + +class Finals: + def __init__(self, db_wrapper): + self.db = db_wrapper + + def delete_by_semester(self, season, year): + # clear cache so this semester does not come up again + self.clear_cache() + return self.db.execute(""" + BEGIN TRANSACTION; + DELETE FROM final + WHERE season=%(Season)s AND year=%(Year)s; + COMMIT; + """, { + "Season": season, + "Year": year + }, isSELECT=False) + + def bulk_delete(self, semesters): + for semester in semesters: + _, error = self.delete_by_semester(semester) + if error: + print("ERROR") + print(error) + return error + # on success, invalidate cache + self.clear_cache() + return None + + def populate_from_csv(self, csv_text): + conn = self.db.get_connection() + reader = csv.DictReader(csv_text) + # for each course entry insert sections and course sessions + with conn.cursor(cursor_factory=RealDictCursor) as transaction: + for row in reader: + try: + # course sessions + days = self.getDays(row['course_days_of_the_week']) + for day in days: + transaction.execute( + """ + INSERT INTO + course_session( + crn, + section, + semester, + time_start, + time_end, + day_of_week, + location, + session_type, + instructor + ) + VALUES ( + NULLIF(%(CRN)s, ''), + NULLIF(%(Section)s, ''), + NULLIF(%(Semester)s, ''), + %(StartTime)s, + %(EndTime)s, + %(WeekDay)s, + NULLIF(%(Location)s, ''), + NULLIF(%(SessionType)s, ''), + NULLIF(%(Instructor)s, '') + ) + ON CONFLICT DO NOTHING; + """, + { + "CRN": row['course_crn'], + "Section": row['course_section'], + "Semester": row['semester'], + "StartTime": row['course_start_time'] if row['course_start_time'] and not row['course_start_time'].isspace() else None, + "EndTime": row['course_end_time'] if row['course_end_time'] and not row['course_end_time'].isspace() else None, + "WeekDay": self.dayToNum(day) if day and not day.isspace() else None, + "Location": row['course_location'], + "SessionType": row['course_type'], + "Instructor": row['course_instructor'] + } + ) + # courses + transaction.execute( + """ + INSERT INTO + course( + crn, + section, + semester, + min_credits, + max_credits, + description, + frequency, + full_title, + date_start, + date_end, + department, + level, + title, + raw_precoreqs, + school, + seats_open, + seats_filled, + seats_total, + tsv + ) + VALUES ( + NULLIF(%(CRN)s, ''), + NULLIF(%(Section)s, ''), + NULLIF(%(Semester)s, ''), + %(MinCredits)s, + %(MaxCredits)s, + NULLIF(%(Description)s, ''), + NULLIF(%(Frequency)s, ''), + NULLIF(%(FullTitle)s, ''), + %(StartDate)s, + %(EndDate)s, + NULLIF(%(Department)s, ''), + %(Level)s, + NULLIF(%(Title)s, ''), + NULLIF(%(RawPrecoreqText)s, ''), + %(School)s, + %(SeatsOpen)s, + %(SeatsFilled)s, + %(SeatsTotal)s, + setweight(to_tsvector(coalesce(%(FullTitle)s, '')), 'A') || + setweight(to_tsvector(coalesce(%(Title)s, '')), 'A') || + setweight(to_tsvector(coalesce(%(Department)s, '')), 'A') || + setweight(to_tsvector(coalesce(%(CRN)s, '')), 'A') || + setweight(to_tsvector(coalesce(%(Level)s, '')), 'B') || + setweight(to_tsvector(coalesce(%(Description)s, '')), 'D') + ) + ON CONFLICT DO NOTHING; + """, + { + "CRN": row['course_crn'], + "Section": row['course_section'], + "Semester": row['semester'], + "MinCredits": row['course_credit_hours'].split("-")[0], + "MaxCredits": row['course_credit_hours'].split("-")[-1], + "Description": row['description'], # new + "Frequency": row['offer_frequency'], # new + "FullTitle": row["full_name"], # new + "StartDate": row['course_start_date'] if row['course_start_date'] and not row['course_start_date'].isspace() else None, + "EndDate": row['course_end_date'] if row['course_end_date'] and not row['course_end_date'].isspace() else None, + "Department": row['course_department'], + "Level": row['course_level'] if row['course_level'] and not row['course_level'].isspace() else None, + "Title": row['course_name'], + "RawPrecoreqText": row['raw_precoreqs'], + "School": row['school'], + "SeatsOpen": row['course_remained'], + "SeatsFilled": row['course_enrolled'], + "SeatsTotal": row['course_max_enroll'] + } + ) + # populate prereqs table, must come after course population b/c ref integrity + # Everything from the CSV comes in as a string, + # but this field is meant to be a list. TODO, what's + # a better way to get around this? + # ast.literal_eval will throw SyntaxError if given an empty string + prereqs = literal_eval(row['prerequisites']) if row['prerequisites'] else [] + for prereq in prereqs: + transaction.execute( + """ + INSERT INTO course_prerequisite ( + department, + level, + prerequisite + ) + VALUES ( + NULLIF(%(Department)s, ''), + %(Level)s, + NULLIF(%(Prerequisite)s, '') + ) + ON CONFLICT DO NOTHING; + """, + { + "Department": row['course_department'], + "Level": row['course_level'] if row['course_level'] and not row['course_level'].isspace() else None, + "Prerequisite": prereq + } + ) + # populate coereqs table, must come after course population b/c ref integrity + coreqs = literal_eval(row['corequisites']) if row['corequisites'] else [] + for coreq in coreqs: + transaction.execute( + """ + INSERT INTO course_corequisite ( + department, + level, + corequisite + ) + VALUES ( + NULLIF(%(Department)s, ''), + %(Level)s, + NULLIF(%(Corequisite)s, '') + ) + ON CONFLICT DO NOTHING; + """, + { + "Department": row['course_department'], + "Level": row['course_level'] if row['course_level'] and not row['course_level'].isspace() else None, + "Corequisite": coreq + } + ) + except Exception as e: + print(e) + conn.rollback() + return (False, e) + conn.commit() + # invalidate cache so we can get new classes + self.clear_cache() + return (True, None) + + def clear_cache(self): + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = None + + if loop and loop.is_running(): + loop.create_task(self.cache.clear(namespace="API_CACHE")) + else: + asyncio.run(self.cache.clear("API_CACHE")) + +if __name__ == "__main__": + # os.chdir(os.path.abspath("../rpi_data")) + # fileNames = glob.glob("*.csv") + csv_text = open('../../../rpi_data/out.csv', 'r') + finals = Finals(connection.db) + finals.populate_from_csv(csv_text) diff --git a/src/api/tables/final.py b/src/api/tables/final.py new file mode 100644 index 000000000..acc28b7ad --- /dev/null +++ b/src/api/tables/final.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, PrimaryKeyConstraint +from sqlalchemy.dialects.postgresql import INTEGER, VARCHAR, TIME + +from .database import Base + +class Final(Base): + __tablename__ = "final" + + season = Column(VARCHAR(length=255)) + year = Column(INTEGER) + major = Column(VARCHAR(length=255)) + course = Column(VARCHAR(length=255)) + section = Column(INTEGER) + start = Column(TIME) + end = Column(TIME) + building = Column(VARCHAR(length=255)) + room = Column(VARCHAR(length=255)) + + __table_args__ = ( + PrimaryKeyConstraint('major', 'course', 'section', 'season', 'year'), + ) From b1a44930a9809f0f364e641d38ae9d929b2e4a93 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 18 Oct 2024 17:31:37 -0400 Subject: [PATCH 13/18] uploading finals schedule --- src/api/db/finals.py | 212 ++---------------- .../versions/2024-10-15_adding_finals.py | 36 +++ src/api/tables/__init__.py | 1 + src/api/tables/final.py | 11 +- 4 files changed, 61 insertions(+), 199 deletions(-) create mode 100644 src/api/migrations/versions/2024-10-15_adding_finals.py diff --git a/src/api/db/finals.py b/src/api/db/finals.py index 0fb58461b..e6f5c1f3b 100644 --- a/src/api/db/finals.py +++ b/src/api/db/finals.py @@ -15,30 +15,6 @@ class Finals: def __init__(self, db_wrapper): self.db = db_wrapper - def delete_by_semester(self, season, year): - # clear cache so this semester does not come up again - self.clear_cache() - return self.db.execute(""" - BEGIN TRANSACTION; - DELETE FROM final - WHERE season=%(Season)s AND year=%(Year)s; - COMMIT; - """, { - "Season": season, - "Year": year - }, isSELECT=False) - - def bulk_delete(self, semesters): - for semester in semesters: - _, error = self.delete_by_semester(semester) - if error: - print("ERROR") - print(error) - return error - # on success, invalidate cache - self.clear_cache() - return None - def populate_from_csv(self, csv_text): conn = self.db.get_connection() reader = csv.DictReader(csv_text) @@ -46,170 +22,35 @@ def populate_from_csv(self, csv_text): with conn.cursor(cursor_factory=RealDictCursor) as transaction: for row in reader: try: - # course sessions - days = self.getDays(row['course_days_of_the_week']) - for day in days: - transaction.execute( - """ - INSERT INTO - course_session( - crn, - section, - semester, - time_start, - time_end, - day_of_week, - location, - session_type, - instructor - ) - VALUES ( - NULLIF(%(CRN)s, ''), - NULLIF(%(Section)s, ''), - NULLIF(%(Semester)s, ''), - %(StartTime)s, - %(EndTime)s, - %(WeekDay)s, - NULLIF(%(Location)s, ''), - NULLIF(%(SessionType)s, ''), - NULLIF(%(Instructor)s, '') - ) - ON CONFLICT DO NOTHING; - """, - { - "CRN": row['course_crn'], - "Section": row['course_section'], - "Semester": row['semester'], - "StartTime": row['course_start_time'] if row['course_start_time'] and not row['course_start_time'].isspace() else None, - "EndTime": row['course_end_time'] if row['course_end_time'] and not row['course_end_time'].isspace() else None, - "WeekDay": self.dayToNum(day) if day and not day.isspace() else None, - "Location": row['course_location'], - "SessionType": row['course_type'], - "Instructor": row['course_instructor'] - } - ) - # courses + # finals transaction.execute( """ INSERT INTO - course( - crn, - section, + final( semester, - min_credits, - max_credits, - description, - frequency, - full_title, - date_start, - date_end, - department, - level, - title, - raw_precoreqs, - school, - seats_open, - seats_filled, - seats_total, - tsv + course, + section, + "start", + "end", + room_assignment ) VALUES ( - NULLIF(%(CRN)s, ''), - NULLIF(%(Section)s, ''), - NULLIF(%(Semester)s, ''), - %(MinCredits)s, - %(MaxCredits)s, - NULLIF(%(Description)s, ''), - NULLIF(%(Frequency)s, ''), - NULLIF(%(FullTitle)s, ''), - %(StartDate)s, - %(EndDate)s, - NULLIF(%(Department)s, ''), - %(Level)s, - NULLIF(%(Title)s, ''), - NULLIF(%(RawPrecoreqText)s, ''), - %(School)s, - %(SeatsOpen)s, - %(SeatsFilled)s, - %(SeatsTotal)s, - setweight(to_tsvector(coalesce(%(FullTitle)s, '')), 'A') || - setweight(to_tsvector(coalesce(%(Title)s, '')), 'A') || - setweight(to_tsvector(coalesce(%(Department)s, '')), 'A') || - setweight(to_tsvector(coalesce(%(CRN)s, '')), 'A') || - setweight(to_tsvector(coalesce(%(Level)s, '')), 'B') || - setweight(to_tsvector(coalesce(%(Description)s, '')), 'D') + %(Semester)s, + %(Course)s, + %(Section)s, + TO_TIMESTAMP(%(Start)s, 'YYYY-MM-DD HH24:MI:SS'), + TO_TIMESTAMP(%(End)s, 'YYYY-MM-DD HH24:MI:SS'), + %(Room_Assignment)s ) - ON CONFLICT DO NOTHING; + ON CONFLICT (semester, course, section, room_assignment) DO NOTHING; """, { - "CRN": row['course_crn'], - "Section": row['course_section'], - "Semester": row['semester'], - "MinCredits": row['course_credit_hours'].split("-")[0], - "MaxCredits": row['course_credit_hours'].split("-")[-1], - "Description": row['description'], # new - "Frequency": row['offer_frequency'], # new - "FullTitle": row["full_name"], # new - "StartDate": row['course_start_date'] if row['course_start_date'] and not row['course_start_date'].isspace() else None, - "EndDate": row['course_end_date'] if row['course_end_date'] and not row['course_end_date'].isspace() else None, - "Department": row['course_department'], - "Level": row['course_level'] if row['course_level'] and not row['course_level'].isspace() else None, - "Title": row['course_name'], - "RawPrecoreqText": row['raw_precoreqs'], - "School": row['school'], - "SeatsOpen": row['course_remained'], - "SeatsFilled": row['course_enrolled'], - "SeatsTotal": row['course_max_enroll'] - } - ) - # populate prereqs table, must come after course population b/c ref integrity - # Everything from the CSV comes in as a string, - # but this field is meant to be a list. TODO, what's - # a better way to get around this? - # ast.literal_eval will throw SyntaxError if given an empty string - prereqs = literal_eval(row['prerequisites']) if row['prerequisites'] else [] - for prereq in prereqs: - transaction.execute( - """ - INSERT INTO course_prerequisite ( - department, - level, - prerequisite - ) - VALUES ( - NULLIF(%(Department)s, ''), - %(Level)s, - NULLIF(%(Prerequisite)s, '') - ) - ON CONFLICT DO NOTHING; - """, - { - "Department": row['course_department'], - "Level": row['course_level'] if row['course_level'] and not row['course_level'].isspace() else None, - "Prerequisite": prereq - } - ) - # populate coereqs table, must come after course population b/c ref integrity - coreqs = literal_eval(row['corequisites']) if row['corequisites'] else [] - for coreq in coreqs: - transaction.execute( - """ - INSERT INTO course_corequisite ( - department, - level, - corequisite - ) - VALUES ( - NULLIF(%(Department)s, ''), - %(Level)s, - NULLIF(%(Corequisite)s, '') - ) - ON CONFLICT DO NOTHING; - """, - { - "Department": row['course_department'], - "Level": row['course_level'] if row['course_level'] and not row['course_level'].isspace() else None, - "Corequisite": coreq + "Semester": row['Season'] + ' ' + row['Year'], + "Course": row['Major'] + '-' + row['Course'], + "Section": 1 if row['Section'] == '' else row['Section'], + "Start": row['Start'], + "End": row['End'], + "Room_Assignment": row['Building'] + '-' + row['Room_Number'] } ) except Exception as e: @@ -217,21 +58,8 @@ def populate_from_csv(self, csv_text): conn.rollback() return (False, e) conn.commit() - # invalidate cache so we can get new classes - self.clear_cache() return (True, None) - def clear_cache(self): - try: - loop = asyncio.get_running_loop() - except RuntimeError: - loop = None - - if loop and loop.is_running(): - loop.create_task(self.cache.clear(namespace="API_CACHE")) - else: - asyncio.run(self.cache.clear("API_CACHE")) - if __name__ == "__main__": # os.chdir(os.path.abspath("../rpi_data")) # fileNames = glob.glob("*.csv") diff --git a/src/api/migrations/versions/2024-10-15_adding_finals.py b/src/api/migrations/versions/2024-10-15_adding_finals.py new file mode 100644 index 000000000..9203cbe79 --- /dev/null +++ b/src/api/migrations/versions/2024-10-15_adding_finals.py @@ -0,0 +1,36 @@ +"""adding finals + +Revision ID: 1ff4f05483ad +Revises: 6c9a1ffc58a5 +Create Date: 2024-10-15 21:38:59.576120 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '6c9a1ffc58a5' +down_revision = 'c959c263997f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('final', + sa.Column('semester', sa.VARCHAR(length=255), nullable=False), + sa.Column('course', sa.VARCHAR(length=255), nullable=False), + sa.Column('section', sa.INTEGER(), nullable=False), + sa.Column('start', postgresql.TIME(), nullable=True), + sa.Column('end', postgresql.TIME(), nullable=True), + sa.Column('room_assignment', sa.VARCHAR(length=255), nullable=False), + sa.PrimaryKeyConstraint('course', 'section', 'semester', 'room_assignment') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('final') + # ### end Alembic commands ### diff --git a/src/api/tables/__init__.py b/src/api/tables/__init__.py index 540c49d23..e26017086 100644 --- a/src/api/tables/__init__.py +++ b/src/api/tables/__init__.py @@ -4,6 +4,7 @@ from .course_session import CourseSession from .course import Course from .event import Event +from .final import Final from .semester_date_range import SemesterDateRange from .semester_info import SemesterInfo from .student_course_selection import StudentCourseSelection diff --git a/src/api/tables/final.py b/src/api/tables/final.py index acc28b7ad..77e51899f 100644 --- a/src/api/tables/final.py +++ b/src/api/tables/final.py @@ -6,16 +6,13 @@ class Final(Base): __tablename__ = "final" - season = Column(VARCHAR(length=255)) - year = Column(INTEGER) - major = Column(VARCHAR(length=255)) + semester = Column(VARCHAR(length=255)) course = Column(VARCHAR(length=255)) section = Column(INTEGER) start = Column(TIME) end = Column(TIME) - building = Column(VARCHAR(length=255)) - room = Column(VARCHAR(length=255)) + room_assignment = Column(VARCHAR(length=255)) __table_args__ = ( - PrimaryKeyConstraint('major', 'course', 'section', 'season', 'year'), - ) + PrimaryKeyConstraint('semester', 'course', 'section'), + ) \ No newline at end of file From 633f194bd855bffdd7b204a22d828fa92c837843 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 25 Oct 2024 16:25:24 -0400 Subject: [PATCH 14/18] bug fixes finalize support for uploading and deleting the finals table --- rpi_data/out.csv | 2 +- src/api/app.py | 17 +++++++----- src/api/db/finals.py | 26 ++++++++++++++++--- .../versions/2024-10-15_adding_finals.py | 6 ++--- src/api/tables/final.py | 2 +- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/rpi_data/out.csv b/rpi_data/out.csv index 9eece6687..4a158ea12 100644 --- a/rpi_data/out.csv +++ b/rpi_data/out.csv @@ -428,4 +428,4 @@ 426,FALL,2024,PHYS,6050,,2024-12-18 08:00:00,2024-12-18 11:00:00,LOW,4050 427,FALL,2024,PHYS,1250,1,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 428,FALL,2024,PHYS,1250,2,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 -429,FALL,2024,PHYS,1250,3,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 +429,FALL,2024,PHYS,1250,3,2024-12-18 11:30:00,2024-12-18 14:30:00,DCC,337 \ No newline at end of file diff --git a/src/api/app.py b/src/api/app.py index b2efdbf6b..3f6ec1990 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -16,6 +16,7 @@ import db.semester_info as SemesterInfo import db.semester_date_mapping as DateMapping import db.admin as AdminInfo +import pandas as pd import db.student_course_selection as CourseSelect import db.user as UserModel import controller.user as user_controller @@ -225,12 +226,16 @@ async def uploadHandler( contents = await file.read() csv_file = StringIO(contents.decode()) # Populate DB from CSV - isSuccess, error = finals.populate_from_csv(csv_file) - if (isSuccess): - return Response(status_code=200) - else: - print(error) - return Response(error.__str__(), status_code=500) + error = finals.populate_from_csv(csv_file) + return Response(error.__str__(), status_code=500) if error else Response("Upload Successful", status_code=200) + +@app.delete('/api/final/{semester}') +async def deleteHandler(semester: str): + if not semester: + return Response("No semester received", 400) + print(semester) + error = finals.bulk_delete(["FALL 2024"]) + return Response(error.__str__(), status_code=500) if error else Response("Delete Successful", status_code=200) @app.post('/api/mapDateRangeToSemesterPart') async def map_date_range_to_semester_part_handler(request: Request): diff --git a/src/api/db/finals.py b/src/api/db/finals.py index e6f5c1f3b..72ad62646 100644 --- a/src/api/db/finals.py +++ b/src/api/db/finals.py @@ -42,12 +42,12 @@ def populate_from_csv(self, csv_text): TO_TIMESTAMP(%(End)s, 'YYYY-MM-DD HH24:MI:SS'), %(Room_Assignment)s ) - ON CONFLICT (semester, course, section, room_assignment) DO NOTHING; + ON CONFLICT (semester, course, section, start, room_assignment) DO NOTHING; """, { "Semester": row['Season'] + ' ' + row['Year'], "Course": row['Major'] + '-' + row['Course'], - "Section": 1 if row['Section'] == '' else row['Section'], + "Section": "1" if row['Section'] == '' else row['Section'], "Start": row['Start'], "End": row['End'], "Room_Assignment": row['Building'] + '-' + row['Room_Number'] @@ -56,9 +56,27 @@ def populate_from_csv(self, csv_text): except Exception as e: print(e) conn.rollback() - return (False, e) + return e conn.commit() - return (True, None) + return None + + def delete_by_semester(self, semester): + return self.db.execute(""" + BEGIN TRANSACTION; + DELETE FROM final + WHERE semester=%(Semester)s; + COMMIT; + """, { + "Semester": semester + }, isSELECT=False) + + def bulk_delete(self, semesters): + for semester in semesters: + _, error = self.delete_by_semester(semester) + if error: + print(error) + return error + return None if __name__ == "__main__": # os.chdir(os.path.abspath("../rpi_data")) diff --git a/src/api/migrations/versions/2024-10-15_adding_finals.py b/src/api/migrations/versions/2024-10-15_adding_finals.py index 9203cbe79..fdaf945ec 100644 --- a/src/api/migrations/versions/2024-10-15_adding_finals.py +++ b/src/api/migrations/versions/2024-10-15_adding_finals.py @@ -22,10 +22,10 @@ def upgrade(): sa.Column('semester', sa.VARCHAR(length=255), nullable=False), sa.Column('course', sa.VARCHAR(length=255), nullable=False), sa.Column('section', sa.INTEGER(), nullable=False), - sa.Column('start', postgresql.TIME(), nullable=True), - sa.Column('end', postgresql.TIME(), nullable=True), + sa.Column('start', postgresql.TIMESTAMP(), nullable=False), + sa.Column('end', postgresql.TIMESTAMP(), nullable=False), sa.Column('room_assignment', sa.VARCHAR(length=255), nullable=False), - sa.PrimaryKeyConstraint('course', 'section', 'semester', 'room_assignment') + sa.PrimaryKeyConstraint('semester', 'course', 'section', 'start', 'room_assignment') ) # ### end Alembic commands ### diff --git a/src/api/tables/final.py b/src/api/tables/final.py index 77e51899f..104fe5eaf 100644 --- a/src/api/tables/final.py +++ b/src/api/tables/final.py @@ -14,5 +14,5 @@ class Final(Base): room_assignment = Column(VARCHAR(length=255)) __table_args__ = ( - PrimaryKeyConstraint('semester', 'course', 'section'), + PrimaryKeyConstraint('semester', 'course', 'section', 'start', 'room_assignment'), ) \ No newline at end of file From 88ca81e9c906170dffd041f6a8b58f8b3d67b9dd Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Fri, 25 Oct 2024 16:34:39 -0400 Subject: [PATCH 15/18] fix error output --- src/api/app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api/app.py b/src/api/app.py index 3f6ec1990..514197088 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -213,7 +213,7 @@ async def uploadJSON( print(error) return Response(error.__str__(), status_code=500) -@app.post('/api/bulkFinalUpload') +@app.post('/api/final') async def uploadHandler( file: UploadFile = File(...)): # check for user files @@ -234,9 +234,10 @@ async def deleteHandler(semester: str): if not semester: return Response("No semester received", 400) print(semester) - error = finals.bulk_delete(["FALL 2024"]) + _, error = finals.delete_by_semester(semester) return Response(error.__str__(), status_code=500) if error else Response("Delete Successful", status_code=200) + @app.post('/api/mapDateRangeToSemesterPart') async def map_date_range_to_semester_part_handler(request: Request): # This depends on date_start, date_end, and semester_part_name being From 43c34c924871a49e1468e594c5ceee4b635f1acf Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Tue, 5 Nov 2024 16:07:49 -0500 Subject: [PATCH 16/18] actually working semester deletion --- .../2024-11-01_semester_course_foreign_key.py | 34 +++++++++++++++++++ ...2024-11-05_semester_course_relationship.py | 34 +++++++++++++++++++ src/api/tables/course.py | 2 ++ src/api/tables/semester_info.py | 9 +++-- 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/api/migrations/versions/2024-11-01_semester_course_foreign_key.py create mode 100644 src/api/migrations/versions/2024-11-05_semester_course_relationship.py diff --git a/src/api/migrations/versions/2024-11-01_semester_course_foreign_key.py b/src/api/migrations/versions/2024-11-01_semester_course_foreign_key.py new file mode 100644 index 000000000..d5be3f692 --- /dev/null +++ b/src/api/migrations/versions/2024-11-01_semester_course_foreign_key.py @@ -0,0 +1,34 @@ +"""semester course foreign key + +Revision ID: 032a3a88b6d2 +Revises: c959c263997f +Create Date: 2024-11-01 21:24:08.801806 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '032a3a88b6d2' +down_revision = 'c959c263997f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('course', sa.Column('professor', sa.VARCHAR(length=255), nullable=True)) + op.add_column('course', sa.Column('semester_id', sa.VARCHAR(length=255), nullable=True)) + op.create_foreign_key(None, 'course', 'semester_info', ['semester_id'], ['semester'], ondelete='CASCADE') + op.drop_column('course', 'semester') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('course', sa.Column('semester', sa.VARCHAR(length=255), autoincrement=False, nullable=True)) + op.drop_constraint(None, 'course', type_='foreignkey') + op.drop_column('course', 'semester_id') + op.drop_column('course', 'professor') + # ### end Alembic commands ### diff --git a/src/api/migrations/versions/2024-11-05_semester_course_relationship.py b/src/api/migrations/versions/2024-11-05_semester_course_relationship.py new file mode 100644 index 000000000..32c011e5b --- /dev/null +++ b/src/api/migrations/versions/2024-11-05_semester_course_relationship.py @@ -0,0 +1,34 @@ +"""semester course relationship + +Revision ID: f62dd5e95906 +Revises: 032a3a88b6d2 +Create Date: 2024-11-05 21:04:15.417023 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f62dd5e95906' +down_revision = '032a3a88b6d2' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('course', sa.Column('semester', sa.VARCHAR(length=255), nullable=True)) + op.drop_constraint('course_semester_id_fkey', 'course', type_='foreignkey') + op.create_foreign_key(None, 'course', 'semester_info', ['semester'], ['semester'], ondelete='CASCADE') + op.drop_column('course', 'semester_id') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('course', sa.Column('semester_id', sa.VARCHAR(length=255), autoincrement=False, nullable=True)) + op.drop_constraint(None, 'course', type_='foreignkey') + op.create_foreign_key('course_semester_id_fkey', 'course', 'semester_info', ['semester_id'], ['semester'], ondelete='CASCADE') + op.drop_column('course', 'semester') + # ### end Alembic commands ### diff --git a/src/api/tables/course.py b/src/api/tables/course.py index 60c235e57..a92930078 100644 --- a/src/api/tables/course.py +++ b/src/api/tables/course.py @@ -11,6 +11,8 @@ class Course(Base): section = Column(VARCHAR(length=255)) semester = Column(VARCHAR(length=255), ForeignKey("semester_info.semester", ondelete="CASCADE")) + semester_r = relationship("SemesterInfo", back_populates="courses") + professor = Column(VARCHAR(length=255)) min_credits = Column(INTEGER) diff --git a/src/api/tables/semester_info.py b/src/api/tables/semester_info.py index 5f647b2f9..da302b663 100644 --- a/src/api/tables/semester_info.py +++ b/src/api/tables/semester_info.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, PrimaryKeyConstraint +from sqlalchemy import Column from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import VARCHAR, BOOLEAN @@ -10,4 +10,9 @@ class SemesterInfo(Base): semester = Column(VARCHAR(length=255), primary_key=True) public = Column(BOOLEAN) - courses = relationship("Course", backref="semester_info", passive_deletes=True) \ No newline at end of file + courses = relationship( + "Course", + back_populates="semester_info", + cascade="all, delete", + passive_deletes=True, + ) \ No newline at end of file From d800f46f51d1c3da04a23f088c24df66e3f99fba Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Tue, 5 Nov 2024 16:11:31 -0500 Subject: [PATCH 17/18] Update course.py --- src/api/tables/course.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/tables/course.py b/src/api/tables/course.py index a92930078..1e42b6363 100644 --- a/src/api/tables/course.py +++ b/src/api/tables/course.py @@ -1,5 +1,5 @@ from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import TEXT, INTEGER, VARCHAR, DATE, TSVECTOR from .database import Base From f2975a6da997c7fc0fa10ec38cb558e08cde9ed5 Mon Sep 17 00:00:00 2001 From: Jonathan Friedrich Date: Tue, 5 Nov 2024 16:42:43 -0500 Subject: [PATCH 18/18] update finals added get requests for finals by semester, also added cache for the finals table --- src/api/app.py | 11 ++++++++++- src/api/db/finals.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/api/app.py b/src/api/app.py index 514197088..6a7419114 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -43,7 +43,7 @@ db_conn = connection.db class_info = ClassInfo.ClassInfo(db_conn) courses = Courses.Courses(db_conn, FastAPICache) -finals = Finals.Finals(db_conn) +finals = Finals.Finals(db_conn, FastAPICache) date_range_map = DateMapping.semester_date_mapping(db_conn) admin_info = AdminInfo.Admin(db_conn) course_select = CourseSelect.student_course_selection(db_conn) @@ -229,6 +229,15 @@ async def uploadHandler( error = finals.populate_from_csv(csv_file) return Response(error.__str__(), status_code=500) if error else Response("Upload Successful", status_code=200) +@app.get('/api/final/{semester}') +@cache(expire=Constants.DAY_IN_SECONDS, coder=PickleCoder, namespace="API_CACHE") +async def getHandler(semester: str): + if not semester: + return Response("No semester received", 400) + print(semester) + final, error = finals.get_by_semester(semester) + return final if not error else Response(error, status_code=500) + @app.delete('/api/final/{semester}') async def deleteHandler(semester: str): if not semester: diff --git a/src/api/db/finals.py b/src/api/db/finals.py index 72ad62646..9ee545d68 100644 --- a/src/api/db/finals.py +++ b/src/api/db/finals.py @@ -12,8 +12,9 @@ class Finals: - def __init__(self, db_wrapper): + def __init__(self, db_wrapper, cache): self.db = db_wrapper + self.cache = cache def populate_from_csv(self, csv_text): conn = self.db.get_connection() @@ -28,7 +29,7 @@ def populate_from_csv(self, csv_text): INSERT INTO final( semester, - course, + course, section, "start", "end", @@ -58,9 +59,20 @@ def populate_from_csv(self, csv_text): conn.rollback() return e conn.commit() + self.clear_cache() return None + def get_by_semester(self, semester): + return self.db.execute(""" + SELECT * FROM final + WHERE semester=%(Semester)s + ORDER BY start ASC; + """, { + "Semester": semester + }, isSELECT=True) + def delete_by_semester(self, semester): + self.clear_cache() return self.db.execute(""" BEGIN TRANSACTION; DELETE FROM final @@ -76,8 +88,20 @@ def bulk_delete(self, semesters): if error: print(error) return error + self.clear_cache() return None + def clear_cache(self): + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = None + + if loop and loop.is_running(): + loop.create_task(self.cache.clear(namespace="API_CACHE")) + else: + asyncio.run(self.cache.clear("API_CACHE")) + if __name__ == "__main__": # os.chdir(os.path.abspath("../rpi_data")) # fileNames = glob.glob("*.csv")