jupyter / nbgrader

A system for assigning and grading notebooks
https://nbgrader.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.3k stars 317 forks source link

one grader multiple courses #1853

Closed bpfrd closed 7 months ago

bpfrd commented 11 months ago

Hello,

In the current nbgrader demos/demo_multiple_courses, you put the below code in grader-course101 home directory.

grader-course101 cat .jupyter/nbgrader_config.py c = get_config() c.CourseDirectory.root = '/home/grader-course101/course101' c.CourseDirectory.course_id = "course101"

what if there is one grader for multiple courses? is it supported? How can I write services in this demo in z2jh?

thanks. best

KrKOo commented 11 months ago

Hey @bpfrd , I've already replied to your Jupyter Forum post. But in case anyone else encounters a similar problem in the future, I would like to share the z2jh + nbgrader Helm chart we created, which addresses most of the issues you may come across during a basic deployment of nbgrader.

https://github.com/CERIT-SC/nbgrader-k8s

PS: Feel free to create an Issue/PR, if you find something ;-)

bpfrd commented 11 months ago

Hi @KrKOo Great work! Thank you very much for sharing it. I followed the instructions in the github repo to run jupyterhub on minikube. The hub deployment is pending. The logs for the pod is empty but here is the output of "kubectl describe". I'm new to k8s. Would you have any idea what the problem is? should I have changed the storage class for minikube? best

kubectl describe pod/hub-699bf99b98-tcvlb Name: hub-699bf99b98-tcvlb Namespace: default Priority: 0 Service Account: hub Node: Labels: app=jupyterhub component=hub hub.jupyter.org/network-access-proxy-api=true hub.jupyter.org/network-access-proxy-http=true hub.jupyter.org/network-access-singleuser=true pod-template-hash=699bf99b98 release=nbgrader Annotations: checksum/config-map: 29f301903c4a63e2828219d4556e94d8f3e5d8b41736d48251811308ff14430c checksum/secret: 540300c90e277cb9ddf05d05135cbbb6d60e73fd0c36d267df92a69d0dd3f4ba Status: Pending SeccompProfile: RuntimeDefault IP: IPs: Controlled By: ReplicaSet/hub-699bf99b98 Containers: hub: Image: cerit.io/hubs/nbgrader-hub:10-11-2023 Port: 8081/TCP Host Port: 0/TCP Args: jupyterhub --config /usr/local/etc/jupyterhub/jupyterhub_config.py --debug --upgrade-db Limits: cpu: 2 memory: 4Gi Requests: cpu: 2 memory: 4Gi Liveness: http-get http://:http/hub/health delay=10s timeout=10s period=10s #success=1 #failure=10 Readiness: http-get http://:http/hub/health delay=10s timeout=10s period=10s #success=1 #failure=10 Environment: PYTHONUNBUFFERED: 1 HELM_RELEASE_NAME: nbgrader POD_NAMESPACE: default (v1:metadata.namespace) CONFIGPROXY_AUTH_TOKEN: <set to the key 'hub.config.ConfigurableHTTPProxy.auth_token' in secret 'hub'> Optional: false Mounts: /etc/jupyter/ from nbgrader-config-global (ro) /mnt/exchange from nbgrader-exchange (rw) /srv/jupyterhub from pvc (rw) /usr/local/etc/jupyterhub/config/ from config (rw) /usr/local/etc/jupyterhub/jupyterhub_config.py from config (rw,path="jupyterhub_config.py") /usr/local/etc/jupyterhub/secret/ from secret (rw) /usr/local/etc/jupyterhub/z2jh.py from config (rw,path="z2jh.py") /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-rbsjc (ro) Conditions: Type Status PodScheduled False Volumes: config: Type: ConfigMap (a volume populated by a ConfigMap) Name: hub Optional: false secret: Type: Secret (a volume populated by a Secret) SecretName: hub Optional: false nbgrader-exchange: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: nbgrader-exchange ReadOnly: false nbgrader-config-global: Type: ConfigMap (a volume populated by a ConfigMap) Name: nbgrader-config-global Optional: false pvc: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: hub-db-dir ReadOnly: false kube-api-access-rbsjc: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: DownwardAPI: true QoS Class: Guaranteed Node-Selectors: Tolerations: hub.jupyter.org/dedicated=core:NoSchedule hub.jupyter.org_dedicated=core:NoSchedule node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message


Warning FailedScheduling 2m13s (x2 over 7m29s) default-scheduler 0/1 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..

PS C:\Users\bpfrd\nbgrader-k8s> kubectl get pod NAME READY STATUS RESTARTS AGE hub-699bf99b98-tcvlb 0/1 Pending 0 8m47s proxy-5998987fc8-dc9dx 1/1 Running 0 8m47s

PS C:\Users\bpfrd\nbgrader-k8s> kubectl logs pod/hub-699bf99b98-tcvlb

KrKOo commented 11 months ago

Yes, if you are using minikube, you should set the "storageClassName" to "standard" (replace every instance of "nfs-csi" in the values.yaml file).

I guess it would be better to make the chart use the default storage class when none is set, but until then just use "standard" which is the default for minikube.

bpfrd commented 11 months ago

Thank you very much. It works now, but there seems to be a problem with nbgrader. When I try to get course1 as instructor1 I get the below error:

503 : Service Unavailable Your server appears to be down. Try restarting it from the hub

