diff --git a/.travis.yml b/.travis.yml index 90e0767..eb7b70e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,9 @@ language: python python: - "3.6" -branches: - only: - - master +#branches: +# only: +# - master script: - pip install . diff --git a/README.md b/README.md index e44b6ce..1826dbc 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,60 @@ for vessel in vessels.models: vessel.max_speed ``` +

[PS03] Vessel Positions of a Dynamic Fleet

+ +```python +vessels = api.dynamic_fleet_vessel_positions(time_span=10) + +for vessel in vessels.models: + vessel.mmsi + vessel.imo + vessel.ship_id + vessel.longitude + vessel.latitude + vessel.speed + vessel.heading + vessel.status + vessel.course + vessel.timestamp + vessel.dsrc + vessel.utc_seconds + vessel.ship_name + vessel.ship_type + vessel.call_sign + vessel.flag + vessel.length + vessel.width + vessel.grt + vessel.dwt + vessel.draught + vessel.year_built + vessel.rot + vessel.type_name + vessel.ais_type_summary + vessel.destination + vessel.eta + vessel.current_port + vessel.last_port + vessel.last_port_time + vessel.current_port_id + vessel.current_port_unlocode + vessel.current_port_country + vessel.last_port_id + vessel.last_port_unlocode + vessel.last_port_country + vessel.next_port_id + vessel.next_port_unlocode + vessel.next_port_name + vessel.next_port_country + vessel.eta_calc + vessel.eta_updated + vessel.distance_to_go + vessel.distance_travelled + vessel.awg_speed + vessel.max_speed +``` +

Voyage Info

[VI03] Port Distance and Routes

