viola

Finished
1class RestAPI:
2 def __init__(self, database: dict | None = None):
3 pass
4
5 def get(self, url: str, payload: str | None = None) -> str:
6 pass
1import json
2from typing import Dict, List, Optional, Any
73
8 def post(self, url: str, payload: str | None = None) -> str:
9 pass
4class RestAPI:
5 """
6 A RESTful API for tracking IOUs between users.
7
8 This class manages user accounts and IOUs in memory, providing endpoints
9 to add users, create IOUs, and retrieve user information.
10 """
11
12 def __init__(self, database: Optional[Dict[str, Any]] = None):
13 """
14 Initialize the REST API with an optional initial database.
15
16 Args:
17 database: Optional initial database dictionary with 'users' key
18 """
19 # Edge Case: Handle None database by initializing empty users list
20 self.users = {}
21 if database and 'users' in database:
22 for user_data in database['users']:
23 self.users[user_data['name']] = user_data
24
25 def _calculate_balance(self, user_data: Dict[str, Any]) -> float:
26 """
27 Calculate the balance for a user based on what they owe and are owed.
28
29 Args:
30 user_data: Dictionary containing user information
31
32 Returns:
33 float: The calculated balance
34 """
35 total_owed_by_others = sum(user_data.get('owed_by', {}).values())
36 total_owed_to_others = sum(user_data.get('owes', {}).values())
37 return round(total_owed_by_others - total_owed_to_others, 2)
38
39 def _get_user(self, name: str) -> Optional[Dict[str, Any]]:
40 """
41 Get a user by name, returning a copy with calculated balance.
42
43 Args:
44 name: Name of the user to retrieve
45
46 Returns:
47 Dictionary with user data or None if user doesn't exist
48 """
49 # Edge Case: Return None for non-existent users
50 if name not in self.users:
51 return None
52
53 user = self.users[name].copy()
54 user['balance'] = self._calculate_balance(user)
55 return user
56
57 def get(self, url: str, payload: Optional[str] = None) -> str:
58 """
59 Handle GET requests to the API.
60
61 Args:
62 url: The endpoint URL
63 payload: Optional JSON payload
64
65 Returns:
66 JSON string response
67 """
68 # Edge Case: Handle /users endpoint
69 if url == '/users':
70 # Edge Case: Handle payload with specific users list
71 if payload:
72 try:
73 data = json.loads(payload)
74 if 'users' in data:
75 # Edge Case: Return only requested users, sorted by name
76 result_users = []
77 for name in sorted(data['users']):
78 user = self._get_user(name)
79 # Edge Case: Skip non-existent users in the list
80 if user:
81 result_users.append(user)
82 return json.dumps({'users': result_users})
83 except json.JSONDecodeError:
84 # Edge Case: Handle invalid JSON payload
85 pass
86 else:
87 # Edge Case: Return all users sorted by name
88 all_users = [self._get_user(name) for name in sorted(self.users.keys())]
89 return json.dumps({'users': all_users})
90
91 # Edge Case: Handle unknown endpoints
92 return json.dumps({})
93
94 def post(self, url: str, payload: Optional[str] = None) -> str:
95 """
96 Handle POST requests to the API.
97
98 Args:
99 url: The endpoint URL
100 payload: Optional JSON payload
101
102 Returns:
103 JSON string response
104 """
105 # Edge Case: Handle invalid JSON payload
106 if not payload:
107 return json.dumps({})
108
109 try:
110 data = json.loads(payload)
111 except json.JSONDecodeError:
112 return json.dumps({})
113
114 # Edge Case: Handle /add endpoint for creating new users
115 if url == '/add':
116 if 'user' in data:
117 name = data['user']
118 # Edge Case: Prevent creating duplicate users
119 if name in self.users:
120 return json.dumps({})
121
122 # Edge Case: Create new user with default empty values
123 new_user = {
124 'name': name,
125 'owes': {},
126 'owed_by': {},
127 'balance': 0.0
128 }
129 self.users[name] = new_user
130 return json.dumps(new_user)
131
132 # Edge Case: Handle /iou endpoint for creating IOUs
133 elif url == '/iou':
134 required_fields = ['lender', 'borrower', 'amount']
135 # Edge Case: Validate all required fields are present
136 if not all(field in data for field in required_fields):
137 return json.dumps({})
138
139 lender_name = data['lender']
140 borrower_name = data['borrower']
141 amount = data['amount']
142
143 # Edge Case: Validate lender and borrower exist
144 if lender_name not in self.users or borrower_name not in self.users:
145 return json.dumps({})
146
147 # Edge Case: Validate amount is a positive number
148 if not isinstance(amount, (int, float)) or amount <= 0:
149 return json.dumps({})
150
151 # Get references to the user objects
152 lender = self.users[lender_name]
153 borrower = self.users[borrower_name]
154
155 # Initialize owes/owed_by dictionaries if they don't exist
156 if 'owes' not in lender:
157 lender['owes'] = {}
158 if 'owed_by' not in lender:
159 lender['owed_by'] = {}
160 if 'owes' not in borrower:
161 borrower['owes'] = {}
162 if 'owed_by' not in borrower:
163 borrower['owed_by'] = {}
164
165 # Update what the borrower owes the lender
166 if borrower_name in lender['owed_by']:
167 lender['owed_by'][borrower_name] += amount
168 else:
169 lender['owed_by'][borrower_name] = amount
170
171 if lender_name in borrower['owes']:
172 borrower['owes'][lender_name] += amount
173 else:
174 borrower['owes'][lender_name] = amount
175
176 # Handle debt cancellation
177 if lender_name in lender['owes'] and borrower_name in borrower['owed_by']:
178 # If they both owe each other, cancel the debts
179 lender_owes_borrower = lender['owes'].get(borrower_name, 0)
180 borrower_owes_lender = borrower['owes'].get(lender_name, 0)
181
182 if lender_owes_borrower > 0 and borrower_owes_lender > 0:
183 if lender_owes_borrower > borrower_owes_lender:
184 lender['owes'][borrower_name] -= borrower_owes_lender
185 del borrower['owes'][lender_name]
186 borrower['owed_by'][lender_name] = lender_owes_borrower - borrower_owes_lender
187 del lender['owed_by'][borrower_name]
188 elif borrower_owes_lender > lender_owes_borrower:
189 borrower['owes'][lender_name] -= lender_owes_borrower
190 del lender['owes'][borrower_name]
191 lender['owed_by'][borrower_name] = borrower_owes_lender - lender_owes_borrower
192 del borrower['owed_by'][lender_name]
193 else: # Equal amounts
194 del lender['owes'][borrower_name]
195 del borrower['owes'][lender_name]
196 del lender['owed_by'][borrower_name]
197 del borrower['owed_by'][lender_name]
198
199 # Return updated user information sorted by name
200 updated_users = [
201 self._get_user(lender_name),
202 self._get_user(borrower_name)
203 ]
204 updated_users.sort(key=lambda u: u['name'])
205 return json.dumps({'users': updated_users})
206
207 # Edge Case: Handle unknown endpoints
208 return json.dumps({})
209
210 # Handled Edge Cases: None database, non-existent users, invalid JSON payload,
211 # unknown endpoints, duplicate users, missing required fields,
212 # non-existent lender/borrower, invalid amount values,
213 # debt cancellation logic
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.