and here is the pod log for jupyter-instructor1 [I 2023-12-07 19:20:43.713 ServerApp] 200 GET /user/instructor1/nbgrader_version?version=0.9.1&1701976843698 (instructor1@::ffff:127.0.0.1) 1.21ms [NbGrader | WARNING] Config option kernel_spec_manager_class not recognized by NbGrader. [W 2023-12-07 19:20:43.720 ServerApp] Local formgrader does not seem to be running [I 2023-12-07 19:20:43.737 ServerApp] 200 GET /user/instructor1/formgraders?1701976843699 (instructor1@::ffff:127.0.0.1) 23.06ms [I 2023-12-07 19:20:44.254 ServerApp] 204 PUT /user/instructor1/lab/api/workspaces/auto-4?1701976844240 (instructor1@::ffff:127.0.0.1) 1.47ms [I 2023-12-07 19:20:49.096 ServerApp] 200 GET /user/instructor1/api/kernels?1701976849087 (instructor1@::ffff:127.0.0.1) 2.18ms [I 2023-12-07 19:20:49.107 ServerApp] 200 GET /user/instructor1/api/sessions?1701976849088 (instructor1@::ffff:127.0.0.1) 1.67ms [I 2023-12-07 19:20:49.111 ServerApp] 200 GET /user/instructor1/api/terminals?1701976849089 (instructor1@::ffff:127.0.0.1) 1.46ms

I have another question: I see in the z2jh tutorial that we can give options for spawning in the singleuser.profileList. But in this case, students and instructors should get courses they are registered in as options. Should I do it in bootstrap_pre_spawn?

KrKOo commented 11 months ago

@bpfrd Nice, you just found an error in the chart 😉 It is fixed now, just pull and "helm upgrade".

Regarding the second question. Depending on your use case, maybe this could help you: https://github.com/jupyterhub/jupyterhub/blob/HEAD/examples/spawn-form/jupyterhub_config.py https://jupyterhub.readthedocs.io/en/stable/reference/spawners.html#spawner-options-form

In the _options_form_default() method you could fetch the groups of the user and generate the form based on that. In options_from_form() you just parse the options from the form, and then in bootstrap_pre_spawn you can access these options by spawner.user_options.get('option_name'). Based on that you can then do whatever you need.

bpfrd commented 11 months ago

Thank you very much. Everything works fine now.

bpfrd commented 11 months ago

I just noticed that in the graphical interface of formgrader there is no autograde.

I could be wrong, but I don't think that that's even a thing in nbgrader. The autograde button is on the page, which comes after you click on the number of submissions.

sorry, it was my bad. the autograde is available under assignments/assignment_id not assignments.

bpfrd commented 11 months ago

I have a general question about jupyterhub which couldn’t really find an answer in the jupyterhub documentation. What is the hierarchy of functions that are called in order in jupyterhub when a user logs in and uses the system. for example you assigned BaseHandler.get_accessible_services = get_accessible_services but when is this function or other functions called? thank you very much in advance

KrKOo commented 11 months ago

I don't think there is an easy way to find these things. The technique used in the chart is called 'monkey patching', which means that some methods from the JupyterHub source are replaced with a modified version of them. You will probably have to read the source code of JupyterHub to understand how those methods work and when they are getting called.

Regarding the BaseHandler.get_accessible_services method. The patch that I have in the chart will probably not be needed with a newer version of JupyterHub, since this PR addresses the same issue.

bpfrd commented 11 months ago

Hello,

I get a permission denied error when trying to install it on another system. Is the error happening on the hub container? how can we fix it? best

$kubectl get pod NAME READY STATUS RESTARTS AGE hub-7dd77cd747-29sd5 0/1 CrashLoopBackOff 3 (37s ago) 2m10s proxy-bffbd5c54-5x62h 1/1 Running 0 2m10s

$kubectl logs pod/hub-7dd77cd747-29sd5 [D 2023-12-12 11:33:46.056 JupyterHub application:902] Looking for /usr/local/etc/jupyterhub/jupyterhub_config in /srv/jupyterhub Loading /usr/local/etc/jupyterhub/secret/values.yaml No config at /usr/local/etc/jupyterhub/existing-secret/values.yaml Loading extra config: 00-extra-config [E 2023-12-12 11:33:46.468 JupyterHub app:3382] Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/jupyterhub/app.py", line 3379, in launch_instance_async await self.initialize(argv) File "/usr/local/lib/python3.11/site-packages/jupyterhub/app.py", line 2857, in initialize self.load_config_file(self.config_file) File "/usr/local/lib/python3.11/site-packages/traitlets/config/application.py", line 113, in inner return method(app, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/traitlets/config/application.py", line 950, in load_config_file for (config, fname) in self._load_config_files( File "/usr/local/lib/python3.11/site-packages/traitlets/config/application.py", line 909, in _load_config_files config = loader.load_config() ^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/traitlets/config/loader.py", line 626, in load_config self._read_file_as_dict() File "/usr/local/lib/python3.11/site-packages/traitlets/config/loader.py", line 659, in _read_file_as_dict exec(compile(f.read(), conf_filename, "exec"), namespace, namespace) # noqa ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/etc/jupyterhub/jupyterhub_config.py", line 497, in exec(config_py) File "", line 174, in File "", line 215, in makedirs File "", line 225, in makedirs PermissionError: [Errno 13] Permission denied: '/mnt/exchange/course1'

[D 2023-12-12 11:33:46.471 JupyterHub application:1028] Exiting application: jupyterhub

bpfrd commented 11 months ago

update: the error in the previous message only happens when the minikube cluster has more than one nodes

brichet commented 11 months ago

@bpfrd maybe you should open an issue in https://github.com/CERIT-SC/nbgrader-k8s ?

brichet commented 7 months ago

I'm closing it since the initial question has been answered, and the other issues are not about nbgrader.

Feel free to reopen it if necessary.