From 8ba0d94bae93e4f40263e92390740ad0ecde69c1 Mon Sep 17 00:00:00 2001 From: Ludwig Kristoffersson Date: Sat, 20 Apr 2024 20:03:50 +0200 Subject: [PATCH 1/4] add admin api that allows users to retrieve the name and id of courses they administrate --- http_api/__init__.py | 2 ++ http_api/routers/admin.py | 41 ++++++++++++++++++++++++++++++++++++ tests/conftest.py | 1 + tests/http_api/admin_test.py | 12 +++++++++++ 4 files changed, 56 insertions(+) create mode 100644 http_api/routers/admin.py create mode 100644 tests/http_api/admin_test.py diff --git a/http_api/__init__.py b/http_api/__init__.py index 94c7bb3..854ae03 100644 --- a/http_api/__init__.py +++ b/http_api/__init__.py @@ -9,6 +9,7 @@ sessions, health, index, + admin, chat, ) from config.logger import log @@ -29,6 +30,7 @@ def get_app(): app.include_router(chat.router) app.include_router(health.router) app.include_router(feedback.router) + app.include_router(admin.router) return app diff --git a/http_api/routers/admin.py b/http_api/routers/admin.py new file mode 100644 index 0000000..59bf1b9 --- /dev/null +++ b/http_api/routers/admin.py @@ -0,0 +1,41 @@ +from typing import List + +from fastapi import APIRouter, Depends +from pydantic import BaseModel + +from db.actions.course import find_course_by_canvas_id +from http_api.auth import get_current_session +from config.logger import log +from db.models import Session + +router = APIRouter() + + +class Course(BaseModel): + canvas_id: str + language: str + name: str + + +class CourseResponse(BaseModel): + courses: List[Course] + + +@router.get( + '/admin/courses', + dependencies=[Depends(get_current_session)], + response_model=CourseResponse +) +async def get_courses_user_can_administrate(session: Session = Depends(get_current_session)) -> CourseResponse: + courses = [] + for canvas_id in session.admin_courses: + course = find_course_by_canvas_id(canvas_id) + if course is None: + log().warning(f"found course id that doesn't exist: {canvas_id}, in session: {session.public_id}") + continue + courses.append(Course( + canvas_id=course.canvas_id, + language=course.language, + name=course.name + )) + return CourseResponse(courses=courses) diff --git a/tests/conftest.py b/tests/conftest.py index d6591bd..f7ace6b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -144,6 +144,7 @@ def __init__(self, session: Session): consent=True, default_llm_model_name=LLMModel.MISTRAL_7B_INSTRUCT, default_index_type=IndexType.NO_INDEX, + admin_courses=[] ) valid_session.save() diff --git a/tests/http_api/admin_test.py b/tests/http_api/admin_test.py new file mode 100644 index 0000000..c7a8111 --- /dev/null +++ b/tests/http_api/admin_test.py @@ -0,0 +1,12 @@ + +def test_user_courses_user_can_administrate_are_returned_in_list_of_courses( + api_client, + valid_course, + authenticated_session, +): + authenticated_session.session.admin_courses.append(valid_course.canvas_id) + authenticated_session.session.save() + + response = api_client.get('/admin/courses', headers=authenticated_session.headers) + assert response.status_code == 200 + assert response.json()['courses'][0]['canvas_id'] == valid_course.canvas_id From 34837c2a1ca6ff92fcd2c320373a7a133345cb3c Mon Sep 17 00:00:00 2001 From: Ludwig Kristoffersson Date: Sat, 20 Apr 2024 20:04:24 +0200 Subject: [PATCH 2/4] add fetchCourses to admin api calls --- gui/api/admin.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 gui/api/admin.ts diff --git a/gui/api/admin.ts b/gui/api/admin.ts new file mode 100644 index 0000000..3b79b7c --- /dev/null +++ b/gui/api/admin.ts @@ -0,0 +1,31 @@ +import { HttpError, getSessionId, makeUrl } from "@/api/http"; + +export interface Course { + canvas_id: string; + language: string; + name: string; +} + +export interface Courses { + courses: Course[]; +} + +export async function fetchCourses(): Promise { + const sessionCookie = getSessionId() as string; + + const response = await fetch(makeUrl(`/admin/courses`), { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-Session-ID": sessionCookie, + }, + }); + + if (!response.ok) { + const errorBody = await response.json(); + throw new HttpError(response, errorBody, response.status); + } + + const data = (await response.json()) as Courses; + return data; +} From 2734c8d48b290f454e2d302710c1d0343dc8f1f0 Mon Sep 17 00:00:00 2001 From: Ludwig Kristoffersson Date: Sat, 20 Apr 2024 20:05:52 +0200 Subject: [PATCH 3/4] add course list component that shows the courses a user can administrate --- .../navigation/CourseList/CourseList.tsx | 39 +++++++++++++++++++ .../navigation/CourseList/styles.module.css | 4 ++ gui/components/navigation/index.ts | 3 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 gui/components/navigation/CourseList/CourseList.tsx create mode 100644 gui/components/navigation/CourseList/styles.module.css diff --git a/gui/components/navigation/CourseList/CourseList.tsx b/gui/components/navigation/CourseList/CourseList.tsx new file mode 100644 index 0000000..d5ecbf1 --- /dev/null +++ b/gui/components/navigation/CourseList/CourseList.tsx @@ -0,0 +1,39 @@ +import { Button, SimpleGrid, Title } from "@mantine/core"; +import { useQuery } from "@tanstack/react-query"; + +import { fetchCourses } from "@/api/admin"; + +import styles from "./styles.module.css"; + +export default function CourseList() { + const coursesQuery = useQuery({ + queryKey: ["courses"], + queryFn: () => fetchCourses(), + }); + + if (!coursesQuery.data) { + return <>; + } + + if (coursesQuery.data.courses.length === 0) { + return <>; + } + + return ( + + Courses + {coursesQuery.data.courses.map((course) => ( + + ))} + + ); +} diff --git a/gui/components/navigation/CourseList/styles.module.css b/gui/components/navigation/CourseList/styles.module.css new file mode 100644 index 0000000..42a7cfe --- /dev/null +++ b/gui/components/navigation/CourseList/styles.module.css @@ -0,0 +1,4 @@ + +.root { + padding-top: 20px; +} diff --git a/gui/components/navigation/index.ts b/gui/components/navigation/index.ts index 726e732..c94d29a 100644 --- a/gui/components/navigation/index.ts +++ b/gui/components/navigation/index.ts @@ -1,3 +1,4 @@ +import CourseList from "./CourseList/CourseList"; import HeaderNavbar from "./HeaderNavbar/HeaderNavbar"; -export { HeaderNavbar }; +export { HeaderNavbar, CourseList }; From f59f53dc7b93bc270532fe5c74a53adf79cb54bf Mon Sep 17 00:00:00 2001 From: Ludwig Kristoffersson Date: Sat, 20 Apr 2024 20:11:44 +0200 Subject: [PATCH 4/4] add course list to navbar --- gui/components/navigation/HeaderNavbar/HeaderNavbar.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/components/navigation/HeaderNavbar/HeaderNavbar.tsx b/gui/components/navigation/HeaderNavbar/HeaderNavbar.tsx index 850a15d..1541ac5 100644 --- a/gui/components/navigation/HeaderNavbar/HeaderNavbar.tsx +++ b/gui/components/navigation/HeaderNavbar/HeaderNavbar.tsx @@ -5,6 +5,8 @@ import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; import { MouseEventHandler, useEffect } from "react"; +import { CourseList } from "@/components/navigation"; + import { getSession } from "@/api/session"; const ContactEmail = "ludwigkr@kth.se"; @@ -68,6 +70,8 @@ export default function HeaderNavbar(props: HeaderNavbarProps) { + + );