perfect

Finished
1import json
2
13class RestAPI:
24 def __init__(self, database: dict | None = None):
3 pass
4
5 """
6 Initialize the REST API with an optional database.
7
8 Args:
9 database: A dictionary containing initial user data.
10 """
11 if database is None:
12 self.database = {"users": []}
13 else:
14 self.database = database
15
516 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
17 """
18 Handle GET requests to the API.
19
20 Args:
21 url: The endpoint URL.
22 payload: Optional JSON payload.
23
24 Returns:
25 A JSON string response.
26
27 Raises:
28 ValueError: If the URL is not recognized.
29 """
30 if url == "/users":
31 if payload is None:
32 # Return all users
33 result = {"users": []}
34 for user in self.database["users"]:
35 # Calculate balance
36 total_owed_by = sum(user.get("owed_by", {}).values())
37 total_owes = sum(user.get("owes", {}).values())
38 balance = total_owed_by - total_owes
39
40 user_obj = {
41 "name": user["name"],
42 "owes": user.get("owes", {}),
43 "owed_by": user.get("owed_by", {}),
44 "balance": balance
45 }
46 result["users"].append(user_obj)
47 return json.dumps(result)
48 else:
49 # Return specific users
50 payload_data = json.loads(payload)
51 user_names = payload_data.get("users", [])
52
53 result = {"users": []}
54
55 # Filter users by name and sort by name
56 filtered_users = [user for user in self.database["users"] if user["name"] in user_names]
57 filtered_users.sort(key=lambda x: x["name"])
58
59 for user in filtered_users:
60 # Calculate balance
61 total_owed_by = sum(user.get("owed_by", {}).values())
62 total_owes = sum(user.get("owes", {}).values())
63 balance = total_owed_by - total_owes
64
65 user_obj = {
66 "name": user["name"],
67 "owes": user.get("owes", {}),
68 "owed_by": user.get("owed_by", {}),
69 "balance": balance
70 }
71 result["users"].append(user_obj)
72
73 return json.dumps(result)
74 else:
75 raise ValueError(f"Unknown URL: {url}")
76
877 def post(self, url: str, payload: str | None = None) -> str:
9 pass
78 """
79 Handle POST requests to the API.
80
81 Args:
82 url: The endpoint URL.
83 payload: JSON payload.
84
85 Returns:
86 A JSON string response.
87
88 Raises:
89 ValueError: If the URL is not recognized or if there are validation errors.
90 """
91 if url == "/add":
92 if payload is None:
93 raise ValueError("Payload is required for /add")
94
95 payload_data = json.loads(payload)
96 user_name = payload_data.get("user")
97
98 # Edge Case: User name is missing
99 if user_name is None:
100 raise ValueError("User name is required")
101
102 # Edge Case: User already exists
103 for user in self.database["users"]:
104 if user["name"] == user_name:
105 raise ValueError(f"User {user_name} already exists")
106
107 # Create new user
108 new_user = {
109 "name": user_name,
110 "owes": {},
111 "owed_by": {}
112 }
113 self.database["users"].append(new_user)
114
115 # Calculate balance
116 total_owed_by = sum(new_user.get("owed_by", {}).values())
117 total_owes = sum(new_user.get("owes", {}).values())
118 balance = total_owed_by - total_owes
119
120 result = {
121 "name": new_user["name"],
122 "owes": new_user.get("owes", {}),
123 "owed_by": new_user.get("owed_by", {}),
124 "balance": balance
125 }
126
127 return json.dumps(result)
128
129 elif url == "/iou":
130 if payload is None:
131 raise ValueError("Payload is required for /iou")
132
133 payload_data = json.loads(payload)
134 lender_name = payload_data.get("lender")
135 borrower_name = payload_data.get("borrower")
136 amount = payload_data.get("amount")
137
138 # Edge Case: Missing required fields
139 if lender_name is None or borrower_name is None or amount is None:
140 raise ValueError("Lender, borrower, and amount are required")
141
142 # Edge Case: Lender and borrower are the same
143 if lender_name == borrower_name:
144 raise ValueError("Lender and borrower cannot be the same")
145
146 # Edge Case: Amount is negative
147 if amount < 0:
148 raise ValueError("Amount cannot be negative")
149
150 # Find lender and borrower
151 lender = None
152 borrower = None
153
154 for user in self.database["users"]:
155 if user["name"] == lender_name:
156 lender = user
157 if user["name"] == borrower_name:
158 borrower = user
159
160 # Edge Case: Lender or borrower does not exist
161 if lender is None:
162 raise ValueError(f"User {lender_name} does not exist")
163 if borrower is None:
164 raise ValueError(f"User {borrower_name} does not exist")
165
166 # Update IOUs
167 # Handle bidirectional relationships
168 # First check if borrower already owes lender money (from lender's perspective)
169 if borrower_name in lender.get("owed_by", {}):
170 existing_owed = lender["owed_by"][borrower_name]
171 # Borrower already owes lender, add to existing debt
172 lender["owed_by"][borrower_name] = existing_owed + amount
173 # Update borrower's owes record
174 if "owes" not in borrower:
175 borrower["owes"] = {}
176 borrower["owes"][lender_name] = existing_owed + amount
177 else:
178 # Check if lender owes borrower money
179 if borrower_name in lender.get("owes", {}):
180 existing_owes = lender["owes"][borrower_name]
181 if existing_owes > amount:
182 # Lender still owes borrower, but less
183 lender["owes"][borrower_name] = existing_owes - amount
184 # Update borrower's owed_by record
185 if "owed_by" not in borrower:
186 borrower["owed_by"] = {}
187 borrower["owed_by"][lender_name] = existing_owes - amount
188 elif existing_owes == amount:
189 # Zero out both directions
190 del lender["owes"][borrower_name]
191 if lender_name in borrower.get("owed_by", {}):
192 del borrower["owed_by"][lender_name]
193 else:
194 # Now borrower owes lender (net effect)
195 del lender["owes"][borrower_name]
196 # Remove borrower's owed_by entry if it exists
197 if lender_name in borrower.get("owed_by", {}):
198 del borrower["owed_by"][lender_name]
199 # Create new relationship: borrower owes lender
200 if "owed_by" not in lender:
201 lender["owed_by"] = {}
202 lender["owed_by"][borrower_name] = amount - existing_owes
203 if "owes" not in borrower:
204 borrower["owes"] = {}
205 borrower["owes"][lender_name] = amount - existing_owes
206 else:
207 # No existing relationship, create new one
208 if "owes" not in borrower:
209 borrower["owes"] = {}
210 borrower["owes"][lender_name] = amount
211
212 if "owed_by" not in lender:
213 lender["owed_by"] = {}
214 lender["owed_by"][borrower_name] = amount
215
216 # Prepare response with updated user objects
217 result = {"users": []}
218
219 # Get updated lender and borrower
220 updated_lender = None
221 updated_borrower = None
222
223 for user in self.database["users"]:
224 if user["name"] == lender_name:
225 updated_lender = user
226 if user["name"] == borrower_name:
227 updated_borrower = user
228
229 # Add lender to result
230 total_owed_by = sum(updated_lender.get("owed_by", {}).values())
231 total_owes = sum(updated_lender.get("owes", {}).values())
232 balance = total_owed_by - total_owes
233
234 lender_obj = {
235 "name": updated_lender["name"],
236 "owes": updated_lender.get("owes", {}),
237 "owed_by": updated_lender.get("owed_by", {}),
238 "balance": balance
239 }
240
241 # Add borrower to result
242 total_owed_by = sum(updated_borrower.get("owed_by", {}).values())
243 total_owes = sum(updated_borrower.get("owes", {}).values())
244 balance = total_owed_by - total_owes
245
246 borrower_obj = {
247 "name": updated_borrower["name"],
248 "owes": updated_borrower.get("owes", {}),
249 "owed_by": updated_borrower.get("owed_by", {}),
250 "balance": balance
251 }
252
253 # Sort by name
254 if lender_name < borrower_name:
255 result["users"].append(lender_obj)
256 result["users"].append(borrower_obj)
257 else:
258 result["users"].append(borrower_obj)
259 result["users"].append(lender_obj)
260
261 return json.dumps(result)
262
263 else:
264 raise ValueError(f"Unknown URL: {url}")
265
266 # Handled Edge Cases:
267 # - User name missing in /add
268 # - User already exists in /add
269 # - Missing required fields in /iou
270 # - Lender and borrower are the same in /iou
271 # - Amount is negative in /iou
272 # - Lender or borrower does not exist in /iou
Test NameStatus
test_add_user
Pass
test_borrower_has_negative_balance
Pass
test_both_users_have_0_balance
Pass
test_get_single_user
Pass
test_lender_has_negative_balance
Pass
test_lender_owes_borrower
Pass
test_lender_owes_borrower_less_than_new_loan
Pass
test_lender_owes_borrower_same_as_new_loan
Pass
test_no_users
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.