Coverage for dormatory/api/routes/attributes.py: 93%

148 statements  

« prev     ^ index     » next       coverage.py v7.10.1, created at 2025-08-04 00:22 +0000

1""" 

2Attributes API routes for DORMATORY. 

3 

4This module provides RESTful API endpoints for managing attribute entities. 

5""" 

6 

7from typing import List, Optional 

8 

9from fastapi import APIRouter, HTTPException, Depends 

10from pydantic import BaseModel 

11from sqlalchemy.orm import Session 

12 

13from dormatory.api.dependencies import get_db 

14from dormatory.models.dormatory_model import Attributes, Object 

15 

16router = APIRouter(tags=["attributes"]) 

17 

18 

19class AttributeCreate(BaseModel): 

20 name: str 

21 value: str 

22 object_id: int 

23 created_on: str 

24 updated_on: str 

25 

26 

27class AttributeUpdate(BaseModel): 

28 name: Optional[str] = None 

29 value: Optional[str] = None 

30 updated_on: Optional[str] = None 

31 

32 

33class AttributeResponse(BaseModel): 

34 id: int 

35 name: str 

36 value: str 

37 object_id: int 

38 created_on: str 

39 updated_on: str 

40 

41 class Config: 

42 from_attributes = True 

43 

44 

45@router.post("/", response_model=AttributeResponse) 

46async def create_attribute(attribute_data: AttributeCreate, db: Session = Depends(get_db)): 

47 """ 

48 Create a new attribute. 

49  

50 Args: 

51 attribute_data: Attribute creation data 

52 db: Database session 

53  

54 Returns: 

55 Created attribute data 

56 """ 

57 # Verify that the object exists 

58 object_obj = db.query(Object).filter(Object.id == attribute_data.object_id).first() 

59 if not object_obj: 

60 raise HTTPException(status_code=404, detail="Object not found") 

61 

62 # Check if attribute already exists for this object 

63 existing_attribute = db.query(Attributes).filter( 

64 Attributes.object_id == attribute_data.object_id, 

65 Attributes.name == attribute_data.name 

66 ).first() 

67 

68 if existing_attribute: 

69 raise HTTPException(status_code=409, detail="Attribute already exists for this object") 

70 

71 # Create the attribute 

72 db_attribute = Attributes( 

73 name=attribute_data.name, 

74 value=attribute_data.value, 

75 object_id=attribute_data.object_id, 

76 created_on=attribute_data.created_on, 

77 updated_on=attribute_data.updated_on 

78 ) 

79 

80 db.add(db_attribute) 

81 db.commit() 

82 db.refresh(db_attribute) 

83 

84 return AttributeResponse.from_orm(db_attribute) 

85 

86 

87@router.get("/{attribute_id}", response_model=AttributeResponse) 

88async def get_attribute_by_id(attribute_id: int, db: Session = Depends(get_db)): 

89 """ 

90 Get an attribute by its ID. 

91  

92 Args: 

93 attribute_id: Attribute ID 

94 db: Database session 

95  

96 Returns: 

97 Attribute data 

98 """ 

99 db_attribute = db.query(Attributes).filter(Attributes.id == attribute_id).first() 

100 if not db_attribute: 

101 raise HTTPException(status_code=404, detail="Attribute not found") 

102 

103 return AttributeResponse.from_orm(db_attribute) 

104 

105 

106@router.get("/", response_model=List[AttributeResponse]) 

107async def get_all_attributes( 

108 skip: int = 0, 

109 limit: int = 100, 

110 object_id: Optional[int] = None, 

111 name: Optional[str] = None, 

112 db: Session = Depends(get_db) 

113): 

114 """ 

115 Get all attributes with optional filtering. 

116  

117 Args: 

118 skip: Number of records to skip 

119 limit: Maximum number of records to return 

120 object_id: Filter by object ID 

121 name: Filter by attribute name 

122 db: Database session 

123  

124 Returns: 

125 List of attributes 

126 """ 

127 query = db.query(Attributes) 

128 

129 # Apply filters 

130 if object_id: 

131 query = query.filter(Attributes.object_id == object_id) 

132 if name: 

133 query = query.filter(Attributes.name == name) 

134 

135 # Apply pagination 

136 attributes = query.offset(skip).limit(limit).all() 

137 

138 return [AttributeResponse.from_orm(attr) for attr in attributes] 

139 

140 

141@router.put("/{attribute_id}", response_model=AttributeResponse) 

