Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 149 additions & 5 deletions src/gfwapiclient/resources/vessels/base/models/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

import datetime

from typing import Any, List, Optional
from typing import Any, Iterator, List, Optional, Type, TypeVar, Union

from pydantic import Field

from gfwapiclient.base.models import BaseModel
from gfwapiclient.http.models import ResultItem
from gfwapiclient.http.models import Result, ResultItem


__all__ = ["VesselItem"]
__all__ = ["VesselItem", "VesselResult"]


class ExtraField(BaseModel):
Expand Down Expand Up @@ -284,10 +284,10 @@ class SelfReportedInfo(BaseModel):
Matched fields.

transmission_date_from (Optional[datetime.datetime]):
Transmission date from.
Transmission date from (start).

transmission_date_to (Optional[datetime.datetime]):
Transmission date to.
Transmission date to (end).
"""

id: Optional[str] = Field(None, alias="id")
Expand Down Expand Up @@ -350,3 +350,147 @@ class VesselItem(ResultItem):
self_reported_info: Optional[List[SelfReportedInfo]] = Field(
None, alias="selfReportedInfo"
)

def _iter_matched_self_reported_infos(self) -> Iterator[SelfReportedInfo]:
"""Yields matched AIS self-reported vessel information.

**Note:** A vessel is considered matched when it includes both `registry_info`
and `self_reported_info` (AIS), as this indicates a successful match between
registry data and AIS information.

See how the Vessel API is used in the Vessel Viewer
here: https://globalfishingwatch.org/our-apis/assets/2024_Vessel_Viewer_and_APIs_behind_It.pdf

Yields:
SelfReportedInfo:
Valid matched AIS self-reported vessel information.
"""
if (
self.registry_info_total_records
and self.registry_info_total_records >= 1
and self.self_reported_info
):
yield from self.self_reported_info


_VesselItemT = TypeVar("_VesselItemT", bound=VesselItem)


class VesselResult(Result[_VesselItemT]):
"""Result for the Vessels API endpoints.

This class extends :class:`Result` to provide a specialized result container
for the Vessels API endpoints.
"""

_result_item_class: Type[_VesselItemT]
_data: Union[List[_VesselItemT], _VesselItemT]

def __init__(self, *, data: Union[List[_VesselItemT], _VesselItemT]) -> None:
"""Initializes a new `VesselResult`.

Args:
data (Union[List[_VesselItemT], _VesselItemT]):
The response data from the Vessels API endpoint, which can
be either a single `ResultItem` or a list of `ResultItem` instances.
"""
super().__init__(data=data)

@property
def vessel_ids(self) -> List[str]:
"""Returns matched AIS self-reported vessel identifiers (IDs).

**Note:** A vessel is considered matched when it includes both `registry_info`
and `self_reported_info` (AIS), as this indicates a successful match between
registry data and AIS information.

See how the Vessel API is used in the Vessel Viewer
here: https://globalfishingwatch.org/our-apis/assets/2024_Vessel_Viewer_and_APIs_behind_It.pdf

Returns:
List[str]:
Valid list of matched AIS self-reported vessel identifier (ID).
"""

def extract_vessel_ids(item: _VesselItemT) -> Iterator[Optional[str]]:
for self_reported_info in item._iter_matched_self_reported_infos():
yield self_reported_info.id

mapped_vessel_ids: Iterator[Optional[str]] = self.flat_map(
mapper=extract_vessel_ids
)
matched_vessel_ids: List[str] = list(
{_vessel_id.strip() for _vessel_id in mapped_vessel_ids if _vessel_id}
)

return matched_vessel_ids

@property
def transmission_dates_from(self) -> List[datetime.date]:
"""Returns matched AIS transmission start dates.

**Note:** A vessel is considered matched when it includes both `registry_info`
and `self_reported_info` (AIS), as this indicates a successful match between
registry data and AIS information.

See how the Vessel API is used in the Vessel Viewer
here: https://globalfishingwatch.org/our-apis/assets/2024_Vessel_Viewer_and_APIs_behind_It.pdf

