Skip to content

Commit

Permalink
PEP8. Fix python2 testing. Remove dependency on envoy. Use nose2 inst…
Browse files Browse the repository at this point in the history
…ead of deprecated nose. Python3.5 support should be good (#15).
  • Loading branch information
frinkelpi committed Oct 22, 2016
1 parent 4acbd8f commit e9b0d6b
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 128 deletions.
40 changes: 24 additions & 16 deletions fahrplan/API.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import json
import dateutil.parser
import six
from pprint import pprint
import sys
from .helpers import perror

occupancies = {
None: '',
Expand All @@ -16,34 +17,37 @@
}
API_URL = 'http://transport.opendata.ch/v1'


def _APIRequest(action, params, proxy=None):
"""
Perform an API request on transport.opendata.ch
"""
#Send request
url="{}/{}".format(API_URL,action)
# Send request
url = "{}/{}".format(API_URL, action)
kwargs = {'params': params}
if proxy is not None:
kwargs['proxies'] = {'http' : proxy}
kwargs['proxies'] = {'http': proxy}
try:
response = requests.get(url, **kwargs)
except requests.exceptions.ConnectionError:
perror('Error: Could not reach network.')
sys.exit(1)
#Check response status
# Check response status
logging.debug('Response status: {0!r}'.format(response.status_code))
if not response.ok:
verbose_status = requests.status_codes._codes[response.status_code][0]
perror('Server Error: HTTP {} ({})'.format(response.status_code, verbose_status))
sys.exit(1)
#Convert response to json
# Convert response to json
try:
return json.loads(response.text)
except ValueError:
logging.debug('Response status code: {0}'.format(response.status_code))
logging.debug('Response content: {0!r}'.format(response.content))
perror('Error: Invalid API response (invalid JSON)')
sys.exit(1)


def _parse_section(con_section, connection):
"""
Parse the section of a connection
Expand All @@ -70,6 +74,8 @@ def _parse_section(con_section, connection):
section['occupancy1st'] = occupancies.get(connection['capacity1st'], '')
section['occupancy2nd'] = occupancies.get(connection['capacity2nd'], '')
return section


def _parse_connection(connection, include_sections=False):
"""Parse a connection.
Expand All @@ -86,38 +92,40 @@ def _parse_connection(connection, include_sections=False):
contained in a list.
"""
data = {}
con_from = connection['from']
con_to = connection['to']
walk = False
keyfunc = lambda s: s['departure']['departure']

def keyfunc(s):
return s['departure']['departure']
data['change_count'] = six.text_type(connection['transfers'])
data['travelwith'] = ', '.join(connection['products'])
data['occupancy1st'] = occupancies.get(connection['capacity1st'], '')
data['occupancy2nd'] = occupancies.get(connection['capacity2nd'], '')
#Sections
# Sections
con_sections = sorted(connection['sections'], key=keyfunc)
data['sections'] = []
if include_sections:
#Full display
# Full display
for con_section in con_sections:
section = _parse_section(con_section, connection)
if con_section.get('walk'):
walk = True
data["sections"].append(section)
else:
#Shortened display, parse only departure and arrivals sections
section = _parse_section(con_sections[0], connection)
# Shortened display, parse only departure and arrivals sections
section = _parse_section(con_sections[0], connection)
to = _parse_section(con_sections[-1], connection)
for p in ["station_to","arrival"]:
for p in ["station_to", "arrival"]:
section[p] = to[p]
#Get information from connection
# Get information from connection
for p in ["occupancy2nd", "occupancy1st", "travelwith", "change_count"]:
section[p] = data[p]
data['sections'] = [section]
#Walk
# Walk
if walk:
data['travelwith'] += ', Walk'
return data


def getConnections(request, include_sections=False, proxy=None):
"""
Get the connections of a request
Expand Down
53 changes: 28 additions & 25 deletions fahrplan/display.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,52 @@
# -*- coding: utf-8 -*-
import six
from texttable.texttable import Texttable


# Output formats
class Formats(object):
SIMPLE = 0
FULL = 1

def _getConnectionRow(i,c):


def _getConnectionRow(i, c):
"""
Get table row for connection.
"""
sections = c["sections"]
firstClass = True
#Create row
# Create row
row = [i]
for p in [
lambda x : [x["station_from"], x["station_to"]], #Station
lambda x : [x["platform_from"], x["platform_to"]], #Platform
lambda x : [x[q].strftime('%d/%m/%y') for q in ["departure","arrival"]], #Date
lambda x : [x[q].strftime('%H:%M') for q in ["departure","arrival"]], #Time
lambda x : [str((x["arrival"]-x["departure"])).rsplit(":",1)[0], " "], #Duration
lambda x: [x["station_from"], x["station_to"]], # Station
lambda x: [x["platform_from"], x["platform_to"]], # Platform
lambda x: [x[q].strftime('%d/%m/%y') for q in ["departure", "arrival"]], # Date
lambda x: [x[q].strftime('%H:%M') for q in ["departure", "arrival"]], # Time
lambda x: [str((x["arrival"] - x["departure"])).rsplit(":", 1)[0], " "], # Duration
None,
lambda x : [x["travelwith"], " "], #With
lambda x : ["1: "+x["occupancy1st"], "2: "+x["occupancy2nd"]] #Occupancy
lambda x: [x["travelwith"], " "], # With
lambda x: ["1: " + x["occupancy1st"], "2: " + x["occupancy2nd"]] # Occupancy
]:
if p==None:
if p is None:
row.append(c["change_count"])
else:
row.append("\n \n".join(["\n".join(p(s)) for s in sections]))
return row
def displayConnections(connections, output_format):


def connectionsTable(connections, output_format):
"""
Display connections in the given output format.
Get connections in the given output format.
"""
table = Texttable(max_width=0)
#Alignments
table.set_cols_valign(["m","t","t","t","t","t","m","t","t"])
table.set_cols_align(["l","l","c","l","l","c","c","l","l"])
#Header
table.add_row(["#", "Station", "Platform","Date","Time","Duration","Chg.","With","Occupancy"])
#Connection rows
for i,c in enumerate(connections):
table.add_row(_getConnectionRow(i,c))
#Display
print(table.draw())
# Alignments
table.set_cols_valign(["m", "t", "t", "t", "t", "t", "m", "t", "t"])
table.set_cols_align(["l", "l", "c", "l", "l", "c", "c", "l", "l"])
# Header
table.add_row(["#", "Station", "Platform", "Date", "Time", "Duration", "Chg.", "With", "Occupancy"])
# Connection rows
for i, c in enumerate(connections):
table.add_row(_getConnectionRow(i, c))
# Display
return table.draw()
# Old display method:
# from .tableprinter import Tableprinter
# def displayConnections(connections, output_format):
Expand Down
7 changes: 7 additions & 0 deletions fahrplan/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-

# Helper function to print directly to sys.stderr
from __future__ import print_function
from functools import partial
import sys
perror = partial(print, file=sys.stderr)
69 changes: 36 additions & 33 deletions fahrplan/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,69 +21,69 @@

import sys
import logging
from functools import partial
import argparse
import six
import dateutil.parser

from . import meta
from .parser import parse_input
from .API import getConnections
from .display import *

__version__ = meta.version
from .display import Formats, connectionsTable
from .helpers import perror

# Base configuration
ENCODING = sys.stdout.encoding or 'utf-8'

# Helper function to print directly to sys.stderr
perror = partial(print, file=sys.stderr)

def main():
output_format = Formats.SIMPLE
proxy_host = None

# 1. Parse command line arguments
parser=argparse.ArgumentParser(epilog='Arguments:\n'
+ ' You can use natural language arguments using the following\n'
+ ' keywords in your desired language:\n'
+ ' en -- from, to, via, departure, arrival\n'
+ ' de -- von, nach, via, ab, an\n'
+ ' fr -- de, à, via, départ, arrivée\n'
+ '\n'
+ ' You can also use natural time specifications in your language, like "now",\n'
+ ' "immediately", "noon" or "midnight".\n'
+ '\n'
+ 'Examples:\n'
+ ' fahrplan from thun to burgdorf\n'
+ ' fahrplan via bern nach basel von zürich, helvetiaplatz ab 15:35\n'
+ ' fahrplan de lausanne à vevey arrivée minuit\n'
+ ' fahrplan -p proxy.mydomain.ch:8080 de lausanne à vevey arrivée minuit\n'
+ '\n', formatter_class=argparse.RawDescriptionHelpFormatter, prog=meta.title, description=meta.description)
parser = argparse.ArgumentParser(epilog='Arguments:\n'
+ u' You can use natural language arguments using the following\n'
+ u' keywords in your desired language:\n'
+ u' en -- from, to, via, departure, arrival\n'
+ u' de -- von, nach, via, ab, an\n'
+ u' fr -- de, à, via, départ, arrivée\n'
+ u'\n'
+ u' You can also use natural time specifications in your language, like "now",\n'
+ u' "immediately", "noon" or "midnight".\n'
+ u'\n'
+ u'Examples:\n'
+ u' fahrplan from thun to burgdorf\n'
+ u' fahrplan via bern nach basel von zürich, helvetiaplatz ab 15:35\n'
+ u' fahrplan de lausanne à vevey arrivée minuit\n'
+ u' fahrplan -p proxy.mydomain.ch:8080 de lausanne à vevey arrivée minuit\n'
+ u'\n', formatter_class=argparse.RawDescriptionHelpFormatter, prog=meta.title, description=meta.description, add_help=False)
parser.add_argument("--full", "-f", action="store_true", help="Show full connection info, including changes")
parser.add_argument("--info", "-i", action="store_true", help="Verbose output")
parser.add_argument("--debug", "-d", action="store_true", help="Debug output")
parser.add_argument("--help", "-h", action="store_true", help="Show this help")
parser.add_argument("--version", "-v", action="store_true", help="Show version number")
parser.add_argument("--proxy", "-p", help="Use proxy for network connections (host:port)")
parser.add_argument("request", nargs=argparse.REMAINDER)
options = parser.parse_args()
#Version
# Version
if options.version:
print('{meta.title} {meta.version}'.format(meta=meta))
sys.exit(0)
#No request
if len(options.request) == 0:
parser.print_help()
# No request or help
if len(options.request) == 0 or options.help:
if six.PY2:
print(parser.format_help().encode(ENCODING))
else:
parser.print_help()
sys.exit(0)
#Options
# Options
if options.full:
output_format = Formats.FULL
if options.debug:
logging.basicConfig(level=logging.DEBUG)
if options.proxy != None:
if options.proxy is not None:
proxy_host = options.proxy

# Parse user request
if six.PY2:
options.request = [o.decode(ENCODING) for o in options.request]
try:
args, language = parse_input(options.request)
except ValueError as e:
Expand All @@ -95,11 +95,14 @@ def main():
connections = data["connections"]

if not connections:
print('No connections found from {} to {}.'.format(data["from"]["name"], data["to"]["name"]))
print("No connections found")
sys.exit(0)

# 3. Output data
displayConnections(connections, output_format)
table = connectionsTable(connections, output_format)
if six.PY2:
table = table.encode(ENCODING)
print(table)

if __name__ == '__main__':
main()
6 changes: 3 additions & 3 deletions fahrplan/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ def process_stack():
def _detect_language(keyword_dicts, tokens):
"""Detect the language of the tokens by finding the highest intersection
with the keywords of a specific language."""
intersection_count = lambda a, b: len(set(a).intersection(b))

def intersection_count(a, b):
return len(set(a).intersection(b))

counts = []
for lang, keywords in keyword_dicts.items():
Expand Down Expand Up @@ -191,8 +193,6 @@ def parse_input(tokens):

logging.debug('Data: ' + repr(data))
return data, language


"""
transport.opendata.ch request params:
x from
Expand Down
Loading

0 comments on commit e9b0d6b

Please sign in to comment.