Skip to content

Commit d8ad82f

Browse files
Migrates students from helper_scripts to core.
1 parent 052e508 commit d8ad82f

File tree

4 files changed

+120
-0
lines changed

4 files changed

+120
-0
lines changed

docs/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ Programming Interface
99
base
1010
files
1111
schedule
12+
students
1213
webapp

docs/api/students.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Students
2+
========
3+
4+
.. automodule:: students
5+
:members:

pythonanywhere_core/students.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import getpass
2+
from typing import Optional
3+
4+
from pythonanywhere_core.base import call_api, get_api_endpoint
5+
6+
7+
class StudentsAPI:
8+
"""Interface for PythonAnywhere students API.
9+
10+
Uses `pythonanywhere.api.base` :method: `get_api_endpoint` to
11+
create url, which is stored in a class variable `StudentsAPI.base_url`,
12+
then calls `call_api` with appropriate arguments to execute student
13+
action.
14+
15+
Covers:
16+
- GET
17+
- DELETE
18+
19+
Methods:
20+
- use :method: `StudentsAPI.get` to get list of students
21+
- use :method: `StudentsAPI.delete` to remove a student
22+
"""
23+
24+
base_url: str = get_api_endpoint().format(username=getpass.getuser(), flavor="students")
25+
26+
def get(self) -> Optional[dict]:
27+
"""Returns list of PythonAnywhere students related with user's account."""
28+
29+
result = call_api(self.base_url, "GET")
30+
31+
if result.status_code == 200:
32+
return result.json()
33+
34+
raise Exception(f"GET to list students failed, got {result.text}")
35+
36+
def delete(self, student_username: str) -> Optional[int]:
37+
"""Returns 204 if student has been successfully removed, raises otherwise."""
38+
39+
url = f"{self.base_url}{student_username}"
40+
41+
result = call_api(url, "DELETE")
42+
43+
if result.status_code == 204:
44+
return result.status_code
45+
46+
detail = f": {result.text}" if result.text else ""
47+
raise Exception(
48+
f"DELETE to remove student {student_username!r} failed, got {result}{detail}"
49+
)

tests/test_students.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import getpass
2+
import json
3+
4+
import pytest
5+
import responses
6+
7+
from pythonanywhere_core.base import get_api_endpoint
8+
from pythonanywhere_core.students import StudentsAPI
9+
10+
11+
@pytest.fixture
12+
def students_base_url():
13+
return get_api_endpoint().format(username=getpass.getuser(), flavor="students")
14+
15+
16+
@pytest.mark.students
17+
class TestStudentsAPIGet:
18+
def test_gets_list_of_students_when_there_are_some(
19+
self, api_token, api_responses, students_base_url
20+
):
21+
students = {
22+
"students": [{"username": "student1"}, {"username": "student2"}]
23+
}
24+
api_responses.add(
25+
responses.GET, url=students_base_url, status=200, body=json.dumps(students)
26+
)
27+
28+
assert StudentsAPI().get() == students
29+
30+
def test_gets_empty_list_of_students_when_there_none(
31+
self, api_token, api_responses, students_base_url
32+
):
33+
students = {"students": []}
34+
api_responses.add(
35+
responses.GET, url=students_base_url, status=200, body=json.dumps(students)
36+
)
37+
38+
assert StudentsAPI().get() == students
39+
40+
41+
@pytest.mark.students
42+
class TestStudentsAPIDelete:
43+
def test_returns_204_when_student_deleted(
44+
self, api_token, api_responses, students_base_url
45+
):
46+
username = "byebye"
47+
url = f"{students_base_url}{username}"
48+
api_responses.add(responses.DELETE, url=url, status=204)
49+
50+
assert StudentsAPI().delete("byebye") == 204
51+
52+
def test_raises_with_404_when_no_student_to_delete_found(
53+
self, api_token, api_responses, students_base_url
54+
):
55+
username = "notyourstudent"
56+
url = f"{students_base_url}{username}"
57+
api_responses.add(responses.DELETE, url=url, status=404)
58+
59+
with pytest.raises(Exception) as e:
60+
StudentsAPI().delete("notyourstudent")
61+
62+
expected_error_msg = (
63+
f"DELETE to remove student {username!r} failed, got <Response [404]>"
64+
)
65+
assert str(e.value) == expected_error_msg

0 commit comments

Comments
 (0)