{"id":24865167,"url":"https://github.com/diku-dk/staffeli","last_synced_at":"2026-03-09T06:32:48.858Z","repository":{"id":57471026,"uuid":"71339108","full_name":"diku-dk/staffeli","owner":"diku-dk","description":"DIKU Support Tools for Canvas LMS","archived":false,"fork":false,"pushed_at":"2024-09-16T10:18:31.000Z","size":592,"stargazers_count":14,"open_issues_count":18,"forks_count":8,"subscribers_count":11,"default_branch":"master","last_synced_at":"2026-01-14T09:27:46.667Z","etag":null,"topics":["canvas-lms","cli","python3","rest-api"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/diku-dk.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-10-19T09:08:17.000Z","updated_at":"2025-02-02T21:07:55.000Z","dependencies_parsed_at":"2022-09-14T17:04:27.132Z","dependency_job_id":null,"html_url":"https://github.com/diku-dk/staffeli","commit_stats":null,"previous_names":["diku-edu/staffeli"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/diku-dk/staffeli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diku-dk%2Fstaffeli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diku-dk%2Fstaffeli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diku-dk%2Fstaffeli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diku-dk%2Fstaffeli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/diku-dk","download_url":"https://codeload.github.com/diku-dk/staffeli/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diku-dk%2Fstaffeli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30284776,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T02:57:19.223Z","status":"ssl_error","status_checked_at":"2026-03-09T02:56:26.373Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","cli","python3","rest-api"],"created_at":"2025-01-31T23:57:59.915Z","updated_at":"2026-03-09T06:32:48.800Z","avatar_url":"https://github.com/diku-dk.png","language":"Python","readme":"``staffeli`` — DIKU Support Tools for Canvas LMS\n================================================\n\nThese tools leverage the `Canvas LMS REST\nAPI \u003chttps://canvas.instructure.com/doc/api/index.html\u003e`__ to create a\nmore pleasant environment for working with\n`Absalon \u003chttps://absalon.ku.dk/\u003e`__.\n\n\"Staffeli\" is Danish for \"easel\" — a support frame for holding up a\ncanvas.\n\n|Documentation Status| |Travis CI (Linux + macOS) Status| |License: EUPL\nv1.1| |PyPI|\n\n.. |Documentation Status| image:: https://readthedocs.org/projects/staffeli/badge/\n   :target: http://staffeli.readthedocs.io/en/latest/\n.. |Travis CI (Linux + macOS) Status| image:: https://travis-ci.org/diku-dk/staffeli.svg\n   :target: https://travis-ci.org/diku-dk/staffeli\n.. |License: EUPL v1.1| image:: https://img.shields.io/badge/license-EUPL%20v1.1-blue.svg\n   :target: https://github.com/DIKU-EDU/Staffeli/blob/master/LICENSE.md\n.. |PyPI| image:: https://img.shields.io/pypi/v/staffeli.svg\n   :target: https://pypi.python.org/pypi/staffeli\n\n.. contents::\n\n.. section-numbering::\n\nPurpose\n-------\n\nThe purpose of Staffeli is two-fold:\n\n1. Leverage the `Canvas LMS REST\n   API \u003chttps://canvas.instructure.com/doc/api/index.html\u003e`__ to get\n   things done better, faster, stronger.\n2. Quick prototyping of new features for `Canvas\n   LMS \u003chttps://www.canvaslms.com/\u003e`__.\n\nInitially, Staffeli is *not* intended for managing course content,\nmerely to snapshot course data (e.g., enrolled students, groups,\nsections, submissions), *and* to get grading done efficiently.\n\nAlthough Staffeli is written in Python 3, it is not intent on forcing you to\nmanage your course using Python, or to have to get intimate with the Staffeli\nAPI to get things done. Staffeli extensively uses YAML files for storage,\nenabling the easy use of both *command-line utilities* and *the programming\nlanguage of your choice*, to get things done quickly, and efficiently.\n\nStatus\n------\n\nStaffeli is maturing. It is being transitioned to be annotated with `type hints\n\u003chttps://www.python.org/dev/peps/pep-0484/\u003e`__, with the types checked\nstatically with `mypy \u003chttp://mypy-lang.org/\u003e`__, and a test-suite `has been\nset up \u003ctests\u003e`__, but full-blown continuous integration remains to be set up.\n\nWe are still covering a fairly small subset of the API. Brace yourself. Lend a\nhand.\n\nInstallation\n------------\n\nThese are instructions for installing Staffeli on your system.\n\n1. `Download this repository \u003chttps://github.com/DIKU-EDU/staffeli/archive/master.zip\u003e`__\n   or ``git clone https://github.com/DIKU-EDU/staffeli.git``\n2. Enter directory and run ``pip3 install -e .``\n\nThese are instructions for installing Staffeli in a user directory.\n\n1. Check that you have Python 3 and pip installed: ``which python3 pip3``\n2. Install `virtualenv \u003chttps://virtualenv.pypa.io/en/stable/\u003e`__ globally,\n   e.g. with ``sudo pip3 install virtualenv``\n3. `Download this repository \u003chttps://github.com/DIKU-EDU/staffeli/archive/master.zip\u003e`__\n   or ``git clone https://github.com/DIKU-EDU/staffeli.git``\n4. Enter directory and create virtualenv with ``virtualenv .``\n5. Activate virtualenv with ``source bin/activate``\n6. Install Staffeli in virtualenv, ``pip3 install -e .``\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``, ``absalon``,\nor similar, for all of you Canvas-related local course clones. Staffeli\nneeds some initial help to be able to login with your credentials. You\nneed to `generate a\ntoken \u003chttps://guides.instructure.com/m/67952/l/1018197-how-do-i-manage-api-access-tokens-in-my-user-account\u003e`__\nfor Staffeli to use, and save it as ``.token``, ``token``, or\n``token.txt`` in this high-level directory.\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\nCloning a Course\n^^^^^^^^^^^^^^^^\n\nTo clone a course:\n\n::\n\n    $ staffeli clone '\u003ccourse name\u003e' [dir]\n\nThe ``'course name'`` is any case-insensitive substring of the course name as\nit appears on your dashboard. Use quotes in case the substring contains spaces.\nIf there are multiple conflicting names matching the substring, Staffeli will\ncomplain and let you try again.\n\nThe ``[dir]`` is an optional destination directory for the local working area,\nin case it should not be named ``\u003ccourse string\u003e``. For example::\n\n    $ staffeli clone 'Advanced programming' ap17\n    Cloning '5100-B1-1E17;Advanced programming' into 'ap17'...\n\nFetch Submissions for a New Assignment\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nUse ``staffeli fetch``. For instance, to fetch all submissions for \"A3\":\n\n::\n\n    $ staffeli fetch subs/A3\n\nTo fetch just the metadata for all submissions, but not the submissions\nthemselves:\n\n::\n\n    $ staffeli fetch subs\n\nFetch Groups\n^^^^^^^^^^^^\n\nThis is a good idea to make sure you are up-to-date with canvas.\n\n::\n\n    $ staffeli fetch groups\n\n\nGrade a Submission\n^^^^^^^^^^^^^^^^^^\n\nAssuming you are in the submission directory, you can use\n``staffeli grade`` to grade the submission:\n\n::\n\n    staffeli grade GRADE [-m COMMENT] [FILEPATH]...\n\n    Where\n        GRADE           pass, fail, or an int.\n        [-m COMMENT]    An optional comment to write.\n        [-f FILEPATH]   Upload the contents of a file as a comment.\n        [FILEPATH]...   Optional files to upload alongside.\n\nSplit Submissions among TAs\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSee `the groups contrib \u003chttps://github.com/DIKU-EDU/staffeli/tree/master/contrib/groups\u003e`_.\n\nDocumentation\n-------------\n\nIt is up-and-coming on\n[ReadTheDocs](http://staffeli.readthedocs.io/en/latest/). The source files for\nthat page are under [docs/source](docs/source), and they are, much like this\nREADME, written in `reStructured Text\n\u003chttp://www.sphinx-doc.org/en/stable/rest.html\u003e`_. It is also suggested to\nfollow the `Python documentation style guide\n\u003chttps://docs.python.org/devguide/documenting.html#style-guide\u003e`_.\n\nContributing\n------------\n\nFirst, take a look at our `design guide \u003cDESIGN.md\u003e`__ and `style\nguide \u003cSTYLE.md\u003e`__.\n\nContact us at dikunix at dikumail dot dk.\n\nTake a look at our on-going\n`issues \u003chttps://github.com/DIKU-EDU/Staffeli/issues\u003e`__.\n\nTesting\n-------\n\nCurrently, `Travis CI \u003chttps://travis-ci.org/DIKU-EDU/staffeli\u003e`__ will\nonly check that you roughly conform to the `PEP 8 Python Style\nGuide \u003chttps://www.python.org/dev/peps/pep-0008/\u003e`__ (using\n`flake8 \u003chttp://flake8.pycqa.org/\u003e`__), and perform static type-checking\nwith `mypy \u003chttp://mypy-lang.org/\u003e`__, all only for selected Python\nfiles in this repository. See (and run?)\n```static_tests.py`` \u003cstatic_tests.py\u003e`__ for further details.\n\nBefore you do that however, you might want to do this:\n\n::\n\n    $ pip3 install -r test-requirements.txt\n\nThis will also install what you need to run the dynamic tests we have in\nstore under `tests \u003ctests\u003e`__, except for **Docker**:\n```start_local_canvas.py`` \u003cstart_local_canvas.py\u003e`__ will fire up a\nDocker image with a local Canvas instance for use with our\n`tests \u003ctests\u003e`__. You will also find it in your browser under the\naddress ``localhost:3000``. The user is ``canvas@example.edu`` and the\npassword is ``canvas``.\n\nThe static and dynamic tests are also part of the\n```pre-commit`` \u003chooks/pre-commit\u003e`__ and\n```pre-push`` \u003chooks/pre-push\u003e`__ hooks, respectively. Install these\nhooks by executing ```hooks/install.sh`` \u003chooks/install.sh\u003e`__.\nUnfortunately, neither these hooks, nor the hooks installer will work on\nWindows.\n\nStatic Testing Framework\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nWe use `flake8 \u003chttp://flake8.pycqa.org/\u003e`__ for style-checking and\n`mypy \u003chttp://mypy-lang.org/\u003e`__ for static type-checking.\n\nAssuming you have these tools installed, you can do this:\n\n::\n\n    $ ./static_tests.py\n\nThis is also part of the ```pre-commit`` \u003chooks/pre-commit\u003e`__ hook.\n\nDynamic Testing Framework\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWe use `pytest \u003chttps://docs.pytest.org/\u003e`__ together with\n`hypothesis \u003chttps://hypothesis.readthedocs.io/\u003e`__.\n\nAssuming you have these tools installed, you can do this:\n\n::\n\n    $ pytest\n\nThis is also part of the `pre-push \u003chooks/pre-push\u003e`__ hook.\n\nDynamic Test Coverage\n^^^^^^^^^^^^^^^^^^^^^\n\nRun ``pytest`` with the option ``--cov=staffeli`` to get an idea of the\ntest coverage of Staffeli proper.\n\nIt is pretty lousy ATM. As of `2017-05-24\n\u003chttps://github.com/DIKU-EDU/staffeli/commit/e5a0811edf26dc70eaad680e54b5763fc64f90fc\u003e`__,\nthe numbers were:\n\n::\n\n    Name                          Stmts   Miss  Cover\n    -------------------------------------------------\n    staffeli/assignment.py           28     28     0%\n    staffeli/cachable.py             22     13    41%\n    staffeli/canvas.py              326    326     0%\n    staffeli/cli.py                 295    295     0%\n    staffeli/course.py               43      4    91%\n    staffeli/files.py                57     41    28%\n    staffeli/gcat.py                 29      5    83%\n    staffeli/group.py                23     23     0%\n    staffeli/listed.py               31      8    74%\n    staffeli/names.py                 3      1    67%\n    staffeli/resubmissions.py       121    121     0%\n    staffeli/speedgrader_url.py       9      9     0%\n    staffeli/submission.py           22     22     0%\n    staffeli/typed_canvas.py        112      4    96%\n    staffeli/upload.py               17     17     0%\n    -------------------------------------------------\n    TOTAL                          1138    917    19%\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiku-dk%2Fstaffeli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiku-dk%2Fstaffeli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiku-dk%2Fstaffeli/lists"}