diff --git a/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/__init__.py b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/models.py b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/models.py new file mode 100644 index 0000000..2250252 --- /dev/null +++ b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/models.py @@ -0,0 +1,186 @@ +from marinetrafficapi.models import Model +from marinetrafficapi.fields import NumberField, RealNumberField, DatetimeField, TextField + + +class DynamicFleetVesselPosition(Model): + """Get positional information for a set of predefined vessels.""" + + mmsi = NumberField(index='MMSI', + desc="Maritime Mobile Service Identity - a nine-digit number " + "sent in digital form over a radio frequency that identifies " + "the vessel's transmitter station") + + imo = NumberField(index='IMO', + desc="International Maritime Organisation number - a " + "seven-digit number that uniquely identifies vessels") + + ship_id = NumberField(index='SHIP_ID', + desc="A uniquely assigned ID by MarineTraffic " + "for the subject vessel") + + longitude = RealNumberField(index='LON', + desc="A geographic coordinate that specifies the " + "east-west position of the vessel on the " + "Earth's surface") + + latitude = RealNumberField(index='LAT', + desc="a geographic coordinate that specifies the " + "north-south position of the vessel on the " + "Earth's surface") + + speed = NumberField(index='SPEED', + desc="The speed (in knots x10) that the subject vessel is " + "reporting according to AIS transmissions") + + heading = NumberField(index='HEADING', + desc="The heading (in degrees) that the subject vessel is " + "reporting according to AIS transmissions") + + status = NumberField(index='STATUS', + desc="The AIS Navigational Status of the subject vessel as " + "input by the vessel's crew - more. There might be " + "discrepancies with the vessel's detail page when vessel " + "speed is near zero (0) knots.") + + course = NumberField(index='COURSE', + desc="The course (in degrees) that the subject vessel is " + "reporting according to AIS transmissions") + + timestamp = DatetimeField(index='TIMESTAMP', + desc="The date and time (in UTC) that the subject vessel's " + "position was recorded by MarineTraffic", + format='%Y-%m-%dT%H:%M:%S') + + dsrc = TextField(index='DSRC', + desc="Data Source - Defines whether the transmitted AIS data was " + "received by a Terrestrial or a Satellite AIS Station") + + utc_seconds = NumberField(index='UTC_SECONDS', + desc="The time slot that the subject vessel uses " + "to transmit information") + + ship_name = TextField(index='SHIPNAME', + desc="The Shipname of the subject vessel") + + ship_type = NumberField(index='SHIPTYPE', + desc="The Shiptype of the subject vessel " + "according to AIS transmissions") + + call_sign = TextField(index='CALLSIGN', + desc="A uniquely designated identifier for " + "the vessel's transmitter station") + + flag = TextField(index='FLAG', + desc="The flag of the subject vessel according to AIS transmissions") + + length = RealNumberField(index='LENGTH', + desc="The overall Length (in metres) of the subject vessel") + + width = RealNumberField(index='WIDTH', + desc="The Breadth (in metres) of the subject vessel") + + grt = NumberField(index='GRT', + desc="Gross Tonnage - unitless measure that calculates the " + "moulded volume of all enclosed spaces of a ship") + + dwt = NumberField(index='DWT', + desc="Deadweight - a measure (in metric tons) of how much weight a " + "vessel can safely carry (excluding the vessel's own weight") + + draught = NumberField(index='DRAUGHT', + desc="The Draught (in metres x10) of the subject vessel " + "according to the AIS transmissions") + + year_built = NumberField(index='YEAR_BUILT', + desc="The year that the subject vessel was built") + + rot = NumberField(index='ROT', + desc="Rate of Turn") + + type_name = TextField(index='TYPE_NAME', + desc="The Type of the subject vessel") + + ais_type_summary = TextField(index='AIS_TYPE_SUMMARY', + desc="Further explanation of the SHIPTYPE ID") + + destination = TextField(index='DESTINATION', + desc="The Destination of the subject vessel " + "according to the AIS transmissions") + + eta = DatetimeField(index='ETA', + desc="The Estimated Time of Arrival to Destination of the " + "subject vessel according to the AIS transmissions", + format='%Y-%m-%dT%H:%M:%S') + + current_port = TextField(index='CURRENT_PORT', + desc="The name of the Port the subject vessel is " + "currently in (NULL if the vessel is underway)") + + last_port = TextField(index='LAST_PORT', + desc="The Name of the Last Port the vessel has visited") + + last_port_time = DatetimeField(index='LAST_PORT_TIME', + desc="The Date and Time (in UTC) that the " + "subject vessel departed from the Last Port", + format='%Y-%m-%dT%H:%M:%S') + + current_port_id = NumberField(index='CURRENT_PORT_ID', + desc="A uniquely assigned ID by " + "MarineTraffic for the Current Port") + + current_port_unlocode = TextField(index='CURRENT_PORT_UNLOCODE', + desc="A uniquely assigned ID by " + "United Nations for the Current Port") + + current_port_country = TextField(index='CURRENT_PORT_COUNTRY', + desc="The Country that the Current Port is located at") + + last_port_id = NumberField(index='LAST_PORT_ID', + desc="A uniquely assigned ID by MarineTraffic for the Last Port") + + last_port_unlocode = TextField(index='LAST_PORT_UNLOCODE', + desc="A uniquely assigned ID by " + "United Nations for the Last Port") + + last_port_country = TextField(index='LAST_PORT_COUNTRY', + desc="The Country that the Last Port is located at") + + next_port_id = NumberField(index='NEXT_PORT_ID', + desc="A uniquely assigned ID by MarineTraffic for the Next Port") + + next_port_unlocode = TextField(index='NEXT_PORT_UNLOCODE', + desc="A uniquely assigned ID by " + "United Nations for the Next Port") + + next_port_name = TextField(index='NEXT_PORT_NAME', + desc="The Name of the Next Port as derived by MarineTraffic " + "based on the subject vessel's reported Destination") + + next_port_country = TextField(index='NEXT_PORT_COUNTRY', + desc="The Country that the Next Port is located at") + + eta_calc = DatetimeField(index='ETA_CALC', + desc="The Estimated Time of Arrival to Destination of " + "the subject vessel according to the MarineTraffic calculations", + format='%Y-%m-%dT%H:%M:%S') + + eta_updated = DatetimeField(index='ETA_UPDATED', + desc="The date and time (in UTC) that the " + "ETA was calculated by MarineTraffic", + format='%Y-%m-%dT%H:%M:%S') + + distance_to_go = NumberField(index='DISTANCE_TO_GO', + desc="The Remaining Distance (in NM) for the subject " + "vessel to reach the reported Destination") + + distance_travelled = NumberField(index='DISTANCE_TRAVELLED', + desc="The Distance (in NM) that the subject " + "vessel has travelled since departing from Last Port") + + awg_speed = RealNumberField(index='AVG_SPEED', + desc="The average speed calculated for the subject " + "vessel during the latest voyage (port to port)") + + max_speed = RealNumberField(index='MAX_SPEED', + desc="The maximum speed reported by the subject " + "vessel during the latest voyage (port to port)") diff --git a/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/query_params.py b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/query_params.py new file mode 100644 index 0000000..9569531 --- /dev/null +++ b/marinetrafficapi/vessels_positions/PS03_vessel_position_of_a_dynamic_fleet/query_params.py @@ -0,0 +1,16 @@ +from marinetrafficapi.query_params import QueryParams + + +class PS03QueryParams(QueryParams): + """Query params params for PS02 API call.""" + + params = { + # The maximum age, in minutes, of the returned positions. + # Maximum value for terrestrial coverage is 60. + # Maximum value for satellite coverage is 180. + 'time_span': 'timespan', + + # Data filter: Vessel type. + # (2=Fishing / 4=High Speed Craft / 6=Passenger / 7=Cargo / 8=Tanker) + 'ship_type': 'shiptype', + } diff --git a/marinetrafficapi/vessels_positions/client.py b/marinetrafficapi/vessels_positions/client.py index e67845f..95fb62d 100644 --- a/marinetrafficapi/vessels_positions/client.py +++ b/marinetrafficapi/vessels_positions/client.py @@ -10,6 +10,11 @@ from marinetrafficapi.vessels_positions.\ PS02_vessel_positions_of_a_static_fleet.query_params import PS02QueryParams +from marinetrafficapi.vessels_positions.\ + PS03_vessel_position_of_a_dynamic_fleet.query_params import PS03QueryParams +from marinetrafficapi.vessels_positions.\ + PS03_vessel_position_of_a_dynamic_fleet.models import DynamicFleetVesselPosition + class VesselPositions: """Retrieve forecasted information for any vessel. @@ -38,3 +43,15 @@ class VesselPositions: 'protocol': 'jsono' } ) + + # PS03 - Monitor vessel activity for your MarineTraffic fleet(s) + dynamic_fleet_vessel_positions = bind_request( + api_path='/exportvessels', + model=DynamicFleetVesselPosition, + query_parameters=PS03QueryParams, + default_parameters={ + 'v': '8', + 'msgtype': 'simple', + 'protocol': 'jsono' + } + ) diff --git a/setup.py b/setup.py index ade9547..d89c09e 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ def readme(): setup( name='Marine Traffic API', - version='0.3.0', + version='0.4.0', description='Marine Traffic Client Api', long_description=readme(), @@ -27,7 +27,7 @@ def readme(): 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3.7', ], - keywords='marine traffic, api, cruise, distance, port', + keywords='marine traffic, api, cruise, distance, port, vessel, track, fleet', packages=find_packages(), install_requires=[ diff --git a/tests/responses/ps03_response.json b/tests/responses/ps03_response.json new file mode 100644 index 0000000..b2e2c00 --- /dev/null +++ b/tests/responses/ps03_response.json @@ -0,0 +1,146 @@ +[ + { + "MMSI":"304010417", + "IMO":"9015462", + "SHIP_ID":"359396", + "LAT":"47.758499", + "LON":"-5.154223", + "SPEED":"74", + "HEADING":"329", + "COURSE":"327", + "STATUS":"0", + "TIMESTAMP":"2017-05-19T09:39:57", + "DSRC":"TER", + "UTC_SECONDS":"54", + "SHIPNAME":"DORNUM", + "SHIPTYPE":"70", + "CALLSIGN":"V2OZ", + "FLAG":"AG", + "LENGTH":"81.7900009", + "WIDTH":"11.3000002", + "GRT":"1662", + "DWT":"2369", + "DRAUGHT":"44", + "YEAR_BUILT":"1993", + "ROT":"6", + "TYPE_NAME":"General Cargo", + "AIS_TYPE_SUMMARY":"Cargo", + "DESTINATION":"GREENORE", + "ETA":"2017-05-20T08:00:00", + "CURRENT_PORT":"", + "LAST_PORT":"BILBAO ANCH", + "LAST_PORT_TIME":"2017-05-16T15:37:00", + "CURRENT_PORT_ID":"", + "CURRENT_PORT_UNLOCODE":"", + "CURRENT_PORT_COUNTRY":"", + "LAST_PORT_ID":"20648", + "LAST_PORT_UNLOCODE":"", + "LAST_PORT_COUNTRY":"ES", + "NEXT_PORT_ID":"251", + "NEXT_PORT_UNLOCODE":"IEGRN", + "NEXT_PORT_NAME":"GREENORE", + "NEXT_PORT_COUNTRY":"IE", + "ETA_CALC":"2017-05-21T09:55:00", + "ETA_UPDATED":"2017-05-19T09:07:00", + "DISTANCE_TO_GO":"407", + "DISTANCE_TRAVELLED":"364", + "AVG_SPEED":"8.5", + "MAX_SPEED":"8.80000019" + }, + { + "MMSI":"215819000", + "IMO":"9034731", + "SHIP_ID":"150559", + "LAT":"47.926899", + "LON":"-5.531450", + "SPEED":"122", + "HEADING":"162", + "COURSE":"157", + "STATUS":"0", + "TIMESTAMP":"2017-05-19T09:44:27", + "DSRC":"TER", + "UTC_SECONDS":"28", + "SHIPNAME":"TOUR MARGAUX", + "SHIPTYPE":"81", + "CALLSIGN":"9HBW8", + "FLAG":"MT", + "LENGTH":"113.639999", + "WIDTH":"17.7000008", + "GRT":"5499", + "DWT":"8674", + "DRAUGHT":"64", + "YEAR_BUILT":"1993", + "ROT":"0", + "TYPE_NAME":"Oil/Chemical Tanker", + "AIS_TYPE_SUMMARY":"Tanker", + "DESTINATION":"BILBAO", + "ETA":"2017-05-20T16:00:00", + "CURRENT_PORT":"", + "LAST_PORT":"HAMBURG", + "LAST_PORT_TIME":"2017-05-16T15:04:00", + "CURRENT_PORT_ID":"", + "CURRENT_PORT_UNLOCODE":"", + "CURRENT_PORT_COUNTRY":"", + "LAST_PORT_ID":"172", + "LAST_PORT_UNLOCODE":"DEHAM", + "LAST_PORT_COUNTRY":"DE", + "NEXT_PORT_ID":"1271", + "NEXT_PORT_UNLOCODE":"ESBIO", + "NEXT_PORT_NAME":"BILBAO", + "NEXT_PORT_COUNTRY":"ES", + "ETA_CALC":"2017-05-20T11:27:00", + "ETA_UPDATED":"2017-05-19T09:13:00", + "DISTANCE_TO_GO":"295", + "DISTANCE_TRAVELLED":"782", + "AVG_SPEED":"12", + "MAX_SPEED":"13.3999996" + }, + { + "MMSI":"255925000", + "IMO":"9184433", + "SHIP_ID":"300518", + "LAT":"47.942631", + "LON":"-5.116510", + "SPEED":"79", + "HEADING":"316", + "COURSE":"311", + "STATUS":"0", + "TIMESTAMP":"2017-05-19T09:43:53", + "DSRC":"TER", + "UTC_SECONDS":"52", + "SHIPNAME":"BULNES", + "SHIPTYPE":"70", + "CALLSIGN":"CQWC", + "FLAG":"PT", + "LENGTH":"85.6600037", + "WIDTH":"13.0200005", + "GRT":"2469", + "DWT":"3700", + "DRAUGHT":"58", + "YEAR_BUILT":"2001", + "ROT":"22", + "TYPE_NAME":"General Cargo", + "AIS_TYPE_SUMMARY":"Cargo", + "DESTINATION":"SAINT MALO", + "ETA":"2017-05-20T09:00:00", + "CURRENT_PORT":"", + "LAST_PORT":"BAYONNE", + "LAST_PORT_TIME":"2017-05-17T16:05:00", + "CURRENT_PORT_ID":"", + "CURRENT_PORT_UNLOCODE":"", + "CURRENT_PORT_COUNTRY":"", + "LAST_PORT_ID":"508", + "LAST_PORT_UNLOCODE":"FRBAY", + "LAST_PORT_COUNTRY":"FR", + "NEXT_PORT_ID":"500", + "NEXT_PORT_UNLOCODE":"FRSML", + "NEXT_PORT_NAME":"SAINT MALO", + "NEXT_PORT_COUNTRY":"FR", + "ETA_CALC":"2017-05-20T09:11:00", + "ETA_UPDATED":"2017-05-19T09:43:00", + "DISTANCE_TO_GO":"183", + "DISTANCE_TRAVELLED":"333", + "AVG_SPEED":"7.80000019", + "MAX_SPEED":"8.5" + } +] \ No newline at end of file diff --git a/tests/test_ps03.py b/tests/test_ps03.py new file mode 100644 index 0000000..b60e5a9 --- /dev/null +++ b/tests/test_ps03.py @@ -0,0 +1,67 @@ +import os +import unittest +from datetime import datetime + +from marinetrafficapi.client import Client + + +class PS03Response(unittest.TestCase): + + def setUp(self): + self._api_key = '_api_key_' + self.fake_ok_response_path_json = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + 'tests', 'responses', 'ps03_response.json' + ) + + def test_ok_response_json(self): + request = Client(self._api_key, + fake_response_path=self.fake_ok_response_path_json)\ + .dynamic_fleet_vessel_positions() + + self.assertEqual(request.models[1].mmsi, 215819000) + self.assertEqual(request.models[1].imo, 9034731) + self.assertEqual(request.models[1].ship_id, 150559) + self.assertEqual(request.models[1].latitude, 47.926899) + self.assertEqual(request.models[1].longitude, -5.531450) + self.assertEqual(request.models[1].speed, 122) + self.assertEqual(request.models[1].heading, 162) + self.assertEqual(request.models[1].course, 157) + self.assertEqual(request.models[1].status, 0) + self.assertEqual(request.models[1].timestamp, datetime(2017, 5, 19, 9, 44, 27)) + self.assertEqual(request.models[1].dsrc, 'TER') + self.assertEqual(request.models[1].utc_seconds, 28) + self.assertEqual(request.models[1].ship_name, 'TOUR MARGAUX') + self.assertEqual(request.models[1].ship_type, 81) + self.assertEqual(request.models[1].call_sign, '9HBW8') + self.assertEqual(request.models[1].flag, 'MT') + self.assertEqual(request.models[1].length, 113.639999) + self.assertEqual(request.models[1].width, 17.7000008) + self.assertEqual(request.models[1].grt, 5499) + self.assertEqual(request.models[1].dwt, 8674) + self.assertEqual(request.models[1].draught, 64) + self.assertEqual(request.models[1].year_built, 1993) + self.assertEqual(request.models[1].rot, 0) + self.assertEqual(request.models[1].type_name, 'Oil/Chemical Tanker') + self.assertEqual(request.models[1].ais_type_summary, 'Tanker') + self.assertEqual(request.models[1].destination, 'BILBAO') + self.assertEqual(request.models[1].current_port, '') + self.assertEqual(request.models[1].current_port_id, 0) + self.assertEqual(request.models[1].current_port_unlocode, '') + self.assertEqual(request.models[1].current_port_country, '') + self.assertEqual(request.models[1].last_port, 'HAMBURG') + self.assertEqual(request.models[1].last_port_time, datetime(2017, 5, 16, 15, 4, 0)) + self.assertEqual(request.models[1].eta, datetime(2017, 5, 20, 16, 0, 0)) + self.assertEqual(request.models[1].last_port_id, 172) + self.assertEqual(request.models[1].last_port_unlocode, 'DEHAM') + self.assertEqual(request.models[1].last_port_country, 'DE') + self.assertEqual(request.models[1].next_port_id, 1271) + self.assertEqual(request.models[1].next_port_unlocode, 'ESBIO') + self.assertEqual(request.models[1].next_port_name, 'BILBAO') + self.assertEqual(request.models[1].next_port_country, 'ES') + self.assertEqual(request.models[1].eta_calc, datetime(2017, 5, 20, 11, 27, 0)) + self.assertEqual(request.models[1].eta_updated, datetime(2017, 5, 19, 9, 13, 0)) + self.assertEqual(request.models[1].distance_to_go, 295) + self.assertEqual(request.models[1].distance_travelled, 782) + self.assertEqual(request.models[1].awg_speed, 12) + self.assertEqual(request.models[1].max_speed, 13.3999996)