11"""TopicList module for OpenProficiency library."""
22
33import json
4+ import re
5+ from datetime import datetime , timezone
46from typing import Dict , Any , Union , List , cast
57from .Topic import Topic
68
@@ -14,7 +16,10 @@ def __init__(
1416 owner : str ,
1517 name : str ,
1618 # Optional
17- description : str = "" ,
19+ description : Union [str , None ] = None ,
20+ version : Union [str , None ] = None ,
21+ timestamp : Union [str , None ] = None ,
22+ certificate : Union [str , None ] = None ,
1823 ):
1924 # Required
2025 self .owner = owner
@@ -25,6 +30,18 @@ def __init__(
2530 self .topics : Dict [str , Topic ] = {}
2631 self .dependencies : Dict [str , "TopicList" ] = {}
2732
33+ # Set version with validation
34+ self .version = version
35+
36+ # Set timestamp (convert string to datetime, default to current UTC if not provided)
37+ if timestamp is None :
38+ self ._timestamp = datetime .now (timezone .utc )
39+ else :
40+ self ._timestamp = datetime .fromisoformat (timestamp .replace ("Z" , "+00:00" ))
41+
42+ # Set certificate
43+ self .certificate = certificate
44+
2845 # Methods
2946 def add_topic (self , topic : Union [str , Topic ]) -> Topic :
3047 """
@@ -44,6 +61,33 @@ def get_topic(self, topic_id: str) -> Union[Topic, None]:
4461 return self .topics .get (topic_id , None )
4562
4663 # Properties
64+ @property
65+ def version (self ) -> Union [str , None ]:
66+ """Get the semantic version of the TopicList."""
67+ return self ._version
68+
69+ @version .setter
70+ def version (self , value : Union [str , None ]) -> None :
71+ """Set the semantic version with X.Y.Z format validation."""
72+ if value is not None and not re .match (r"^\d+\.\d+\.\d+$" , value ):
73+ raise ValueError (f"Invalid version format: '{ value } '. Must be semantic versioning (X.Y.Z)" )
74+ self ._version = value
75+
76+ @property
77+ def timestamp (self ) -> datetime :
78+ """Get the timestamp as a datetime object."""
79+ return self ._timestamp
80+
81+ @timestamp .setter
82+ def timestamp (self , value : Union [str , datetime , None ]) -> None :
83+ """Set the timestamp from a string or datetime object."""
84+ if value is None :
85+ self ._timestamp = datetime .now (timezone .utc )
86+ elif isinstance (value , str ):
87+ self ._timestamp = datetime .fromisoformat (value .replace ("Z" , "+00:00" ))
88+ else :
89+ self ._timestamp = value
90+
4791 @property
4892 def full_name (self ) -> str :
4993 """Get the full name of the TopicList in 'owner/name' format."""
@@ -74,7 +118,10 @@ def from_json(cls, json_data: str) -> "TopicList":
74118 topic_list = TopicList (
75119 owner = data .get ("owner" , "" ),
76120 name = data .get ("name" , "" ),
77- description = data .get ("description" , "" ),
121+ description = data .get ("description" ),
122+ version = data .get ("version" ),
123+ timestamp = data .get ("timestamp" ),
124+ certificate = data .get ("certificate" ),
78125 )
79126
80127 # Add each topic
@@ -211,10 +258,22 @@ def to_dict(self) -> Dict[str, Any]:
211258 data : Dict [str , Any ] = {
212259 "owner" : self .owner ,
213260 "name" : self .name ,
214- "description " : self .description ,
261+ "timestamp " : self .timestamp . isoformat () ,
215262 "topics" : {},
216263 }
217264
265+ # Add description if set
266+ if self .description is not None :
267+ data ["description" ] = self .description
268+
269+ # Add version if set
270+ if self .version is not None :
271+ data ["version" ] = self .version
272+
273+ # Add certificate if set
274+ if self .certificate is not None :
275+ data ["certificate" ] = self .certificate
276+
218277 # Add each topic
219278 for topic_id , topic in self .topics .items ():
220279 # Create topic
0 commit comments