21
21
22
22
23
23
class TodoStatus (str , Enum ):
24
+ """
25
+ An enum for todo status
26
+ """
27
+
24
28
todo = "todo"
25
29
in_progress = "in progress"
26
30
complete = "complete"
27
31
28
32
29
33
class Todo (BaseModel ):
34
+ """
35
+ Defines a todo
36
+ """
37
+
30
38
name : str
31
39
status : TodoStatus
32
40
created_date : datetime | None = None
@@ -42,28 +50,44 @@ def serialize_updated_date(self, dt: datetime) -> str:
42
50
43
51
44
52
class TodoDocument (BaseModel ):
53
+ """
54
+ Defines a todo document including id as returned from redis
55
+ """
56
+
45
57
id : str
46
58
value : Todo
47
59
48
60
49
61
class Todos (BaseModel ):
62
+ """
63
+ Defines a list of todos and a total, mapping to the results of FT.SEARCH
64
+ """
65
+
50
66
total : int
51
67
documents : List [TodoDocument ]
52
68
53
69
54
70
class TodoStore :
55
- """Stores todos"""
71
+ """
72
+ Stores and retrieves todos in redis
73
+ """
56
74
57
75
def __init__ (self , redis : Redis ):
58
76
self .redis = redis
59
77
self .INDEX = TODOS_INDEX
60
78
self .PREFIX = TODOS_PREFIX
61
79
62
80
async def initialize (self ) -> None :
81
+ """
82
+ Sets up redis to be used with todos
83
+ """
63
84
await self .create_index_if_not_exists ()
64
85
return None
65
86
66
87
async def have_index (self ) -> bool :
88
+ """
89
+ Checks if the TODOS_INDEX already exists in Redis
90
+ """
67
91
try :
68
92
await self .redis .ft (self .INDEX ).info ()
69
93
except ResponseError as e :
@@ -75,6 +99,9 @@ async def have_index(self) -> bool:
75
99
return True
76
100
77
101
async def create_index_if_not_exists (self ) -> None :
102
+ """
103
+ Creates the TODOS_INDEX if it doesn't exist already
104
+ """
78
105
if await self .have_index ():
79
106
return None
80
107
@@ -101,6 +128,9 @@ async def create_index_if_not_exists(self) -> None:
101
128
return None
102
129
103
130
async def drop_index (self ) -> None :
131
+ """
132
+ Drops the TODOS_INDEX if it exists
133
+ """
104
134
if not await self .have_index ():
105
135
return None
106
136
@@ -115,36 +145,52 @@ async def drop_index(self) -> None:
115
145
return None
116
146
117
147
def format_id (self , id : str ) -> str :
148
+ """
149
+ Allow for id with or without TODOS_PREFIX
150
+ """
118
151
if re .match (f"^{ self .PREFIX } " , id ):
119
152
return id
120
153
121
154
return f"{ self .PREFIX } { id } "
122
155
123
- def parse_todo_document (self , todo : Document ) -> TodoDocument :
156
+ def deserialize_todo_document (self , todo : Document ) -> TodoDocument :
157
+ """
158
+ Deserializes a TodoDocument from JSON
159
+ """
124
160
return TodoDocument (
125
161
id = todo .id ,
126
162
value = Todo (** from_json (todo .json , allow_partial = True )), # type: ignore
127
163
)
128
164
129
- def parse_todo_documents (self , todos : list [Document ]) -> list [TodoDocument ]:
165
+ def deserialize_todo_documents (self , todos : list [Document ]) -> list [TodoDocument ]:
166
+ """
167
+ Deserializes a list[TodoDocument] from list[JSON]
168
+ """
130
169
todo_docs = []
131
170
132
171
for doc in todos :
133
- todo_docs .append (self .parse_todo_document (doc ))
172
+ todo_docs .append (self .deserialize_todo_document (doc ))
134
173
135
174
return todo_docs
136
175
137
176
async def all (self ) -> Todos :
177
+ """
178
+ Gets all todos
179
+ """
138
180
try :
139
181
result = await self .redis .ft (self .INDEX ).search ("*" )
140
182
return Todos (
141
- total = result .total , documents = self .parse_todo_documents (result .docs )
183
+ total = result .total ,
184
+ documents = self .deserialize_todo_documents (result .docs ),
142
185
)
143
186
except Exception as e :
144
187
logger .error (f"Error getting all todos: { e } " )
145
188
raise
146
189
147
190
async def one (self , id : str ) -> Todo :
191
+ """
192
+ Gets a todo by id
193
+ """
148
194
id = self .format_id (id )
149
195
150
196
try :
@@ -156,6 +202,9 @@ async def one(self, id: str) -> Todo:
156
202
return Todo (** json )
157
203
158
204
async def search (self , name : str | None , status : TodoStatus | None ) -> Todos :
205
+ """
206
+ Searches for todos by name and/or status
207
+ """
159
208
searches = []
160
209
161
210
if name is not None and len (name ) > 0 :
@@ -167,13 +216,17 @@ async def search(self, name: str | None, status: TodoStatus | None) -> Todos:
167
216
try :
168
217
result = await self .redis .ft (self .INDEX ).search (Query (" " .join (searches )))
169
218
return Todos (
170
- total = result .total , documents = self .parse_todo_documents (result .docs )
219
+ total = result .total ,
220
+ documents = self .deserialize_todo_documents (result .docs ),
171
221
)
172
222
except Exception as e :
173
223
logger .error (f"Error getting todo { id } : { e } " )
174
224
raise
175
225
176
226
async def create (self , id : Optional [str ], name : Optional [str ]) -> TodoDocument :
227
+ """
228
+ Creates a todo
229
+ """
177
230
dt = datetime .now (UTC )
178
231
179
232
if name is None :
@@ -201,6 +254,9 @@ async def create(self, id: Optional[str], name: Optional[str]) -> TodoDocument:
201
254
return todo
202
255
203
256
async def update (self , id : str , status : TodoStatus ) -> Todo :
257
+ """
258
+ Updates a todo
259
+ """
204
260
dt = datetime .now (UTC )
205
261
206
262
todo = await self .one (id )
@@ -222,6 +278,9 @@ async def update(self, id: str, status: TodoStatus) -> Todo:
222
278
return todo
223
279
224
280
async def delete (self , id : str ) -> None :
281
+ """
282
+ Deletes a todo
283
+ """
225
284
try :
226
285
await self .redis .json ().delete (self .format_id (id ))
227
286
except Exception as e :
@@ -231,6 +290,9 @@ async def delete(self, id: str) -> None:
231
290
return None
232
291
233
292
async def delete_all (self ) -> None :
293
+ """
294
+ Delete all todos
295
+ """
234
296
todos = await self .all ()
235
297
coros = []
236
298
0 commit comments