Returns:
List[str]:
Valid list of matched AIS self-reported transmission date from.
"""

def extract_transmission_date_from(
item: _VesselItemT,
) -> Iterator[Optional[datetime.datetime]]:
for self_reported_info in item._iter_matched_self_reported_infos():
yield self_reported_info.transmission_date_from

mapped_transmission_dates_from: Iterator[Optional[datetime.datetime]] = (
self.flat_map(mapper=extract_transmission_date_from)
)
matched_transmission_dates_from: List[datetime.date] = list(
{
_transmission_date_from.date()
for _transmission_date_from in mapped_transmission_dates_from
if _transmission_date_from
}
)

return matched_transmission_dates_from

@property
def transmission_dates_to(self) -> List[datetime.date]:
"""Returns matched AIS transmission end dates.

**Note:** A vessel is considered matched when it includes both `registry_info`
and `self_reported_info` (AIS), as this indicates a successful match between
registry data and AIS information.

See how the Vessel API is used in the Vessel Viewer
here: https://globalfishingwatch.org/our-apis/assets/2024_Vessel_Viewer_and_APIs_behind_It.pdf

Returns:
List[str]:
Valid list of matched AIS self-reported transmission date to.
"""

def extract_transmission_date_to(
item: _VesselItemT,
) -> Iterator[Optional[datetime.datetime]]:
for self_reported_info in item._iter_matched_self_reported_infos():
yield self_reported_info.transmission_date_to

mapped_transmission_dates_to: Iterator[Optional[datetime.datetime]] = (
self.flat_map(mapper=extract_transmission_date_to)
)
matched_transmission_dates_to: List[datetime.date] = list(
{
_transmission_date_to.date()
for _transmission_date_to in mapped_transmission_dates_to
if _transmission_date_to
}
)

return matched_transmission_dates_to
7 changes: 3 additions & 4 deletions src/gfwapiclient/resources/vessels/detail/models/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

from typing import Type

from gfwapiclient.http.models import Result
from gfwapiclient.resources.vessels.base.models.response import VesselItem
from gfwapiclient.resources.vessels.base.models.response import VesselItem, VesselResult


__all__ = ["VesselDetailItem", "VesselDetailResult"]
Expand All @@ -22,10 +21,10 @@ class VesselDetailItem(VesselItem):
pass


class VesselDetailResult(Result[VesselDetailItem]):
class VesselDetailResult(VesselResult[VesselDetailItem]):
"""Result for the get vessel by ID API endpoint.

This class extends :class:`Result` to provide a specialized result container
This class extends :class:`VesselResult` to provide a specialized result container
for the vessel detail endpoint.
"""

Expand Down
7 changes: 3 additions & 4 deletions src/gfwapiclient/resources/vessels/list/models/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

from typing import List, Type

from gfwapiclient.http.models import Result
from gfwapiclient.resources.vessels.base.models.response import VesselItem
from gfwapiclient.resources.vessels.base.models.response import VesselItem, VesselResult


__all__ = ["VesselListItem", "VesselListResult"]
Expand All @@ -22,10 +21,10 @@ class VesselListItem(VesselItem):
pass


class VesselListResult(Result[VesselListItem]):
class VesselListResult(VesselResult[VesselListItem]):
"""Result for the get vessels by IDs API endpoint.

This class extends :class:`Result` to provide a specialized result container
This class extends :class:`VesselResult` to provide a specialized result container
for the vessel list endpoint.
"""

Expand Down
7 changes: 3 additions & 4 deletions src/gfwapiclient/resources/vessels/search/models/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

from typing import List, Type

from gfwapiclient.http.models import Result
from gfwapiclient.resources.vessels.base.models.response import VesselItem
from gfwapiclient.resources.vessels.base.models.response import VesselItem, VesselResult


__all__ = ["VesselSearchItem", "VesselSearchResult"]
Expand All @@ -22,10 +21,10 @@ class VesselSearchItem(VesselItem):
pass


class VesselSearchResult(Result[VesselSearchItem]):
class VesselSearchResult(VesselResult[VesselSearchItem]):
"""Result for the vessels search API endpoint.

This class extends :class:`Result` to provide a specialized result container
This class extends :class:`VesselResult` to provide a specialized result container
for the vessel search endpoint.
"""

Expand Down
1 change: 1 addition & 0 deletions tests/resources/vessels/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for `gfwapiclient.resources.vessels.base`."""
1 change: 1 addition & 0 deletions tests/resources/vessels/base/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for `gfwapiclient.resources.vessels.base.models`."""
Loading
Loading