mirror of
https://github.com/Code-For-Groningen/temmies.git
synced 2025-03-15 15:10:15 +01:00
Updated api to reflect new changes
This commit is contained in:
parent
3d9ba063ce
commit
82a072ee14
250
docs/api.md
250
docs/api.md
@ -17,8 +17,8 @@ On the first run, you will be prompted for your password. Then, on the next run(
|
|||||||
#### `login()`
|
#### `login()`
|
||||||
Logs in to Themis. Runs automatically when the class is initialized.
|
Logs in to Themis. Runs automatically when the class is initialized.
|
||||||
|
|
||||||
#### `get_year(start, end)`
|
#### `get_year(year_path)`
|
||||||
Returns an instance of a [`Year`](#year) (academic year) between `start` and `end`.
|
Returns an instance of a [`Year`](#year) for the academic year specified by `year_path`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
year = themis.get_year(2023, 2024)
|
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
|
```python
|
||||||
years = themis.all_years()
|
years = themis.all_years()
|
||||||
```
|
```
|
||||||
<sub> I don't see why you would need this, but it's here. </sub>
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## `Year`
|
## `Year`
|
||||||
@ -41,13 +41,20 @@ year = themis.get_year(2023, 2024)
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
#### `get_course(name)`
|
#### `get_course(course_title)`
|
||||||
Returns an instance of a [`Course`](#course) with the name `name`.
|
Returns an instance of a [`Course`](#course) with the title `course_title`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
pf = year.get_course("Programming Fundamentals (for CS)")
|
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()`
|
#### `all_courses()`
|
||||||
Returns a list of `Course` instances corresponding to all courses visible to the user in a given `Year`.
|
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
|
### Methods
|
||||||
#### `get_groups(full=False)`
|
#### `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.
|
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.
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
ai_group = ai_course.get_groups(full=True)
|
ai_groups = ai_course.get_groups(full=True)
|
||||||
exercise = ai_group[7].exercises[1] # Week 11 -> Suitcase packing
|
exercise = ai_groups[7].exercises[1]
|
||||||
exercise.submit(["suitcase.py"], silent=False)
|
exercise.submit(["solution.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)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_group(name, full=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
|
```python
|
||||||
week1 = pf.get_group("Week 1")
|
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`
|
## `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.
|
### Additional Methods
|
||||||
- Folders will have the `am_exercise` attribute set to `False`.
|
#### `submit(files)`
|
||||||
- Folders can have the `download_files` method called on them.
|
Submits files to the exercise. Raises an error if the item is not submittable.
|
||||||
- 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:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
pf = year.get_course("Programming Fundamentals (for CS)")
|
exercise.submit(["solution.py"])
|
||||||
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())
|
|
||||||
```
|
```
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## `Submission`
|
## `Submission`
|
||||||
### Usage
|
|
||||||
```python
|
Represents a submission for a specific exercise.
|
||||||
submission = pf.get_group("Week 1").get_group("Exercise 1").get_group("Part 1").get_status()["leading"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
#### `get_test_cases()`
|
#### `get_test_cases()`
|
||||||
@ -227,33 +170,13 @@ Returns a dictionary of test cases and their statuses.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
test_cases = submission.get_test_cases()
|
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()`
|
#### `get_info()`
|
||||||
Returns a dictionary of information about the submission.
|
Returns detailed information about the submission.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
info = submission.get_info()
|
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()`
|
#### `get_files()`
|
||||||
@ -261,13 +184,4 @@ Returns a list of uploaded files in the format `(name, URL)`.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
files = submission.get_files()
|
files = submission.get_files()
|
||||||
print(files)
|
|
||||||
|
|
||||||
# Output:
|
|
||||||
[
|
|
||||||
('recurrence.c', '/file/.../recurrence.c'),
|
|
||||||
('compile.log', '/file/.../compile.log')
|
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
----
|
|
||||||
|
@ -12,3 +12,10 @@
|
|||||||
- Prepended `get_` to all methods in `Submission`
|
- Prepended `get_` to all methods in `Submission`
|
||||||
- Created base `Group` from which `Course` and `ExerciseGroup` inherit.
|
- 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.
|
@ -24,7 +24,7 @@ from temmies.themis import Themis
|
|||||||
themis = Themis("s-number") # You will be prompted for your password
|
themis = Themis("s-number") # You will be prompted for your password
|
||||||
|
|
||||||
# Get a year
|
# Get a year
|
||||||
year = themis.get_year(2023, 2024)
|
year = themis.get_year("2023-2024")
|
||||||
|
|
||||||
# Get a course
|
# Get a course
|
||||||
course = year.get_course("Programming Fundamentals (for CS)")
|
course = year.get_course("Programming Fundamentals (for CS)")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user