From 82a072ee14f8f26c0ab7df08b9702f12f4a34b94 Mon Sep 17 00:00:00 2001 From: Boyan <36108495+confestim@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:17:26 +0100 Subject: [PATCH] Updated api to reflect new changes --- docs/api.md | 250 +++++++++++++++------------------------------- docs/changelog.md | 9 +- docs/index.md | 2 +- 3 files changed, 91 insertions(+), 170 deletions(-) diff --git a/docs/api.md b/docs/api.md index 2768270..3a98534 100644 --- a/docs/api.md +++ b/docs/api.md @@ -17,8 +17,8 @@ On the first run, you will be prompted for your password. Then, on the next run( #### `login()` Logs in to Themis. Runs automatically when the class is initialized. -#### `get_year(start, end)` -Returns an instance of a [`Year`](#year) (academic year) between `start` and `end`. +#### `get_year(year_path)` +Returns an instance of a [`Year`](#year) for the academic year specified by `year_path`. ```python year = themis.get_year(2023, 2024) @@ -30,7 +30,7 @@ Returns a list of `Year` instances corresponding to all years visible to the use ```python years = themis.all_years() ``` - I don't see why you would need this, but it's here. + ---- ## `Year` @@ -41,13 +41,20 @@ year = themis.get_year(2023, 2024) ``` ### Methods -#### `get_course(name)` -Returns an instance of a [`Course`](#course) with the name `name`. +#### `get_course(course_title)` +Returns an instance of a [`Course`](#course) with the title `course_title`. ```python pf = year.get_course("Programming Fundamentals (for CS)") ``` +#### `get_course_by_tag(course_tag)` +Returns an instance of a [`Course`](#course) using the course identifier `course_tag`. + +```python +ai_course = year.get_course_by_tag("adinc-ai") +``` + #### `all_courses()` Returns a list of `Course` instances corresponding to all courses visible to the user in a given `Year`. @@ -66,160 +73,96 @@ assignments = pf.get_groups() ### Methods #### `get_groups(full=False)` -Returns a list of `ExerciseGroup` instances corresponding to all exercise groups visible to the user in a given `Course`. The default argument is `full=False`, which will only return the top-level (name, link) of each exercise and folder in the group. If `full=True`, it will traverse the whole course. - -You can traverse the course in both cases, although in different ways. - -When you have fully traversed the course, you can access everything via indices and the `exercises` and `folders` attributes of the `ExerciseGroup` instances: +Returns a list of `ExerciseGroup` or `Group` instances corresponding to all items visible to the user in a given `Course`. The default argument is `full=False`, which will only return the top-level (name, link) of each item. If `full=True`, it will traverse the whole course. ```python -ai_group = ai_course.get_groups(full=True) -exercise = ai_group[7].exercises[1] # Week 11 -> Suitcase packing -exercise.submit(["suitcase.py"], silent=False) -``` - -This is equivalent to the case in which we don't traverse the whole course using `get_group` like so: - -```python -ai_group = ai_course.get_group("Week 11") -exercise = ai_group.get_group("Suitcase packing") -exercise.submit(["suitcase.py"], silent=False) +ai_groups = ai_course.get_groups(full=True) +exercise = ai_groups[7].exercises[1] +exercise.submit(["solution.py"], silent=False) ``` #### `get_group(name, full=False)` -Returns an instance of an `ExerciseGroup` with the name `name`. The default argument is `full=False`, which will only return the (name, link) of each exercise and folder in the group. If `full=True`, it will traverse the whole group. +Returns an instance of an `ExerciseGroup` or `Group` with the name `name`. The default argument is `full=False`, which will only return the (name, link) of the group. If `full=True`, it will traverse the whole group. ```python week1 = pf.get_group("Week 1") ``` +#### `create_group(item_data)` +Creates and returns a `Group` or `ExerciseGroup` instance based on `item_data`. + +```python +group = course.create_group(item_data) +``` + +---- + +## `Group` + +Represents an item in Themis, which can be either a folder (non-submittable) or an assignment (submittable). + +### Methods +#### `get_items()` +Returns all items (groups and assignments) under this group. + +```python +items = week1.get_items() +``` + +#### `get_item_by_title(title)` +Returns a single item by its title (case-insensitive). + +```python +item = week1.get_item_by_title("Exercise 2") +``` + +#### `get_status(text=False)` +Retrieves the status of the group. When `text=True`, returns the status as strings. Otherwise, returns submission objects or strings. + +```python +status = group.get_status() +leading_submission = status["leading"] +``` + +#### `download_files(path=".")` +Downloads all files available for this group to a directory `path`. Defaults to the current directory. + +```python +group.download_files() +``` + +#### `download_tcs(path=".")` +Downloads all test cases for this group to a directory `path`. Defaults to the current directory. + +```python +group.download_tcs() +``` + +#### `submit(files, judge=True, wait=True, silent=True)` +Submits the files to the group. Default arguments are `judge=True`, `wait=True`, and `silent=True`. + +```python +group.submit(["solution.py"], silent=False) +``` + ---- ## `ExerciseGroup` -Setting the `full` flag to `True` will traverse the whole group. +Represents a submittable exercise. Inherits from `Group`. -- Both folders and exercises are represented as `ExerciseGroup` instances. -- Folders will have the `am_exercise` attribute set to `False`. -- Folders can have the `download_files` method called on them. -- Exercises can have the `submit`, `download_files`, and `download_tcs` methods called on them. - -### Example of folder traversal -Let's say we have a folder structure like this: -``` -- Course Name - - Week 1 - - Exercise 1 - - Exercise 2 - - Part 1 - - Part 2 - - Week 2 - - Exercise 1 - - Exercise 2 -``` -And we want to get to `Part 2` of `Week 1`'s `Exercise 2`. We would do this: +### Additional Methods +#### `submit(files)` +Submits files to the exercise. Raises an error if the item is not submittable. ```python -pf = year.get_course("Programming Fundamentals (for CS)") -assignments = pf.get_groups() -week1 = assignments[0] # Week 1 -exercise2 = week1.folders[1] # Exercise 2 -part2 = exercise2.exercises[1] # Part 2 - -# Or, if you don't want to traverse the whole course: -week1 = pf.get_group("Week 1") -exercise2 = week1.get_group("Exercise 2") -part2 = exercise2.get_group("Part 2") -``` - -### Methods -#### `download_files(path=".")` -Downloads all files in the exercise group to a directory `path`. Defaults to the current directory. - -```python -assignment.download_files() -``` - -#### `download_tcs(path=".")` -Downloads all test cases in the exercise group to a directory `path`. Defaults to the current directory. - -```python -assignment.download_tcs() -``` - -#### `get_group(name, full=False)` -This is used when you want to traverse the course dynamically (not recurse through the whole thing). You can use it even if you've traversed the whole course. - -```python -# Week 1 -> Exercise 2 -> Part 2 -week1 = pf.get_group("Week 1") -exercise2 = week1.get_group("Exercise 2") -part2 = exercise2.get_group("Part 2") - -# This is equivalent to (but faster than): -week1 = pf.get_groups(full=True)[0] -exercise2 = week1.folders[1] -part2 = exercise2.exercises[1] -``` - -#### `submit(files, judge=True, wait=True, silent=True)` -Submits the files to the exercise. The default arguments are `judge=True`, `wait=True`, and `silent=True`. Setting `judge=False` will not judge the submission immediately. Setting `wait=False` will not wait for the submission to finish. Turning off `silent` will print the submission status dynamically. - -```python -suitcase = ai_course.get_group("Week 11").get_group("Suitcase packing") -suitcase.submit(["suitcase.py"], silent=False) - -# Output: -# Submitting to Suitcase packing -# • suitcase.py -# 1: ✅ -# 2: ✅ -# 3: ✅ -# ... -``` - -#### `get_status(text=False)` -Retrieves the status of the exercise group. When `text` is set to `True`, it will return the status as a dictionary of strings. Otherwise, it will return a dictionary where keys map to either strings or `Submission` objects. Common keys include `'leading'`, `'best'`, `'latest'`, etc. - -```python -pf = year.get_course("Programming Fundamentals (for CS)") -exercise = pf.get_group("Lab Session 2").get_group("Recurrence") - -# Get status -status = exercise.get_status() -print(status) - -# Output: -{ - 'assignment': 'Recurrence', - 'group': 'Y.N. Here', - 'status': 'passed: Passed all test cases', - 'grade': '2.00', - 'total': '2', - 'output limit': '1', - 'passed': '1', - 'leading': , - 'best': , - 'latest': , - 'first_pass': , - 'last_pass': , - 'visible': 'Yes' -} -``` - -To access submission details: - -```python -leading_submission = status["leading"] -print(leading_submission.get_files()) +exercise.submit(["solution.py"]) ``` ---- ## `Submission` -### Usage -```python -submission = pf.get_group("Week 1").get_group("Exercise 1").get_group("Part 1").get_status()["leading"] -``` + +Represents a submission for a specific exercise. ### Methods #### `get_test_cases()` @@ -227,33 +170,13 @@ Returns a dictionary of test cases and their statuses. ```python test_cases = submission.get_test_cases() -print(test_cases) - -# Output: -{'1': 'passed', '2': 'passed', '3': 'passed', '4': 'passed', '5': 'passed', '6': 'passed', '7': 'passed', '8': 'passed', '9': 'passed', '10': 'passed'} ``` #### `get_info()` -Returns a dictionary of information about the submission. +Returns detailed information about the submission. ```python info = submission.get_info() -print(info) - -# Output: -{ - 'assignment': 'Part 1', - 'group': 'Y.N. Here', - 'uploaded_by': 'Y.N. Here s1234567', - 'created_on': 'Wed Sep 13 2023 12:51:37 GMT+0200', - 'submitted_on': 'Wed Sep 13 2023 12:51:37 GMT+0200', - 'status': 'passed: Passed all test cases', - 'files': [ - ('recurrence.c', '/file/.../recurrence.c'), - ('compile.log', '/file/.../compile.log') - ], - 'language': 'c' -} ``` #### `get_files()` @@ -261,13 +184,4 @@ Returns a list of uploaded files in the format `(name, URL)`. ```python files = submission.get_files() -print(files) - -# Output: -[ - ('recurrence.c', '/file/.../recurrence.c'), - ('compile.log', '/file/.../compile.log') -] ``` - ----- diff --git a/docs/changelog.md b/docs/changelog.md index 216b1f3..901e502 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -11,4 +11,11 @@ #### **Codebase** - Prepended `get_` to all methods in `Submission` - Created base `Group` from which `Course` and `ExerciseGroup` inherit. -- Using system keyring to store passwords (Issue #11) \ No newline at end of file +- Using system keyring to store passwords (Issue #11) + +### **Version 1.2.0** + +#### **Codebase** +- Moved all methods related to downloading files (including test cases) to `Group`. +- Created `get_test_cases` and `get_files` methods in `Group`. +- We are now using the [API](https://themis.housing.rug.nl/api/navigation/2023-2024) (which mysteriously appeared) to get the year/course structure. \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 3483587..f7ef0d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,7 +24,7 @@ from temmies.themis import Themis themis = Themis("s-number") # You will be prompted for your password # Get a year -year = themis.get_year(2023, 2024) +year = themis.get_year("2023-2024") # Get a course course = year.get_course("Programming Fundamentals (for CS)")