Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat attribute endpoint #197

Merged
merged 15 commits into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions tests/api/test_cargo_movement.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ def test_convert_to_flat_dict(self):
"vessels.0.imo": 9480980,
"vessels.0.mmsi": 255804460,
"vessels.0.name": "JOHANN ESSBERGER",
"vessels.0.year": None,
"vessels.0.scrubber": None,
"vessels.0.flag": None,
"vessels.0.ice_class": None,
"vessels.0.propulsion": None,
"vessels.0.start_timestamp": "2019-10-18T21:38:34+0000",
"vessels.0.status": "vessel_status_laden_known",
"vessels.0.vessel_class": "tiny_tanker",
Expand Down
5 changes: 5 additions & 0 deletions tests/api/test_vessel_movement.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,11 @@ def test_flatten(self):
"vessel.status": "vessel_status_ballast",
"vessel.vessel_class": "suezmax",
"vessel.voyage_id": None,
"vessel.year": None,
"vessel.flag": None,
"vessel.scrubber": None,
"vessel.ice_class": None,
"vessel.propulsion": None,
}

assert expected == flat
5 changes: 0 additions & 5 deletions tests/endpoints/test_cargo_movements_real.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ def test_search_single_filter_id(self):
assert len(df) == 2

def test_exlusion_filter(self):
crude = [
p.id
for p in Products().search("Crude/Condensates").to_list()
if p.layer == ["group"]
]
arab_medium = [
p.id
for p in Products().search("Arab Medium").to_list()
Expand Down
50 changes: 49 additions & 1 deletion tests/endpoints/test_vessel_movements_real.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime

from tests.testcases import TestCaseUsingRealAPI
from vortexasdk import VesselMovements, Geographies, Corporations
from vortexasdk import VesselMovements, Geographies, Corporations, Attributes
from vortexasdk.endpoints import vessel_movements_result


Expand Down Expand Up @@ -151,3 +151,51 @@ def test_filter_activity(self):

print(df.head())
assert len(df) == 2

def test_age_flag_scrubbers_filters(self):
panama = [
g.id
for g in Geographies().search("panama").to_list()
if "country" in g.layer
]

df = (
VesselMovements()
.search(
filter_vessel_scrubbers="inc",
filter_vessel_age_min=2,
filter_vessel_age_max=15,
filter_vessel_flags=panama,
filter_time_min=datetime(2017, 10, 1),
filter_time_max=datetime(2017, 10, 1),
)
.to_df()
.head(2)
)

print(df.head())
assert len(df) == 2

def test_ice_class_filters(self):
ice_classes = [
g.id for g in Attributes().search(type="ice_class").to_list()
]

propulsion = [
g.id for g in Attributes().search(type="propulsion").to_list()
]

df = (
VesselMovements()
.search(
filter_vessel_ice_class=ice_classes,
filter_vessel_propulsion=propulsion,
filter_time_min=datetime(2017, 10, 1),
filter_time_max=datetime(2017, 10, 1),
)
.to_df()
.head(2)
)

print(df.head())
assert len(df) == 2
36 changes: 36 additions & 0 deletions vortexasdk/api/shared_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,39 @@ class Tag:
tag: str
start_timestamp: Optional[ISODate] = None
end_timestamp: Optional[ISODate] = None


@dataclass(frozen=True)
class Flag:
"""

Represents a property that is associated with a vessel's flag.

- `flag` key will be a Geography Entity ID.
- `flag_country` key will be the ISO code for the country

[Geography Entity Further Documentation](https://docs.vortexa.com/reference/intro-geography-entries)

"""

tag: str
flag: str
flag_country: str


@dataclass(frozen=True)
class Scrubber:
"""

Represents information about scrubbers fitted to a vessel.

- `scrubber` key will be the type of scrubber.
- `planned` key is if this scrubber has not yet been fitted but is planned.

An empty `scrubber` List may mean the scrubber status is unknown or a vessel has none fitted.

"""

tag: str
scrubber: str
planned: bool
20 changes: 19 additions & 1 deletion vortexasdk/api/vessel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
from vortexasdk.api.corporation import CorporateEntity
from vortexasdk.api.id import ID
from vortexasdk.api.serdes import FromDictMixin
from vortexasdk.api.shared_types import IDName, ISODate, Node, Tag
from vortexasdk.api.shared_types import (
IDName,
ISODate,
Node,
Tag,
Scrubber,
Flag,
)


@dataclass(frozen=True,)
Expand Down Expand Up @@ -34,6 +41,11 @@ class Vessel(Node, FromDictMixin):
imo: Optional[int] = None
gross_tonnage: Optional[int] = None

scrubber: Optional[Scrubber] = None
flag: Optional[Flag] = None
ice_class: Optional[str] = None
propulsion: Optional[str] = None


@dataclass(frozen=True)
class VesselEntity(IDName):
Expand All @@ -54,6 +66,7 @@ class VesselEntity(IDName):
corporate_entities: List[CorporateEntity]
tags: List[Tag]
status: str
year: Optional[int] = None

start_timestamp: Optional[ISODate] = None

Expand All @@ -62,3 +75,8 @@ class VesselEntity(IDName):
fixture_fulfilled: Optional[bool] = None
end_timestamp: Optional[ISODate] = None
fixture_id: Optional[str] = None

scrubber: Optional[Scrubber] = None
flag: Optional[Flag] = None
ice_class: Optional[str] = None
propulsion: Optional[str] = None
44 changes: 44 additions & 0 deletions vortexasdk/endpoints/cargo_movements.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,21 @@ def search(
filter_storage_locations: Union[ID, List[ID]] = None,
filter_ship_to_ship_locations: Union[ID, List[ID]] = None,
filter_waypoints: Union[ID, List[ID]] = None,
filter_vessel_age_min: int = None,
filter_vessel_age_max: int = None,
filter_vessel_scrubbers: str = "disabled",
filter_vessel_flags: Union[ID, List[ID]] = None,
filter_vessel_ice_class: Union[ID, List[ID]] = None,
filter_vessel_propulsion: Union[ID, List[ID]] = None,
exclude_origins: Union[ID, List[ID]] = None,
exclude_destinations: Union[ID, List[ID]] = None,
exclude_products: Union[ID, List[ID]] = None,
exclude_vessels: Union[ID, List[ID]] = None,
exclude_charterers: Union[ID, List[ID]] = None,
exclude_owners: Union[ID, List[ID]] = None,
exclude_vessel_flags: Union[ID, List[ID]] = None,
exclude_vessel_ice_class: Union[ID, List[ID]] = None,
exclude_vessel_propulsion: Union[ID, List[ID]] = None,
disable_geographic_exclusion_rules: bool = None,
timeseries_activity_time_span_min: int = None,
timeseries_activity_time_span_max: int = None,
Expand Down Expand Up @@ -84,6 +93,18 @@ def search(

filter_waypoints: A geography ID, or list of geography IDs to filter on.

filter_vessel_age_min: A number between 1 and 100 (representing years).

filter_vessel_age_max: A number between 1 and 100 (representing years).

filter_vessel_scrubbers: Either inactive 'disabled', or included 'inc' or excluded 'exc'.

filter_vessel_flags: A geography ID, or list of geography IDs to filter on.

filter_vessel_ice_class: An attribute ID, or list of attribute IDs to filter on.

filter_vessel_propulsion: An attribute ID, or list of attribute IDs to filter on.

exclude_origins: A geography ID, or list of geography IDs to exclude.

exclude_destinations: A geography ID, or list of geography IDs to exclude.
Expand All @@ -96,6 +117,12 @@ def search(

exclude_owners: An owner ID, or list of owner IDs to exclude.

exclude_vessel_flags: A geography ID, or list of geography IDs to exclude.

exclude_vessel_ice_class: An attribute ID, or list of attribute IDs to exclude.

exclude_vessel_propulsion: An attribute ID, or list of attribute IDs to exclude.

disable_geographic_exclusion_rules: This controls a popular industry term "intra-movements" and determines
the filter behaviour for cargo leaving then entering the same geographic area.

Expand Down Expand Up @@ -183,6 +210,13 @@ def search(
"filter_vessels": convert_to_list(exclude_vessels),
"filter_charterers": convert_to_list(exclude_charterers),
"filter_owners": convert_to_list(exclude_owners),
"filter_vessel_flags": convert_to_list(exclude_vessel_flags),
"filter_vessel_ice_class": convert_to_list(
exclude_vessel_ice_class
),
"filter_vessel_propulsion": convert_to_list(
exclude_vessel_propulsion
),
}

params = {
Expand All @@ -205,6 +239,16 @@ def search(
filter_ship_to_ship_locations
),
"filter_waypoints": convert_to_list(filter_waypoints),
"filter_vessel_age_min": filter_vessel_age_min,
"filter_vessel_age_max": filter_vessel_age_max,
"filter_vessel_scrubbers": filter_vessel_scrubbers,
"filter_vessel_flags": convert_to_list(filter_vessel_flags),
"filter_vessel_ice_class": convert_to_list(
filter_vessel_ice_class
),
"filter_vessel_propulsion": convert_to_list(
filter_vessel_propulsion
),
"exclude": exclude_params,
"disable_geographic_exclusion_rules": disable_geographic_exclusion_rules,
"size": self._MAX_PAGE_RESULT_SIZE,
Expand Down
73 changes: 59 additions & 14 deletions vortexasdk/endpoints/vessel_movements.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime
from typing import List, Union

from vortexasdk.api import ID
from vortexasdk.api.shared_types import to_ISODate
from vortexasdk.endpoints.endpoints import VESSEL_MOVEMENTS_RESOURCE
from vortexasdk.endpoints.vessel_movements_result import VesselMovementsResult
Expand Down Expand Up @@ -36,21 +37,30 @@ def search(
filter_time_max: datetime = datetime(2019, 10, 1, 1),
unit: str = "b",
filter_activity: str = None,
filter_charterers: Union[str, List[str]] = None,
filter_destinations: Union[str, List[str]] = None,
filter_origins: Union[str, List[str]] = None,
filter_owners: Union[str, List[str]] = None,
filter_products: Union[str, List[str]] = None,
filter_vessels: Union[str, List[str]] = None,
filter_vessel_classes: Union[str, List[str]] = None,
filter_charterers: Union[ID, List[ID]] = None,
filter_destinations: Union[ID, List[ID]] = None,
filter_origins: Union[ID, List[ID]] = None,
filter_owners: Union[ID, List[ID]] = None,
filter_products: Union[ID, List[ID]] = None,
filter_vessels: Union[ID, List[ID]] = None,
filter_vessel_classes: Union[ID, List[ID]] = None,
filter_vessel_status: str = None,
exclude_origins: Union[str, List[str]] = None,
exclude_destinations: Union[str, List[str]] = None,
exclude_products: Union[str, List[str]] = None,
exclude_vessels: Union[str, List[str]] = None,
exclude_vessel_classes: Union[str, List[str]] = None,
exclude_charterers: Union[str, List[str]] = None,
exclude_owners: Union[str, List[str]] = None,
filter_vessel_age_min: int = None,
filter_vessel_age_max: int = None,
filter_vessel_scrubbers: str = "disabled",
filter_vessel_flags: Union[ID, List[ID]] = None,
filter_vessel_ice_class: Union[ID, List[ID]] = None,
filter_vessel_propulsion: Union[ID, List[ID]] = None,
exclude_origins: Union[ID, List[ID]] = None,
exclude_destinations: Union[ID, List[ID]] = None,
exclude_products: Union[ID, List[ID]] = None,
exclude_vessels: Union[ID, List[ID]] = None,
exclude_vessel_classes: Union[ID, List[ID]] = None,
exclude_charterers: Union[ID, List[ID]] = None,
exclude_owners: Union[ID, List[ID]] = None,
exclude_vessel_flags: Union[ID, List[ID]] = None,
exclude_vessel_ice_class: Union[ID, List[ID]] = None,
exclude_vessel_propulsion: Union[ID, List[ID]] = None,
) -> VesselMovementsResult:
"""
Find VesselMovements matching the given search parameters.
Expand Down Expand Up @@ -83,6 +93,18 @@ def search(

filter_vessel_status: The vessel status on which to base the filter. Enter 'vessel_status_ballast' for ballast vessels, 'vessel_status_laden_known' for laden vessels with known cargo (i.e. a type of cargo that Vortexa currently tracks) or 'vessel_status_laden_unknown' for laden vessels with unknown cargo (i.e. a type of cargo that Vortexa currently does not track).

filter_vessel_age_min: A number between 1 and 100 (representing years).

filter_vessel_age_max: A number between 1 and 100 (representing years).

filter_vessel_scrubbers: Either inactive 'disabled', or included 'inc' or excluded 'exc'.

filter_vessel_flags: A geography ID, or list of geography IDs to filter on.

filter_vessel_ice_class: An attribute ID, or list of attribute IDs to filter on.

filter_vessel_propulsion: An attribute ID, or list of attribute IDs to filter on.

exclude_origins: A geography ID, or list of geography IDs to exclude.

exclude_destinations: A geography ID, or list of geography IDs to exclude.
Expand All @@ -97,6 +119,12 @@ def search(

exclude_owners: An owner ID, or list of owner IDs to exclude.

exclude_vessel_flags: A geography ID, or list of geography IDs to exclude.

exclude_vessel_ice_class: An attribute ID, or list of attribute IDs to exclude.

exclude_vessel_propulsion: An attribute ID, or list of attribute IDs to exclude.


# Returns
`VesselMovementsResult`, containing all the vessel movements matching the given search terms.
Expand Down Expand Up @@ -132,6 +160,13 @@ def search(
"filter_vessel_classes": convert_to_list(exclude_vessel_classes),
"filter_charterers": convert_to_list(exclude_charterers),
"filter_owners": convert_to_list(exclude_owners),
"filter_vessel_flags": convert_to_list(exclude_vessel_flags),
"filter_vessel_ice_class": convert_to_list(
exclude_vessel_ice_class
),
"filter_vessel_propulsion": convert_to_list(
exclude_vessel_propulsion
),
}

params = {
Expand All @@ -147,6 +182,16 @@ def search(
"filter_vessels": convert_to_list(filter_vessels),
"filter_vessel_classes": convert_to_list(filter_vessel_classes),
"filter_vessel_status": filter_vessel_status,
"filter_vessel_age_min": filter_vessel_age_min,
"filter_vessel_age_max": filter_vessel_age_max,
"filter_vessel_scrubbers": filter_vessel_scrubbers,
"filter_vessel_flags": convert_to_list(filter_vessel_flags),
"filter_vessel_ice_class": convert_to_list(
filter_vessel_ice_class
),
"filter_vessel_propulsion": convert_to_list(
filter_vessel_propulsion
),
"exclude": exclude_params,
"size": self._MAX_PAGE_RESULT_SIZE,
}
Expand Down