Yakova commited on
Commit
f1f8899
·
verified ·
1 Parent(s): cd8dd1d

Update App/routers/portfolio/routes.py

Browse files
Files changed (1) hide show
  1. App/routers/portfolio/routes.py +78 -0
App/routers/portfolio/routes.py CHANGED
@@ -1200,6 +1200,84 @@ async def get_portfolio_performance(
1200
  raise AppException(status_code=500, detail=f"An unexpected error occurred: {e}")
1201
 
1202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1203
  @router.get(
1204
  "/{portfolio_id}/calendar",
1205
  response_model=ResponseModel,
 
1200
  raise AppException(status_code=500, detail=f"An unexpected error occurred: {e}")
1201
 
1202
 
1203
+ @router.post(
1204
+ "/{portfolio_id}/recalculate-timeseries",
1205
+ response_model=ResponseModel,
1206
+ summary="Recalculate Entire Portfolio Timeseries",
1207
+ )
1208
+ async def recalculate_portfolio_timeseries(
1209
+ portfolio_id: int,
1210
+ background_tasks: BackgroundTasks,
1211
+ current_user=Depends(get_current_user),
1212
+ ):
1213
+ """
1214
+ Recalculates the entire portfolio timeseries by regenerating all historical snapshots
1215
+ from the first transaction date to today. This endpoint:
1216
+
1217
+ 1. Validates the portfolio exists and belongs to the user
1218
+ 2. Checks if a regeneration task is already running
1219
+ 3. Deletes all existing snapshots for the portfolio
1220
+ 4. Queues a background task to regenerate snapshots from the earliest transaction
1221
+
1222
+ Returns task_id for polling the regeneration status.
1223
+ """
1224
+ try:
1225
+ # 1. AUTHENTICATION & VALIDATION
1226
+ portfolio = await Portfolio.get_or_none(
1227
+ id=portfolio_id, user_id=current_user.id
1228
+ )
1229
+ if not portfolio:
1230
+ raise AppException(status_code=404, detail="Portfolio not found")
1231
+
1232
+ # 2. CHECK FOR ACTIVE REGENERATION TASKS
1233
+ active_task = await ImportTask.filter(
1234
+ Q(details__contains={"portfolio_id": portfolio_id}),
1235
+ Q(task_type__in=["portfolio_regeneration", "portfolio_snapshot_history"]),
1236
+ status__in=["pending", "running"],
1237
+ ).first()
1238
+
1239
+ if active_task:
1240
+ return ResponseModel(
1241
+ success=False,
1242
+ message="A timeseries recalculation is already in progress for this portfolio.",
1243
+ data={"task_id": active_task.id, "status": active_task.status},
1244
+ )
1245
+
1246
+ # 3. CREATE A NEW TASK AND QUEUE BACKGROUND WORK
1247
+ task = await ImportTask.create(
1248
+ task_type="portfolio_regeneration",
1249
+ status="pending",
1250
+ details={
1251
+ "portfolio_id": portfolio_id,
1252
+ "reason": "Manual full timeseries recalculation requested by user.",
1253
+ },
1254
+ )
1255
+
1256
+ # Queue the regeneration task without a start_date,
1257
+ # so it will find the earliest transaction and start from there
1258
+ background_tasks.add_task(
1259
+ PortfolioService.regenerate_snapshots_task, task.id, portfolio_id
1260
+ )
1261
+
1262
+ return ResponseModel(
1263
+ success=True,
1264
+ message="Timeseries recalculation started. This may take a few moments.",
1265
+ data={
1266
+ "task_id": task.id,
1267
+ "status": "pending",
1268
+ "portfolio_id": portfolio_id,
1269
+ },
1270
+ )
1271
+
1272
+ except AppException:
1273
+ raise
1274
+ except Exception as e:
1275
+ raise AppException(
1276
+ status_code=500, detail=f"Failed to start timeseries recalculation: {str(e)}"
1277
+ )
1278
+
1279
+
1280
+
1281
  @router.get(
1282
  "/{portfolio_id}/calendar",
1283
  response_model=ResponseModel,