diff --git a/README.md b/README.md
index 10cffc1..d52d215 100644
--- a/README.md
+++ b/README.md
@@ -130,4 +130,3 @@ Details how to manage database migrations using Alembic, including TimescaleDB-s
### 4. [Adding a New Endpoint](docs/add_new_endpoint.md)
A step-by-step guide on how to add a new endpoint to the application.
-
diff --git a/controllers/metis_controller.py b/controllers/metis_controller.py
index 57266b2..a4432c9 100644
--- a/controllers/metis_controller.py
+++ b/controllers/metis_controller.py
@@ -8,7 +8,7 @@
from config.database import get_db
from typing import Optional
from utils import page_size
-from utils import validate_timebin_params, prepare_timebin_range
+from utils import validate_timebin_params
router = APIRouter(prefix="/metis/atlas", tags=["Metis"])
@@ -48,7 +48,7 @@ async def get_metis_atlas_deployments(
Limitations: At most 31 days of data can be fetched per request. For bulk downloads see: https://ihr-archive.iijlab.net/.
"""
- timebin__gte, timebin__lte = prepare_timebin_range(
+ timebin__gte, timebin__lte = validate_timebin_params(
timebin, timebin__gte, timebin__lte, max_days=31)
deployments, total_count = MetisController.service.get_metis_atlas_deployments(
@@ -108,7 +108,7 @@ async def get_metis_atlas_selections(
Limitations: At most 31 days of data can be fetched per request. For bulk downloads see: https://ihr-archive.iijlab.net/.
"""
- timebin__gte, timebin__lte = prepare_timebin_range(
+ timebin__gte, timebin__lte = validate_timebin_params(
timebin, timebin__gte, timebin__lte, max_days=31)
selections, total_count = MetisController.service.get_metis_atlas_selections(
diff --git a/controllers/tr_hegemony_controller.py b/controllers/tr_hegemony_controller.py
index 2bafb52..4ef83b8 100644
--- a/controllers/tr_hegemony_controller.py
+++ b/controllers/tr_hegemony_controller.py
@@ -7,7 +7,7 @@
from typing import Optional
from datetime import datetime
from utils import page_size
-from utils import prepare_timebin_range
+from utils import validate_timebin_params
router = APIRouter(prefix="/tr_hegemony", tags=["TR Hegemony"])
@@ -57,7 +57,7 @@ async def get_hegemony(
Limitations: At most 31 days of data can be fetched per request. For bulk downloads see: https://ihr-archive.iijlab.net/.
"""
- timebin__gte, timebin__lte = prepare_timebin_range(
+ timebin__gte, timebin__lte = validate_timebin_params(
timebin, timebin__gte, timebin__lte, max_days=31)
hegemony_data, total_count = TRHegemonyController.service.get_tr_hegemony(
diff --git a/docs/project_structure.md b/docs/project_structure.md
index 8d7c317..9f83b96 100644
--- a/docs/project_structure.md
+++ b/docs/project_structure.md
@@ -6,14 +6,14 @@ This document provides an overview of the project's file and folder structure. E
```plaintext
.
-├── alembic/ # Database migration scripts
-├── config/ # App configuration
-├── controllers/ # API endpoints and HTTP route handlers (Controller Layer)
+├── alembic/ # [Alembic] Database migration scripts
+├── config/ # [API] App configuration
+├── controllers/ # [API] API endpoints and HTTP route handlers (Controller Layer)
├── docs/ # Documentation files
-├── dtos/ # Data Transfer Objects for request/response schemas
-├── models/ # Database models and ORM classes (Model Layer)
-├── repositories/ # Data access logic and database interaction (Repository Layer)
-├── services/ # Business logic layer (Service Layer)
+├── dtos/ # [API] Data Transfer Objects for request/response schemas
+├── models/ # [API & Alembic] Database models and ORM classes (Model Layer)
+├── repositories/ # [API] Data access logic and database interaction (Repository Layer)
+├── services/ # [API] Business logic layer (Service Layer)
├── .env # Environment variables (e.g., database credentials)
├── .gitignore # Specifies intentionally untracked files to ignore
├── alembic.ini # Alembic configuration file
diff --git a/repositories/atlas_delay_alarms_repository.py b/repositories/atlas_delay_alarms_repository.py
index 782419a..1520bd3 100644
--- a/repositories/atlas_delay_alarms_repository.py
+++ b/repositories/atlas_delay_alarms_repository.py
@@ -4,6 +4,7 @@
from datetime import datetime
from typing import List, Optional, Tuple
from utils import page_size
+from sqlalchemy import func
class AtlasDelayAlarmsRepository:
@@ -38,6 +39,12 @@ def get_alarms(
.join(Startpoint, AtlasDelayAlarms.startpoint_relation)\
.join(Endpoint, AtlasDelayAlarms.endpoint_relation)
+
+ # If no time filters specified, get rows with max timebin
+ if not timebin and not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(AtlasDelayAlarms.timebin)).scalar()
+ query = query.filter(AtlasDelayAlarms.timebin == max_timebin)
+
if timebin:
query = query.filter(AtlasDelayAlarms.timebin == timebin)
if timebin_gte:
diff --git a/repositories/atlas_delay_repository.py b/repositories/atlas_delay_repository.py
index 40c1ae6..f2c76a3 100644
--- a/repositories/atlas_delay_repository.py
+++ b/repositories/atlas_delay_repository.py
@@ -4,6 +4,7 @@
from datetime import datetime
from typing import List, Optional, Tuple
from utils import page_size
+from sqlalchemy import func
class AtlasDelayRepository:
@@ -41,6 +42,12 @@ def get_delays(
.join(Startpoint, AtlasDelay.startpoint_relation)\
.join(Endpoint, AtlasDelay.endpoint_relation)
+
+ # If no time filters specified, get rows with max timebin
+ if not timebin and not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(AtlasDelay.timebin)).scalar()
+ query = query.filter(AtlasDelay.timebin == max_timebin)
+
# Apply timebin filters
if timebin:
query = query.filter(AtlasDelay.timebin == timebin)
diff --git a/repositories/hegemony_alarms_repository.py b/repositories/hegemony_alarms_repository.py
index e628104..b3883f1 100644
--- a/repositories/hegemony_alarms_repository.py
+++ b/repositories/hegemony_alarms_repository.py
@@ -3,6 +3,7 @@
from models.hegemony_alarms import HegemonyAlarms
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class HegemonyAlarmsRepository:
@@ -21,6 +22,11 @@ def get_all(
) -> Tuple[List[HegemonyAlarms], int]:
query = db.query(HegemonyAlarms)
+ # If no time filters specified, get rows with max timebin
+ if not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(HegemonyAlarms.timebin)).scalar()
+ query = query.filter(HegemonyAlarms.timebin == max_timebin)
+
# Apply filters
if timebin_gte:
query = query.filter(HegemonyAlarms.timebin >= timebin_gte)
diff --git a/repositories/hegemony_cone_repository.py b/repositories/hegemony_cone_repository.py
index 1eed074..52b8da7 100644
--- a/repositories/hegemony_cone_repository.py
+++ b/repositories/hegemony_cone_repository.py
@@ -3,6 +3,7 @@
from models.hegemony_cone import HegemonyCone
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class HegemonyConeRepository:
@@ -18,6 +19,11 @@ def get_all(
) -> Tuple[List[HegemonyCone], int]:
query = db.query(HegemonyCone)
+ # If no time filters specified, get rows with max timebin
+ if not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(HegemonyCone.timebin)).scalar()
+ query = query.filter(HegemonyCone.timebin == max_timebin)
+
# Apply filters
if timebin_gte:
query = query.filter(HegemonyCone.timebin >= timebin_gte)
diff --git a/repositories/hegemony_country_repository.py b/repositories/hegemony_country_repository.py
index 906cb68..691cb12 100644
--- a/repositories/hegemony_country_repository.py
+++ b/repositories/hegemony_country_repository.py
@@ -3,6 +3,7 @@
from models.hegemony_country import HegemonyCountry
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class HegemonyCountryRepository:
@@ -24,6 +25,11 @@ def get_all(
) -> Tuple[List[HegemonyCountry], int]:
query = db.query(HegemonyCountry)
+ # If no time filters specified, get rows with max timebin
+ if not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(HegemonyCountry.timebin)).scalar()
+ query = query.filter(HegemonyCountry.timebin == max_timebin)
+
# Apply filters
if timebin_gte:
query = query.filter(HegemonyCountry.timebin >= timebin_gte)
diff --git a/repositories/hegemony_prefix_repository.py b/repositories/hegemony_prefix_repository.py
index ee2b472..5c2a363 100644
--- a/repositories/hegemony_prefix_repository.py
+++ b/repositories/hegemony_prefix_repository.py
@@ -3,6 +3,7 @@
from models.hegemony_prefix import HegemonyPrefix
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class HegemonyPrefixRepository:
@@ -29,6 +30,11 @@ def get_all(
) -> Tuple[List[HegemonyPrefix], int]:
query = db.query(HegemonyPrefix)
+ # If no time filters specified, get rows with max timebin
+ if not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(HegemonyPrefix.timebin)).scalar()
+ query = query.filter(HegemonyPrefix.timebin == max_timebin)
+
# Apply filters
if timebin_gte:
query = query.filter(HegemonyPrefix.timebin >= timebin_gte)
diff --git a/repositories/hegemony_repository.py b/repositories/hegemony_repository.py
index ed7f902..2f2ca48 100644
--- a/repositories/hegemony_repository.py
+++ b/repositories/hegemony_repository.py
@@ -3,6 +3,7 @@
from models.hegemony import Hegemony
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class HegemonyRepository:
@@ -22,6 +23,10 @@ def get_all(
) -> Tuple[List[Hegemony], int]:
query = db.query(Hegemony)
+ # If no time filters specified, get rows with max timebin
+ if not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(Hegemony.timebin)).scalar()
+ query = query.filter(Hegemony.timebin == max_timebin)
# Apply filters
if timebin_gte:
query = query.filter(Hegemony.timebin >= timebin_gte)
diff --git a/repositories/metis_atlas_deployment_repository.py b/repositories/metis_atlas_deployment_repository.py
index 2e4edcf..abe9967 100644
--- a/repositories/metis_atlas_deployment_repository.py
+++ b/repositories/metis_atlas_deployment_repository.py
@@ -3,6 +3,7 @@
from models.metis_atlas_deployment import MetisAtlasDeployment
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class MetisAtlasDeploymentRepository:
@@ -23,6 +24,11 @@ def get_all(
query = db.query(MetisAtlasDeployment).join(
MetisAtlasDeployment.asn_relation)
+ # If no time filters specified, get rows with max timebin
+ if not timebin and not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(MetisAtlasDeployment.timebin)).scalar()
+ query = query.filter(MetisAtlasDeployment.timebin == max_timebin)
+
# Apply filters
if timebin:
query = query.filter(MetisAtlasDeployment.timebin == timebin)
diff --git a/repositories/metis_atlas_selection_repository.py b/repositories/metis_atlas_selection_repository.py
index 0d9d882..abd6e99 100644
--- a/repositories/metis_atlas_selection_repository.py
+++ b/repositories/metis_atlas_selection_repository.py
@@ -3,6 +3,7 @@
from models.metis_atlas_selection import MetisAtlasSelection
from typing import Optional, List, Tuple
from utils import page_size
+from sqlalchemy import func
class MetisAtlasSelectionRepository:
@@ -23,6 +24,12 @@ def get_all(
query = db.query(MetisAtlasSelection).join(
MetisAtlasSelection.asn_relation)
+ # If no time filters specified, get rows with max timebin
+ if not timebin and not timebin_gte and not timebin_lte:
+ max_timebin = db.query(
+ func.max(MetisAtlasSelection.timebin)).scalar()
+ query = query.filter(MetisAtlasSelection.timebin == max_timebin)
+
# Apply filters
if timebin:
query = query.filter(MetisAtlasSelection.timebin == timebin)
diff --git a/repositories/tr_hegemony_repository.py b/repositories/tr_hegemony_repository.py
index 1a6f008..5f711c3 100644
--- a/repositories/tr_hegemony_repository.py
+++ b/repositories/tr_hegemony_repository.py
@@ -4,6 +4,7 @@
from datetime import datetime
from typing import List, Optional, Tuple
from utils import page_size
+from sqlalchemy import func
class TRHegemonyRepository:
@@ -35,6 +36,11 @@ def get_tr_hegemony(
.join(Origin, TRHegemony.origin_relation)\
.join(Dependency, TRHegemony.dependency_relation)
+ # If no time filters specified, get rows with max timebin
+ if not timebin and not timebin_gte and not timebin_lte:
+ max_timebin = db.query(func.max(TRHegemony.timebin)).scalar()
+ query = query.filter(TRHegemony.timebin == max_timebin)
+
if timebin:
query = query.filter(TRHegemony.timebin == timebin)
if timebin_gte:
diff --git a/utils.py b/utils.py
index f29bcde..fa53ec7 100644
--- a/utils.py
+++ b/utils.py
@@ -14,6 +14,7 @@
page_size = int(os.getenv("PAGE_SIZE"))
+
def validate_timebin_params(
timebin: Optional[datetime],
timebin_gte: Optional[datetime],
@@ -21,15 +22,7 @@ def validate_timebin_params(
max_days: int = 7
) -> tuple[datetime, datetime]:
- # Check if at least one time parameter exists
- if not any([timebin, timebin_gte, timebin_lte]):
- raise HTTPException(
- status_code=400,
- detail="No timebin parameter. Please provide a timebin value or a range of values with timebin__lte and timebin__gte."
- )
-
- # If timebin is not provided, both timebin_gte and timebin_lte must be provided
- if not timebin and not (timebin_gte and timebin_lte):
+ if (not timebin_gte and timebin_lte) or (timebin_gte and not timebin_lte):
raise HTTPException(
status_code=400,
detail="Invalid timebin range. Please provide both timebin__lte and timebin__gte."
@@ -49,33 +42,4 @@ def validate_timebin_params(
detail=f"The given timebin range is too large. Should be less than {max_days} days."
)
- return timebin_gte, timebin_lte
-
-
-def prepare_timebin_range(
- timebin: Optional[datetime],
- timebin_gte: Optional[datetime],
- timebin_lte: Optional[datetime],
- max_days: int = 7
-) -> Tuple[datetime, Optional[datetime]]:
-
- if (not timebin_gte and timebin_lte) or (timebin_gte and not timebin_lte):
- raise HTTPException(
- status_code=400,
- detail="Invalid timebin range. Please provide both timebin__lte and timebin__gte."
- )
- # If no time filters provided, default to last 6 days (including today)
- if not any([timebin, timebin_gte, timebin_lte]):
- today = datetime.combine(date.today(), datetime.min.time())
- timebin_gte = today - timedelta(days=6)
-
- # Validate range size if both are given
- if timebin_gte and timebin_lte:
- delta = timebin_lte - timebin_gte
- if delta > timedelta(days=max_days):
- raise HTTPException(
- status_code=400,
- detail=f"The given timebin range is too large. Should be less than {max_days} days."
- )
-
- return timebin_gte, timebin_lte
+ return timebin_gte, timebin_lte
\ No newline at end of file