Skip to content

Commit

Permalink
feat: add endpoints vessels/excursions/segments & vessels/positions/last
Browse files Browse the repository at this point in the history
  • Loading branch information
herve.le-bars committed Jun 4, 2024
1 parent 742560d commit 5af3139
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 18 deletions.
9 changes: 7 additions & 2 deletions backend/bloom/domain/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
from typing import Union

from pydantic import BaseModel, ConfigDict
from shapely import Point
from shapely import Point,Geometry
from shapely.geometry import mapping, shape


class Segment(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
model_config = ConfigDict(arbitrary_types_allowed=True,
json_encoders = {
Geometry: lambda geometry: mapping(geometry),
},
)
id: Union[int, None] = None
excursion_id: int
timestamp_start: Union[datetime, None] = None
Expand Down
23 changes: 23 additions & 0 deletions backend/bloom/domain/vessel_last_position.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from datetime import datetime

from pydantic import BaseModel, ConfigDict
from shapely import Geometry, Point
from shapely.geometry import mapping, shape
from bloom.domain.vessel import Vessel
from bloom.domain.port import Port

from typing import Union


class VesselLastPosition(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True,
json_encoders = {
Geometry: lambda p: mapping(p),
},)
vessel: Vessel = None
excursion_id: Union[int, None] = None
position: Union[Point, None] = None
timestamp: Union[datetime, None] = None
heading: Union[float, None] = None
speed: Union[float, None] = None
arrival_port: Union[Port, None] = None
8 changes: 6 additions & 2 deletions backend/bloom/domain/vessel_position.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from datetime import datetime

from pydantic import BaseModel, ConfigDict
from shapely import Point
from shapely import Geometry, Point
from shapely.geometry import mapping, shape

from typing import Union


class VesselPosition(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
model_config = ConfigDict(arbitrary_types_allowed=True,
json_encoders = {
Geometry: lambda p: mapping(p),
},)

id: Union[int, None] = None
timestamp: datetime
Expand Down
21 changes: 20 additions & 1 deletion backend/bloom/infra/repositories/repository_excursion.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from contextlib import AbstractContextManager
from typing import Union
from typing import Any, List, Union

import pandas as pd
from dependency_injector.providers import Callable
Expand Down Expand Up @@ -34,6 +34,25 @@ def get_param_from_last_excursion(self, session: Session, vessel_id: int) -> Uni
return None
return {"arrival_port_id": result.arrival_port_id, "arrival_position": result.arrival_position}

def get_excursions_by_vessel_id(self, session: Session, vessel_id: int) -> List[Excursion]:
"""Recheche l'excursion en cours d'un bateau, c'est-à-dire l'excursion qui n'a pas de date d'arrivée"""
stmt = select(sql_model.Excursion).where(sql_model.Excursion.vessel_id == vessel_id)
result = session.execute(stmt).scalars()
if result is None:
return []
return [ ExcursionRepository.map_to_domain(r) for r in result]

def get_vessel_excursion_by_id(self, session: Session, vessel_id: int, excursion_id:int) -> Union[Excursion,None]:
"""Recheche l'excursion en cours d'un bateau, c'est-à-dire l'excursion qui n'a pas de date d'arrivée"""
stmt = select(sql_model.Excursion).where( (sql_model.Excursion.vessel_id == vessel_id )
& (sql_model.Excursion.id == excursion_id ))
result = session.execute(stmt)
if result is None:
return None
return [ ExcursionRepository.map_to_domain(r) for r in result.scalars()][0]



def get_excursion_by_id(self, session: Session, id: int) -> Union[Excursion, None]:
"""Recheche l'excursion en cours d'un bateau, c'est-à-dire l'excursion qui n'a pas de date d'arrivée"""
sql = select(sql_model.Excursion).where(sql_model.Excursion.id == id)
Expand Down
94 changes: 92 additions & 2 deletions backend/bloom/infra/repositories/repository_segment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from contextlib import AbstractContextManager
from datetime import datetime
from typing import Any, List, Union

import pandas as pd
from dependency_injector.providers import Callable
Expand All @@ -10,9 +11,11 @@
from sqlalchemy.orm import Session

from bloom.domain.segment import Segment
from bloom.domain.vessel_last_position import VesselLastPosition
from bloom.domain.zone import Zone
from bloom.infra.database import sql_model
from bloom.infra.repositories.repository_zone import ZoneRepository
from bloom.infra.repositories.repository_vessel import VesselRepository


class SegmentRepository:
Expand All @@ -35,9 +38,96 @@ def get_segments_by_excursions(self, session: Session, id: int) -> pd.DataFrame:
df = pd.DataFrame(q, columns=["segment_duration", "in_amp_zone", "in_territorial_waters", "in_costal_waters"])
return df

def get_all_vessels_last_position(self, session: Session) -> List[Segment]:
stmt = select(
sql_model.Vessel,
sql_model.Segment.excursion_id,
sql_model.Segment.end_position,
sql_model.Segment.timestamp_end,
sql_model.Segment.heading_at_end,
sql_model.Segment.speed_at_end,
sql_model.Excursion.arrival_port_id
).join(
sql_model.Vessel,
sql_model.Excursion.vessel_id == sql_model.Vessel.id
).filter(
sql_model.Segment.last_vessel_segment == True
)
result = session.execute(stmt)
if result is not None :
return [VesselLastPosition(
vessel=VesselRepository.map_to_domain(record[0]),
excursion_id=record[1],
position=to_shape(record[2]),
timestamp=record[3],
heading=record[4],
speed=record[5],
arrival=record[6],
) for record in result]
else:
return []

def get_vessel_last_position(self, session: Session,vessel_id:int) -> List[Segment]:
stmt = select(
sql_model.Vessel,
sql_model.Segment.excursion_id,
sql_model.Segment.end_position,
sql_model.Segment.timestamp_end,
sql_model.Segment.heading_at_end,
sql_model.Segment.speed_at_end,
sql_model.Excursion.arrival_port_id
).join(
sql_model.Vessel,
sql_model.Excursion.vessel_id == sql_model.Vessel.id
).filter(
sql_model.Segment.last_vessel_segment == True,
sql_model.Vessel.id == vessel_id,
)
result = session.execute(stmt)
if result is not None:
return [VesselLastPosition(
vessel=VesselRepository.map_to_domain(record[0]),
excursion_id=record[1],
position=to_shape(record[2]),
timestamp=record[3],
heading=record[4],
speed=record[5],
arrival_port_id=record[6],
) for record in result][0]
else:
return None

def list_vessel_excursion_segments(self,session,vessel_id:int,excursions_id: int) -> List[Segment]:
stmt = select(
sql_model.Segment
).join(
sql_model.Excursion,
sql_model.Segment.excursion_id == sql_model.Excursion.id
).where( sql_model.Segment.excursion_id == excursions_id,
sql_model.Excursion.vessel_id == vessel_id)
result = session.execute(stmt)
if result is not None :
return [ SegmentRepository.map_to_domain(record) for record in result.scalars()]
else:
return []

def get_vessel_excursion_segment_by_id(self,session,vessel_id:int,excursions_id: int, segment_id:int) -> Union[Segment,None]:
stmt = select(
sql_model.Segment
).join(
sql_model.Excursion,
sql_model.Segment.excursion_id == sql_model.Excursion.id
).where( sql_model.Segment.excursion_id == excursions_id,
sql_model.Excursion.vessel_id == vessel_id,
sql_model.Segment.id == segment_id)
result = session.execute(stmt)
if result is not None :
return [ SegmentRepository.map_to_domain(record) for record in result.scalars()][0]
else:
return []

def get_last_vessel_id_segments(self, session: Session) -> pd.DataFrame:
stmt = select(
sql_model.Vessel.id,
sql_model.Segment.excursion_id,
sql_model.Segment.end_position,
sql_model.Segment.timestamp_end,
Expand Down Expand Up @@ -154,4 +244,4 @@ def map_to_orm(segment: Segment) -> sql_model.Segment:
last_vessel_segment=segment.last_vessel_segment,
created_at=segment.created_at,
updated_at=segment.updated_at
)
)
27 changes: 27 additions & 0 deletions backend/bloom/infra/repositories/repository_vessel_position.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime
from typing import Any, List, Union

import pandas as pd
from dependency_injector.providers import Callable
Expand All @@ -10,6 +11,8 @@
from bloom.domain.vessel_position import VesselPosition
from bloom.infra.database import sql_model

from bloom.logger import logger


class VesselPositionRepository:
def __init__(self, session_factory: Callable) -> None:
Expand All @@ -26,6 +29,30 @@ def batch_create_vessel_position(self, session: Session, vessel_positions: list[
session.add_all(orm_list)
return [VesselPositionRepository.map_to_domain(orm) for orm in orm_list]

def get_all_vessel_last_positions(self, session: Session) -> List[VesselPosition]:

stmt=select(sql_model.VesselPosition)\
.order_by(sql_model.VesselPosition.timestamp.desc())\
.group_by(sql_model.VesselPosition.vessel_id)
result = session.execute(stmt).scalars()
#logger.info(type(result))
if result is not None :
return [VesselPositionRepository.map_to_domain(record) for record in result]
else:
return []

def get_vessel_positions(self, session: Session, vessel_id:int,
start:datetime=datetime.now(),
end:datetime=None) -> List[VesselPosition]:

stmt=select(sql_model.VesselPosition).filter_by(vessel_id=vessel_id).order_by(sql_model.VesselPosition.timestamp.desc())
result = session.execute(stmt).scalars()
#logger.info(type(result))
if result is not None :
return [VesselPositionRepository.map_to_domain(record) for record in result]
else:
return []

def get_positions_with_vessel_created_updated_after(self, session: Session,
created_updated_after: datetime) -> pd.DataFrame:
stmt = select(sql_model.VesselPosition.id, sql_model.VesselPosition.timestamp,
Expand Down
55 changes: 44 additions & 11 deletions backend/bloom/services/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

import redis
import json
from datetime import datetime

from bloom.config import settings
from bloom.container import UseCases
from bloom.domain.vessel import Vessel

rd = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0)

from datetime import datetime


app = FastAPI()

@app.get("/vessels")
Expand Down Expand Up @@ -38,31 +39,55 @@ async def get_vessel(vessel_id: int):
with db.session() as session:
return vessel_repository.get_vessel_by_id(session,vessel_id)

@app.get("/vessels/{vessel_id}/positions")
async def list_vessel_positions(vessel_id: int, start: datetime = datetime.now(), end:datetime=None):
return {"positions": ["TODO"]}
@app.get("/vessels/all/positions/last")
async def list_all_vessel_last_position():
use_cases = UseCases()
segment_repository = use_cases.segment_repository()
db = use_cases.db()
with db.session() as session:
return segment_repository.get_all_vessels_last_position(session)

@app.get("/vessels/{vessel_id}/positions/last")
async def list_vessel_position_last(vessel_id: int, date:datetime = datetime.now()):
return {"position": ["TODO"]}
async def get_vessel_last_position(vessel_id: int):
use_cases = UseCases()
segment_repository = use_cases.segment_repository()
db = use_cases.db()
with db.session() as session:
return segment_repository.get_vessel_last_position(session,vessel_id)

@app.get("/vessels/{vessel_id}/excursions")
async def list_vessel_excursions(vessel_id: int):
return {"excursions": ["TODO"]}
use_cases = UseCases()
excursion_repository = use_cases.excursion_repository()
db = use_cases.db()
with db.session() as session:
return excursion_repository.get_excursions_by_vessel_id(session,vessel_id)


@app.get("/vessels/{vessel_id}/excursions/{excursions_id}")
async def get_vessel_excursion(vessel_id: int,excursions_id: int):
return {"excursion": "TODO"}
use_cases = UseCases()
excursion_repository = use_cases.excursion_repository()
db = use_cases.db()
with db.session() as session:
return excursion_repository.get_vessel_excursion_by_id(session,vessel_id,excursions_id)


@app.get("/vessels/{vessel_id}/excursions/{excursions_id}/segments")
async def list_vessel_excursion_segments(vessel_id: int,excursions_id: int):
return {"segments": ["TODO"]}
use_cases = UseCases()
segment_repository = use_cases.segment_repository()
db = use_cases.db()
with db.session() as session:
return segment_repository.list_vessel_excursion_segments(session,vessel_id,excursions_id)

@app.get("/vessels/{vessel_id}/excursions/{excursions_id}/segments/{segment_id}")
async def get_vessel_excursion_segment(vessel_id: int,excursions_id: int, segment_id:int):
return {"segment": "TODO"}
use_cases = UseCases()
segment_repository = use_cases.segment_repository()
db = use_cases.db()
with db.session() as session:
return segment_repository.get_vessel_excursion_segment_by_id(session,vessel_id,excursions_id,segment_id)

@app.get("/ports")
async def list_ports(request:Request):
Expand All @@ -89,6 +114,14 @@ async def get_port(port_id:int):
with db.session() as session:
return port_repository.get_port_by_id(session,port_id)

@app.get("/vessels/all/positions/last")
async def list_vessel_positions(vessel_id: int, date:datetime=datetime.now()):
use_cases = UseCases()
vessel_position_repository = use_cases.vessel_position_repository()
db = use_cases.db()
with db.session() as session:
return vessel_position_repository.get_vessel_positions(session,vessel_id)

@app.get("/zones")
async def list_zones():
cache= rd.get(app.url_path_for('list_zones'))
Expand Down

0 comments on commit 5af3139

Please sign in to comment.