142async def update_attribute(attribute_id: int, attribute_data: AttributeUpdate, db: Session = Depends(get_db)): 

143 """ 

144 Update an existing attribute. 

145  

146 Args: 

147 attribute_id: Attribute ID to update 

148 attribute_data: Updated attribute data 

149 db: Database session 

150  

151 Returns: 

152 Updated attribute data 

153 """ 

154 db_attribute = db.query(Attributes).filter(Attributes.id == attribute_id).first() 

155 if not db_attribute: 

156 raise HTTPException(status_code=404, detail="Attribute not found") 

157 

158 # Update fields if provided 

159 if attribute_data.name is not None: 

160 # Check if the new name conflicts with existing attribute for the same object 

161 if attribute_data.name != db_attribute.name: 

162 existing_attribute = db.query(Attributes).filter( 

163 Attributes.object_id == db_attribute.object_id, 

164 Attributes.name == attribute_data.name, 

165 Attributes.id != attribute_id 

166 ).first() 

167 

168 if existing_attribute: 

169 raise HTTPException(status_code=409, detail="Attribute name already exists for this object") 

170 

171 db_attribute.name = attribute_data.name 

172 

173 if attribute_data.value is not None: 

174 db_attribute.value = attribute_data.value 

175 

176 if attribute_data.updated_on is not None: 

177 db_attribute.updated_on = attribute_data.updated_on 

178 

179 db.commit() 

180 db.refresh(db_attribute) 

181 

182 return AttributeResponse.from_orm(db_attribute) 

183 

184 

185@router.delete("/{attribute_id}") 

186async def delete_attribute(attribute_id: int, db: Session = Depends(get_db)): 

187 """ 

188 Delete an attribute. 

189  

190 Args: 

191 attribute_id: Attribute ID to delete 

192 db: Database session 

193  

194 Returns: 

195 Success message 

196 """ 

197 db_attribute = db.query(Attributes).filter(Attributes.id == attribute_id).first() 

198 if not db_attribute: 

199 raise HTTPException(status_code=404, detail="Attribute not found") 

200 

201 db.delete(db_attribute) 

202 db.commit() 

203 

204 return {"message": "Attribute deleted successfully"} 

205 

206 

207@router.post("/bulk", response_model=List[AttributeResponse]) 

208async def create_attributes_bulk(attribute_data: List[AttributeCreate], db: Session = Depends(get_db)): 

209 """ 

210 Create multiple attributes in a single operation. 

211  

212 Args: 

213 attribute_data: List of attribute creation data 

214 db: Database session 

215  

216 Returns: 

217 List of created attributes 

218 """ 

219 created_attributes = [] 

220 

221 for item in attribute_data: 

222 # Verify that the object exists 

223 object_obj = db.query(Object).filter(Object.id == item.object_id).first() 

224 if not object_obj: 

225 raise HTTPException(status_code=404, detail=f"Object {item.object_id} not found") 

226 

227 # Check if attribute already exists for this object 

228 existing_attribute = db.query(Attributes).filter( 

229 Attributes.object_id == item.object_id, 

230 Attributes.name == item.name 

231 ).first() 

232 

233 if existing_attribute: 

234 raise HTTPException(status_code=409, detail=f"Attribute {item.name} already exists for object {item.object_id}") 

235 

236 # Create the attribute 

237 db_attribute = Attributes( 

238 name=item.name, 

239 value=item.value, 

240 object_id=item.object_id, 

241 created_on=item.created_on, 

242 updated_on=item.updated_on 

243 ) 

244 

245 db.add(db_attribute) 

246 created_attributes.append(db_attribute) 

247 

248 db.commit() 

249 

250 # Refresh all attributes to get their IDs 

251 for attr in created_attributes: 

252 db.refresh(attr) 

253 

254 return [AttributeResponse.from_orm(attr) for attr in created_attributes] 

255 

256 

257@router.get("/object/{object_id}") 

258async def get_attributes_by_object(object_id: int, db: Session = Depends(get_db)): 

259 """ 

260 Get all attributes for a specific object. 

261  

262 Args: 

263 object_id: Object ID 

264 db: Database session 

265  

266 Returns: 

267 List of attributes for the object 

268 """ 

269 # Verify that the object exists 

270 object_obj = db.query(Object).filter(Object.id == object_id).first() 

271 if not object_obj: 

272 raise HTTPException(status_code=404, detail="Object not found") 

273 

274 attributes = db.query(Attributes).filter(Attributes.object_id == object_id).all() 

275 

276 return [AttributeResponse.from_orm(attr) for attr in attributes] 

