Skip to content

Commit 81aeca9

Browse files
authored
chore: general cleanup (#3)
1 parent f1d5965 commit 81aeca9

11 files changed

Lines changed: 219 additions & 92 deletions

File tree

.devcontainer/devcontainer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
"extensions": [
1616
"ms-python.python",
1717
"ms-python.debugpy",
18-
"esbenp.prettier-vscode"
18+
"esbenp.prettier-vscode",
19+
"ms-python.black-formatter",
20+
"ms-python.autopep8"
1921
],
2022
"settings": {
2123
"editor.formatOnSave": true,

.github/workflows/unit-tests.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ jobs:
2626
pip install -r requirements.txt
2727
pip install -r requirements-dev.txt
2828
29+
- name: Check formatting
30+
run: |
31+
black --check .
32+
autopep8 --diff --exit-code --recursive .
33+
2934
- name: Run tests with pytest
3035
run: |
3136
pytest

.vscode/settings.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
11
{
2+
// JSON
3+
"[json]": {
4+
"editor.defaultFormatter": "esbenp.prettier-vscode",
5+
"prettier.printWidth": 120
6+
},
27
// Python
3-
"python.analysis.typeCheckingMode": "standard",
48
"python.testing.pytestEnabled": true,
59
"python.testing.unittestEnabled": false,
610
"python.testing.pytestArgs": [
711
"tests"
812
],
9-
13+
"[python]": {
14+
"editor.defaultFormatter": "ms-python.black-formatter",
15+
},
16+
"black-formatter.args": [
17+
"--line-length",
18+
"120"
19+
],
20+
"autopep8.args": [
21+
"--max-line-length",
22+
"120"
23+
],
1024
// VS Code
25+
"editor.formatOnSave": true,
1126
"files.exclude": {
1227
"*.pytest_cache": true,
13-
"*.egg-info": true,
28+
"*.egg-info": true
1429
}
1530
}

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ from openproficiency import Topic
2626
topic_arithmetic = Topic(
2727
id="arithmetic",
2828
description="Basic operations for numeric calculations",
29-
subtopics=["addition", "subtraction"]
30-
pretopics=['writing']
29+
subtopics=["addition", "subtraction"],
30+
pretopics=["numbers"]
3131
)
3232
```
3333

@@ -72,7 +72,7 @@ t_arithmetic = Topic(
7272
"addition",
7373
"subtraction",
7474
"multiplication",
75-
"division
75+
"division"
7676
]
7777
)
7878
topic_list.add_topic(t_arithmetic)
@@ -86,7 +86,7 @@ t_algebra = Topic(
8686
"single-variable-equations",
8787
"multiple-variable-equations"
8888
],
89-
pretopics=[ "arithmetic" ]
89+
pretopics=["arithmetic"]
9090
)
9191
```
9292

@@ -95,6 +95,8 @@ t_algebra = Topic(
9595
A **Proficiency Score** represents a person's level of proficiency in a specific topic.
9696

9797
```python
98+
from openproficiency import ProficiencyScore, ProficiencyScoreName
99+
98100
proficiency_score = ProficiencyScore(
99101
topic_id="addition",
100102
score=ProficiencyScoreName.PROFICIENT
@@ -106,6 +108,8 @@ proficiency_score = ProficiencyScore(
106108
All score are internally numeric from 0.0 to 1.0, even if set using the score name (above).
107109

108110
```python
111+
from openproficiency import ProficiencyScore
112+
109113
proficiency_score = ProficiencyScore(
110114
topic_id="arithmetic",
111115
score=0.8 # Same as 'ProficiencyScoreName.PROFICIENT'
@@ -121,10 +125,10 @@ from openproficiency import TranscriptEntry
121125

122126
# Create a transcript entry
123127
entry = TranscriptEntry(
124-
user_id="john-doe",
128+
user_id="first.last@my-email.com",
125129
topic_id="arithmetic",
126130
score=0.9,
127-
issuer="university-of-example"
131+
issuer="example.com"
128132
)
129133

130134
# Access the transcript entry information

openproficiency/Topic.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def __init__(
1414
# Optional
1515
description: str = "",
1616
subtopics: List[Union[str, "Topic"]] = [],
17-
pretopics: List[Union[str, "Topic"]] = []
17+
pretopics: List[Union[str, "Topic"]] = [],
1818
):
1919
# Required
2020
self.id = id
@@ -43,8 +43,7 @@ def add_subtopic(self, subtopic: Union[str, "Topic"]) -> None:
4343
self.subtopics.append(subtopic.id)
4444

4545
else:
46-
raise ValueError(
47-
"Subtopic must be a string or a dictionary with an 'id' key.")
46+
raise ValueError("Subtopic must be a string or a dictionary with an 'id' key.")
4847

4948
def add_subtopics(self, subtopics: List[Union[str, "Topic"]]) -> None:
5049
"""
@@ -67,8 +66,7 @@ def add_pretopic(self, pretopic: Union[str, "Topic"]) -> None:
6766
elif isinstance(pretopic, Topic):
6867
self.pretopics.append(pretopic.id)
6968
else:
70-
raise ValueError(
71-
"Pretopic must be a string or a dictionary with an 'id' key.")
69+
raise ValueError("Pretopic must be a string or a dictionary with an 'id' key.")
7270

7371
def add_pretopics(self, pretopics: List[Union[str, "Topic"]]) -> None:
7472
"""
@@ -78,13 +76,13 @@ def add_pretopics(self, pretopics: List[Union[str, "Topic"]]) -> None:
7876
for pretopic in pretopics:
7977
self.add_pretopic(pretopic)
8078

81-
def to_dict(self) -> dict:
79+
def to_dict(self) -> dict[str, Union[str, List[str]]]:
8280
"""Convert Topic to JSON-serializable dictionary."""
8381
return {
8482
"id": self.id,
8583
"description": self.description,
8684
"subtopics": self.subtopics,
87-
"pretopics": self.pretopics
85+
"pretopics": self.pretopics,
8886
}
8987

9088
def to_json(self) -> str:

openproficiency/TopicList.py

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
"""TopicList module for OpenProficiency library."""
22

33
import json
4-
from datetime import datetime
5-
from typing import Optional, Dict, Any, Union
4+
from typing import Dict, Any, Union, List, cast
65
from .Topic import Topic
76

87

@@ -15,7 +14,7 @@ def __init__(
1514
owner: str,
1615
name: str,
1716
# Optional
18-
description: str = ""
17+
description: str = "",
1918
):
2019
# Required
2120
self.owner = owner
@@ -65,7 +64,7 @@ def from_json(cls, json_data: str) -> "TopicList":
6564

6665
# Verify input is json string
6766
try:
68-
data = json.loads(json_data)
67+
data = cast(Dict[str, Any], json.loads(json_data))
6968
except TypeError:
7069
raise TypeError("Unable to import. 'json_data' must be a JSON string")
7170
except Exception as e:
@@ -79,7 +78,7 @@ def from_json(cls, json_data: str) -> "TopicList":
7978
)
8079

8180
# Add each topic
82-
topics = data.get("topics", {})
81+
topics = cast(Dict[str, Any], data.get("topics", {}))
8382
for topic_id, topic_data in topics.items():
8483

8584
# Find or create Topic
@@ -88,20 +87,21 @@ def from_json(cls, json_data: str) -> "TopicList":
8887
topic = topic_list.add_topic(Topic(id=topic_id))
8988

9089
if isinstance(topic_data, dict):
91-
topic.description = topic_data.get("description", "")
90+
topic_dict = cast(Dict[str, Any], topic_data)
91+
topic.description = topic_dict.get("description", "")
9292

9393
# Add subtopics
9494
cls._add_subtopics_recursive(
9595
topic_list=topic_list,
9696
parent_topic=topic,
97-
subtopics=topic_data.get("subtopics", []),
97+
subtopics=cast(List[Any], topic_dict.get("subtopics", [])),
9898
)
9999

100100
# Add pretopics
101101
cls._add_pretopics_recursive(
102102
topic_list=topic_list,
103103
child_topic=topic,
104-
pretopics=topic_data.get("pretopics", []),
104+
pretopics=cast(List[Any], topic_dict.get("pretopics", [])),
105105
)
106106

107107
else:
@@ -113,13 +113,13 @@ def from_json(cls, json_data: str) -> "TopicList":
113113
def _add_subtopics_recursive(
114114
topic_list: "TopicList",
115115
parent_topic: Topic,
116-
subtopics: list,
116+
subtopics: List[Any],
117117
) -> None:
118118
"""
119119
Process subtopics and add them to the topic list.
120120
Handles nested subtopics at any depth using an iterative approach.
121121
"""
122-
stack = [(subtopics, parent_topic)]
122+
stack: List[tuple[List[Any], Topic]] = [(subtopics, parent_topic)]
123123

124124
while stack:
125125
current_subtopics, current_parent = stack.pop()
@@ -136,16 +136,17 @@ def _add_subtopics_recursive(
136136

137137
# Handle dictionary with id and optional nested subtopics
138138
elif isinstance(subtopic_object, dict) and "id" in subtopic_object:
139+
subtopic_dict = cast(Dict[str, Any], subtopic_object)
139140
# Check if the topic already exists
140-
subtopic = topic_list.get_topic(subtopic_object["id"])
141+
subtopic = topic_list.get_topic(subtopic_dict["id"])
141142
if subtopic is None:
142143
subtopic = Topic(
143-
id=subtopic_object["id"],
144-
description=subtopic_object.get("description", ""),
144+
id=subtopic_dict["id"],
145+
description=subtopic_dict.get("description", ""),
145146
)
146147

147148
# Queue nested subtopics for processing
148-
nested_subtopics = subtopic_object.get("subtopics", [])
149+
nested_subtopics = cast(List[Any], subtopic_dict.get("subtopics", []))
149150
if nested_subtopics:
150151
stack.append((nested_subtopics, subtopic))
151152

@@ -158,14 +159,14 @@ def _add_subtopics_recursive(
158159
def _add_pretopics_recursive(
159160
topic_list: "TopicList",
160161
child_topic: Topic,
161-
pretopics: list,
162+
pretopics: List[Any],
162163
) -> None:
163164
"""
164165
Process pretopics and add them to the topic list.
165166
Handles nested pretopics at any depth using an iterative approach.
166167
Pretopics inherit description from child topic if not explicitly set.
167168
"""
168-
stack = [(pretopics, child_topic)]
169+
stack: List[tuple[List[Any], Topic]] = [(pretopics, child_topic)]
169170

170171
while stack:
171172
current_pretopics, current_child = stack.pop()
@@ -178,24 +179,21 @@ def _add_pretopics_recursive(
178179
# Check if the topic already exists
179180
pretopic = topic_list.get_topic(pretopic_object)
180181
if pretopic is None:
181-
pretopic = Topic(
182-
id=pretopic_object, description=current_child.description
183-
)
182+
pretopic = Topic(id=pretopic_object, description=current_child.description)
184183

185184
# Handle dictionary with id and optional nested pretopics
186185
elif isinstance(pretopic_object, dict) and "id" in pretopic_object:
186+
pretopic_dict = cast(Dict[str, Any], pretopic_object)
187187
# Check if the topic already exists
188-
pretopic = topic_list.get_topic(pretopic_object["id"])
188+
pretopic = topic_list.get_topic(pretopic_dict["id"])
189189
if pretopic is None:
190190
pretopic = Topic(
191-
id=pretopic_object["id"],
192-
description=pretopic_object.get(
193-
"description", current_child.description
194-
),
191+
id=pretopic_dict["id"],
192+
description=pretopic_dict.get("description", current_child.description),
195193
)
196194

197195
# Queue nested pretopics for processing
198-
nested_pretopics = pretopic_object.get("pretopics", [])
196+
nested_pretopics = cast(List[Any], pretopic_dict.get("pretopics", []))
199197
if nested_pretopics:
200198
stack.append((nested_pretopics, pretopic))
201199

@@ -204,9 +202,9 @@ def _add_pretopics_recursive(
204202
topic_list.add_topic(pretopic)
205203
current_child.add_pretopic(pretopic)
206204

207-
def to_dict(self) -> dict:
205+
def to_dict(self) -> Dict[str, Any]:
208206
"""
209-
Export the TopicList to a JSON string.
207+
Export the TopicList to a dictionary.
210208
"""
211209

212210
# Create dictionary

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,13 @@ Documentation = "https://github.com/openproficiency/model"
3131
Repository = "https://github.com/openproficiency/python-sdk.git"
3232
Issues = "https://github.com/openproficiency/python-sdk/issues"
3333

34+
[tool.black]
35+
line-length = 120
36+
target-version = ["py310"]
37+
38+
[tool.autopep8]
39+
max_line_length = 120
40+
aggressive = 1
41+
3442
[tool.setuptools]
3543
packages = ["openproficiency"]

pyrightconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typeCheckingMode": "strict"
3+
}

requirements-dev.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Local package
22
-e .
33

4+
# Formatting
5+
black>=26.1.0
6+
autopep8>=2.0
7+
48
# Testing
59
pytest>=6.0
610
pytest-cov>=2.0

0 commit comments

Comments
 (0)