Skip to content

Commit 5d0f43e

Browse files
authored
feat: add missing fields for TranscriptEntry (#6)
1 parent c4f96f4 commit 5d0f43e

File tree

4 files changed

+178
-61
lines changed

4 files changed

+178
-61
lines changed

openproficiency/TopicList.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,11 @@ def timestamp(self, value: Union[str, datetime, None]) -> None:
113113

114114
@property
115115
def full_name(self) -> str:
116-
"""Get the full name of the TopicList in 'owner/name' format."""
117-
return f"{self.owner}/{self.name}"
116+
"""Get the full name of the TopicList in 'owner/name@version' format."""
117+
full_name = f"{self.owner}/{self.name}"
118+
if self.version:
119+
full_name += f"@{self.version}"
120+
return full_name
118121

119122
# Debugging
120123
def __repr__(self) -> str:

openproficiency/TranscriptEntry.py

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import json
44
from datetime import datetime
5-
from typing import Optional, Union
5+
from typing import Any, Dict, Optional, Union
66
from .ProficiencyScore import ProficiencyScore
77

88

@@ -15,66 +15,128 @@ def __init__(
1515
# Required
1616
user_id: str,
1717
topic_id: str,
18+
topic_list: str,
1819
score: float,
20+
timestamp: Union[datetime, str],
1921
issuer: str,
2022
# Optional
21-
timestamp: Optional[datetime] = None,
23+
certificate: Optional[str] = None,
2224
):
2325
# Required
2426
self.user_id = user_id
2527
self._proficiency_score = ProficiencyScore(
2628
topic_id=topic_id,
2729
score=score,
2830
)
31+
self.topic_list = topic_list
32+
self.timestamp = timestamp
2933
self.issuer = issuer
30-
3134
# Optional
32-
self.timestamp = timestamp or datetime.now()
35+
self.certificate = certificate
3336

3437
# Properties
3538
@property
3639
def proficiency_score(self) -> ProficiencyScore:
3740
"""Get the topic ID from the proficiency score."""
3841
return self._proficiency_score
3942

43+
@property
44+
def topic_list(self) -> str:
45+
"""Get the topic list reference."""
46+
return self._topic_list
47+
48+
@topic_list.setter
49+
def topic_list(self, value: str) -> None:
50+
"""Set the topic list reference from TopicList instance"""
51+
self._topic_list = value
52+
53+
@property
54+
def certificate(self) -> Optional[str]:
55+
"""Get the certificate text."""
56+
return self._certificate
57+
58+
@certificate.setter
59+
def certificate(self, value: Optional[str]) -> None:
60+
"""Set the certificate text."""
61+
self._certificate = value
62+
63+
@property
64+
def timestamp(self) -> datetime:
65+
"""Get the time this entry was created"""
66+
return self._timestamp
67+
68+
@timestamp.setter
69+
def timestamp(self, value: Union[datetime, str]) -> None:
70+
"""Set the time this entry was created"""
71+
# Directly store datetime object.
72+
if isinstance(value, datetime):
73+
self._timestamp = value
74+
75+
# Convert ISO 8601 string to datetime object.
76+
elif isinstance(value, str):
77+
self._timestamp = datetime.fromisoformat(value)
78+
79+
else:
80+
raise ValueError("Timestamp must be a datetime object or ISO 8601 string")
81+
4082
# Methods
41-
def to_dict(self) -> dict[str, Union[str, int, float]]:
83+
def to_dict(self) -> Dict[str, Union[str, float]]:
4284
"""Convert TranscriptEntry to JSON-serializable dictionary."""
43-
return {
85+
data = {
4486
"user_id": self.user_id,
45-
"topic_id": self._proficiency_score.topic_id,
87+
"topic": self._proficiency_score.topic_id,
88+
"topic_list": self.topic_list,
4689
"score": self._proficiency_score.score,
47-
"issuer": self.issuer,
4890
"timestamp": self.timestamp.isoformat(),
91+
"issuer": self.issuer,
4992
}
5093

94+
# Attach certificate if it exists
95+
if self.certificate is not None:
96+
data["certificate"] = self.certificate
97+
98+
return data
99+
51100
def to_json(self) -> str:
52101
"""Convert TranscriptEntry to JSON string."""
53-
return json.dumps(self.to_dict())
102+
data = self.to_dict()
103+
# Change keys to camelCase for JSON output
104+
data["userID"] = data.pop("user_id")
105+
data["topicList"] = data.pop("topic_list")
106+
return json.dumps(data)
54107

55108
# Methods - Static
56109
@staticmethod
57-
def from_dict(data: dict[str, Union[str, int, float]]) -> "TranscriptEntry":
110+
def from_dict(data: Dict[str, Any]) -> "TranscriptEntry":
58111
"""Create a TranscriptEntry from a dictionary."""
59112
return TranscriptEntry(
113+
# Required
60114
user_id=data["user_id"],
61-
topic_id=data["topic_id"],
115+
topic_id=data["topic"],
116+
topic_list=data["topic_list"],
62117
score=data["score"],
118+
timestamp=data["timestamp"],
63119
issuer=data["issuer"],
64-
timestamp=datetime.fromisoformat(data["timestamp"]),
120+
# Optional
121+
certificate=data.get("certificate"),
65122
)
66123

67124
@staticmethod
68125
def from_json(json_str: str) -> "TranscriptEntry":
69126
"""Create a TranscriptEntry from a JSON string."""
70-
return TranscriptEntry.from_dict(json.loads(json_str))
127+
# Load JSON string. Map camelCase to snake_case if needed
128+
data = json.loads(json_str)
129+
data["user_id"] = data.pop("userID")
130+
data["topic_list"] = data.pop("topicList")
131+
return TranscriptEntry.from_dict(data)
71132

72133
# Debugging
73134
def __repr__(self) -> str:
74135
"""String representation of TranscriptEntry."""
75136
return (
76137
f"TranscriptEntry(user_id='{self.user_id}', "
77138
f"topic_id='{self._proficiency_score.topic_id}', "
139+
f"topic_list='{self.topic_list}', "
78140
f"score={self._proficiency_score.score}, "
79141
f"issuer='{self.issuer}'"
80142
)

tests/TopicList_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,24 @@ def test_full_name(self):
220220
# Assert
221221
assert full_name == "example.com/example-topic-list"
222222

223+
def test_full_name_with_version(self):
224+
"""Test getting the full name of the topic list, including version."""
225+
226+
# Arrange
227+
owner = "example.com"
228+
name = "example-topic-list"
229+
topic_list = TopicList(
230+
owner=owner,
231+
name=name,
232+
version="1.2.3",
233+
)
234+
235+
# Act
236+
full_name = topic_list.full_name
237+
238+
# Assert
239+
assert full_name == "example.com/example-topic-list@1.2.3"
240+
223241
def test_version_setter_valid_format(self):
224242
"""Test setting version with valid semantic versioning."""
225243

0 commit comments

Comments
 (0)