weekends

Finished
1class RestAPI:
2 def __init__(self, database: dict | None = None):
3 pass
4
5 def get(self, url: str, payload: str | None = None) -> str:
6 pass
1import json
2from typing import Dict, List, Optional, Any
73
8 def post(self, url: str, payload: str | None = None) -> str:
9 pass
4class RestAPI:
5 """
6 A RESTful API for tracking IOUs between users.
7
8 This class manages user accounts and IOU transactions in memory.
9 """
10
11 def __init__(self, database: Optional[Dict[str, Any]] = None):
12 """
13 Initialize the RestAPI with an optional database.
14
15 Args:
16 database: Optional initial database with users data
17 """
18 if database is None:
19 self.database = {"users": []}
20 else:
21 self.database = database
22
23 def get(self, url: str, payload: Optional[str] = None) -> str:
24 """
25 Handle GET requests to the API.
26
27 Args:
28 url: The endpoint URL
29 payload: Optional JSON payload
30
31 Returns:
32 JSON response string
33 """
34 if url == "/users":
35 if payload is None:
36 # Return all users sorted by name
37 users = self._get_all_users()
38 return json.dumps({"users": users})
39 else:
40 # Return specific users
41 data = json.loads(payload)
42 user_names = data.get("users", [])
43 users = self._get_users_by_names(user_names)
44 return json.dumps({"users": users})
45
46 # Edge Case: Invalid URL
47 return json.dumps({"error": "Invalid URL"})
48
49 def post(self, url: str, payload: Optional[str] = None) -> str:
50 """
51 Handle POST requests to the API.
52
53 Args:
54 url: The endpoint URL
55 payload: Optional JSON payload
56
57 Returns:
58 JSON response string
59 """
60 if payload is None:
61 # Edge Case: Missing payload
62 return json.dumps({"error": "Payload required"})
63
64 try:
65 data = json.loads(payload)
66 except json.JSONDecodeError:
67 # Edge Case: Invalid JSON payload
68 return json.dumps({"error": "Invalid JSON payload"})
69
70 if url == "/add":
71 user_name = data.get("user")
72 if not user_name:
73 # Edge Case: Missing user name
74 return json.dumps({"error": "User name required"})
75
76 # Edge Case: User already exists
77 if self._user_exists(user_name):
78 user = next(user for user in self.database["users"] if user["name"] == user_name)
79 return json.dumps(user)
80
81 user = self._create_user(user_name)
82 self.database["users"].append(user)
83 return json.dumps(user)
84
85 elif url == "/iou":
86 lender_name = data.get("lender")
87 borrower_name = data.get("borrower")
88 amount = data.get("amount")
89
90 # Edge Case: Missing required fields
91 if not lender_name or not borrower_name or amount is None:
92 return json.dumps({"error": "Lender, borrower, and amount required"})
93
94 # Edge Case: Invalid amount type
95 if not isinstance(amount, (int, float)):
96 return json.dumps({"error": "Amount must be a number"})
97
98 # Edge Case: Negative amount
99 if amount < 0:
100 return json.dumps({"error": "Amount must be positive"})
101
102 # Edge Case: Lender or borrower does not exist
103 if not self._user_exists(lender_name) or not self._user_exists(borrower_name):
104 return json.dumps({"error": "Both users must exist"})
105
106 # Edge Case: Lender and borrower are the same
107 if lender_name == borrower_name:
108 return json.dumps({"error": "Lender and borrower cannot be the same"})
109
110 self._update_iou(lender_name, borrower_name, amount)
111 users = self._get_users_by_names([lender_name, borrower_name])
112 return json.dumps({"users": users})
113
114 # Edge Case: Invalid URL
115 return json.dumps({"error": "Invalid URL"})
116
117 def _get_all_users(self) -> List[Dict]:
118 """
119 Get all users sorted by name.
120
121 Returns:
122 List of user objects sorted by name
123 """
124 return sorted(self.database["users"], key=lambda u: u["name"])
125
126 def _get_users_by_names(self, names: List[str]) -> List[Dict]:
127 """
128 Get users by their names, sorted by name.
129
130 Args:
131 names: List of user names
132
133 Returns:
134 List of user objects sorted by name
135 """
136 users = [user for user in self.database["users"] if user["name"] in names]
137 return sorted(users, key=lambda u: u["name"])
138
139 def _user_exists(self, name: str) -> bool:
140 """
141 Check if a user exists.
142
143 Args:
144 name: User name to check
145
146 Returns:
147 True if user exists, False otherwise
148 """
149 return any(user["name"] == name for user in self.database["users"])
150
151 def _create_user(self, name: str) -> Dict:
152 """
153 Create a new user object.
154
155 Args:
156 name: Name of the user
157
158 Returns:
159 New user object
160 """
161 return {
162 "name": name,
163 "owes": {},
164 "owed_by": {},
165 "balance": 0.0
166 }
167
168 def _update_iou(self, lender_name: str, borrower_name: str, amount: float) -> None:
169 """
170 Update the IOU between lender and borrower.
171
172 Args:
173 lender_name: Name of the lender
174 borrower_name: Name of the borrower
175 amount: Amount of the IOU
176 """
177 lender = next(user for user in self.database["users"] if user["name"] == lender_name)
178 borrower = next(user for user in self.database["users"] if user["name"] == borrower_name)
179
180 # Check if there's an existing debt in the opposite direction
181 # If borrower already owes lender money, this is additional debt in the same direction
182 if lender_name in borrower["owes"]:
183 # Simply add to existing debt
184 borrower["owes"][lender_name] += amount
185 if borrower_name in lender["owed_by"]:
186 lender["owed_by"][borrower_name] += amount
187 else:
188 lender["owed_by"][borrower_name] = amount
189 # Update balances
190 lender["balance"] += amount
191 borrower["balance"] -= amount
192 return
193
194 # Check if there's an existing debt in the same direction (lender owes borrower)
195 # If lender already owes borrower money, reduce that debt first
196 if borrower_name in lender["owes"]:
197 existing_debt = lender["owes"][borrower_name]
198 if amount < existing_debt:
199 # New IOU is smaller than existing debt, reduce the existing debt
200 lender["owes"][borrower_name] = existing_debt - amount
201 # Borrower is still owed money, but less than before
202 if lender_name in borrower["owed_by"]:
203 borrower["owed_by"][lender_name] = existing_debt - amount
204 # Update balances
205 lender["balance"] += amount
206 borrower["balance"] -= amount
207 return
208 elif amount == existing_debt:
209 # New IOU exactly cancels existing debt
210 del lender["owes"][borrower_name]
211 if lender_name in borrower["owed_by"]:
212 del borrower["owed_by"][lender_name]
213 # Update balances
214 lender["balance"] += amount
215 borrower["balance"] -= amount
216 return
217 else:
218 # New IOU is larger than existing debt, net the difference
219 net_amount = amount - existing_debt
220 del lender["owes"][borrower_name]
221 if lender_name in borrower["owed_by"]:
222 del borrower["owed_by"][lender_name]
223 # Now the borrower owes the lender the net amount
224 borrower["owes"][lender_name] = net_amount
225 lender["owed_by"][borrower_name] = net_amount
226 # Update balances
227 lender["balance"] += amount
228 borrower["balance"] -= amount
229 return
230
231 # No existing debt, add new debt
232 if borrower_name in lender["owed_by"]:
233 lender["owed_by"][borrower_name] += amount
234 else:
235 lender["owed_by"][borrower_name] = amount
236
237 if lender_name in borrower["owes"]:
238 borrower["owes"][lender_name] += amount
239 else:
240 borrower["owes"][lender_name] = amount
241
242 # Update balances
243 lender["balance"] += amount
244 borrower["balance"] -= amount
245
246 # Clean up zero balances
247 if borrower_name in lender["owed_by"] and lender["owed_by"][borrower_name] == 0:
248 del lender["owed_by"][borrower_name]
249 if lender_name in borrower["owes"] and borrower["owes"][lender_name] == 0:
250 del borrower["owes"][lender_name]
251 if borrower_name in lender["owes"] and lender["owes"][borrower_name] == 0:
252 del lender["owes"][borrower_name]
253 if lender_name in borrower["owed_by"] and borrower["owed_by"][lender_name] == 0:
254 del borrower["owed_by"][lender_name]
255
256 # Handled Edge Cases: Invalid URL, Missing payload, Invalid JSON payload, Missing user name,
257 # User already exists, Missing required fields, Invalid amount type, Negative amount,
258 # Lender or borrower does not exist, Lender and borrower are the same
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.