goodagent

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 users = [self._create_user_object(user) for user in self.database["users"]]
34 # Sort by name
35 users.sort(key=lambda u: u["name"])
36 return json.dumps({"users": users})
37 else:
38 # Return specific users
39 data = json.loads(payload)
40 user_names = data.get("users", [])
41
42 # Edge Case: Empty user list in payload
43 if not user_names:
44 return json.dumps({"users": []})
45
46 # Edge Case: Non-existent users in payload
47 result_users = []
48 for name in user_names:
49 user = self._find_user(name)
50 if user:
51 result_users.append(self._create_user_object(user))
52
53 # Sort by name
54 result_users.sort(key=lambda u: u["name"])
55 return json.dumps({"users": result_users})
56 else:
57 # Edge Case: Invalid URL
58 raise ValueError(f"Invalid URL: {url}")
59
860 def post(self, url: str, payload: str | None = None) -> str:
9 pass
61 """
62 Handle POST requests to the API.
63
64 Args:
65 url: The endpoint URL.
66 payload: JSON payload.
67
68 Returns:
69 A JSON string response.
70
71 Raises:
72 ValueError: If the URL is not recognized or payload is missing/invalid.
73 """
74 if payload is None:
75 # Edge Case: Missing payload
76 raise ValueError("Payload is required")
77
78 data = json.loads(payload)
79
80 if url == "/add":
81 user_name = data.get("user")
82
83 # Edge Case: Missing user name
84 if not user_name:
85 raise ValueError("User name is required")
86
87 # Edge Case: User already exists
88 if self._find_user(user_name):
89 raise ValueError(f"User {user_name} already exists")
90
91 new_user = {
92 "name": user_name,
93 "owes": {},
94 "owed_by": {},
95 "balance": 0.0
96 }
97
98 self.database["users"].append(new_user)
99 return json.dumps(self._create_user_object(new_user))
100
101 elif url == "/iou":
102 lender_name = data.get("lender")
103 borrower_name = data.get("borrower")
104 amount = data.get("amount")
105
106 # Edge Case: Missing required fields
107 if not lender_name or not borrower_name or amount is None:
108 raise ValueError("Lender, borrower, and amount are required")
109
110 # Edge Case: Lender and borrower are the same
111 if lender_name == borrower_name:
112 raise ValueError("Lender and borrower cannot be the same")
113
114 # Edge Case: Negative amount
115 if amount < 0:
116 raise ValueError("Amount must be non-negative")
117
118 lender = self._find_user(lender_name)
119 borrower = self._find_user(borrower_name)
120
121 # Edge Case: Non-existent lender or borrower
122 if not lender:
123 raise ValueError(f"User {lender_name} does not exist")
124 if not borrower:
125 raise ValueError(f"User {borrower_name} does not exist")
126
127 # Handle debt offsetting when lender already owes borrower
128 if borrower_name in lender["owes"]:
129 # Lender already owes borrower - offset the debts
130 existing_debt = lender["owes"][borrower_name]
131 if amount > existing_debt:
132 # New loan is larger than existing debt
133 # Remove the existing debt from lender's owes and borrower's owed_by
134 del lender["owes"][borrower_name]
135 del borrower["owed_by"][lender_name]
136 # Add the difference to lender's owed_by and borrower's owes
137 lender["owed_by"][borrower_name] = amount - existing_debt
138 borrower["owes"][lender_name] = amount - existing_debt
139 elif amount < existing_debt:
140 # New loan is smaller than existing debt
141 # Reduce the existing debt
142 lender["owes"][borrower_name] = existing_debt - amount
143 borrower["owed_by"][lender_name] = existing_debt - amount
144 else:
145 # New loan equals existing debt - cancel out both debts
146 del lender["owes"][borrower_name]
147 del borrower["owed_by"][lender_name]
148 else:
149 # No existing debt from lender to borrower
150 # Update lender's owed_by
151 if borrower_name in lender["owed_by"]:
152 lender["owed_by"][borrower_name] += amount
153 else:
154 lender["owed_by"][borrower_name] = amount
155
156 # Update borrower's owes
157 if lender_name in borrower["owes"]:
158 borrower["owes"][lender_name] += amount
159 else:
160 borrower["owes"][lender_name] = amount
161
162 # Recalculate balances based on current owes/owed_by
163 lender_total_owed = sum(lender["owes"].values())
164 lender_total_owed_by = sum(lender["owed_by"].values())
165 lender["balance"] = round(lender_total_owed_by - lender_total_owed, 2)
166
167 borrower_total_owed = sum(borrower["owes"].values())
168 borrower_total_owed_by = sum(borrower["owed_by"].values())
169 borrower["balance"] = round(borrower_total_owed_by - borrower_total_owed, 2)
170
171 # Return updated users
172 updated_users = [
173 self._create_user_object(lender),
174 self._create_user_object(borrower)
175 ]
176 updated_users.sort(key=lambda u: u["name"])
177 return json.dumps({"users": updated_users})
178 else:
179 # Edge Case: Invalid URL
180 raise ValueError(f"Invalid URL: {url}")
181
182 def _find_user(self, name: str):
183 """
184 Find a user by name in the database.
185
186 Args:
187 name: The name of the user to find.
188
189 Returns:
190 The user dictionary if found, None otherwise.
191 """
192 for user in self.database["users"]:
193 if user["name"] == name:
194 return user
195 return None
196
197 def _create_user_object(self, user: dict):
198 """
199 Create a user object for API response.
200
201 Args:
202 user: The user dictionary from the database.
203
204 Returns:
205 A user object for API response.
206 """
207 # Calculate balance from owes and owed_by
208 total_owed = sum(user["owes"].values())
209 total_owed_by = sum(user["owed_by"].values())
210 balance = round(total_owed_by - total_owed, 2)
211
212 # Ensure balance is always a float
213 if balance == int(balance):
214 balance = float(balance)
215
216 return {
217 "name": user["name"],
218 "owes": user["owes"],
219 "owed_by": user["owed_by"],
220 "balance": balance
221 }
222
223 # Handled Edge Cases: Invalid URL, Missing payload, Missing user name, User already exists,
224 # Missing required fields, Lender and borrower are the same, Negative amount,
225 # Non-existent lender or borrower, 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.