From 2eb8cd3583a879c2000d01c4037fd31883872346 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Fri, 13 Dec 2024 16:43:26 +0100
Subject: [PATCH 01/11] feat #309 migrer endpoint /metrics/activity-in-mpas
---
backend/bloom/domain/metrics.py | 5 +++
backend/bloom/routers/v1/metrics.py | 20 ++++++++++
backend/bloom/services/metrics.py | 62 ++++++++++++++++++++++++-----
3 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/backend/bloom/domain/metrics.py b/backend/bloom/domain/metrics.py
index d59af644..133c5d75 100644
--- a/backend/bloom/domain/metrics.py
+++ b/backend/bloom/domain/metrics.py
@@ -39,6 +39,11 @@ class ResponseMetricsVesselInActivitySchema(BaseModel):
vessel: VesselListView
total_time_at_sea: Optional[timedelta]
+class ResponseMetricsVesselInMpasSchema(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ vessel: VesselListView
+ total_time_in_mpas: Optional[timedelta]
+
class ResponseMetricsZoneVisitedSchema(BaseModel):
zone: ZoneListView
visiting_duration: timedelta
diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py
index 0f3e6cdf..1b5566bc 100644
--- a/backend/bloom/routers/v1/metrics.py
+++ b/backend/bloom/routers/v1/metrics.py
@@ -123,3 +123,23 @@ async def read_metrics_all_vessels_visiting_time_by_zone(request: Request,
category=category,
sub_category=sub_category)
return jsonable_encoder(payload)
+
+
+@router.get("/metrics/vessels/activity-in-mpas")
+# @cache
+async def read_metrics_all_vessels_visiting_time_in_mpas(
+ request: Request,
+ datetime_range: DatetimeRangeRequest = Depends(),
+ pagination: PageParams = Depends(),
+ order: OrderByRequest = Depends(),
+ key: str = Depends(X_API_KEY_HEADER),
+):
+ check_apikey(key)
+ use_cases = UseCases()
+ MetricsService = use_cases.metrics_service()
+ payload = MetricsService.get_vessels_in_mpas(
+ datetime_range=datetime_range,
+ pagination=pagination,
+ order=order
+ )
+ return jsonable_encoder(payload)
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index c9a08ef1..6279d94c 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -18,6 +18,7 @@
from bloom.domain.metrics import (ResponseMetricsVesselInActivitySchema,
ResponseMetricsZoneVisitedSchema,
+ ResponseMetricsVesselInMpasSchema,
ResponseMetricsZoneVisitingTimeByVesselSchema,
ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema,
ResponseMetricsVesselVisitingTimeByZoneSchema)
@@ -72,7 +73,50 @@ def getVesselsInActivity(self,
total_time_at_sea=item[1]
)\
for item in payload]
-
+
+ def get_vessels_in_mpas(self,
+ datetime_range: DatetimeRangeRequest,
+ pagination: PageParams,
+ order: OrderByRequest):
+ payload=[]
+ with self.session_factory() as session:
+ stmt = (
+ select(
+ sql_model.Vessel,
+ func.sum(sql_model.Metrics.duration_total).label(
+ "total_time_in_mpas"
+ ),
+ )
+ .select_from(sql_model.Metrics)
+ .join(
+ sql_model.Vessel,
+ sql_model.Vessel.id == sql_model.Metrics.vessel_id,
+ )
+ .where(
+ sql_model.Metrics.timestamp.between(
+ datetime_range.start_at, datetime_range.end_at
+ )
+ )
+ .where(sql_model.Metrics.zone_category == "amp")
+ .group_by(sql_model.Vessel)
+ )
+ stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
+ stmt = (
+ stmt.order_by(asc("total_time_in_mpas"))
+ if order.order == OrderByEnum.ascending
+ else stmt.order_by(desc("total_time_in_mpas"))
+ )
+ stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
+ payload=session.execute(stmt).all()
+
+ return [
+ ResponseMetricsVesselInMpasSchema(
+ vessel=VesselRepository.map_to_domain(item[0]).model_dump(),
+ total_time_in_mpas=item[1],
+ )
+ for item in payload
+ ]
+
def getVesselsAtSea(self,
datetime_range: DatetimeRangeRequest,
):
@@ -95,7 +139,6 @@ def getVesselsAtSea(self,
)
return session.execute(stmt).scalar()
-
def getZoneVisited(self,
datetime_range: DatetimeRangeRequest,
pagination: PageParams,
@@ -137,7 +180,7 @@ def getZoneVisited(self,
visiting_duration=item[1]
)\
for item in payload]
-
+
def getZoneVisitingTimeByVessel(self,
zone_id: int,
datetime_range: DatetimeRangeRequest,
@@ -145,7 +188,7 @@ def getZoneVisitingTimeByVessel(self,
pagination: PageParams,):
payload=[]
with self.session_factory() as session:
-
+
stmt=select(
sql_model.Zone,
sql_model.Vessel,
@@ -166,7 +209,7 @@ def getZoneVisitingTimeByVessel(self,
)
)\
.group_by(sql_model.Zone.id,sql_model.Vessel.id)
-
+
stmt = stmt.order_by(func.sum(sql_model.Segment.segment_duration).asc())\
if order.order == OrderByEnum.ascending \
else stmt.order_by(func.sum(sql_model.Segment.segment_duration).desc())
@@ -185,7 +228,7 @@ def getZoneVisitingTimeByVessel(self,
zone_visiting_time_by_vessel=item[2]
)\
for item in payload]
-
+
def getVesselVisitingTimeByZone(self,
order: OrderByRequest,
datetime_range: DatetimeRangeRequest,
@@ -226,14 +269,12 @@ def getVesselVisitingTimeByZone(self,
stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
if vessel_id is not None:
stmt=stmt.where(sql_model.Vessel.id==vessel_id)
-
-
+
return [ResponseMetricsVesselVisitingTimeByZoneSchema(
vessel=VesselListView(**VesselRepository.map_to_domain(model[0]).model_dump()),
zone=ZoneListView(**ZoneRepository.map_to_domain(model[1]).model_dump()),
vessel_visiting_time_by_zone=model[2]) for model in session.execute(stmt).all()]
-
def getVesselVisitsByActivityType(self,
vessel_id: int,
activity_type: TotalTimeActivityTypeRequest,
@@ -261,10 +302,9 @@ def getVesselVisitsByActivityType(self,
literal_column('0 seconds'),
))
payload=session.execute(stmt.limit(1)).scalar_one_or_none()
-
+
return [ ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema(
vessel_id=item.id,
activity=item.activity,
total_activity_time=item.total_activity_time,
) for item in payload]
-
\ No newline at end of file
From 2dcfa2ae1a0e6b7a3eeb92adeb9298693c2afdb6 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Fri, 13 Dec 2024 17:06:46 +0100
Subject: [PATCH 02/11] feat #309: migrate getTopVesselsInActivity to
getTopVesselsInMpas (dashboard)
---
frontend/app/dashboard/page.tsx | 26 ++++++++++------
.../dashboard/dashboard-overview.tsx | 31 +++++++++++--------
frontend/libs/mapper.tsx | 2 +-
frontend/services/backend-rest-client.ts | 4 +--
frontend/services/dashboard.service.ts | 20 ++++++------
frontend/types/vessel.ts | 2 +-
6 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx
index 95bd06e8..46457ba5 100644
--- a/frontend/app/dashboard/page.tsx
+++ b/frontend/app/dashboard/page.tsx
@@ -1,11 +1,17 @@
-"use client"
+"use client";
+
+import { useMemo, useState } from "react";
+import { useDashboardData } from "@/services/dashboard.service";
+
+
+
+import { getDateRange } from "@/libs/dateUtils";
+import DashboardHeader from "@/components/dashboard/dashboard-header";
+import DashboardOverview from "@/components/dashboard/dashboard-overview";
+
+
-import { useMemo, useState } from "react"
-import { useDashboardData } from "@/services/dashboard.service"
-import { getDateRange } from "@/libs/dateUtils"
-import DashboardHeader from "@/components/dashboard/dashboard-header"
-import DashboardOverview from "@/components/dashboard/dashboard-overview"
export default function DashboardPage() {
const [selectedDays, setSelectedDays] = useState(7)
@@ -14,7 +20,7 @@ export default function DashboardPage() {
}, [selectedDays])
const {
- topVesselsInActivity,
+ topVesselsInMpas,
topAmpsVisited,
totalVesselsInActivity,
totalAmpsVisited,
@@ -31,7 +37,7 @@ export default function DashboardPage() {
{
setSelectedDays(Number(value))
}}
- topVesselsInActivityLoading={isLoading.topVesselsInActivity}
+ topVesselsInMpasLoading={isLoading.topVesselsInMpas}
topAmpsVisitedLoading={isLoading.topAmpsVisited}
totalVesselsActiveLoading={isLoading.totalVesselsInActivity}
totalAmpsVisitedLoading={isLoading.totalAmpsVisited}
@@ -49,4 +55,4 @@ export default function DashboardPage() {
)
-}
+}
\ No newline at end of file
diff --git a/frontend/components/dashboard/dashboard-overview.tsx b/frontend/components/dashboard/dashboard-overview.tsx
index 7f4c3c1d..ad992733 100644
--- a/frontend/components/dashboard/dashboard-overview.tsx
+++ b/frontend/components/dashboard/dashboard-overview.tsx
@@ -1,16 +1,21 @@
-"use client"
+"use client";
-import { TOTAL_AMPS, TOTAL_VESSELS } from "@/constants/totals.constants"
+import { TOTAL_AMPS, TOTAL_VESSELS } from "@/constants/totals.constants";
-import { Item } from "@/types/item"
-import ListCard from "@/components/ui/list-card"
-import KPICard from "@/components/dashboard/kpi-card"
-import { DateRangeSelector } from "../ui/date-range-selector"
+
+import { Item } from "@/types/item";
+import ListCard from "@/components/ui/list-card";
+import KPICard from "@/components/dashboard/kpi-card";
+
+
+
+import { DateRangeSelector } from "../ui/date-range-selector";
+
type Props = {
- topVesselsInActivity: Item[]
- topVesselsInActivityLoading: boolean
+ topVesselsInMpas: Item[]
+ topVesselsInMpasLoading: boolean
topAmpsVisited: Item[]
topAmpsVisitedLoading: boolean
totalVesselsActive: number
@@ -23,8 +28,8 @@ type Props = {
}
export default function DashboardOverview({
- topVesselsInActivity,
- topVesselsInActivityLoading,
+ topVesselsInMpas,
+ topVesselsInMpasLoading,
topAmpsVisited,
topAmpsVisitedLoading,
totalVesselsActive,
@@ -74,9 +79,9 @@ export default function DashboardOverview({
@@ -84,4 +89,4 @@ export default function DashboardOverview({
)
-}
+}
\ No newline at end of file
diff --git a/frontend/libs/mapper.tsx b/frontend/libs/mapper.tsx
index 9799cb0e..6778da8f 100644
--- a/frontend/libs/mapper.tsx
+++ b/frontend/libs/mapper.tsx
@@ -11,7 +11,7 @@ export function convertVesselDtoToItem(metrics: VesselMetrics[]): Item[] {
id: `${vessel.id}`,
title: vessel.ship_name,
description: `IMO ${vessel.imo} / MMSI ${vessel.mmsi} / ${vessel.length} m`,
- value: convertDurationToString(vesselMetrics.total_time_at_sea),
+ value: convertDurationToString(vesselMetrics.total_time_in_mpas),
type: "vessel",
countryIso3: vessel.country_iso3,
}
diff --git a/frontend/services/backend-rest-client.ts b/frontend/services/backend-rest-client.ts
index f4f600f6..3aa81fe2 100644
--- a/frontend/services/backend-rest-client.ts
+++ b/frontend/services/backend-rest-client.ts
@@ -83,12 +83,12 @@ export async function getVesselFirstExcursionSegments(vesselId: number) {
}
}
-export function getTopVesselsInActivity(
+export function getTopVesselsInMpas(
startAt: string,
endAt: string,
topVesselsLimit: number
) {
- const url = `${BASE_URL}/metrics/vessels-in-activity?start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
+ const url = `${BASE_URL}/metrics/vessels/activity-in-mpas?start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
console.log(`GET ${url}`)
return axios.get(url)
}
diff --git a/frontend/services/dashboard.service.ts b/frontend/services/dashboard.service.ts
index ef74e743..832c5b96 100644
--- a/frontend/services/dashboard.service.ts
+++ b/frontend/services/dashboard.service.ts
@@ -1,6 +1,6 @@
import { TOTAL_VESSELS } from "@/constants/totals.constants"
import {
- getTopVesselsInActivity,
+ getTopVesselsInMpas,
getTopZonesVisited,
getVesselsAtSea,
getVesselsTrackedCount,
@@ -13,13 +13,13 @@ import { convertVesselDtoToItem, convertZoneDtoToItem } from "@/libs/mapper"
const TOP_ITEMS_SIZE = 5
type DashboardData = {
- topVesselsInActivity: any[]
+ topVesselsInMpas: any[]
topAmpsVisited: any[]
totalVesselsInActivity: number
totalAmpsVisited: number
totalVesselsTracked: number
isLoading: {
- topVesselsInActivity: boolean
+ topVesselsInMpas: boolean
topAmpsVisited: boolean
totalVesselsInActivity: boolean
totalAmpsVisited: boolean
@@ -32,13 +32,13 @@ export const useDashboardData = (
endAt: string
): DashboardData => {
const {
- data: topVesselsInActivity = [],
- isLoading: topVesselsInActivityLoading,
+ data: topVesselsInMpas = [],
+ isLoading: topVesselsInMpasLoading,
} = useSWR(
- `topVesselsInActivity-${startAt}`,
+ `topVesselsInMpas-${startAt}`,
async () => {
try {
- const response = await getTopVesselsInActivity(
+ const response = await getTopVesselsInMpas(
startAt,
endAt,
TOP_ITEMS_SIZE
@@ -46,7 +46,7 @@ export const useDashboardData = (
return convertVesselDtoToItem(response?.data || [])
} catch (error) {
console.log(
- "An error occurred while fetching top vessels in activity: " + error
+ "An error occurred while fetching top vessels in MPAs: " + error
)
return []
}
@@ -137,13 +137,13 @@ export const useDashboardData = (
)
return {
- topVesselsInActivity,
+ topVesselsInMpas,
topAmpsVisited,
totalVesselsInActivity,
totalAmpsVisited,
totalVesselsTracked,
isLoading: {
- topVesselsInActivity: topVesselsInActivityLoading,
+ topVesselsInMpas: topVesselsInMpasLoading,
topAmpsVisited: topAmpsVisitedLoading,
totalVesselsInActivity: totalVesselsInActivityLoading,
totalAmpsVisited: totalAmpsVisitedLoading,
diff --git a/frontend/types/vessel.ts b/frontend/types/vessel.ts
index f8626549..af5933ed 100644
--- a/frontend/types/vessel.ts
+++ b/frontend/types/vessel.ts
@@ -17,7 +17,7 @@ export type Vessel = {
export type VesselMetrics = {
vessel: VesselDetails
- total_time_at_sea: string
+ total_time_in_mpas: string
}
export type VesselDetails = {
From 83553f3f0aad42a87a465b8cae11281014d133f6 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Fri, 13 Dec 2024 18:40:41 +0100
Subject: [PATCH 03/11] feat #204: global endpoints for dashboard rankings
---
backend/bloom/domain/metrics.py | 4 +-
backend/bloom/routers/v1/metrics.py | 29 ++++++++--
backend/bloom/services/metrics.py | 86 +++++++++++++++++++++++------
3 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/backend/bloom/domain/metrics.py b/backend/bloom/domain/metrics.py
index 133c5d75..c7294177 100644
--- a/backend/bloom/domain/metrics.py
+++ b/backend/bloom/domain/metrics.py
@@ -39,10 +39,10 @@ class ResponseMetricsVesselInActivitySchema(BaseModel):
vessel: VesselListView
total_time_at_sea: Optional[timedelta]
-class ResponseMetricsVesselInMpasSchema(BaseModel):
+class ResponseMetricsVesselInZonesSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
vessel: VesselListView
- total_time_in_mpas: Optional[timedelta]
+ total_time_in_zones: Optional[timedelta]
class ResponseMetricsZoneVisitedSchema(BaseModel):
zone: ZoneListView
diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py
index 1b5566bc..b86827e4 100644
--- a/backend/bloom/routers/v1/metrics.py
+++ b/backend/bloom/routers/v1/metrics.py
@@ -125,11 +125,12 @@ async def read_metrics_all_vessels_visiting_time_by_zone(request: Request,
return jsonable_encoder(payload)
-@router.get("/metrics/vessels/activity-in-mpas")
+@router.get("/metrics/vessels-activity/{category}")
# @cache
-async def read_metrics_all_vessels_visiting_time_in_mpas(
+async def read_metrics_all_vessels_visiting_time_in_zones(
request: Request,
datetime_range: DatetimeRangeRequest = Depends(),
+ category: Optional[str] = None,
pagination: PageParams = Depends(),
order: OrderByRequest = Depends(),
key: str = Depends(X_API_KEY_HEADER),
@@ -137,9 +138,29 @@ async def read_metrics_all_vessels_visiting_time_in_mpas(
check_apikey(key)
use_cases = UseCases()
MetricsService = use_cases.metrics_service()
- payload = MetricsService.get_vessels_in_mpas(
+ payload = MetricsService.get_vessels_activity_in_zones(
datetime_range=datetime_range,
pagination=pagination,
- order=order
+ order=order,
+ category=category,
+ )
+ return jsonable_encoder(payload)
+
+
+@router.get("/metrics/zones-visited/{category}")
+# @cache
+async def read_metrics_all_zones_visited(
+ request: Request,
+ datetime_range: DatetimeRangeRequest = Depends(),
+ category: Optional[str] = None,
+ pagination: PageParams = Depends(),
+ order: OrderByRequest = Depends(),
+ key: str = Depends(X_API_KEY_HEADER),
+):
+ check_apikey(key)
+ use_cases = UseCases()
+ MetricsService = use_cases.metrics_service()
+ payload = MetricsService.get_zones_visited(
+ datetime_range=datetime_range, pagination=pagination, order=order, category=category
)
return jsonable_encoder(payload)
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index 6279d94c..b9d11351 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -16,12 +16,14 @@
from bloom.infra.repositories.repository_zone import ZoneRepository
from bloom.domain.metrics import TotalTimeActivityTypeRequest
-from bloom.domain.metrics import (ResponseMetricsVesselInActivitySchema,
- ResponseMetricsZoneVisitedSchema,
- ResponseMetricsVesselInMpasSchema,
- ResponseMetricsZoneVisitingTimeByVesselSchema,
- ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema,
- ResponseMetricsVesselVisitingTimeByZoneSchema)
+from bloom.domain.metrics import (
+ ResponseMetricsVesselInActivitySchema,
+ ResponseMetricsZoneVisitedSchema,
+ ResponseMetricsVesselInZonesSchema,
+ ResponseMetricsZoneVisitingTimeByVesselSchema,
+ ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema,
+ ResponseMetricsVesselVisitingTimeByZoneSchema,
+)
class MetricsService():
def __init__(
@@ -74,17 +76,20 @@ def getVesselsInActivity(self,
)\
for item in payload]
- def get_vessels_in_mpas(self,
- datetime_range: DatetimeRangeRequest,
- pagination: PageParams,
- order: OrderByRequest):
+ def get_vessels_activity_in_zones(
+ self,
+ datetime_range: DatetimeRangeRequest,
+ pagination: PageParams,
+ order: OrderByRequest,
+ category: Optional[str] = None,
+ ):
payload=[]
with self.session_factory() as session:
stmt = (
select(
sql_model.Vessel,
func.sum(sql_model.Metrics.duration_total).label(
- "total_time_in_mpas"
+ "total_time_in_zones"
),
)
.select_from(sql_model.Metrics)
@@ -97,22 +102,71 @@ def get_vessels_in_mpas(self,
datetime_range.start_at, datetime_range.end_at
)
)
- .where(sql_model.Metrics.zone_category == "amp")
.group_by(sql_model.Vessel)
)
stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
+ if category:
+ stmt = stmt.where(sql_model.Zone.category == category)
stmt = (
- stmt.order_by(asc("total_time_in_mpas"))
+ stmt.order_by(asc("total_time_in_zones"))
if order.order == OrderByEnum.ascending
- else stmt.order_by(desc("total_time_in_mpas"))
+ else stmt.order_by(desc("total_time_in_zones"))
)
stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
payload=session.execute(stmt).all()
return [
- ResponseMetricsVesselInMpasSchema(
+ ResponseMetricsVesselInZonesSchema(
vessel=VesselRepository.map_to_domain(item[0]).model_dump(),
- total_time_in_mpas=item[1],
+ total_time_in_zones=item[1],
+ )
+ for item in payload
+ ]
+
+ def get_zones_visited(
+ self,
+ datetime_range: DatetimeRangeRequest,
+ pagination: PageParams,
+ order: OrderByRequest,
+ category: Optional[str] = None,
+ ):
+ payload = []
+ with self.session_factory() as session:
+ stmt = (
+ select(
+ sql_model.Zone,
+ func.sum(sql_model.Metrics.duration_total).label(
+ "visiting_duration"
+ ),
+ )
+ .select_from(sql_model.Metrics)
+ .join(
+ sql_model.Zone,
+ sql_model.Zone.id == sql_model.Metrics.zone_id,
+ )
+ .where(
+ sql_model.Metrics.timestamp.between(
+ datetime_range.start_at, datetime_range.end_at
+ )
+ )
+ .where(sql_model.Metrics.zone_category == category)
+ .group_by(sql_model.Zone)
+ )
+ stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
+ if category:
+ stmt = stmt.where(sql_model.Zone.category == category)
+ stmt = (
+ stmt.order_by(asc("visiting_duration"))
+ if order.order == OrderByEnum.ascending
+ else stmt.order_by(desc("visiting_duration"))
+ )
+ stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
+ payload = session.execute(stmt).all()
+
+ return [
+ ResponseMetricsZoneVisitedSchema(
+ zone=ZoneRepository.map_to_domain(item[0]).model_dump(),
+ visiting_duration=item[1],
)
for item in payload
]
From 5a7b7db064ed30e8fecad5340f9a1d756c218f97 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Mon, 16 Dec 2024 11:51:09 +0100
Subject: [PATCH 04/11] feat: #204 global endpoint for dashboard rankings
---
backend/bloom/routers/v1/metrics.py | 4 ++--
backend/bloom/services/metrics.py | 9 ++++++---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py
index b86827e4..4cd3afbf 100644
--- a/backend/bloom/routers/v1/metrics.py
+++ b/backend/bloom/routers/v1/metrics.py
@@ -125,7 +125,7 @@ async def read_metrics_all_vessels_visiting_time_by_zone(request: Request,
return jsonable_encoder(payload)
-@router.get("/metrics/vessels-activity/{category}")
+@router.get("/metrics/vessels-activity")
# @cache
async def read_metrics_all_vessels_visiting_time_in_zones(
request: Request,
@@ -147,7 +147,7 @@ async def read_metrics_all_vessels_visiting_time_in_zones(
return jsonable_encoder(payload)
-@router.get("/metrics/zones-visited/{category}")
+@router.get("/metrics/zones-visited")
# @cache
async def read_metrics_all_zones_visited(
request: Request,
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index b9d11351..b1909d35 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -87,7 +87,7 @@ def get_vessels_activity_in_zones(
with self.session_factory() as session:
stmt = (
select(
- sql_model.Vessel,
+ sql_model.Vessel,
func.sum(sql_model.Metrics.duration_total).label(
"total_time_in_zones"
),
@@ -95,14 +95,17 @@ def get_vessels_activity_in_zones(
.select_from(sql_model.Metrics)
.join(
sql_model.Vessel,
- sql_model.Vessel.id == sql_model.Metrics.vessel_id,
+ sql_model.Metrics.vessel_id == sql_model.Vessel.id,
+ isouter=True,
)
.where(
sql_model.Metrics.timestamp.between(
datetime_range.start_at, datetime_range.end_at
)
)
- .group_by(sql_model.Vessel)
+ .group_by(
+ sql_model.Vessel
+ )
)
stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
if category:
From 5039e44d7841328b9ab99f87fff35b9e3da573de Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Mon, 16 Dec 2024 11:57:28 +0100
Subject: [PATCH 05/11] migrate endpoints ranking dashboard
---
frontend/libs/mapper.tsx | 2 +-
frontend/services/backend-rest-client.ts | 4 ++--
frontend/types/vessel.ts | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/frontend/libs/mapper.tsx b/frontend/libs/mapper.tsx
index 6778da8f..e0ef0a9e 100644
--- a/frontend/libs/mapper.tsx
+++ b/frontend/libs/mapper.tsx
@@ -11,7 +11,7 @@ export function convertVesselDtoToItem(metrics: VesselMetrics[]): Item[] {
id: `${vessel.id}`,
title: vessel.ship_name,
description: `IMO ${vessel.imo} / MMSI ${vessel.mmsi} / ${vessel.length} m`,
- value: convertDurationToString(vesselMetrics.total_time_in_mpas),
+ value: convertDurationToString(vesselMetrics.total_time_in_zones),
type: "vessel",
countryIso3: vessel.country_iso3,
}
diff --git a/frontend/services/backend-rest-client.ts b/frontend/services/backend-rest-client.ts
index 3aa81fe2..d94e8321 100644
--- a/frontend/services/backend-rest-client.ts
+++ b/frontend/services/backend-rest-client.ts
@@ -88,7 +88,7 @@ export function getTopVesselsInMpas(
endAt: string,
topVesselsLimit: number
) {
- const url = `${BASE_URL}/metrics/vessels/activity-in-mpas?start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
+ const url = `${BASE_URL}/metrics/vessels-activity?category=amp&start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
console.log(`GET ${url}`)
return axios.get(url)
}
@@ -99,7 +99,7 @@ export function getTopZonesVisited(
topZonesLimit: number,
category?: string
) {
- const url = `${BASE_URL}/metrics/zone-visited?${
+ const url = `${BASE_URL}/metrics/zones-visited?${
category ? `category=${category}&` : ""
}start_at=${startAt}&end_at=${endAt}&limit=${topZonesLimit}&order=DESC`
console.log(`GET ${url}`)
diff --git a/frontend/types/vessel.ts b/frontend/types/vessel.ts
index af5933ed..b6e109d9 100644
--- a/frontend/types/vessel.ts
+++ b/frontend/types/vessel.ts
@@ -17,7 +17,7 @@ export type Vessel = {
export type VesselMetrics = {
vessel: VesselDetails
- total_time_in_mpas: string
+ total_time_in_zones: string
}
export type VesselDetails = {
From 21bc9dcb390e84f74ab3fa7d73b792017e63434f Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Fri, 13 Dec 2024 16:43:26 +0100
Subject: [PATCH 06/11] feat #309 migrer endpoint /metrics/activity-in-mpas
---
backend/bloom/domain/metrics.py | 5 +++
backend/bloom/routers/v1/metrics.py | 20 ++++++++++
backend/bloom/services/metrics.py | 62 ++++++++++++++++++++++++-----
3 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/backend/bloom/domain/metrics.py b/backend/bloom/domain/metrics.py
index d59af644..133c5d75 100644
--- a/backend/bloom/domain/metrics.py
+++ b/backend/bloom/domain/metrics.py
@@ -39,6 +39,11 @@ class ResponseMetricsVesselInActivitySchema(BaseModel):
vessel: VesselListView
total_time_at_sea: Optional[timedelta]
+class ResponseMetricsVesselInMpasSchema(BaseModel):
+ model_config = ConfigDict(from_attributes=True)
+ vessel: VesselListView
+ total_time_in_mpas: Optional[timedelta]
+
class ResponseMetricsZoneVisitedSchema(BaseModel):
zone: ZoneListView
visiting_duration: timedelta
diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py
index 0f3e6cdf..1b5566bc 100644
--- a/backend/bloom/routers/v1/metrics.py
+++ b/backend/bloom/routers/v1/metrics.py
@@ -123,3 +123,23 @@ async def read_metrics_all_vessels_visiting_time_by_zone(request: Request,
category=category,
sub_category=sub_category)
return jsonable_encoder(payload)
+
+
+@router.get("/metrics/vessels/activity-in-mpas")
+# @cache
+async def read_metrics_all_vessels_visiting_time_in_mpas(
+ request: Request,
+ datetime_range: DatetimeRangeRequest = Depends(),
+ pagination: PageParams = Depends(),
+ order: OrderByRequest = Depends(),
+ key: str = Depends(X_API_KEY_HEADER),
+):
+ check_apikey(key)
+ use_cases = UseCases()
+ MetricsService = use_cases.metrics_service()
+ payload = MetricsService.get_vessels_in_mpas(
+ datetime_range=datetime_range,
+ pagination=pagination,
+ order=order
+ )
+ return jsonable_encoder(payload)
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index c9a08ef1..6279d94c 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -18,6 +18,7 @@
from bloom.domain.metrics import (ResponseMetricsVesselInActivitySchema,
ResponseMetricsZoneVisitedSchema,
+ ResponseMetricsVesselInMpasSchema,
ResponseMetricsZoneVisitingTimeByVesselSchema,
ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema,
ResponseMetricsVesselVisitingTimeByZoneSchema)
@@ -72,7 +73,50 @@ def getVesselsInActivity(self,
total_time_at_sea=item[1]
)\
for item in payload]
-
+
+ def get_vessels_in_mpas(self,
+ datetime_range: DatetimeRangeRequest,
+ pagination: PageParams,
+ order: OrderByRequest):
+ payload=[]
+ with self.session_factory() as session:
+ stmt = (
+ select(
+ sql_model.Vessel,
+ func.sum(sql_model.Metrics.duration_total).label(
+ "total_time_in_mpas"
+ ),
+ )
+ .select_from(sql_model.Metrics)
+ .join(
+ sql_model.Vessel,
+ sql_model.Vessel.id == sql_model.Metrics.vessel_id,
+ )
+ .where(
+ sql_model.Metrics.timestamp.between(
+ datetime_range.start_at, datetime_range.end_at
+ )
+ )
+ .where(sql_model.Metrics.zone_category == "amp")
+ .group_by(sql_model.Vessel)
+ )
+ stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
+ stmt = (
+ stmt.order_by(asc("total_time_in_mpas"))
+ if order.order == OrderByEnum.ascending
+ else stmt.order_by(desc("total_time_in_mpas"))
+ )
+ stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
+ payload=session.execute(stmt).all()
+
+ return [
+ ResponseMetricsVesselInMpasSchema(
+ vessel=VesselRepository.map_to_domain(item[0]).model_dump(),
+ total_time_in_mpas=item[1],
+ )
+ for item in payload
+ ]
+
def getVesselsAtSea(self,
datetime_range: DatetimeRangeRequest,
):
@@ -95,7 +139,6 @@ def getVesselsAtSea(self,
)
return session.execute(stmt).scalar()
-
def getZoneVisited(self,
datetime_range: DatetimeRangeRequest,
pagination: PageParams,
@@ -137,7 +180,7 @@ def getZoneVisited(self,
visiting_duration=item[1]
)\
for item in payload]
-
+
def getZoneVisitingTimeByVessel(self,
zone_id: int,
datetime_range: DatetimeRangeRequest,
@@ -145,7 +188,7 @@ def getZoneVisitingTimeByVessel(self,
pagination: PageParams,):
payload=[]
with self.session_factory() as session:
-
+
stmt=select(
sql_model.Zone,
sql_model.Vessel,
@@ -166,7 +209,7 @@ def getZoneVisitingTimeByVessel(self,
)
)\
.group_by(sql_model.Zone.id,sql_model.Vessel.id)
-
+
stmt = stmt.order_by(func.sum(sql_model.Segment.segment_duration).asc())\
if order.order == OrderByEnum.ascending \
else stmt.order_by(func.sum(sql_model.Segment.segment_duration).desc())
@@ -185,7 +228,7 @@ def getZoneVisitingTimeByVessel(self,
zone_visiting_time_by_vessel=item[2]
)\
for item in payload]
-
+
def getVesselVisitingTimeByZone(self,
order: OrderByRequest,
datetime_range: DatetimeRangeRequest,
@@ -226,14 +269,12 @@ def getVesselVisitingTimeByZone(self,
stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
if vessel_id is not None:
stmt=stmt.where(sql_model.Vessel.id==vessel_id)
-
-
+
return [ResponseMetricsVesselVisitingTimeByZoneSchema(
vessel=VesselListView(**VesselRepository.map_to_domain(model[0]).model_dump()),
zone=ZoneListView(**ZoneRepository.map_to_domain(model[1]).model_dump()),
vessel_visiting_time_by_zone=model[2]) for model in session.execute(stmt).all()]
-
def getVesselVisitsByActivityType(self,
vessel_id: int,
activity_type: TotalTimeActivityTypeRequest,
@@ -261,10 +302,9 @@ def getVesselVisitsByActivityType(self,
literal_column('0 seconds'),
))
payload=session.execute(stmt.limit(1)).scalar_one_or_none()
-
+
return [ ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema(
vessel_id=item.id,
activity=item.activity,
total_activity_time=item.total_activity_time,
) for item in payload]
-
\ No newline at end of file
From 0b514d0a221b28e1176c63e03c3099031f4fda95 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Fri, 13 Dec 2024 17:06:46 +0100
Subject: [PATCH 07/11] feat #309: migrate getTopVesselsInActivity to
getTopVesselsInMpas (dashboard)
---
frontend/app/dashboard/page.tsx | 26 ++++++++++------
.../dashboard/dashboard-overview.tsx | 31 +++++++++++--------
frontend/libs/mapper.tsx | 2 +-
frontend/services/backend-rest-client.ts | 4 +--
frontend/services/dashboard.service.ts | 20 ++++++------
frontend/types/vessel.ts | 2 +-
6 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx
index 95bd06e8..46457ba5 100644
--- a/frontend/app/dashboard/page.tsx
+++ b/frontend/app/dashboard/page.tsx
@@ -1,11 +1,17 @@
-"use client"
+"use client";
+
+import { useMemo, useState } from "react";
+import { useDashboardData } from "@/services/dashboard.service";
+
+
+
+import { getDateRange } from "@/libs/dateUtils";
+import DashboardHeader from "@/components/dashboard/dashboard-header";
+import DashboardOverview from "@/components/dashboard/dashboard-overview";
+
+
-import { useMemo, useState } from "react"
-import { useDashboardData } from "@/services/dashboard.service"
-import { getDateRange } from "@/libs/dateUtils"
-import DashboardHeader from "@/components/dashboard/dashboard-header"
-import DashboardOverview from "@/components/dashboard/dashboard-overview"
export default function DashboardPage() {
const [selectedDays, setSelectedDays] = useState(7)
@@ -14,7 +20,7 @@ export default function DashboardPage() {
}, [selectedDays])
const {
- topVesselsInActivity,
+ topVesselsInMpas,
topAmpsVisited,
totalVesselsInActivity,
totalAmpsVisited,
@@ -31,7 +37,7 @@ export default function DashboardPage() {
{
setSelectedDays(Number(value))
}}
- topVesselsInActivityLoading={isLoading.topVesselsInActivity}
+ topVesselsInMpasLoading={isLoading.topVesselsInMpas}
topAmpsVisitedLoading={isLoading.topAmpsVisited}
totalVesselsActiveLoading={isLoading.totalVesselsInActivity}
totalAmpsVisitedLoading={isLoading.totalAmpsVisited}
@@ -49,4 +55,4 @@ export default function DashboardPage() {
)
-}
+}
\ No newline at end of file
diff --git a/frontend/components/dashboard/dashboard-overview.tsx b/frontend/components/dashboard/dashboard-overview.tsx
index 7f4c3c1d..ad992733 100644
--- a/frontend/components/dashboard/dashboard-overview.tsx
+++ b/frontend/components/dashboard/dashboard-overview.tsx
@@ -1,16 +1,21 @@
-"use client"
+"use client";
-import { TOTAL_AMPS, TOTAL_VESSELS } from "@/constants/totals.constants"
+import { TOTAL_AMPS, TOTAL_VESSELS } from "@/constants/totals.constants";
-import { Item } from "@/types/item"
-import ListCard from "@/components/ui/list-card"
-import KPICard from "@/components/dashboard/kpi-card"
-import { DateRangeSelector } from "../ui/date-range-selector"
+
+import { Item } from "@/types/item";
+import ListCard from "@/components/ui/list-card";
+import KPICard from "@/components/dashboard/kpi-card";
+
+
+
+import { DateRangeSelector } from "../ui/date-range-selector";
+
type Props = {
- topVesselsInActivity: Item[]
- topVesselsInActivityLoading: boolean
+ topVesselsInMpas: Item[]
+ topVesselsInMpasLoading: boolean
topAmpsVisited: Item[]
topAmpsVisitedLoading: boolean
totalVesselsActive: number
@@ -23,8 +28,8 @@ type Props = {
}
export default function DashboardOverview({
- topVesselsInActivity,
- topVesselsInActivityLoading,
+ topVesselsInMpas,
+ topVesselsInMpasLoading,
topAmpsVisited,
topAmpsVisitedLoading,
totalVesselsActive,
@@ -74,9 +79,9 @@ export default function DashboardOverview({
@@ -84,4 +89,4 @@ export default function DashboardOverview({
)
-}
+}
\ No newline at end of file
diff --git a/frontend/libs/mapper.tsx b/frontend/libs/mapper.tsx
index 9799cb0e..6778da8f 100644
--- a/frontend/libs/mapper.tsx
+++ b/frontend/libs/mapper.tsx
@@ -11,7 +11,7 @@ export function convertVesselDtoToItem(metrics: VesselMetrics[]): Item[] {
id: `${vessel.id}`,
title: vessel.ship_name,
description: `IMO ${vessel.imo} / MMSI ${vessel.mmsi} / ${vessel.length} m`,
- value: convertDurationToString(vesselMetrics.total_time_at_sea),
+ value: convertDurationToString(vesselMetrics.total_time_in_mpas),
type: "vessel",
countryIso3: vessel.country_iso3,
}
diff --git a/frontend/services/backend-rest-client.ts b/frontend/services/backend-rest-client.ts
index 58293271..834ee4d4 100644
--- a/frontend/services/backend-rest-client.ts
+++ b/frontend/services/backend-rest-client.ts
@@ -114,12 +114,12 @@ export async function getVesselFirstExcursionSegments(vesselId: number) {
}
}
-export function getTopVesselsInActivity(
+export function getTopVesselsInMpas(
startAt: string,
endAt: string,
topVesselsLimit: number
) {
- const url = `${BASE_URL}/metrics/vessels-in-activity?start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
+ const url = `${BASE_URL}/metrics/vessels/activity-in-mpas?start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
console.log(`GET ${url}`)
return axios.get(url)
}
diff --git a/frontend/services/dashboard.service.ts b/frontend/services/dashboard.service.ts
index ef74e743..832c5b96 100644
--- a/frontend/services/dashboard.service.ts
+++ b/frontend/services/dashboard.service.ts
@@ -1,6 +1,6 @@
import { TOTAL_VESSELS } from "@/constants/totals.constants"
import {
- getTopVesselsInActivity,
+ getTopVesselsInMpas,
getTopZonesVisited,
getVesselsAtSea,
getVesselsTrackedCount,
@@ -13,13 +13,13 @@ import { convertVesselDtoToItem, convertZoneDtoToItem } from "@/libs/mapper"
const TOP_ITEMS_SIZE = 5
type DashboardData = {
- topVesselsInActivity: any[]
+ topVesselsInMpas: any[]
topAmpsVisited: any[]
totalVesselsInActivity: number
totalAmpsVisited: number
totalVesselsTracked: number
isLoading: {
- topVesselsInActivity: boolean
+ topVesselsInMpas: boolean
topAmpsVisited: boolean
totalVesselsInActivity: boolean
totalAmpsVisited: boolean
@@ -32,13 +32,13 @@ export const useDashboardData = (
endAt: string
): DashboardData => {
const {
- data: topVesselsInActivity = [],
- isLoading: topVesselsInActivityLoading,
+ data: topVesselsInMpas = [],
+ isLoading: topVesselsInMpasLoading,
} = useSWR(
- `topVesselsInActivity-${startAt}`,
+ `topVesselsInMpas-${startAt}`,
async () => {
try {
- const response = await getTopVesselsInActivity(
+ const response = await getTopVesselsInMpas(
startAt,
endAt,
TOP_ITEMS_SIZE
@@ -46,7 +46,7 @@ export const useDashboardData = (
return convertVesselDtoToItem(response?.data || [])
} catch (error) {
console.log(
- "An error occurred while fetching top vessels in activity: " + error
+ "An error occurred while fetching top vessels in MPAs: " + error
)
return []
}
@@ -137,13 +137,13 @@ export const useDashboardData = (
)
return {
- topVesselsInActivity,
+ topVesselsInMpas,
topAmpsVisited,
totalVesselsInActivity,
totalAmpsVisited,
totalVesselsTracked,
isLoading: {
- topVesselsInActivity: topVesselsInActivityLoading,
+ topVesselsInMpas: topVesselsInMpasLoading,
topAmpsVisited: topAmpsVisitedLoading,
totalVesselsInActivity: totalVesselsInActivityLoading,
totalAmpsVisited: totalAmpsVisitedLoading,
diff --git a/frontend/types/vessel.ts b/frontend/types/vessel.ts
index ca4ff5e7..7fe7e09c 100644
--- a/frontend/types/vessel.ts
+++ b/frontend/types/vessel.ts
@@ -19,7 +19,7 @@ export type Vessel = {
export type VesselMetrics = {
vessel: VesselDetails
- total_time_at_sea: string
+ total_time_in_mpas: string
}
export type VesselDetails = {
From c7e924991b7c5e5121cc19786d8773f04c142782 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Fri, 13 Dec 2024 18:40:41 +0100
Subject: [PATCH 08/11] feat #204: global endpoints for dashboard rankings
---
backend/bloom/domain/metrics.py | 4 +-
backend/bloom/routers/v1/metrics.py | 29 ++++++++--
backend/bloom/services/metrics.py | 86 +++++++++++++++++++++++------
3 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/backend/bloom/domain/metrics.py b/backend/bloom/domain/metrics.py
index 133c5d75..c7294177 100644
--- a/backend/bloom/domain/metrics.py
+++ b/backend/bloom/domain/metrics.py
@@ -39,10 +39,10 @@ class ResponseMetricsVesselInActivitySchema(BaseModel):
vessel: VesselListView
total_time_at_sea: Optional[timedelta]
-class ResponseMetricsVesselInMpasSchema(BaseModel):
+class ResponseMetricsVesselInZonesSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
vessel: VesselListView
- total_time_in_mpas: Optional[timedelta]
+ total_time_in_zones: Optional[timedelta]
class ResponseMetricsZoneVisitedSchema(BaseModel):
zone: ZoneListView
diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py
index 1b5566bc..b86827e4 100644
--- a/backend/bloom/routers/v1/metrics.py
+++ b/backend/bloom/routers/v1/metrics.py
@@ -125,11 +125,12 @@ async def read_metrics_all_vessels_visiting_time_by_zone(request: Request,
return jsonable_encoder(payload)
-@router.get("/metrics/vessels/activity-in-mpas")
+@router.get("/metrics/vessels-activity/{category}")
# @cache
-async def read_metrics_all_vessels_visiting_time_in_mpas(
+async def read_metrics_all_vessels_visiting_time_in_zones(
request: Request,
datetime_range: DatetimeRangeRequest = Depends(),
+ category: Optional[str] = None,
pagination: PageParams = Depends(),
order: OrderByRequest = Depends(),
key: str = Depends(X_API_KEY_HEADER),
@@ -137,9 +138,29 @@ async def read_metrics_all_vessels_visiting_time_in_mpas(
check_apikey(key)
use_cases = UseCases()
MetricsService = use_cases.metrics_service()
- payload = MetricsService.get_vessels_in_mpas(
+ payload = MetricsService.get_vessels_activity_in_zones(
datetime_range=datetime_range,
pagination=pagination,
- order=order
+ order=order,
+ category=category,
+ )
+ return jsonable_encoder(payload)
+
+
+@router.get("/metrics/zones-visited/{category}")
+# @cache
+async def read_metrics_all_zones_visited(
+ request: Request,
+ datetime_range: DatetimeRangeRequest = Depends(),
+ category: Optional[str] = None,
+ pagination: PageParams = Depends(),
+ order: OrderByRequest = Depends(),
+ key: str = Depends(X_API_KEY_HEADER),
+):
+ check_apikey(key)
+ use_cases = UseCases()
+ MetricsService = use_cases.metrics_service()
+ payload = MetricsService.get_zones_visited(
+ datetime_range=datetime_range, pagination=pagination, order=order, category=category
)
return jsonable_encoder(payload)
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index 6279d94c..b9d11351 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -16,12 +16,14 @@
from bloom.infra.repositories.repository_zone import ZoneRepository
from bloom.domain.metrics import TotalTimeActivityTypeRequest
-from bloom.domain.metrics import (ResponseMetricsVesselInActivitySchema,
- ResponseMetricsZoneVisitedSchema,
- ResponseMetricsVesselInMpasSchema,
- ResponseMetricsZoneVisitingTimeByVesselSchema,
- ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema,
- ResponseMetricsVesselVisitingTimeByZoneSchema)
+from bloom.domain.metrics import (
+ ResponseMetricsVesselInActivitySchema,
+ ResponseMetricsZoneVisitedSchema,
+ ResponseMetricsVesselInZonesSchema,
+ ResponseMetricsZoneVisitingTimeByVesselSchema,
+ ResponseMetricsVesselTotalTimeActivityByActivityTypeSchema,
+ ResponseMetricsVesselVisitingTimeByZoneSchema,
+)
class MetricsService():
def __init__(
@@ -74,17 +76,20 @@ def getVesselsInActivity(self,
)\
for item in payload]
- def get_vessels_in_mpas(self,
- datetime_range: DatetimeRangeRequest,
- pagination: PageParams,
- order: OrderByRequest):
+ def get_vessels_activity_in_zones(
+ self,
+ datetime_range: DatetimeRangeRequest,
+ pagination: PageParams,
+ order: OrderByRequest,
+ category: Optional[str] = None,
+ ):
payload=[]
with self.session_factory() as session:
stmt = (
select(
sql_model.Vessel,
func.sum(sql_model.Metrics.duration_total).label(
- "total_time_in_mpas"
+ "total_time_in_zones"
),
)
.select_from(sql_model.Metrics)
@@ -97,22 +102,71 @@ def get_vessels_in_mpas(self,
datetime_range.start_at, datetime_range.end_at
)
)
- .where(sql_model.Metrics.zone_category == "amp")
.group_by(sql_model.Vessel)
)
stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
+ if category:
+ stmt = stmt.where(sql_model.Zone.category == category)
stmt = (
- stmt.order_by(asc("total_time_in_mpas"))
+ stmt.order_by(asc("total_time_in_zones"))
if order.order == OrderByEnum.ascending
- else stmt.order_by(desc("total_time_in_mpas"))
+ else stmt.order_by(desc("total_time_in_zones"))
)
stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
payload=session.execute(stmt).all()
return [
- ResponseMetricsVesselInMpasSchema(
+ ResponseMetricsVesselInZonesSchema(
vessel=VesselRepository.map_to_domain(item[0]).model_dump(),
- total_time_in_mpas=item[1],
+ total_time_in_zones=item[1],
+ )
+ for item in payload
+ ]
+
+ def get_zones_visited(
+ self,
+ datetime_range: DatetimeRangeRequest,
+ pagination: PageParams,
+ order: OrderByRequest,
+ category: Optional[str] = None,
+ ):
+ payload = []
+ with self.session_factory() as session:
+ stmt = (
+ select(
+ sql_model.Zone,
+ func.sum(sql_model.Metrics.duration_total).label(
+ "visiting_duration"
+ ),
+ )
+ .select_from(sql_model.Metrics)
+ .join(
+ sql_model.Zone,
+ sql_model.Zone.id == sql_model.Metrics.zone_id,
+ )
+ .where(
+ sql_model.Metrics.timestamp.between(
+ datetime_range.start_at, datetime_range.end_at
+ )
+ )
+ .where(sql_model.Metrics.zone_category == category)
+ .group_by(sql_model.Zone)
+ )
+ stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
+ if category:
+ stmt = stmt.where(sql_model.Zone.category == category)
+ stmt = (
+ stmt.order_by(asc("visiting_duration"))
+ if order.order == OrderByEnum.ascending
+ else stmt.order_by(desc("visiting_duration"))
+ )
+ stmt = stmt.limit(pagination.limit) if pagination.limit != None else stmt
+ payload = session.execute(stmt).all()
+
+ return [
+ ResponseMetricsZoneVisitedSchema(
+ zone=ZoneRepository.map_to_domain(item[0]).model_dump(),
+ visiting_duration=item[1],
)
for item in payload
]
From 183ffa5dc99dcca086c37a414fa8396f201490f0 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Mon, 16 Dec 2024 11:51:09 +0100
Subject: [PATCH 09/11] feat: #204 global endpoint for dashboard rankings
---
backend/bloom/routers/v1/metrics.py | 4 ++--
backend/bloom/services/metrics.py | 9 ++++++---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/backend/bloom/routers/v1/metrics.py b/backend/bloom/routers/v1/metrics.py
index b86827e4..4cd3afbf 100644
--- a/backend/bloom/routers/v1/metrics.py
+++ b/backend/bloom/routers/v1/metrics.py
@@ -125,7 +125,7 @@ async def read_metrics_all_vessels_visiting_time_by_zone(request: Request,
return jsonable_encoder(payload)
-@router.get("/metrics/vessels-activity/{category}")
+@router.get("/metrics/vessels-activity")
# @cache
async def read_metrics_all_vessels_visiting_time_in_zones(
request: Request,
@@ -147,7 +147,7 @@ async def read_metrics_all_vessels_visiting_time_in_zones(
return jsonable_encoder(payload)
-@router.get("/metrics/zones-visited/{category}")
+@router.get("/metrics/zones-visited")
# @cache
async def read_metrics_all_zones_visited(
request: Request,
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index b9d11351..b1909d35 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -87,7 +87,7 @@ def get_vessels_activity_in_zones(
with self.session_factory() as session:
stmt = (
select(
- sql_model.Vessel,
+ sql_model.Vessel,
func.sum(sql_model.Metrics.duration_total).label(
"total_time_in_zones"
),
@@ -95,14 +95,17 @@ def get_vessels_activity_in_zones(
.select_from(sql_model.Metrics)
.join(
sql_model.Vessel,
- sql_model.Vessel.id == sql_model.Metrics.vessel_id,
+ sql_model.Metrics.vessel_id == sql_model.Vessel.id,
+ isouter=True,
)
.where(
sql_model.Metrics.timestamp.between(
datetime_range.start_at, datetime_range.end_at
)
)
- .group_by(sql_model.Vessel)
+ .group_by(
+ sql_model.Vessel
+ )
)
stmt = stmt.offset(pagination.offset) if pagination.offset != None else stmt
if category:
From 32eca8dadfbe56a46fe6b7ed1fc020ee4b579fe6 Mon Sep 17 00:00:00 2001
From: marthevienne <123016211+marthevienne@users.noreply.github.com>
Date: Mon, 16 Dec 2024 11:57:28 +0100
Subject: [PATCH 10/11] migrate endpoints ranking dashboard
---
frontend/libs/mapper.tsx | 2 +-
frontend/services/backend-rest-client.ts | 4 ++--
frontend/types/vessel.ts | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/frontend/libs/mapper.tsx b/frontend/libs/mapper.tsx
index 6778da8f..e0ef0a9e 100644
--- a/frontend/libs/mapper.tsx
+++ b/frontend/libs/mapper.tsx
@@ -11,7 +11,7 @@ export function convertVesselDtoToItem(metrics: VesselMetrics[]): Item[] {
id: `${vessel.id}`,
title: vessel.ship_name,
description: `IMO ${vessel.imo} / MMSI ${vessel.mmsi} / ${vessel.length} m`,
- value: convertDurationToString(vesselMetrics.total_time_in_mpas),
+ value: convertDurationToString(vesselMetrics.total_time_in_zones),
type: "vessel",
countryIso3: vessel.country_iso3,
}
diff --git a/frontend/services/backend-rest-client.ts b/frontend/services/backend-rest-client.ts
index 834ee4d4..fbd05297 100644
--- a/frontend/services/backend-rest-client.ts
+++ b/frontend/services/backend-rest-client.ts
@@ -119,7 +119,7 @@ export function getTopVesselsInMpas(
endAt: string,
topVesselsLimit: number
) {
- const url = `${BASE_URL}/metrics/vessels/activity-in-mpas?start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
+ const url = `${BASE_URL}/metrics/vessels-activity?category=amp&start_at=${startAt}&end_at=${endAt}&limit=${topVesselsLimit}&order=DESC`
console.log(`GET ${url}`)
return axios.get(url)
}
@@ -130,7 +130,7 @@ export function getTopZonesVisited(
topZonesLimit: number,
category?: string
) {
- const url = `${BASE_URL}/metrics/zone-visited?${
+ const url = `${BASE_URL}/metrics/zones-visited?${
category ? `category=${category}&` : ""
}start_at=${startAt}&end_at=${endAt}&limit=${topZonesLimit}&order=DESC`
console.log(`GET ${url}`)
diff --git a/frontend/types/vessel.ts b/frontend/types/vessel.ts
index 7fe7e09c..43153845 100644
--- a/frontend/types/vessel.ts
+++ b/frontend/types/vessel.ts
@@ -19,7 +19,7 @@ export type Vessel = {
export type VesselMetrics = {
vessel: VesselDetails
- total_time_in_mpas: string
+ total_time_in_zones: string
}
export type VesselDetails = {
From cc731b50e4c965aab19b1e27e1cd32f7dc4093ed Mon Sep 17 00:00:00 2001
From: RV
Date: Tue, 17 Dec 2024 20:53:58 +0100
Subject: [PATCH 11/11] fix: request - isouter + join dim_zone (to check)
---
backend/bloom/services/metrics.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/backend/bloom/services/metrics.py b/backend/bloom/services/metrics.py
index b1909d35..7984178a 100644
--- a/backend/bloom/services/metrics.py
+++ b/backend/bloom/services/metrics.py
@@ -95,8 +95,11 @@ def get_vessels_activity_in_zones(
.select_from(sql_model.Metrics)
.join(
sql_model.Vessel,
- sql_model.Metrics.vessel_id == sql_model.Vessel.id,
- isouter=True,
+ sql_model.Metrics.vessel_id == sql_model.Vessel.id
+ )
+ .join(
+ sql_model.Zone,
+ sql_model.Metrics.zone_id == sql_model.Zone.id
)
.where(
sql_model.Metrics.timestamp.between(