relax

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 an optional database.
8
9 Args:
10 database: Optional initial database dictionary
11 """
12 if database is None:
13 self.database = {"users": []}
14 else:
15 self.database = database
16
17 def get_user_by_name(self, name: str) -> Optional[Dict]:
18 """
19 Find a user by their name in the database.
20
21 Args:
22 name: Name of the user to find
23
24 Returns:
25 User dictionary if found, None otherwise
26 """
27 for user in self.database["users"]:
28 if user["name"] == name:
29 return user
30 return None
31
32 def calculate_balance(self, user: Dict) -> float:
33 """
34 Calculate the balance for a user based on what they owe and are owed.
35
36 Args:
37 user: User dictionary
38
39 Returns:
40 Calculated balance
41 """
42 total_owed_by = sum(user["owed_by"].values())
43 total_owes = sum(user["owes"].values())
44 return round(total_owed_by - total_owes, 2)
45
46 def update_user_balances(self) -> None:
47 """Update balance for all users in the database."""
48 for user in self.database["users"]:
49 user["balance"] = self.calculate_balance(user)
50
551 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
52 """
53 Handle GET requests to the API.
54
55 Args:
56 url: Request URL
57 payload: Optional JSON payload
58
59 Returns:
60 JSON response string
61 """
62 # Edge Case: Invalid URL
63 if url != "/users":
64 return json.dumps({"error": "Invalid URL"})
65
66 # Edge Case: Malformed payload
67 if payload is not None:
68 try:
69 payload_data = json.loads(payload)
70 except json.JSONDecodeError:
71 return json.dumps({"error": "Invalid payload"})
72 else:
73 payload_data = None
74
75 # Update all user balances before returning data
76 self.update_user_balances()
77
78 # Edge Case: No users in database
79 if not self.database["users"]:
80 return json.dumps({"users": []})
81
82 # If no payload, return all users
83 if payload_data is None:
84 # Sort users by name
85 sorted_users = sorted(self.database["users"], key=lambda x: x["name"])
86 return json.dumps({"users": sorted_users})
87
88 # Edge Case: Payload missing 'users' key
89 if "users" not in payload_data:
90 return json.dumps({"error": "Invalid payload"})
91
92 # Edge Case: Empty users list in payload
93 if not payload_data["users"]:
94 return json.dumps({"users": []})
95
96 # Filter users based on payload
97 requested_users = []
98 for username in payload_data["users"]:
99 user = self.get_user_by_name(username)
100 # Edge Case: Requested user does not exist
101 if user is not None:
102 requested_users.append(user)
103
104 # Sort users by name
105 sorted_users = sorted(requested_users, key=lambda x: x["name"])
106 return json.dumps({"users": sorted_users})
107
8108 def post(self, url: str, payload: str | None = None) -> str:
9 pass
109 """
110 Handle POST requests to the API.
111
112 Args:
113 url: Request URL
114 payload: Optional JSON payload
115
116 Returns:
117 JSON response string
118 """
119 # Edge Case: Invalid URL
120 if url not in ["/add", "/iou"]:
121 return json.dumps({"error": "Invalid URL"})
122
123 # Edge Case: Missing payload
124 if payload is None:
125 return json.dumps({"error": "Missing payload"})
126
127 # Edge Case: Malformed payload
128 try:
129 payload_data = json.loads(payload)
130 except json.JSONDecodeError:
131 return json.dumps({"error": "Invalid payload"})
132
133 if url == "/add":
134 # Edge Case: Missing 'user' in payload
135 if "user" not in payload_data:
136 return json.dumps({"error": "Missing user"})
137
138 username = payload_data["user"]
139
140 # Edge Case: User already exists
141 if self.get_user_by_name(username) is not None:
142 return json.dumps({"error": "User already exists"})
143
144 # Create new user
145 new_user = {
146 "name": username,
147 "owes": {},
148 "owed_by": {},
149 "balance": 0.0
150 }
151 self.database["users"].append(new_user)
152 return json.dumps(new_user)
153
154 elif url == "/iou":
155 # Edge Case: Missing required fields in payload
156 required_fields = ["lender", "borrower", "amount"]
157 for field in required_fields:
158 if field not in payload_data:
159 return json.dumps({"error": f"Missing {field}"})
160
161 lender_name = payload_data["lender"]
162 borrower_name = payload_data["borrower"]
163 amount = payload_data["amount"]
164
165 # Edge Case: Invalid amount type
166 if not isinstance(amount, (int, float)):
167 return json.dumps({"error": "Invalid amount"})
168
169 # Edge Case: Negative amount
170 if amount < 0:
171 return json.dumps({"error": "Invalid amount"})
172
173 # Edge Case: Lender and borrower are the same
174 if lender_name == borrower_name:
175 return json.dumps({"error": "Cannot create IOU between same user"})
176
177 # Find users
178 lender = self.get_user_by_name(lender_name)
179 borrower = self.get_user_by_name(borrower_name)
180
181 # Edge Case: Lender does not exist
182 if lender is None:
183 return json.dumps({"error": "Lender does not exist"})
184
185 # Edge Case: Borrower does not exist
186 if borrower is None:
187 return json.dumps({"error": "Borrower does not exist"})
188
189 # Update IOUs
190 # Update what borrower owes to lender
191 if lender_name in borrower["owes"]:
192 borrower["owes"][lender_name] += amount
193 else:
194 borrower["owes"][lender_name] = amount
195
196 # Update what lender is owed by borrower
197 if borrower_name in lender["owed_by"]:
198 lender["owed_by"][borrower_name] += amount
199 else:
200 lender["owed_by"][borrower_name] = amount
201
202 # Handle offsetting debts
203 # If borrower also owes money to lender in owed_by, offset them
204 if lender_name in borrower["owed_by"]:
205 owed_amount = borrower["owed_by"][lender_name]
206 owes_amount = borrower["owes"][lender_name]
207
208 if owed_amount > owes_amount:
209 borrower["owed_by"][lender_name] = round(owed_amount - owes_amount, 2)
210 del borrower["owes"][lender_name]
211 # Update lender's records
212 del lender["owed_by"][borrower_name]
213 lender["owes"][borrower_name] = round(owed_amount - owes_amount, 2)
214 elif owes_amount > owed_amount:
215 borrower["owes"][lender_name] = round(owes_amount - owed_amount, 2)
216 del borrower["owed_by"][lender_name]
217 # Update lender's records
218 del lender["owes"][borrower_name]
219 lender["owed_by"][borrower_name] = round(owes_amount - owed_amount, 2)
220 else: # They are equal
221 del borrower["owed_by"][lender_name]
222 del borrower["owes"][lender_name]
223 del lender["owed_by"][borrower_name]
224 del lender["owes"][borrower_name]
225
226 # Update balances
227 lender["balance"] = self.calculate_balance(lender)
228 borrower["balance"] = self.calculate_balance(borrower)
229
230 # Return updated users sorted by name
231 updated_users = sorted([lender, borrower], key=lambda x: x["name"])
232 return json.dumps({"users": updated_users})
233
234 # Handled Edge Cases: Invalid URL, Malformed payload, No users in database, Empty users list in payload,
235 # Requested user does not exist, Missing 'user' in payload, User already exists, Missing required fields in payload,
236 # Invalid amount type, Negative amount, Lender and borrower are the same, Lender does not exist, Borrower 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.