{"id":24865519,"url":"https://github.com/kfl/staffeli_nt","last_synced_at":"2025-10-15T10:30:20.597Z","repository":{"id":38302708,"uuid":"290555072","full_name":"kfl/staffeli_nt","owner":"kfl","description":"Staffeli NT Technology","archived":false,"fork":false,"pushed_at":"2023-09-27T20:04:16.000Z","size":67,"stargazers_count":8,"open_issues_count":22,"forks_count":6,"subscribers_count":5,"default_branch":"master","last_synced_at":"2023-09-27T21:55:38.246Z","etag":null,"topics":["canvas-lms-api","hacktoberfest","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kfl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-26T16:57:28.000Z","updated_at":"2023-09-20T08:15:24.000Z","dependencies_parsed_at":"2023-01-21T08:05:38.156Z","dependency_job_id":null,"html_url":"https://github.com/kfl/staffeli_nt","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfl%2Fstaffeli_nt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfl%2Fstaffeli_nt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfl%2Fstaffeli_nt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kfl%2Fstaffeli_nt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kfl","download_url":"https://codeload.github.com/kfl/staffeli_nt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236600319,"owners_count":19175173,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["canvas-lms-api","hacktoberfest","python"],"created_at":"2025-01-31T23:59:59.701Z","updated_at":"2025-10-15T10:30:15.270Z","avatar_url":"https://github.com/kfl.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# staffeli_nt\n\nStaffeli NT Technology\n\n\nGetting Started\n===============\n\nWith Staffeli, we work with local course clones. We aim to keep these\nclones compatible with git.\n\nWe recommend that you create a local directory ``canvas``,\n``absalon``, or similar, for all of you Canvas-related local course\nclones.\n\nObtain your personal Canvas token\n---------------------------------\nStaffeli needs some initial help to be able to login with your\ncredentials. You need to [generate a\ntoken](https://guides.instructure.com/m/4214/l/40399-how-do-i-obtain-an-api-access-token-for-an-account)\nfor Staffeli to use, and save it in your home directory in a file with\nthe name `.canvas.token`.\n\n**NB!** This is your personal token so **do not** share it with others,\nelse they can easily impersonate you using a tool like Staffeli.\nUnfortunately, to the best of our knowledge, Canvas has no means to\nsegregate or specialize tokens, so this is really \"all or nothing\".\n\nInstall required libraries\n--------------------------\n\n    $ pip3 install -r requirements.txt\n\nOr you can install in a [virtual\nenvironment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment):\n\n 1. Create a virtual environment called `env`.\n\n    On macOS and Linux:\n\n        $ python3 -m venv env\n\n    On Windows:\n\n        $ py -m venv env\n\n 2. Activate `env`\n\n    On macOS and Linux:\n\n        $ source env/bin/activate\n\n    On Windows:\n\n        $ .\\env\\Scripts\\activate\n\n 3. Now install the requirements for `staffeli_nt` in `env`\n\n        $ pip3 install -r requirements.txt\n\n\n\n\nFetch Submissions for an Assignment\n-----------------------------------\nThere are multiple options for fetching submissions.\n\nThe general command is `python \u003cstaffeli_nt_path\u003e download \u003ccourse_id\u003e \u003ctemplate.yaml\u003e \u003cassignment-dir\u003e [flags]`, where\n- `\u003cstaffeli_nt_path\u003e` is the path to the directory where `staffeli_nt` is located, i.e. where the files `download.py` and `upload.py` etc. can be found.\n- `\u003ccourse_id\u003e` is the canvas `course_id` for the course.\n- `\u003ctemplate.yaml\u003e` is the template file to use when generating the `grade.yml` file for each submission\n- `\u003cassignment_dir\u003e` is a *non-existing* directory, that staffeli will create and store the submissions in.\n\n**Windows**:  \nSince `staffeli_nt` is written in `python3`, you will need to invoke it via your `python3` interpreter. \nExample: `python \u003cstaffeli_nt_path\u003e download \u003ccourse_id\u003e \u003ctemplate.yml\u003e \u003cassignment-dir\u003e [flags]`\n\n**Fetching all submissions**:  \nTo fetch **all** submissions from the course with id `12345`, using the template-file `ass1-template.yml` and create a new directory \"ass1dir\" to store the submissions in:\n\n    $ python \u003cstaffeli_nt_path\u003e download 12345 ass1-template.yml ass1dir\n\nThis will present you with a list of assignments for the course, where you will interactively choose which assignment to fetch.\nFor each submission, a directory will be created in `\u003cassignment_dir\u003e`, in which the handed-in files of the submission will be stored, alongside a file `grade.yml` generated form the `\u003ctemplate.yml\u003e` for a TA to fill out during grading of the assignment.\nSubmission comments, if any, will be downloaded as well, and stored alongside `grade.yml` and the files of the hand-in.\n\n*In case the student hands in a file called `grade.yml` it will be overwritten by staffeli. If the student hands in a file called `submission_comments.txt` and has written submission comments on the submission, the downloaded submission comments will be renamed.*\n\n### Flags\n#### Fetching all submissions for a section\nWhat we call \"Hold\", canvas/absalon calls sections.\nTo fetch all submissions for an assignment, where the student belongs to a given section, and the `\u003ccourse_id\u003e` is `12345`:\n\n    $ python \u003cstaffeli_nt_path\u003e download 12345 ass1-template.yml ass1dir --select-section\n\nThis will present you with a list of assignments for the course, where you will interactively choose which assignment to fetch, followed by a list of sections for you to choose from.\n\n#### Fetching specific submissions (based on kuid)\nIt is possible to fetch specific submissions based on a list of kuids.\nTo do this, create a YAML-file with the following format:\n\n``` yaml\nTA1:\n- kuid1\n- kuid2\n- kuid3\nTA2:\n- kuid4\n- kuid5\n```\n\nTo then fetch all submissions for an assignment for a given TA:\n\n    $ python \u003cstaffeli_nt_path\u003e download \u003ccourse_id\u003e ass1-template.yml ass1dir --select-ta ta_list.yml\n\nwhere `ta_list.yml` is a YAML-file following the above format.\n\n\nThis will present you with a list of assignments for the course, where you will interactively choose which assignment to fetch, followed by the list of TA's from your `ta_list.yml` file.\nSelecting a TA, will fetch submissions from each `kuid` in the file, associated with the chosen TA, i.e. selecting `TA1` will fetch submission from `kuid1`, `kuid2` and `kuid3`.\n\n\n### Automatically running onlineTA for each submission\nIn the `template.yml`-file you can add a field:\n\n`onlineTA: https://address.of.onlineTA.dk/grade/assignmentName`\n\nThis will (attempt to) run onlineTA for each downloaded submission.\n\n\n#### Fetching only ungraded submissions (resubs)\nIt is possile to only fetch submissions that are either ungraded or have a score \u003c 1.0.\nCurrently this is implemented specifically for the PoP-course and might not be available in the current form in later releases. \nThis can be achieved by appending the `--resub` flag to any use of the `download` subcommand.\n\n\n\nUpload Feedback and grades\n--------------------------\n\nUse `python \u003cstaffeli_nt_path\u003e upload \u003ctemplate.yaml\u003e \u003cassignment-dir\u003e [--live] [--step]`.\nThe default to do a *dry run*, that is **not** to upload anything\nunless the `--live` flag is given.\n\nFor instance, to review all feedback for submissions in the directory\n`ass1` before uploading:\n\n    $ python \u003cstaffeli_nt_path\u003e upload ass1-template.yml ass1 --step\n\n\nTo upload all feedback for submissions in the directory\n`ass1`:\n\n    $ python \u003cstaffeli_nt_path\u003e upload ass1-template.yml ass1 --live\n\nTo upload feedback for a single submission:\n\n    $ python \u003cstaffeli_nt_path\u003e upload-single \u003cPOINTS\u003e \u003cmeta.yml\u003e \u003cgrade.yml\u003e \u003cfeedback.txt\u003e [--live]\n\n\nTo generate `feedback.txt` locally for submissions in the directory `ass1`:\n\n    $ python \u003cstaffeli_nt_path\u003e upload ass1-template.yml ass1 --write-local\n\n\nTemplate format\n---------------\n\nA (minimal) template could look like:\n\n```yaml\nname: Mini assignment\n\ntasks:\n  - overall:\n      title: Overall\n      points: 6\n      rubric: |\n        Some default feedback.\n\n        Your code and report are unreadable.\n\n        Wow, that's really clever.\n```\n\n### Optional fields\n\nThe template files support a few optional fields.\n\n- `passing-points: N`:\nAdding this field will have the effect, that the grade posted is `1` if the total sum of points is\ngreater than or equal to `passing-points`, and `0` otherwise.\n- `show-points: BOOL`\nSetting show-points to `false` will exclude the `points/grade` from the generated `feedback.txt` files.\nUse this, if you do not want the students to see the points-per-task, but only receive an overall grade.\n\n- `onlineTA: ADDR`\nInclude this field to (attempt to) run onlineTA at address `ADDR` for each submission, when downloading submissions.\n\n\n### Fully fledged example template\n```yaml\nname: Mega assignment\npassing-points: 42\nshow-points: false\nonlineTA: https://yeah-this-is-not-a-real-address.dk/grade/megaassignment\n\ntasks:\n  - megaAssignmentGeneral:\n      title: Mega assignment - General comments and adherence to hand-in format requirements\n      points: 100\n      rubric: |\n        [*] You should spell check your assignments before handing them in\n        [-] You are using the charset iso-8859-1. Please move to the modern age.\n        [-] Your zip-file contains a lot of junk. Please be aware of what you hand in.\n\n  - megaAssignmentTask1:\n      title: Task 1\n      points: 2\n      rubric: |\n        [+] Your implementation follows the API\n        [-] Your implementation does not follow the API\n        [+] Your tests are brilliant\n        [-] Your tests are not tests, just print-statements.\n            This is equivalent to an exam without an examinator, where you shout\n            in a room for half an hour and give yourself the grade 12.\n\n  - megaAssignmentTask2:\n      title: Task 2\n      points: 2\n      rubric: |\n        [+] Very good points.\n        [+] Very good points. However, I disagree with ...\n        [-] I fail to comprehend you answer to this task.\n\n  - megaAssignmentBonusTask:\n      title: Bonus tasks that do not give points, or another option for general comments\n      rubric: |\n        [*] You did extra work! It won't help you though.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkfl%2Fstaffeli_nt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkfl%2Fstaffeli_nt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkfl%2Fstaffeli_nt/lists"}