diff --git a/README.md b/README.md index 615ad1e..3bcb6b2 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ for the frontend extension. ## Requirements -- JupyterLab >= 3.0.0 +- JupyterLab >= 4.0.0 ## Install @@ -53,24 +53,27 @@ The `jlpm` command is JupyterLab's pinned version of [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use `yarn` or `npm` in lieu of `jlpm` below. +Installing the extension ```bash # Clone the repo to your local environment # Change directory to the jupyterlab_eduhelx_submission directory # Install package in development mode -pip install -e ".[test]" +pip install -ve ".[test]" # Link your development version of the extension with JupyterLab jupyter labextension develop . --overwrite # Server extension must be manually installed in develop mode jupyter server extension enable jupyterlab_eduhelx_submission -# Rebuild extension Typescript source after making changes -jlpm build ``` -You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension. - +Frontend Development (after install) ```bash -# Watch the source directory in one terminal, automatically rebuilding when needed jlpm watch +``` + +Backend Development (after install) +```bash +cp .env.sample .env +source .env # Run JupyterLab in another terminal jupyter lab ``` diff --git a/jupyterlab_eduhelx_submission/handlers.py b/jupyterlab_eduhelx_submission/handlers.py index 015b34f..1541a55 100644 --- a/jupyterlab_eduhelx_submission/handlers.py +++ b/jupyterlab_eduhelx_submission/handlers.py @@ -9,25 +9,30 @@ from jupyter_server.utils import url_path_join from pathlib import Path from .config import ExtensionConfig -from .api import Api -from .git import ( +from eduhelx_utils.git import ( InvalidGitRepositoryException, - get_remote, get_repo_root, clone_repository, + clone_repository, get_tail_commit_id, get_repo_name, add_remote, - stage_files, commit, push + stage_files, commit, push, get_commit_info ) +from eduhelx_utils.api import Api from .student_repo import StudentClassRepo, NotStudentClassRepositoryException from .process import execute from ._version import __version__ -class DependencyContainer: +class AppContext: def __init__(self, serverapp): self.serverapp = serverapp self.config = ExtensionConfig(self.serverapp) - self.api = Api(self.config) + self.api = Api( + api_url=self.config.GRADER_API_URL, + user_onyen=self.config.USER_ONYEN, + user_autogen_password=self.config.USER_AUTOGEN_PASSWORD, + jwt_refresh_leeway_seconds=self.config.JWT_REFRESH_LEEWAY_SECONDS + ) class BaseHandler(APIHandler): - context: DependencyContainer = None + context: AppContext = None @property def config(self) -> ExtensionConfig: @@ -121,7 +126,7 @@ async def post(self): class CourseAndStudentHandler(BaseHandler): @tornado.web.authenticated async def get(self): - student = await self.api.get_student() + student = await self.api.get_my_user() course = await self.api.get_course() self.finish(json.dumps({ "student": student, @@ -134,8 +139,8 @@ async def get(self): current_path: str = self.get_argument("path") current_path_abs = os.path.realpath(current_path) - student = await self.api.get_student() - assignments = await self.api.get_assignments() + student = await self.api.get_my_user() + assignments = await self.api.get_my_assignments() course = await self.api.get_course() value = { @@ -167,7 +172,9 @@ async def get(self): # The student is in their repo, but we still need to check if they're actually in an assignment directory. current_assignment = student_repo.current_assignment if current_assignment is not None: - submissions = await self.api.get_assignment_submissions(current_assignment["id"], git_path=student_repo.repo_root) + submissions = await self.api.get_my_submissions(current_assignment["id"]) + for submission in submissions: + submission["commit"] = get_commit_info(submission["commit_id"], path=student_repo.repo_root) current_assignment["submissions"] = submissions value["current_assignment"] = current_assignment @@ -185,8 +192,8 @@ async def post(self): current_path: str = data["current_path"] current_path_abs = os.path.realpath(current_path) - student = await self.api.get_student() - assignments = await self.api.get_assignments() + student = await self.api.get_my_user() + assignments = await self.api.get_my_assignments() course = await self.api.get_course() try: @@ -220,7 +227,7 @@ async def post(self): ) push("origin", "master", path=student_repo.repo_root) try: - await self.api.post_submission( + await self.api.create_submission( student_repo.current_assignment["id"], commit_id ) @@ -242,7 +249,7 @@ async def get(self): def setup_handlers(server_app): web_app = server_app.web_app - BaseHandler.context = DependencyContainer(server_app) + BaseHandler.context = AppContext(server_app) host_pattern = ".*$" diff --git a/jupyterlab_eduhelx_submission/student_repo.py b/jupyterlab_eduhelx_submission/student_repo.py index eec6ed3..7556c18 100644 --- a/jupyterlab_eduhelx_submission/student_repo.py +++ b/jupyterlab_eduhelx_submission/student_repo.py @@ -1,7 +1,7 @@ import os from pathlib import Path -from .git import InvalidGitRepositoryException -from . import git +from eduhelx_utils.git import InvalidGitRepositoryException +from eduhelx_utils import git class NotStudentClassRepositoryException(Exception): pass diff --git a/package.json b/package.json index de8b563..86db218 100644 --- a/package.json +++ b/package.json @@ -58,16 +58,16 @@ "watch:labextension": "jupyter labextension watch ." }, "dependencies": { - "@jupyterlab/application": "^3.6.5", - "@jupyterlab/apputils": "^3.6.5", + "@jupyterlab/application": "^4.0.0", + "@jupyterlab/apputils": "^4.0.0", "@jupyterlab/coreutils": "^6.0.0", - "@jupyterlab/filebrowser": "^3.6.5", + "@jupyterlab/filebrowser": "^4.0.11", "@jupyterlab/services": "^7.0.0", - "@jupyterlab/ui-components": "^3.6.5", - "@lumino/commands": "^1.21.1", - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4", - "@lumino/signaling": "^1.11.1", + "@jupyterlab/ui-components": "^4.0.11", + "@lumino/commands": "^2.0.0", + "@lumino/coreutils": "^2.1.2", + "@lumino/disposable": "^2.1.2", + "@lumino/signaling": "^2.1.2", "@lumino/widgets": "^2.2.0", "@material-ui/core": "^4.8.2", "@material-ui/icons": "^4.5.1", @@ -81,7 +81,7 @@ "uuid": "^9.0.0" }, "devDependencies": { - "@jupyterlab/builder": "^3.6.5", + "@jupyterlab/builder": "^4.0.0", "@jupyterlab/testutils": "^4.0.0", "@types/jest": "^29.2.0", "@types/json-schema": "^7.0.11", diff --git a/pyproject.toml b/pyproject.toml index b35327e..f778161 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version"] +requires = ["hatchling==1.10.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version"] build-backend = "hatchling.build" [project] @@ -25,7 +25,8 @@ dependencies = [ "jupyter_server>=2.0.1,<3", "requests>=2.0.1,<3", "httpx>=0,<1", - "PyJWT>=2.0.0,<3" + "PyJWT>=2.0.0,<3", + "eduhelx_utils@git+https://github.com/helxplatform/eduhelx-utils" ] dynamic = ["version", "description", "authors", "urls", "keywords"] @@ -41,6 +42,9 @@ test = [ [tool.hatch.version] source = "nodejs" +[tool.hatch.metadata] +allow-direct-references = true + [tool.hatch.metadata.hooks.nodejs] fields = ["description", "authors", "urls"] diff --git a/src/components/assignment-panel/assignment-submissions/assignment-submissions.tsx b/src/components/assignment-panel/assignment-submissions/assignment-submissions.tsx index 93c9bea..007708f 100644 --- a/src/components/assignment-panel/assignment-submissions/assignment-submissions.tsx +++ b/src/components/assignment-panel/assignment-submissions/assignment-submissions.tsx @@ -38,12 +38,12 @@ export const AssignmentSubmissions = ({ ...props }: AssignmentSubmissionsProps)
Submissions
- { submissionSource!.map((submission) => ( + { submissionSource!.map((submission, i) => ( }> - { `#${ submission.id }` } + { `#${ submissionSource!.length - i }` }
diff --git a/src/index.ts b/src/index.ts index 6108556..5b9309d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,22 +4,22 @@ import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application' -import { IFileBrowserFactory, FileBrowserModel } from '@jupyterlab/filebrowser' +import { FileBrowserModel, IDefaultFileBrowser } from '@jupyterlab/filebrowser' import { Dialog, showErrorMessage } from '@jupyterlab/apputils' import { IChangedArgs } from '@jupyterlab/coreutils' import { getServerSettings, IServerSettings } from './api' import { AssignmentWidget } from './widgets' import { EduhelxSubmissionModel } from './model' import { submissionIcon } from './style/icons' +import { IFileBrowserFactory } from '@jupyterlab/filebrowser' async function activate ( app: JupyterFrontEnd, + fileBrowser: IDefaultFileBrowser, restorer: ILayoutRestorer, shell: ILabShell, - fileBrowserFactory: IFileBrowserFactory ) { let serverSettings: IServerSettings - const fileBrowser = fileBrowserFactory.defaultBrowser try { serverSettings = await getServerSettings() } catch (e: any) { @@ -31,7 +31,7 @@ async function activate ( ) return } - + // await (fileBrowser.model as any)._restored.promise const model = new EduhelxSubmissionModel() Promise.all([app.restored, fileBrowser.model.restored]).then(() => { model.currentPath = fileBrowser.model.path @@ -61,9 +61,9 @@ const plugin: JupyterFrontEndPlugin = { description: 'A JupyterLab extension tfor submitting assignments in EduHeLx', autoStart: true, requires: [ + IDefaultFileBrowser, ILayoutRestorer, ILabShell, - IFileBrowserFactory ], activate }; diff --git a/src/model.ts b/src/model.ts index 08ad6b0..1550f5f 100644 --- a/src/model.ts +++ b/src/model.ts @@ -163,7 +163,8 @@ export class EduhelxSubmissionModel implements IEduhelxSubmissionModel { this.currentAssignment = undefined this.assignments = undefined this.student = undefined - await this._assignmentPoll.refresh() + // await this._assignmentPoll.refresh() + this._refreshModel() await this._assignmentPoll.tick }