277 

278 

279@router.get("/name/{name}") 

280async def get_attribute_by_name(name: str, db: Session = Depends(get_db)): 

281 """ 

282 Get all attributes with a specific name. 

283  

284 Args: 

285 name: Attribute name 

286 db: Database session 

287  

288 Returns: 

289 List of attributes with this name 

290 """ 

291 attributes = db.query(Attributes).filter(Attributes.name == name).all() 

292 

293 return [AttributeResponse.from_orm(attr) for attr in attributes] 

294 

295 

296@router.get("/object/{object_id}/map") 

297async def get_object_attributes_map(object_id: int, db: Session = Depends(get_db)): 

298 """ 

299 Get all attributes for an object as a key-value map. 

300  

301 Args: 

302 object_id: Object ID 

303 db: Database session 

304  

305 Returns: 

306 Dictionary of attribute name-value pairs 

307 """ 

308 # Verify that the object exists 

309 object_obj = db.query(Object).filter(Object.id == object_id).first() 

310 if not object_obj: 

311 raise HTTPException(status_code=404, detail="Object not found") 

312 

313 attributes = db.query(Attributes).filter(Attributes.object_id == object_id).all() 

314 

315 return {attr.name: attr.value for attr in attributes} 

316 

317 

318@router.post("/object/{object_id}/set") 

319async def set_object_attributes(object_id: int, attributes: dict, db: Session = Depends(get_db)): 

320 """ 

321 Set multiple attributes for an object. 

322  

323 Args: 

324 object_id: Object ID 

325 attributes: Dictionary of attribute name-value pairs 

326 db: Database session 

327  

328 Returns: 

329 Success message 

330 """ 

331 # Verify that the object exists 

332 object_obj = db.query(Object).filter(Object.id == object_id).first() 

333 if not object_obj: 

334 raise HTTPException(status_code=404, detail="Object not found") 

335 

336 from datetime import datetime, UTC 

337 

338 current_time = datetime.now(UTC).isoformat() 

339 created_attributes = [] 

340 

341 for name, value in attributes.items(): 

342 # Check if attribute already exists 

343 existing_attribute = db.query(Attributes).filter( 

344 Attributes.object_id == object_id, 

345 Attributes.name == name 

346 ).first() 

347 

348 if existing_attribute: 

349 # Update existing attribute 

350 existing_attribute.value = str(value) 

351 existing_attribute.updated_on = current_time 

352 else: 

353 # Create new attribute 

354 db_attribute = Attributes( 

355 name=name, 

356 value=str(value), 

357 object_id=object_id, 

358 created_on=current_time, 

359 updated_on=current_time 

360 ) 

361 db.add(db_attribute) 

362 created_attributes.append(db_attribute) 

363 

364 db.commit() 

365 

366 return {"message": f"Set {len(attributes)} attributes for object {object_id}"} 

367 

368 

369@router.delete("/object/{object_id}/name/{name}") 

370async def delete_attribute_by_name(object_id: int, name: str, db: Session = Depends(get_db)): 

371 """ 

372 Delete a specific attribute by name for an object. 

373  

374 Args: 

375 object_id: Object ID 

376 name: Attribute name 

377 db: Database session 

378  

379 Returns: 

380 Success message 

381 """ 

382 # Verify that the object exists 

383 object_obj = db.query(Object).filter(Object.id == object_id).first() 

384 if not object_obj: 

385 raise HTTPException(status_code=404, detail="Object not found") 

386 

387 # Find and delete the attribute 

388 db_attribute = db.query(Attributes).filter( 

389 Attributes.object_id == object_id, 

390 Attributes.name == name 

391 ).first() 

392 

393 if not db_attribute: 

394 raise HTTPException(status_code=404, detail="Attribute not found") 

395 

396 db.delete(db_attribute) 

397 db.commit() 

398 

399 return {"message": f"Attribute '{name}' deleted for object {object_id}"} 

400 

401 

402@router.get("/search/{query}") 

403async def search_attributes(query: str, db: Session = Depends(get_db)): 

404 """ 

405 Search attributes by name or value. 

406  

407 Args: 

408 query: Search query 

409 db: Database session 

410  

411 Returns: 

412 List of matching attributes 

413 """ 

414 # Search in both name and value fields 

415 attributes = db.query(Attributes).filter( 

416 (Attributes.name.contains(query)) | (Attributes.value.contains(query)) 

417 ).all() 

418 

419 return [AttributeResponse.from_orm(attr) for attr in attributes]