Updated api to reflect new changes

This commit is contained in:
Boyan 2024-11-18 20:17:26 +01:00
parent 3d9ba063ce
commit 82a072ee14
3 changed files with 91 additions and 170 deletions

View File

@ -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()
```
<sub> I don't see why you would need this, but it's here. </sub>
----
## `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': <temmies.submission.Submission object at 0x...>,
'best': <temmies.submission.Submission object at 0x...>,
'latest': <temmies.submission.Submission object at 0x...>,
'first_pass': <temmies.submission.Submission object at 0x...>,
'last_pass': <temmies.submission.Submission object at 0x...>,
'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')
]
```
----

View File

@ -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)
- 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.

View File

@ -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)")