├── tests ├── __init__.py └── fleetmonger_tests.py ├── .gitignore ├── fleetmonger ├── utils.py ├── weather.py ├── __init__.py ├── port.py ├── vessel.py └── fleetmonger.py ├── LICENSE ├── Makefile ├── MANIFEST.in ├── setup.py └── readme.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import fleetmonger_tests 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | build 4 | dist 5 | readme.rst 6 | -------------------------------------------------------------------------------- /tests/fleetmonger_tests.py: -------------------------------------------------------------------------------- 1 | from fleetmonger import Fleetmonger 2 | import unittest 3 | 4 | class FleetMongerTestCase(unittest.TestCase): 5 | 6 | def setUp(self): 7 | user, key = '', '' 8 | self.f = Fleetmonger(user, key) 9 | 10 | def test_fleet(self): 11 | fleet = self.f.myfleet() 12 | assert fleet 13 | assert len(fleet) > 0 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | -------------------------------------------------------------------------------- /fleetmonger/utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import pytz 3 | 4 | 5 | def setup_dt(string): 6 | "2014-10-31 12:10:52+00:00" 7 | try: 8 | dt = datetime.strptime(string[:-6], '%Y-%m-%d %H:%M:%S') 9 | return pytz.utc.localize(dt) 10 | except ValueError: 11 | return None 12 | 13 | 14 | def setup_date(string): 15 | try: 16 | return datetime.strptime(string, '%Y-%m-%d').date() 17 | except ValueError: 18 | return None 19 | -------------------------------------------------------------------------------- /fleetmonger/weather.py: -------------------------------------------------------------------------------- 1 | from .utils import setup_dt 2 | 3 | 4 | class weather(object): 5 | 6 | def __init__(self, kwargs): 7 | self.arg = kwargs['arg'] 8 | 9 | self.temperature_air = kwargs['temperature_air'] 10 | self.pressure = kwargs['pressure'] 11 | self.ice_cover = kwargs['ice_cover'] 12 | self.wind_dir = kwargs['wind_dir'] 13 | self.wind_speed = kwargs['wind_speed'] 14 | self.wind_gust = kwargs['wind_gust'] 15 | self.wave_sigh = kwargs['wave_sigh'] 16 | self.wave_dir = kwargs['wave_dir'] 17 | self.wave_per = kwargs['wave_per'] 18 | self.timestamp = setup_dt(kwargs['timestamp']) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Neil Freeman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This file is part of fleetmonger. 2 | # http://github.com/fitnr/fleetmonger 3 | 4 | # Copyright (c) 2014 Neil Freeman 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | README.rst: README.md 25 | pandoc $< -o $@ || touch $@ 26 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # This file is part of fleetmonger. 2 | # http://github.com/fitnr/fleetmonger 3 | 4 | # Copyright (c) 2014 Neil Freeman 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | 25 | include README.md 26 | recursive-include tests *.py 27 | exclude */.DS_Store 28 | -------------------------------------------------------------------------------- /fleetmonger/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of fleetmonger. 2 | # http://github.com/fitnr/fleetmonger 3 | 4 | # Copyright (c) 2014 Neil Freeman 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | __all__ = ['fleetmonger', 'vessel_wrapper', 'vessel', 'port', 'utils'] 25 | __version__ = '0.0.4' 26 | 27 | from .fleetmonger import Fleetmonger 28 | -------------------------------------------------------------------------------- /fleetmonger/port.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from .utils import setup_dt, setup_date 3 | 4 | 5 | class port_wrapper(list): 6 | 7 | def __init__(self, kwargs): 8 | super(port_wrapper, self).__init__() 9 | 10 | self.meta = kwargs['meta'] 11 | ports = kwargs['objects'] 12 | 13 | for _p in ports: 14 | self.append(Port(_p)) 15 | 16 | 17 | class Port(object): 18 | 19 | '''A port''' 20 | country_isocode = None 21 | country_name = None 22 | name = None 23 | locode = None 24 | 25 | def __init__(self, kwargs): 26 | if kwargs.get('arrival'): 27 | self.arrival = setup_dt(kwargs['arrival']) 28 | self.departure = setup_dt(kwargs['departure']) 29 | 30 | if kwargs.get('eta'): 31 | self.eta = setup_date(kwargs['eta']) 32 | self.etd = setup_date(kwargs['etd']) 33 | 34 | for k, v in kwargs.items(): 35 | if k in ('eta', 'etd', 'arrival', 'departure'): 36 | pass 37 | 38 | elif k in ('name', 'port_name', 'portname'): 39 | setattr(self, 'name', v) 40 | 41 | elif k in ('locode', 'port_locode'): 42 | setattr(self, 'locode', v) 43 | 44 | elif k == 'publicurl': 45 | setattr(self, 'url', v) 46 | 47 | else: 48 | setattr(self, k, v) 49 | 50 | @property 51 | def duration(self): 52 | if hasattr(self, 'etd') and hasattr(self, 'eta'): 53 | return self.etd - self.eta 54 | elif hasattr(self, 'departure') and hasattr(self, 'arrival'): 55 | return self.departure - self.arrival 56 | 57 | return None 58 | 59 | def __repr__(self): 60 | return '<' + self.name + '>' 61 | -------------------------------------------------------------------------------- /fleetmonger/vessel.py: -------------------------------------------------------------------------------- 1 | from .utils import setup_dt 2 | from .port import port_wrapper, Port 3 | 4 | 5 | class vessel_wrapper(list): 6 | 7 | """A list of vessels with a few extra properties""" 8 | 9 | def __init__(self, kwargs): 10 | super(vessel_wrapper, self).__init__() 11 | 12 | self.meta = kwargs['meta'] 13 | vessels = kwargs['objects'] 14 | 15 | for v in vessels: 16 | self.append(Vessel(v)) 17 | 18 | 19 | class Vessel(object): 20 | 21 | """A single vessel""" 22 | 23 | name = 'Vessel' 24 | destination = None 25 | etatime = None 26 | flag = None 27 | flag_iso = None 28 | heading = None 29 | imo = None 30 | latitude = None 31 | location = None 32 | longitude = None 33 | mmsi = None 34 | navigationstatus = None 35 | photos = None 36 | positionreceived = None 37 | url = None 38 | type = None 39 | last_ports = None 40 | last_port = None 41 | 42 | def __init__(self, kwargs): 43 | 44 | if kwargs.get('vessel'): 45 | kwargs = kwargs['vessel'] 46 | 47 | for k, v in kwargs.items(): 48 | if not v: 49 | continue 50 | 51 | if k in ['positionreceived', 'etatime']: 52 | setattr(self, k, setup_dt(v)) 53 | 54 | elif k in ('mmsinumber', 'mmsi'): 55 | self.mmsi = v 56 | 57 | elif k in ('imonumber', 'imo'): 58 | self.imo = v 59 | 60 | elif k == 'last_ports': 61 | self.last_ports = port_wrapper(v) 62 | 63 | elif k == 'lastport': 64 | self.last_port = Port(v) 65 | 66 | elif k == 'public_url': 67 | self.url = v 68 | 69 | elif k == 'flag': 70 | # e.g. "US|United States" 71 | self.flag_iso, self.flag = v.split('|') 72 | 73 | elif k == 'photos': 74 | self.photos = v.split('|') 75 | 76 | else: 77 | setattr(self, k, v) 78 | 79 | @property 80 | def coords(self): 81 | return (self.latitude, self.longitude) 82 | 83 | def __repr__(self): 84 | return '<' + self.name + '>' 85 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This file is part of fleetmonger. 4 | # http://github.com/fitnr/fleetmonger 5 | 6 | # Copyright (c) 2014 Neil Freeman 7 | 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | from setuptools import setup 27 | 28 | try: 29 | readme = open('README.rst').read() 30 | except IOError: 31 | try: 32 | readme = open('README.md').read() 33 | except IOError: 34 | readme = '' 35 | 36 | setup( 37 | name='fleetmonger', 38 | 39 | version='0.0.4', 40 | 41 | description='Fleetmon API wrapper for ship data', 42 | 43 | long_description=readme, 44 | 45 | author='Neil Freeman', 46 | 47 | author_email='contact@fakeisthenewreal.org', 48 | 49 | url='https://github.com/fitnr/fleetmonger', 50 | 51 | packages=['fleetmonger'], 52 | 53 | license='MIT', 54 | 55 | install_requires=[ 56 | 'requests >=2.4.1, <3', 57 | 'pytz==2014.10' 58 | ], 59 | 60 | test_suite='tests', 61 | 62 | classifiers=[ 63 | 'License :: OSI Approved :: MIT License', 64 | 'Programming Language :: Python :: 2.7', 65 | 'Intended Audience :: Science/Research', 66 | ], 67 | ) 68 | -------------------------------------------------------------------------------- /fleetmonger/fleetmonger.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from .vessel import Vessel, vessel_wrapper 3 | from .port import port_wrapper 4 | 5 | def _check_params(named): 6 | if not any(v for v in named.values()): 7 | raise ValueError("Missing arguments, must provide one of {}".format(named.keys())) 8 | 9 | class Fleetmonger(object): 10 | 11 | _version = 'personal-v1' 12 | _endpoint = 'http://www.fleetmon.com/api/p' 13 | 14 | _status_code = None 15 | _raw_result = None 16 | 17 | """API class for Fleetmon""" 18 | 19 | def __init__(self, username, api_key): 20 | self.username = username 21 | self.api_key = api_key 22 | 23 | @property 24 | def status_code(self): 25 | return self._status_code 26 | 27 | @status_code.setter 28 | def status_code(self, value): 29 | self._status_code = value 30 | 31 | def _url(self, resource): 32 | return '{endpoint}/{version}/{resource}/'.format( 33 | endpoint=self._endpoint, 34 | version=self._version, 35 | resource=resource 36 | ) 37 | 38 | def _call(self, resource, **params): 39 | self._raw_result, self.status_code = None, None 40 | 41 | params = {k:v for k, v in params.items() if v is not None} 42 | params['format'] = 'json' 43 | params['api_key'] = self.api_key 44 | params['username'] = self.username 45 | 46 | r = requests.get(self._url(resource), params=params) 47 | 48 | self.status_code = r.status_code 49 | self._raw_result = r.text 50 | 51 | if self.status_code != 200: 52 | return {} 53 | 54 | return r.json() 55 | 56 | def myfleet(self, **params): 57 | return vessel_wrapper(self._call('myfleet', **params)) 58 | 59 | def vessel(self, imo=None, name=None, lastports=None, **params): 60 | _check_params({'imo': imo, 'name': name}) 61 | return Vessel(self._call('vessels_terrestrial', imonumber=imo, q=name, lastports=lastports, **params)) 62 | 63 | def vesselparticulars(self, imo=None, mmsi=None, name=None, **params): 64 | _check_params({'imo': imo, 'mmsi': mmsi, 'name': name}) 65 | return Vessel(self._call('vesselparticulars', imonumber=imo, mmsinumber=mmsi, q=name, **params)) 66 | 67 | def vesselurl(self, imo=None, mmsi=None, name=None, **params): 68 | _check_params({'imo': imo, 'mmsi': mmsi, 'name': name}) 69 | return vessel_wrapper(self._call('vesselurl', imonumber=imo, mmsinumber=mmsi, q=name, **params)) 70 | 71 | def porturl(self, name=None, locode=None, country=None, **params): 72 | _check_params({'locode': locode, 'name': name}) 73 | return port_wrapper(self._call('porturl', q=name, locode=locode, country_isocode=country, **params)) 74 | 75 | def weather(self, lat=None, lon=None, vessel=None, **params): 76 | _check_params({'(lat and lon)': lat and lon, 'vessel': vessel}) 77 | if vessel: 78 | lat, lon = vessel.coords 79 | return self._call('weather_at_position', lat=lat, lon=lon, **params) 80 | 81 | def containerschedule(self, imo, weeks_ahead=1, **params): 82 | return port_wrapper(self._call('container_schedule', imo=imo, weeks_ahead=weeks_ahead, **params)) 83 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Fleetmonger 2 | 3 | Python wrapper for the [fleetmon.com](fleetmon.com) ship-tracking API. 4 | 5 | ## API Calls 6 | [Read the details of the API at Fleetmonger](https://www.fleetmon.com/faq/public_api). 7 | * fleetmonger.myfleet 8 | * fleetmonger.vessel 9 | * fleetmonger.vesselparticulars 10 | * fleetmonger.vesselurl 11 | * fleetmonger.porturl 12 | * fleetmonger.weather 13 | * fleetmonger.containerschedule 14 | 15 | Note that some API calls require the purchase of credits at Fleetmon. This package is unaffiliated with Fleetmon, use at your own risk. 16 | 17 | # Usage 18 | 19 | ````python 20 | from fleetmonger import Fleetmonger 21 | 22 | fm = Fleetmonger('username', 'your key') 23 | 24 | fleet = fm.myfleet() 25 | 26 | for ship in fleet: 27 | print ship.name, ship.destination 28 | ```` 29 | 30 | ### Vessels 31 | 32 | ````python 33 | 34 | my_vessel = fleet[0] 35 | # or 36 | my_vessel = fm.vessel(mmsi='239725000') 37 | # or 38 | my_vessel = fm.vessel(imo='9197545') 39 | # or 40 | my_vessel = fm.vessel(name='MINNOW') 41 | 42 | # Passing incomplete information will raise an error 43 | my_vessel = fm.vessel() 44 | # ValueError 45 | 46 | my_vessel.name 47 | # 48 | 49 | my_vessel.navigationstatus 50 | # 'On a three hour tour' 51 | 52 | my_vessel.etatime 53 | # datetime.datetime(1964, 9, 26, 12, 0, tzinfo=) 54 | 55 | my_vessel.coords 56 | # (3.469557, -167.255859) 57 | 58 | # Missing attributes return None 59 | my_vessel.location 60 | # None 61 | 62 | my_vessel.flag 63 | # 'United States' 64 | 65 | my_vessel.flag_so 66 | # 'US' 67 | 68 | # Some vessels have photos 69 | myvessel.photos 70 | # ["//img1.fleetmon.com/thumbnails/MINNOW_1.220x146.jpg", "//img1.fleetmon.com/thumbnails/MINNOW_2.570x1140.jpg"] 71 | 72 | ```` 73 | 74 | Minimum list of `Vessel` attributes: 75 | 76 | `coords` (lat, lon), `course`, `destination`, `draught`, `etatime`, `flag`, `heading`, `imo`, `last_port`, `latitude`, `location`, `longitude`, `mmsi`, `name`, `navigationstatus`, `photos`, `positionreceived`, `publicurl`, `speed`, `type` 77 | 78 | ### Ports 79 | 80 | ````python 81 | 82 | my_vessel.last_port 83 | # 84 | 85 | port = my_vessel.last_port 86 | 87 | port.name 88 | # 'Honolulu, HI' 89 | 90 | port.duration 91 | # datetime.timedelta(...) 92 | 93 | ```` 94 | ### Port urls and Vessel urls 95 | 96 | Pass mmsi, imo or name to vessel methods. Pass locode or name to port methods. 97 | 98 | ````python 99 | fm.vesselurl(name='MINNOW') 100 | 101 | fm.porturl(locode='USLAX') 102 | 103 | # Porturl also takes an optional country isocode parameter 104 | fm.porturl(name='new', country='US') 105 | # [, , , ] 106 | ```` 107 | 108 | ### Weather at Location 109 | 110 | ````python 111 | fm.weather(lat=3.469557, lon=-167.255859) 112 | 113 | # You can also pass a vessel object to the weather call 114 | fm.weather(vessel=my_vessel) 115 | ```` 116 | 117 | ## Parameters 118 | 119 | In general, the parameters that Fleetmonger expects match those of the Fleetmon API, with the following exceptions: 120 | 121 | ``` 122 | instead of mmsinumber, use mmsi 123 | ... imonumber ... imo 124 | ... q ... name 125 | ``` 126 | --------------------------------------------------------------------------------