xas

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: Optional initial database dictionary containing users 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 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 return json.dumps({"users": users})
35 else:
36 # Return specific users
37 data = json.loads(payload)
38 user_names = data.get("users", [])
39
40 # Edge Case: Empty user list in payload
41 if not user_names:
42 return json.dumps({"users": []})
43
44 # Edge Case: Non-existent users in payload
45 result_users = []
46 for name in user_names:
47 user = self._find_user(name)
48 if user:
49 result_users.append(self._create_user_object(user))
50
51 # Sort by name
52 result_users.sort(key=lambda u: u["name"])
53 return json.dumps({"users": result_users})
54 else:
55 raise ValueError(f"Unknown GET endpoint: {url}")
56
57 # Handled Edge Cases: Empty user list in payload, Non-existent users in payload
58
859 def post(self, url: str, payload: str | None = None) -> str:
9 pass
60 """
61 Handle POST requests to the API.
62
63 Args:
64 url: The endpoint URL.
65 payload: JSON payload.
66
67 Returns:
68 JSON string response.
69
70 Raises:
71 ValueError: If the URL is not recognized or payload is missing/invalid.
72 """
73 if payload is None:
74 raise ValueError("Payload is required for POST requests")
75
76 data = json.loads(payload)
77
78 if url == "/add":
79 user_name = data.get("user")
80
81 # Edge Case: Missing user name in payload
82 if not user_name:
83 raise ValueError("User name is required")
84
85 # Edge Case: User already exists - return existing user
86 existing_user = self._find_user(user_name)
87 if existing_user:
88 return json.dumps(self._create_user_object(existing_user))
89
90 new_user = {
91 "name": user_name,
92 "owes": {},
93 "owed_by": {},
94 "balance": 0.0
95 }
96 self.database["users"].append(new_user)
97 return json.dumps(self._create_user_object(new_user))
98
99 elif url == "/iou":
100 lender_name = data.get("lender")
101 borrower_name = data.get("borrower")
102 amount = data.get("amount")
103
104 # Edge Case: Missing required fields
105 if not lender_name or not borrower_name or amount is None:
106 raise ValueError("Lender, borrower, and amount are required")
107
108 # Edge Case: Lender and borrower are the same person
109 if lender_name == borrower_name:
110 raise ValueError("Lender and borrower cannot be the same person")
111
112 lender = self._find_user(lender_name)
113 borrower = self._find_user(borrower_name)
114
115 # Edge Case: Non-existent lender or borrower
116 if not lender:
117 raise ValueError(f"Lender {lender_name} does not exist")
118 if not borrower:
119 raise ValueError(f"Borrower {borrower_name} does not exist")
120
121 # Edge Case: Negative amount
122 if amount < 0:
123 raise ValueError("Amount must be non-negative")
124
125 # Handle IOU logic with existing debt consideration
126 # Check if there's existing debt in the opposite direction (lender owes borrower)
127 existing_lender_owes_borrower = lender["owes"].get(borrower_name, 0.0)
128
129 if existing_lender_owes_borrower > 0:
130 # Lender owes borrower (opposite direction) - need to reduce this debt
131 if amount < existing_lender_owes_borrower:
132 # Reduce the opposite debt
133 lender["owes"][borrower_name] = existing_lender_owes_borrower - amount
134 borrower["owed_by"][lender_name] = existing_lender_owes_borrower - amount
135 # When reducing debt, the new IOU creates debt in the new direction
136 # So borrower now owes lender the amount
137 borrower["owes"][lender_name] = amount
138 lender["owed_by"][borrower_name] = amount
139 # Keep 0.0 entries to show the relationship exists
140 if lender["owes"][borrower_name] == 0:
141 pass # Keep the 0.0 entry
142 if borrower["owed_by"][lender_name] == 0:
143 pass # Keep the 0.0 entry
144 elif amount == existing_lender_owes_borrower:
145 # Exactly zero out the debt
146 lender["owes"][borrower_name] = 0.0
147 borrower["owed_by"][lender_name] = 0.0
148 # Create 0.0 entries in the new direction to show relationship
149 lender["owed_by"][borrower_name] = 0.0
150 borrower["owes"][lender_name] = 0.0
151 else:
152 # Switch direction - amount > existing debt
153 remaining_amount = amount - existing_lender_owes_borrower
154 # Zero out the old direction
155 lender["owes"][borrower_name] = 0.0
156 borrower["owed_by"][lender_name] = 0.0
157 # Create new debt in the correct direction (lender is owed by borrower)
158 lender["owed_by"][borrower_name] = remaining_amount
159 borrower["owes"][lender_name] = remaining_amount
160 else:
161 # No existing debt in opposite direction
162 existing_lender_owed_by_borrower = lender["owed_by"].get(borrower_name, 0.0)
163 if existing_lender_owed_by_borrower > 0:
164 # Same direction debt exists, add to it
165 lender["owed_by"][borrower_name] = existing_lender_owed_by_borrower + amount
166 borrower["owes"][lender_name] = existing_lender_owed_by_borrower + amount
167 else:
168 # No existing debt, create new (don't create 0.0 entries)
169 lender["owed_by"][borrower_name] = amount
170 borrower["owes"][lender_name] = amount
171
172 # Return updated users
173 users = [self._create_user_object(lender), self._create_user_object(borrower)]
174 users.sort(key=lambda u: u["name"])
175 return json.dumps({"users": users})
176 else:
177 raise ValueError(f"Unknown POST endpoint: {url}")
178
179 # Handled Edge Cases: Missing user name in payload, User already exists, Missing required fields,
180 # Lender and borrower are the same person, Non-existent lender or borrower, Negative amount
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):
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 dictionary representing the user object for API response.
206 """
207 # Calculate balance dynamically: (total owed by others) - (total owed to others)
208 total_owed_by = sum(user["owed_by"].values())
209 total_owes = sum(user["owes"].values())
210 balance = total_owed_by - total_owes
211
212 return {
213 "name": user["name"],
214 "owes": user["owes"],
215 "owed_by": user["owed_by"],
216 "balance": balance
217 }
218
219 # Handled Edge Cases: None in this helper method
Test NameStatus
test_lender_owes_borrower
Fail
test_lender_owes_borrower_less_than_new_loan
Fail
test_lender_owes_borrower_same_as_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_no_users
Pass

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