sn62

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 or None to start with an empty database.
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 being accessed.
22 payload: Optional JSON string containing request parameters.
23
24 Returns:
25 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 sorted by name
33 users = sorted(self.database["users"], key=lambda u: u["name"])
34 result = {"users": [self._format_user(user) for user in users]}
35 return json.dumps(result)
36 else:
37 # Return only specified users
38 data = json.loads(payload)
39 user_names = data.get("users", [])
40
41 # Edge Case: Empty user list in payload
42 if not user_names:
43 return json.dumps({"users": []})
44
45 # Filter users by name
46 users = [user for user in self.database["users"] if user["name"] in user_names]
47 # Sort by name
48 users = sorted(users, key=lambda u: u["name"])
49 result = {"users": [self._format_user(user) for user in users]}
50 return json.dumps(result)
51 else:
52 raise ValueError(f"Unknown URL: {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 being accessed.
60 payload: JSON string containing request data.
61
62 Returns:
63 JSON string response.
64
65 Raises:
66 ValueError: If the URL is not recognized or if required data is missing.
67 """
68 if url == "/add":
69 if payload is None:
70 raise ValueError("Payload required for /add endpoint")
71
72 data = json.loads(payload)
73 user_name = data.get("user")
74
75 # Edge Case: Missing user name in payload
76 if user_name is None:
77 raise ValueError("User name is required")
78
79 # Edge Case: User already exists
80 for user in self.database["users"]:
81 if user["name"] == user_name:
82 raise ValueError(f"User {user_name} already exists")
83
84 # Create new user
85 new_user = {
86 "name": user_name,
87 "owes": {},
88 "owed_by": {}
89 }
90 self.database["users"].append(new_user)
91
92 return json.dumps(self._format_user(new_user))
93
94 elif url == "/iou":
95 if payload is None:
96 raise ValueError("Payload required for /iou endpoint")
97
98 data = json.loads(payload)
99 lender_name = data.get("lender")
100 borrower_name = data.get("borrower")
101 amount = data.get("amount")
102
103 # Edge Case: Missing required fields
104 if lender_name is None or borrower_name is None or amount is None:
105 raise ValueError("Lender, borrower, and amount are all required")
106
107 # Edge Case: Lender and borrower are the same person
108 if lender_name == borrower_name:
109 raise ValueError("Lender and borrower cannot be the same person")
110
111 # Edge Case: Negative amount
112 if amount < 0:
113 raise ValueError("Amount must be non-negative")
114
115 # Find lender and borrower
116 lender = None
117 borrower = None
118
119 for user in self.database["users"]:
120 if user["name"] == lender_name:
121 lender = user
122 elif user["name"] == borrower_name:
123 borrower = user
124
125 # Edge Case: Lender or borrower does not exist
126 if lender is None:
127 raise ValueError(f"User {lender_name} does not exist")
128 if borrower is None:
129 raise ValueError(f"User {borrower_name} does not exist")
130
131 # Update debts
132 # If borrower already owes lender, increase the amount
133 if borrower_name in lender["owed_by"]:
134 lender["owed_by"][borrower_name] += amount
135 else:
136 lender["owed_by"][borrower_name] = amount
137
138 # If lender already owes borrower, decrease the amount (or vice versa)
139 if lender_name in borrower["owes"]:
140 borrower["owes"][lender_name] += amount
141 else:
142 borrower["owes"][lender_name] = amount
143
144 # Simplify debts if they cancel out
145 if lender_name in borrower["owed_by"] and borrower["owed_by"][lender_name] > 0:
146 # They both owe each other, simplify
147 lender_owes_borrower = borrower["owed_by"][lender_name]
148 borrower_owes_lender = lender["owed_by"][borrower_name]
149
150 if lender_owes_borrower > borrower_owes_lender:
151 # Borrower owes more than lender owes
152 borrower["owed_by"][lender_name] = lender_owes_borrower - borrower_owes_lender
153 del lender["owed_by"][borrower_name]
154 del borrower["owes"][lender_name]
155 del lender["owes"][borrower_name]
156 elif borrower_owes_lender > lender_owes_borrower:
157 # Lender owes more than borrower owes
158 lender["owed_by"][borrower_name] = borrower_owes_lender - lender_owes_borrower
159 del borrower["owed_by"][lender_name]
160 del lender["owes"][borrower_name]
161 del borrower["owes"][lender_name]
162 else:
163 # They owe each other exactly the same amount
164 del borrower["owed_by"][lender_name]
165 del lender["owed_by"][borrower_name]
166 del borrower["owes"][lender_name]
167 del lender["owes"][borrower_name]
168
169 # Get updated user objects
170 updated_lender = None
171 updated_borrower = None
172
173 for user in self.database["users"]:
174 if user["name"] == lender_name:
175 updated_lender = user
176 elif user["name"] == borrower_name:
177 updated_borrower = user
178
179 # Return updated users sorted by name
180 users = sorted([updated_lender, updated_borrower], key=lambda u: u["name"])
181 result = {"users": [self._format_user(user) for user in users]}
182 return json.dumps(result)
183
184 else:
185 raise ValueError(f"Unknown URL: {url}")
186
187 def _format_user(self, user: dict) -> dict:
188 """
189 Format a user object to include the balance.
190
191 Args:
192 user: A user dictionary.
193
194 Returns:
195 Formatted user dictionary with balance calculated.
196 """
197 total_owed_by = sum(user["owed_by"].values())
198 total_owes = sum(user["owes"].values())
199 balance = round(total_owed_by - total_owes, 2)
200
201 # Edge Case: Rounding to handle floating point precision issues
202 # This ensures that values like 3.3099999999999996 become 3.31
203 return {
204 "name": user["name"],
205 "owes": user["owes"],
206 "owed_by": user["owed_by"],
207 "balance": balance
208 }
209# Handled Edge Cases: Empty user list in payload, Missing user name in payload, User already exists, Missing required fields, Lender and borrower are the same person, Negative amount, Lender or borrower does not exist, Rounding to handle floating point precision issues
Test NameStatus
test_lender_owes_borrower
Fail
test_lender_owes_borrower_less_than_new_loan
Fail
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_same_as_new_loan
Pass
test_no_users
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.