BC Ferries Python Library
=========================

This is the Python client library for interacting with information
published on the `BC Ferries mobile
site <http://mobile.bcferries.com/>`__. It is essentially a wrapper
around a BeautifulSoup-powered scraper. Better documentation and tests
are still in the works; feel free to contribute!

This library is used to power project like
`FerryTime <https://ferryti.me>`__. The source code for this library can
be found at `yasyf/bcferries <https://github.com/yasyf/bcferries>`__ on
GitHub.

Installation
------------

``pip install bcferries``

Setup
-----

Some functions require interaction with a geocoding service; the Google
Maps API is used for this. In order to prevent severe rate limiting,
you'll want to acquire an API key. To let ``bcferries`` know about this
key, set it as the ``GOOGLE_MAPS_API_KEY`` environment variable.
Alternatively, you can pass it as an optional keyword argument to the
constructor.

.. code:: python

    from bcferries import BCFerries

    bc = BCFerries(google_maps_api_key='xxx-xxx-xxx')

Usage
-----

.. code:: python

    bc = BCFerries()

Terminals
~~~~~~~~~

.. code:: python

    bc.nearest_terminal('Qualicum Beach')
    # BCFerriesTerminal(Nanaimo (Duke Pt))

    terminals = bc.terminals()
    # {u'Horseshoe Bay': BCFerriesTerminal(Horseshoe Bay), u'Tsawwassen': BCFerriesTerminal(Tsawwassen)}
    t = terminals['Tsawwassen']
    # BCFerriesTerminal(Tsawwassen)
    t.updated_at()
    # datetime.datetime(2014, 12, 29, 0, 4)
    t.next_crossing()
    # BCFerriesCrossing(Tsawwassen to Duke Point at 5:15am)
    t.location().address
    # u'Ferry Causeway, Delta, BC V4M, Canada'

Routes
~~~~~~

.. code:: python

    routes = t.routes()
    # {u'Tsawwassen to Duke Point': BCFerriesRoute(Tsawwassen to Duke Point)}
    r = routes['Tsawwassen to Duke Point']
    # BCFerriesRoute(Tsawwassen to Duke Point)

    r.from_, r.to
    # (BCFerriesTerminal(Tsawwassen), BCFerriesTerminal(Nanaimo (Duke Pt)))
    r.distance()
    # Distance(61.9591068557)
    r.car_waits
    # 0

Crossings
~~~~~~~~~

.. code:: python

    crossing = r.crossings()['10:45pm']
    # BCFerriesCrossing(Tsawwassen to Duke Point at 5:45pm)
    crossing.capacity
    # BCFerriesCapacity(18% Full)

Schedules
~~~~~~~~~

.. code:: python

    schedule = r.scheduled('12:45 PM')
    # BCFerriesScheduledCrossing(Queen of Alberni at 12:45 PM)
    schedule.status
    # u'On Time'
    schedule.sailing_time
    # datetime.timedelta(0, 7200)
    schedule.is_late()
    # False
    schedule.is_departed()
    # True

Fuzzy Results
-------------

All returned dictionaries have fuzzy string matching on they keys.

.. code:: python

    routes['Tsawwassen to Duke Point'] == routes['Tsaw to DP']
    # True

There is also fuzzy time matching on keys that represent a nearby time.

.. code:: python

    r = routes['HBay to DBay']
    schedule = r.schedule()
    schedule['6:12 PM']
    # BCFerriesScheduledCrossing(Queen of Cowichan at 6:30 PM)

``datetime`` objects can also be used as keys.

.. code:: python

    from datetime import datetime

    datetime.datetime.now()
    # datetime.datetime(2014, 12, 28, 10, 42, 35, 630996)
    schedule[datetime.datetime.now()]
    # BCFerriesScheduledCrossing(Coastal Renaissance at 10:40 AM)

Caching
-------

``bcferries`` caches the 16 most used API calls for up to five minutes
by default. You can change this behavior as below. This must be done
before creating a ``BCFerries`` object.

.. code:: python

    import bcferries
    import datetime

    bcferries.set_cache_size(16)
    bcferries.set_cache_timeout(datetime.timedelta(minutes=5))

You can also pass any function the ``ignore_cache`` keyword argument to
bypass the cache, or call the ``flush_cache`` method on ``BCFerries`` to
clear the entire cache.

.. code:: python

    terminals = bc.terminals() # initial call takes multiple seconds
    terminals = bc.terminals() # repeated call returns almost instantly
    terminals = bc.terminals(ignore_cache=True) # takes multiple seconds to return

    bc.flush_cache() # wipes the cache

Export
------

You can export any subset of information with a call to ``to_dict`` on
any object. You can also use ``to_fuzzy_dict`` and ``to_json`` as
needed.

By default, complex objects which require further API calls will not be
created, and only their names will be returned. You can disable this
behavior with the ``shallow`` keyword argument. To export all available
information, do this on a ``BCFerries`` instance, and be prepared to
wait a while.

.. code:: python

    crossing.capacity
    # BCFerriesCapacity(18% Full)
    crossing.capacity.to_dict()
    # {'passenger_filled': 32, 'mixed_filled': 4, 'name': '18% Full', 'filled': 18}

    bc.to_dict() # quick
    bc.to_dict(shallow=False) # takes all day
