{"id":35314102,"url":"https://github.com/dbosk/ladok3","last_synced_at":"2026-03-08T14:00:57.902Z","repository":{"id":49243618,"uuid":"322012386","full_name":"dbosk/ladok3","owner":"dbosk","description":"Python wrapper around the LADOK3 REST API.","archived":false,"fork":false,"pushed_at":"2026-02-18T13:32:11.000Z","size":1912,"stargazers_count":7,"open_issues_count":38,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-04T20:57:46.660Z","etag":null,"topics":["api","cli","ladok","ladok3","python3"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/ladok3/","language":"Makefile","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/dbosk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2020-12-16T14:47:44.000Z","updated_at":"2026-02-18T13:31:57.000Z","dependencies_parsed_at":"2026-02-01T00:04:09.454Z","dependency_job_id":null,"html_url":"https://github.com/dbosk/ladok3","commit_stats":{"total_commits":352,"total_committers":4,"mean_commits":88.0,"dds":"0.17613636363636365","last_synced_commit":"e838b3b17073ed6581d75c2c4803647fc1de0b6f"},"previous_names":[],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/dbosk/ladok3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbosk%2Fladok3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbosk%2Fladok3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbosk%2Fladok3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbosk%2Fladok3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dbosk","download_url":"https://codeload.github.com/dbosk/ladok3/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbosk%2Fladok3/sbom","scorecard":{"id":329066,"data":{"date":"2025-08-11","repo":{"name":"github.com/dbosk/ladok3","commit":"5d3d6fcc5fe0eb8fb333ca9359b55097f5838a62"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.1,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/15 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":4,"reason":"4 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: containerImage not pinned by hash: docker/Dockerfile:1: pin your Docker image by updating python:3 to python:3@sha256:a66f18ee22c568a3d45191dfd70bdea2e1bd8d303f982ea1bca276a065285a21","Warn: pipCommand not pinned by hash: docker/Dockerfile:3-4","Info:   0 out of   1 containerImage dependencies pinned","Info:   0 out of   1 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v5.2 not signed: https://api.github.com/repos/dbosk/ladok3/releases/224268794","Warn: release artifact v5.1 not signed: https://api.github.com/repos/dbosk/ladok3/releases/215727650","Warn: release artifact v5.0 not signed: https://api.github.com/repos/dbosk/ladok3/releases/215112662","Warn: release artifact v4.19 not signed: https://api.github.com/repos/dbosk/ladok3/releases/209798415","Warn: release artifact v4.18 not signed: https://api.github.com/repos/dbosk/ladok3/releases/208089084","Warn: release artifact v5.2 does not have provenance: https://api.github.com/repos/dbosk/ladok3/releases/224268794","Warn: release artifact v5.1 does not have provenance: https://api.github.com/repos/dbosk/ladok3/releases/215727650","Warn: release artifact v5.0 does not have provenance: https://api.github.com/repos/dbosk/ladok3/releases/215112662","Warn: release artifact v4.19 does not have provenance: https://api.github.com/repos/dbosk/ladok3/releases/209798415","Warn: release artifact v4.18 does not have provenance: https://api.github.com/repos/dbosk/ladok3/releases/208089084"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"15 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-230 / GHSA-248v-346w-9cwc","Warn: Project is vulnerable to: GHSA-3ww4-gg4f-jr7f","Warn: Project is vulnerable to: PYSEC-2024-225 / GHSA-6vqw-3v5j-54x4","Warn: Project is vulnerable to: GHSA-9v9h-cgj8-h64p","Warn: Project is vulnerable to: GHSA-h4gh-qq45-vh27","Warn: Project is vulnerable to: PYSEC-2023-254 / GHSA-jfhm-5ghh-2f97","Warn: Project is vulnerable to: GHSA-v8gr-m533-ghj9","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: GHSA-34jh-p97f-mpxf","Warn: Project is vulnerable to: PYSEC-2023-212 / GHSA-g4mx-q9vg-27p4","Warn: Project is vulnerable to: GHSA-pq67-6m6q-mj2v","Warn: Project is vulnerable to: PYSEC-2023-192 / GHSA-v845-jxx5-vc9f","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T03:09:12.373Z","repository_id":49243618,"created_at":"2025-08-18T03:09:12.373Z","updated_at":"2025-08-18T03:09:12.373Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30260014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-08T08:59:44.879Z","status":"ssl_error","status_checked_at":"2026-03-08T08:58:02.867Z","response_time":56,"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":["api","cli","ladok","ladok3","python3"],"created_at":"2025-12-30T18:25:33.709Z","updated_at":"2026-03-08T14:00:57.896Z","avatar_url":"https://github.com/dbosk.png","language":"Makefile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ladok3: Python wrapper for LADOK3 API\n\nThis package provides a wrapper for the LADOK3 API used by \n[start.ladok.se][ladok]. This makes it easy to automate reporting grades, \ncompute statistics etc.\n\n## Installation\n\nTo install, run:\n```bash\npip install ladok3\nsudo cp $(find / -name ladok.bash) /etc/bash_completion.d\nladok login\n```\nIf you run the second line above, you'll get tab completion for the `ladok` \ncommand when you use the `bash` shell.\n\nThe third command above is to log in, you only do this once.\n\nAn alternative to installing the package is to run the [Docker image][docker].\n```bash\ndocker run -it dbosk/ladok3 /bin/bash\n```\nOr simply adapt your own image.\n\n## Usage\n\nThere are two ways to use the package: as a Python package or through the \ncommand-line tool `ladok`.\n\n### On the command line\n\nLet's assume that we have a student with personnummer 123456-1234.\nLet's also assume that this student has taken a course with course code AB1234 \nand finished the module LAB1 on date 2021-03-15.\nSay also that the student's assignments were graded by the teacher and two TAs:\n\n  - Daniel Bosk \u003cdbosk@kth.se\u003e (teacher)\n  - Teaching Assistantsdotter \u003ctad@kth.se\u003e\n  - Teaching Assistantsson \u003ctas@kth.se\u003e\n\nThen we can report this result like this:\n```bash\nladok report 123456-1234 AB1234 LAB1 -d 2021-03-15 -f \\\n  \"Daniel Bosk \u003cdbosk@kth.se\u003e\" \"Teaching Assistantsdotter \u003ctad@kth.se\u003e\" \\\n  \"Teaching Assistantsson \u003ctas@kth.se\u003e\"\n```\n\nIf we use Canvas for all results, we can even report all results for a \ncourse.\n```bash\npip install canvaslms\ncanvaslms login\ncanvaslms results -c AB1234 -A LAB1 | ladok report -v\n```\nThe `canvaslms results` command will export the results in CSV format, this \nwill be piped to `ladok report` that can read it and report it in bulk.\n\nMost likely you'll need to pass the CSV through `sed` to change the column \ncontaining the course identifier to just contain the course code. At KTH, the \ncourse code attribute in Canvas contains course code and the semester. So I \nhave to `sed` away the semester part.\n\n### As a Python package\n\nTo use the package, it's just to import the package as usual.\n```python\nimport ladok3\n\ncredentials = {\n  \"username\": \"dbosk@ug.kth.se\",\n  \"password\": \"password ...\"\n}\n\nls = ladok3.LadokSession(\"KTH Royal Institute of Technology\",\n                         vars=credentials)\n\nstudent = ls.get_student(\"123456-1234\")\n\ncourse_participation = student.courses(code=\"AB1234\")[0]\nfor result in course_participation.results():\n  print(f\"{course_participation.code} {result.component}: \"\n    f\"{result.grade} ({result.date})\")\n\ncomponent_result = course_participation.results(component=\"LAB1\")[0]\ncomponent_result.set_grade(\"P\", \"2021-03-15\")\ncomponent_result.finalize()\n```\n\nThe simplest way, if you've already run `ladok login`, is to use the\nmodule-level `session` attribute:\n```python\nimport ladok3\n\nstudent = ladok3.session.get_student(\"123456-1234\")\n# ...\n```\n\nAnother way is to use the `load_credentials` function of the CLI.\n```python\nimport ladok3\nimport ladok3.cli\n\nls = ladok3.LadokSession(*ladok3.cli.load_credentials())\n\nstudent = ls.get_student(\"123456-1234\")\n# ...\n```\n\nIf you want to reuse a cached session (to avoid re-authenticating), use\n`get_session`:\n```python\nimport ladok3.cli\n\nls, credentials = ladok3.cli.get_session()\n\nstudent = ls.get_student(\"123456-1234\")\n# ...\n```\n\n## More documentation\n\nThere are more detailed usage examples in the details documentation that can be \nround with the [releases][releases] and in the `examples` directory.\n\n[ladok]: https://start.ladok.se\n[docker]: https://hub.docker.com/repository/docker/dbosk/ladok3\n[releases]: https://github.com/dbosk/ladok3/releases\n\n\n# The examples\n\nThere are some examples that can be found in the `examples` directory:\n\n  - `example_LadokSession.py` just shows how to establish a session.\n  - `example_Course.py` shows course data related examples.\n  - `example_Student.py` shows student data related examples.\n  - `canvas2ladok.py` shows how to transfer grades from KTH Canvas to LADOK.\n  - `statsdata.py` shows how to extract data for doing statistics for a course \n    and the students' results.\n\nWe also have a few more examples described in the sections below.\n\n## `canvas_ladok3_spreadsheet.py`\n\nPurpose: Use the data in a Canvas course room together with the data from Ladok3 to create a spreadsheet of students in the course\nand include their Canvas user_id, name, Ladok3 Uid, program_code, program name, etc.\n\nNote that the course_id can be given as a numeric value or a string which will be matched against the courses in the user's dashboard cards. It will first match against course codes, then short name, then original names.\n\nInput: \n```\ncanvas_ladok3_spreadsheet.py canvas_course_id\n```\nAdd the \"-T\" flag to run in the Ladok test environment.\n\nOutput: outputs a file ('users_programs-COURSE_ID.xlsx) containing a spreadsheet of the users information\n\n```\ncanvas_ladok3_spreadsheet.py 12162\n\ncanvas_ladok3_spreadsheet.py -t 'II2202 HT20-1'\n```\n\n\n## `ladok3_course_instance_to_spreadsheet.py`\n\nPurpose: Use the data in Ladok3 together with the data from Canvas to create a spreadsheet of students in a course\ninstance and include their Canvas user_id (or \"not in Canvas\" if they do not have a Canvas user_id), name, Ladok3 Uid, program_code, program name, etc.\n\nNote that the course_id can be given as a numeric value or a string which will be matched against the courses in the user's dashboard cards. It will first match against course codes, then short name, then original names.\n\nInput: \n```\nladok3_course_instance_to_spreadsheet.py course_code course_instance\n```\nor\n```\nladok3_course_instance_to_spreadsheet.py canvas_course_id\n```\nor\n```\n./ladok3_course_instance_to_spreadsheet.py course_code\n```\n\nOptionally include their personnumber with the flag -p or --personnumbers \n\nAdd the \"-T\" flag to run in the Ladok test environment.\n\nOutput: outputs a file ('users_programs-instance-COURSE_INSTANCE.xlsx) containing a spreadsheet of the users information\n\n```\n# for II2202 the P1 instance in 2019 the course instance is 50287\nladok3_course_instance_to_spreadsheet.py II2202 50287\n```\nor\n```\n# Canvas course_id for II2202 in P1 is 20979\nladok3_course_instance_to_spreadsheet.py 20979\n```\nor\n```\n# P1P2 is a nickname on a dashboard card for II2202 duing P1 and P2\n./ladok3_course_instance_to_spreadsheet.py P1P2\n```\n\n\n## `canvas_students_missing_integration_ids.py`\n\nPurpose: Use the data in a Canvas course room to create a spreadsheet of students in the course who are missing an integration ID.\n\nInput: \n```\ncanvas_students_missing_integration_ids.py canvas_course_id\n```\nOutput: outputs a file ('users_without_integration_ids-COURSE_ID.xlsx) containing a spreadsheet of the users information\n\n\n## `cl_user_info.py`\n\nPurpose: Use the data in a Canvas course room together with the data from Ladok3 to find information about a user.\n\nInput: \n```\ncl_user_info.py Canvas_user_id|KTHID|Ladok_id [course_id]\n```\nThe course_id can be a Canvas course_id **or** if you have dashboard cards, you can specific a course code, a nickname, unique part of the short name or original course name.\n\nAdd the \"-k\" or '--kthid' flag to get the KTHID (i.e., the 'sis_user_id) you need to specify a course_id for a course (where this user is a teacher or student) on the command line.\n\nAdd the \"-T\" flag to run in the Ladok test environment.\n\nIf you know the Ladok_id, i.e., the integration_id - then you do not need to specify a course_id.\n\nThe program can also take an argument in the form https://canvas.kth.se/courses/course_id/users/user_id\n- this is the URL when you are on a user's page in a course.\n\nOutput:\\\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;from Canvas: sortable name, user_id, and integration_id\\\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;if you specified a course_id, you will also get KTHID and login_id\\\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;from Ladok:  pnr (personnumber) and [program_code, program_name, specialization/track code, admissions info]\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbosk%2Fladok3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdbosk%2Fladok3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbosk%2Fladok3/lists"}