rockstar

Finished
1import json
2
13class RestAPI:
24 def __init__(self, database: dict | None = None):
3 pass
4
5 """
6 Initialize the REST API with a database.
7
8 Args:
9 database: A dictionary containing user data.
10 """
11 self.database = database if database else {"users": []}
12
513 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
14 """
15 Handle GET requests to the API.
16
17 Args:
18 url: The endpoint URL.
19 payload: Optional JSON payload.
20
21 Returns:
22 A JSON string response.
23 """
24 if url == "/users":
25 if payload is None:
26 # Return all users
27 users = self._get_all_users()
28 return json.dumps({"users": users})
29 else:
30 # Return specific users
31 data = json.loads(payload)
32 user_names = data.get("users", [])
33 users = self._get_users_by_names(user_names)
34 return json.dumps({"users": users})
35
36 # Edge Case: Invalid URL
37 return json.dumps({"error": "Invalid URL"})
38
839 def post(self, url: str, payload: str | None = None) -> str:
9 pass
40 """
41 Handle POST requests to the API.
42
43 Args:
44 url: The endpoint URL.
45 payload: Optional JSON payload.
46
47 Returns:
48 A JSON string response.
49 """
50 if payload is None:
51 # Edge Case: Missing payload
52 return json.dumps({"error": "Payload required"})
53
54 data = json.loads(payload)
55
56 if url == "/add":
57 user_name = data.get("user")
58
59 # Edge Case: Missing user name
60 if not user_name:
61 return json.dumps({"error": "User name required"})
62
63 # If user already exists, return the existing user
64 if self._user_exists(user_name):
65 existing_user = self._get_user_by_name(user_name)
66 user_copy = existing_user.copy()
67 user_copy["balance"] = self._calculate_balance(existing_user)
68 return json.dumps(user_copy)
69
70 new_user = self._create_user(user_name)
71 self.database["users"].append(new_user)
72 # Add balance to the returned user object
73 new_user_copy = new_user.copy()
74 new_user_copy["balance"] = 0.0
75 return json.dumps(new_user_copy)
76
77 elif url == "/iou":
78 lender_name = data.get("lender")
79 borrower_name = data.get("borrower")
80 amount = data.get("amount")
81
82 # Edge Case: Missing required fields
83 if not lender_name or not borrower_name or amount is None:
84 return json.dumps({"error": "Lender, borrower, and amount required"})
85
86 # Edge Case: Lender and borrower are the same
87 if lender_name == borrower_name:
88 return json.dumps({"error": "Lender and borrower cannot be the same"})
89
90 # Edge Case: Negative amount
91 if amount < 0:
92 return json.dumps({"error": "Amount must be positive"})
93
94 # Edge Case: User does not exist
95 if not self._user_exists(lender_name) or not self._user_exists(borrower_name):
96 return json.dumps({"error": "User does not exist"})
97
98 # Update IOUs
99 self._update_iou(lender_name, borrower_name, amount)
100
101 # Return updated users with recalculated balances
102 lender = self._get_user_by_name(lender_name)
103 borrower = self._get_user_by_name(borrower_name)
104
105 # Create copies with updated balances
106 lender_copy = lender.copy()
107 lender_copy["balance"] = self._calculate_balance(lender)
108 borrower_copy = borrower.copy()
109 borrower_copy["balance"] = self._calculate_balance(borrower)
110
111 users = sorted([lender_copy, borrower_copy], key=lambda x: x["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:
118 """
119 Get all users from the database with calculated balances.
120
121 Returns:
122 A list of user objects with calculated balances.
123 """
124 users = []
125 for user in self.database["users"]:
126 user_copy = user.copy()
127 user_copy["balance"] = self._calculate_balance(user)
128 users.append(user_copy)
129 return sorted(users, key=lambda x: x["name"])
130
131
132 def _get_users_by_names(self, names: list) -> list:
133 """
134 Get specific users by their names.
135
136 Args:
137 names: A list of user names.
138
139 Returns:
140 A list of user objects with calculated balances, sorted by name.
141 """
142 users = []
143 for name in names:
144 user = self._get_user_by_name(name)
145 if user:
146 user_copy = user.copy()
147 user_copy["balance"] = self._calculate_balance(user)
148 users.append(user_copy)
149 return sorted(users, key=lambda x: x["name"])
150
151 def _get_user_by_name(self, name: str) -> dict | None:
152 """
153 Get a user by their name.
154
155 Args:
156 name: The name of the user.
157
158 Returns:
159 The user object or None if not found.
160 """
161 for user in self.database["users"]:
162 if user["name"] == name:
163 return user
164 return None
165
166 def _user_exists(self, name: str) -> bool:
167 """
168 Check if a user exists in the database.
169
170 Args:
171 name: The name of the user.
172
173 Returns:
174 True if the user exists, False otherwise.
175 """
176 return self._get_user_by_name(name) is not None
177
178 def _create_user(self, name: str) -> dict:
179 """
180 Create a new user object.
181
182 Args:
183 name: The name of the user.
184
185 Returns:
186 A new user object.
187 """
188 return {
189 "name": name,
190 "owes": {},
191 "owed_by": {}
192 }
193
194 def _calculate_balance(self, user: dict) -> float:
195 """
196 Calculate the balance for a user.
197
198 Args:
199 user: The user object.
200
201 Returns:
202 The calculated balance.
203 """
204 total_owed_by = sum(user["owed_by"].values())
205 total_owes = sum(user["owes"].values())
206 return round(total_owed_by - total_owes, 2)
207
208 def _update_iou(self, lender_name: str, borrower_name: str, amount: float) -> None:
209 """
210 Update the IOU records between lender and borrower.
211
212 Args:
213 lender_name: The name of the lender.
214 borrower_name: The name of the borrower.
215 amount: The amount of the IOU.
216 """
217 lender = self._get_user_by_name(lender_name)
218 borrower = self._get_user_by_name(borrower_name)
219
220 # Check if lender already owes borrower (opposite debt)
221 lender_owes_borrower = lender["owes"].get(borrower_name, 0)
222 # Check if borrower already owes lender
223 borrower_owes_lender = borrower["owes"].get(lender_name, 0)
224
225 if lender_owes_borrower > 0:
226 # Lender already owes borrower - net the debts
227 if amount > lender_owes_borrower:
228 # New IOU is larger than existing debt - cancel debt and create reverse
229 remaining_amount = round(amount - lender_owes_borrower, 2)
230 del lender["owes"][borrower_name]
231 del borrower["owed_by"][lender_name]
232
233 # Create new debt in reverse direction
234 lender["owed_by"][borrower_name] = remaining_amount
235 borrower["owes"][lender_name] = remaining_amount
236 elif amount == lender_owes_borrower:
237 # Debts exactly cancel out
238 del lender["owes"][borrower_name]
239 del borrower["owed_by"][lender_name]
240 else:
241 # New IOU is smaller than existing debt - reduce the existing debt
242 new_debt = round(lender_owes_borrower - amount, 2)
243 lender["owes"][borrower_name] = new_debt
244 borrower["owed_by"][lender_name] = new_debt
245 elif borrower_owes_lender > 0:
246 # Borrower already owes lender - create new debt relationship in opposite direction
247 # This maintains both debt relationships rather than netting them
248 lender["owes"][borrower_name] = amount
249 borrower["owed_by"][lender_name] = amount
250 else:
251 # No existing debts - create new debt
252 # Update lender's owed_by (who owes them money)
253 if borrower_name in lender["owed_by"]:
254 lender["owed_by"][borrower_name] = round(lender["owed_by"][borrower_name] + amount, 2)
255 else:
256 lender["owed_by"][borrower_name] = amount
257
258 # Update borrower's owes (who they owe money to)
259 if lender_name in borrower["owes"]:
260 borrower["owes"][lender_name] = round(borrower["owes"][lender_name] + amount, 2)
261 else:
262 borrower["owes"][lender_name] = amount
263
264 # Handled Edge Cases: Invalid URL, Missing payload, Missing user name, User already exists, Missing required fields, Lender and borrower are the same, Negative amount, User does not exist
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.