chris

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 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 if url == "/users":
28 if payload is None:
29 # Return all users
30 result = {"users": []}
31 for user in self.database["users"]:
32 user_copy = user.copy()
33 user_copy["balance"] = self._calculate_balance(user)
34 result["users"].append(user_copy)
35 return json.dumps(result)
36 else:
37 # Return specific users
38 data = json.loads(payload)
39 user_names = data.get("users", [])
40 result = {"users": []}
41
42 # Edge Case: Handle when users list is empty
43 if not user_names:
44 return json.dumps(result)
45
46 # Get specified users
47 for user in self.database["users"]:
48 if user["name"] in user_names:
49 user_copy = user.copy()
50 user_copy["balance"] = self._calculate_balance(user)
51 result["users"].append(user_copy)
52
53 # Sort by name
54 result["users"].sort(key=lambda x: x["name"])
55 return json.dumps(result)
56
57 # Edge Case: Handle unknown URL
58 return json.dumps({"error": "Not found"})
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 if payload is None:
72 # Edge Case: Handle missing payload
73 return json.dumps({"error": "Payload required"})
74
75 data = json.loads(payload)
76
77 if url == "/add":
78 user_name = data.get("user")
79
80 # Edge Case: Handle missing user name
81 if not user_name:
82 return json.dumps({"error": "User name required"})
83
84 # Edge Case: Handle duplicate user
85 for user in self.database["users"]:
86 if user["name"] == user_name:
87 return json.dumps({"error": "User already exists"})
88
89 # Create new user
90 new_user = {
91 "name": user_name,
92 "owes": {},
93 "owed_by": {}
94 }
95 self.database["users"].append(new_user)
96
97 # Return user with balance
98 result_user = new_user.copy()
99 result_user["balance"] = 0.0
100 return json.dumps(result_user)
101
102 elif url == "/iou":
103 lender_name = data.get("lender")
104 borrower_name = data.get("borrower")
105 amount = data.get("amount")
106
107 # Edge Case: Handle missing fields
108 if not lender_name or not borrower_name or amount is None:
109 return json.dumps({"error": "Lender, borrower, and amount required"})
110
111 # Edge Case: Handle invalid amount type
112 if not isinstance(amount, (int, float)):
113 return json.dumps({"error": "Amount must be a number"})
114
115 # Edge Case: Handle negative amount
116 if amount < 0:
117 return json.dumps({"error": "Amount must be positive"})
118
119 # Edge Case: Handle same lender and borrower
120 if lender_name == borrower_name:
121 return json.dumps({"error": "Cannot create IOU between same user"})
122
123 # Find users
124 lender = None
125 borrower = None
126
127 for user in self.database["users"]:
128 if user["name"] == lender_name:
129 lender = user
130 elif user["name"] == borrower_name:
131 borrower = user
132
133 # Edge Case: Handle user not found
134 if lender is None or borrower is None:
135 return json.dumps({"error": "User not found"})
136
137 # Handle the bidirectional nature of debts
138 # We need to properly net the amounts between lender and borrower
139
140 # Get current amounts
141 lender_owes_borrower = lender["owes"].get(borrower_name, 0)
142 borrower_owes_lender = borrower["owes"].get(lender_name, 0)
143
144 # Calculate net debt after IOU
145 # Positive means borrower owes lender, negative means lender owes borrower
146 net_debt = borrower_owes_lender + amount - lender_owes_borrower
147
148 # Update lender's records
149 if borrower_name in lender["owes"]:
150 del lender["owes"][borrower_name]
151 if borrower_name in lender["owed_by"]:
152 del lender["owed_by"][borrower_name]
153
154 # Update borrower's records
155 if lender_name in borrower["owes"]:
156 del borrower["owes"][lender_name]
157 if lender_name in borrower["owed_by"]:
158 del borrower["owed_by"][lender_name]
159
160 # Set new amounts based on net debt
161 if net_debt > 0:
162 # Borrower owes lender
163 borrower["owes"][lender_name] = net_debt
164 lender["owed_by"][borrower_name] = net_debt
165 elif net_debt < 0:
166 # Lender owes borrower
167 lender["owes"][borrower_name] = abs(net_debt)
168 borrower["owed_by"][lender_name] = abs(net_debt)
169
170 # Return updated users
171 result = {"users": []}
172
173 lender_result = lender.copy()
174 lender_result["balance"] = self._calculate_balance(lender)
175 result["users"].append(lender_result)
176
177 borrower_result = borrower.copy()
178 borrower_result["balance"] = self._calculate_balance(borrower)
179 result["users"].append(borrower_result)
180
181 # Sort by name
182 result["users"].sort(key=lambda x: x["name"])
183 return json.dumps(result)
184
185 # Edge Case: Handle unknown URL
186 return json.dumps({"error": "Not found"})
187
188 def _calculate_balance(self, user: dict) -> float:
189 """
190 Calculate the balance for a user.
191
192 Args:
193 user: A user dictionary.
194
195 Returns:
196 The calculated balance.
197 """
198 total_owed_by = sum(user["owed_by"].values())
199 total_owes = sum(user["owes"].values())
200 return round(total_owed_by - total_owes, 2)
201
202# Handled Edge Cases: Missing payload, unknown URL, missing user name, duplicate user, missing fields, invalid amount type, negative amount, same lender and borrower, user not found, empty users list
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.