@ngel

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 or {"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 JSON string response.
23 """
24 if url == "/users":
25 if payload is None:
26 # Return all users sorted by name
27 users = [self._create_user_object(user) for user in self.database["users"]]
28 users.sort(key=lambda x: x["name"])
29 return json.dumps({"users": users})
30 else:
31 # Return specified users sorted by name
32 data = json.loads(payload)
33 user_names = data.get("users", [])
34
35 # Edge Case: Empty user list in payload - return all users
36 if not user_names:
37 users = [self._create_user_object(user) for user in self.database["users"]]
38 users.sort(key=lambda x: x["name"])
39 return json.dumps({"users": users})
40
41 # Edge Case: Non-existent users in payload
42 users = []
43 for user_name in user_names:
44 user = self._get_user(user_name)
45 if user:
46 users.append(self._create_user_object(user))
47
48 users.sort(key=lambda x: x["name"])
49 return json.dumps({"users": users})
50
51 # Edge Case: Invalid URL
52 return json.dumps({"error": "Invalid URL"})
53
854 def post(self, url: str, payload: str | None = None) -> str:
9 pass
55 """
56 Handle POST requests to the API.
57
58 Args:
59 url: The endpoint URL.
60 payload: JSON payload.
61
62 Returns:
63 JSON string response.
64 """
65 if payload is None:
66 # Edge Case: Missing payload
67 return json.dumps({"error": "Payload required"})
68
69 data = json.loads(payload)
70
71 if url == "/add":
72 user_name = data.get("user")
73
74 # Edge Case: Missing user name
75 if not user_name:
76 return json.dumps({"error": "User name required"})
77
78 # Edge Case: User already exists - return existing user
79 existing_user = self._get_user(user_name)
80 if existing_user:
81 return json.dumps(self._create_user_object(existing_user))
82
83 new_user = {
84 "name": user_name,
85 "owes": {},
86 "owed_by": {},
87 "balance": 0.0
88 }
89 self.database["users"].append(new_user)
90 return json.dumps(self._create_user_object(new_user))
91
92 elif url == "/iou":
93 lender_name = data.get("lender")
94 borrower_name = data.get("borrower")
95 amount = data.get("amount")
96
97 # Edge Case: Missing required fields
98 if not lender_name or not borrower_name or amount is None:
99 return json.dumps({"error": "Lender, borrower, and amount required"})
100
101 # Edge Case: Invalid amount type
102 if not isinstance(amount, (int, float)):
103 return json.dumps({"error": "Amount must be a number"})
104
105 # Edge Case: Negative amount
106 if amount < 0:
107 return json.dumps({"error": "Amount must be positive"})
108
109 lender = self._get_user(lender_name)
110 borrower = self._get_user(borrower_name)
111
112 # Edge Case: Non-existent lender or borrower
113 if not lender or not borrower:
114 return json.dumps({"error": "Lender or borrower does not exist"})
115
116 # Edge Case: Lender and borrower are the same person
117 if lender_name == borrower_name:
118 return json.dumps({"error": "Cannot create IOU between same user"})
119
120 # Check if there's already an IOU in the opposite direction
121 existing_lender_owes = lender["owes"].get(borrower_name, 0)
122 existing_borrower_owed_by = borrower["owed_by"].get(lender_name, 0)
123
124 if existing_lender_owes > 0 or existing_borrower_owed_by > 0:
125 # There's an existing IOU in opposite direction, need to net them
126 net_amount = amount - existing_lender_owes
127
128 if net_amount > 0:
129 # New IOU is larger, update in the new direction
130 # Remove old opposite direction entries
131 if borrower_name in lender["owes"]:
132 del lender["owes"][borrower_name]
133 if lender_name in borrower["owed_by"]:
134 del borrower["owed_by"][lender_name]
135
136 # Add new direction entries
137 lender["owed_by"][borrower_name] = net_amount
138 borrower["owes"][lender_name] = net_amount
139
140 # Update balances - lender gains amount, borrower loses amount
141 lender["balance"] += amount
142 borrower["balance"] -= amount
143
144 elif net_amount < 0:
145 # Old IOU is larger, update in the old direction
146 # Remove any entries that might exist in new direction
147 if borrower_name in lender["owed_by"]:
148 del lender["owed_by"][borrower_name]
149 if lender_name in borrower["owes"]:
150 del borrower["owes"][lender_name]
151
152 # Update old direction entries
153 lender["owes"][borrower_name] = abs(net_amount)
154 borrower["owed_by"][lender_name] = abs(net_amount)
155
156 # Update balances - lender loses amount, borrower gains amount
157 lender["balance"] -= amount
158 borrower["balance"] += amount
159
160 else:
161 # Net to zero, remove all entries
162 if borrower_name in lender["owes"]:
163 del lender["owes"][borrower_name]
164 if lender_name in borrower["owed_by"]:
165 del borrower["owed_by"][lender_name]
166 if borrower_name in lender["owed_by"]:
167 del lender["owed_by"][borrower_name]
168 if lender_name in borrower["owes"]:
169 del borrower["owes"][lender_name]
170
171 # Update balances - lender gains existing_lender_owes, borrower loses existing_lender_owes
172 lender["balance"] += existing_lender_owes
173 borrower["balance"] -= existing_lender_owes
174 else:
175 # No existing IOU in opposite direction, add new IOU
176 # Update lender's owed_by
177 if borrower_name in lender["owed_by"]:
178 lender["owed_by"][borrower_name] += amount
179 else:
180 lender["owed_by"][borrower_name] = amount
181
182 # Update borrower's owes
183 if lender_name in borrower["owes"]:
184 borrower["owes"][lender_name] += amount
185 else:
186 borrower["owes"][lender_name] = amount
187
188 # Update balances
189 lender["balance"] += amount
190 borrower["balance"] -= amount
191
192 # Return updated user objects sorted by name
193 users = [self._create_user_object(lender), self._create_user_object(borrower)]
194 users.sort(key=lambda x: x["name"])
195 return json.dumps({"users": users})
196
197 # Edge Case: Invalid URL
198 return json.dumps({"error": "Invalid URL"})
199
200 def _get_user(self, name: str):
201 """
202 Find a user by name in the database.
203
204 Args:
205 name: The name of the user to find.
206
207 Returns:
208 The user dictionary if found, None otherwise.
209 """
210 for user in self.database["users"]:
211 if user["name"] == name:
212 return user
213 return None
214
215 def _create_user_object(self, user: dict) -> dict:
216 """
217 Create a user object with calculated balance.
218
219 Args:
220 user: The user dictionary from the database.
221
222 Returns:
223 A user object with calculated balance.
224 """
225 # Calculate balance from owes and owed_by
226 total_owed = sum(user["owes"].values())
227 total_owed_by = sum(user["owed_by"].values())
228 balance = total_owed_by - total_owed
229
230 return {
231 "name": user["name"],
232 "owes": user["owes"],
233 "owed_by": user["owed_by"],
234 "balance": balance
235 }
236# Handled Edge Cases: Invalid URL, Missing payload, Missing user name, User already exists, Missing required fields, Invalid amount type, Negative amount, Non-existent lender or borrower, Lender and borrower are the same person, Empty user list in payload, Non-existent users in payload
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.