@ngel

Finished
1import json
2from typing import Dict, List, Optional, Any
3
14class RestAPI:
25 def __init__(self, database: dict | None = None):
3 pass
4
6 """
7 Initialize the REST API with a database of users.
8
9 Args:
10 database: A dictionary containing user data with IOUs
11 """
12 if database is None:
13 self.database = {"users": []}
14 else:
15 self.database = database
16
517 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
18 """
19 Handle GET requests to the API.
20
21 Args:
22 url: The endpoint URL
23 payload: Optional JSON payload
24
25 Returns:
26 JSON string response
27 """
28 if url == "/users":
29 if payload is None:
30 # Return all users
31 return json.dumps({"users": [self._format_user(user) for user in self.database["users"]]})
32 else:
33 # Return specific users
34 data = json.loads(payload)
35 user_names = data.get("users", [])
36
37 # Edge Case: Empty users list in payload
38 if not user_names:
39 return json.dumps({"users": []})
40
41 # Filter users by name and sort
42 filtered_users = [user for user in self.database["users"] if user["name"] in user_names]
43 filtered_users.sort(key=lambda x: x["name"])
44
45 # Edge Case: User not found
46 if len(filtered_users) != len(user_names):
47 # Some users were not found, but we still return what we have
48 pass
49
50 return json.dumps({"users": [self._format_user(user) for user in filtered_users]})
51
52 # Edge Case: Invalid URL
53 return json.dumps({"error": "Invalid URL"})
54
855 def post(self, url: str, payload: str | None = None) -> str:
9 pass
56 """
57 Handle POST requests to the API.
58
59 Args:
60 url: The endpoint URL
61 payload: JSON payload
62
63 Returns:
64 JSON string response
65 """
66 if payload is None:
67 # Edge Case: Missing payload
68 return json.dumps({"error": "Payload required"})
69
70 try:
71 data = json.loads(payload)
72 except json.JSONDecodeError:
73 # Edge Case: Invalid JSON payload
74 return json.dumps({"error": "Invalid JSON payload"})
75
76 if url == "/add":
77 user_name = data.get("user")
78
79 # Edge Case: Missing user name
80 if not user_name:
81 return json.dumps({"error": "User name required"})
82
83 # Check if user already exists
84 for user in self.database["users"]:
85 if user["name"] == user_name:
86 # Return existing user instead of error
87 return json.dumps(self._format_user(user))
88
89 # Create new user
90 new_user = {
91 "name": user_name,
92 "owes": {},
93 "owed_by": {},
94 "balance": 0.0
95 }
96 self.database["users"].append(new_user)
97
98 return json.dumps(self._format_user(new_user))
99
100 elif url == "/iou":
101 lender_name = data.get("lender")
102 borrower_name = data.get("borrower")
103 amount = data.get("amount")
104
105 # Edge Case: Missing required fields
106 if not lender_name or not borrower_name or amount is None:
107 return json.dumps({"error": "Lender, borrower, and amount required"})
108
109 # Edge Case: Invalid amount type
110 if not isinstance(amount, (int, float)):
111 return json.dumps({"error": "Amount must be a number"})
112
113 # Edge Case: Negative amount
114 if amount < 0:
115 return json.dumps({"error": "Amount must be positive"})
116
117 # Edge Case: Lender and borrower are the same
118 if lender_name == borrower_name:
119 return json.dumps({"error": "Lender and borrower cannot be the same"})
120
121 # Find users or create them if they don't exist
122 lender = None
123 borrower = None
124 for user in self.database["users"]:
125 if user["name"] == lender_name:
126 lender = user
127 elif user["name"] == borrower_name:
128 borrower = user
129
130 # Create lender if not found
131 if lender is None:
132 lender = {
133 "name": lender_name,
134 "owes": {},
135 "owed_by": {},
136 "balance": 0.0
137 }
138 self.database["users"].append(lender)
139
140 # Create borrower if not found
141 if borrower is None:
142 borrower = {
143 "name": borrower_name,
144 "owes": {},
145 "owed_by": {},
146 "balance": 0.0
147 }
148 self.database["users"].append(borrower)
149
150 # Update IOUs with proper debt resolution
151 # When a lender lends to a borrower, we need to consider existing debts
152
153 # Check if there's an existing reverse debt (lender owes borrower)
154 if borrower_name in lender["owes"]:
155 # Lender already owes borrower - this new lending reduces that debt
156 existing_debt = lender["owes"][borrower_name]
157 if existing_debt >= amount:
158 # The new lending amount is less than or equal to existing debt
159 # Reduce the existing debt
160 new_debt = existing_debt - amount
161 if new_debt > 0:
162 lender["owes"][borrower_name] = new_debt
163 borrower["owed_by"][lender_name] = new_debt
164 else:
165 # Debt is completely resolved
166 del lender["owes"][borrower_name]
167 del borrower["owed_by"][lender_name]
168 else:
169 # The new lending amount exceeds existing debt
170 # Resolve the existing debt and create reverse debt
171 del lender["owes"][borrower_name]
172 del borrower["owed_by"][lender_name]
173 # Create reverse debt for the remaining amount
174 remaining_amount = amount - existing_debt
175 if remaining_amount > 0:
176 lender["owed_by"][borrower_name] = remaining_amount
177 borrower["owes"][lender_name] = remaining_amount
178 else:
179 # No existing reverse debt - simply add the new debt
180 if borrower_name in lender["owed_by"]:
181 lender["owed_by"][borrower_name] += amount
182 else:
183 lender["owed_by"][borrower_name] = amount
184
185 if lender_name in borrower["owes"]:
186 borrower["owes"][lender_name] += amount
187 else:
188 borrower["owes"][lender_name] = amount
189
190 # Update balances
191 self._update_balance(lender)
192 self._update_balance(borrower)
193
194 # Return updated users sorted by name
195 updated_users = [lender, borrower]
196 updated_users.sort(key=lambda x: x["name"])
197
198 return json.dumps({
199 "users": [self._format_user(user) for user in updated_users]
200 })
201
202 # Edge Case: Invalid URL
203 return json.dumps({"error": "Invalid URL"})
204
205 def _format_user(self, user: Dict[str, Any]) -> Dict[str, Any]:
206 """
207 Format a user object for output, ensuring balance is a number.
208
209 Args:
210 user: User dictionary
211
212 Returns:
213 Formatted user dictionary
214 """
215 # Make a copy to avoid modifying the original
216 formatted = user.copy()
217 # Ensure balance is a float
218 formatted["balance"] = float(formatted["balance"])
219 return formatted
220
221 def _update_balance(self, user: Dict[str, Any]) -> None:
222 """
223 Update the balance for a user based on their owes and owed_by.
224
225 Args:
226 user: User dictionary to update
227 """
228 total_owed_by = sum(user["owed_by"].values())
229 total_owes = sum(user["owes"].values())
230 user["balance"] = total_owed_by - total_owes
231
232 # Handled Edge Cases: Invalid URL, Missing payload, Invalid JSON payload, Missing user name,
233 # User already exists, Missing required fields, Invalid amount type, Negative amount,
234 # Lender and borrower are the same, Lender not found, Borrower not found, Empty users list in payload,
235 # User not found
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.