Skip to content

Commit

Permalink
Rework settings
Browse files Browse the repository at this point in the history
- Combine settings under one keyword
- Allow custom decimal places setting
- Enable custom user units
  • Loading branch information
deanishe committed Dec 26, 2014
1 parent 1004faf commit 2318554
Show file tree
Hide file tree
Showing 18 changed files with 6,516 additions and 859 deletions.
Binary file modified Convert.alfredworkflow
Binary file not shown.
273 changes: 217 additions & 56 deletions README.md

Large diffs are not rendered by default.

Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file removed screenshot1.png
Binary file not shown.
Binary file removed screenshot2.png
Binary file not shown.
Binary file removed screenshot3.png
Binary file not shown.
6,020 changes: 5,376 additions & 644 deletions src/README.html

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
CURRENCY_CACHE_AGE = 3600 * 12 # 12 hours
CURRENCY_CACHE_NAME = 'exchange_rates'

DECIMAL_PLACES_DEFAULT = 2

CUSTOM_DEFINITIONS_FILENAME = 'unit_definitions.txt'

BUILTIN_UNIT_DEFINITIONS = os.path.join(os.path.dirname(__file__),
CUSTOM_DEFINITIONS_FILENAME)

with open(os.path.join(os.path.dirname(__file__),
'currencies.json'), 'rb') as fp:
CURRENCIES = json.load(fp)
Expand All @@ -36,4 +43,4 @@

UPDATE_SETTINGS = {'github_slug': 'deanishe/alfred-convert'}

DEFAULT_SETTINGS = {}
DEFAULT_SETTINGS = {'decimal_places': DECIMAL_PLACES_DEFAULT}
41 changes: 31 additions & 10 deletions src/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@

from __future__ import print_function, unicode_literals

import os
import shutil
import sys

from pint import UnitRegistry, UndefinedUnitError

from workflow import Workflow, ICON_WARNING, ICON_INFO
from workflow.background import run_in_background, is_running
from config import (CURRENCY_CACHE_AGE, CURRENCY_CACHE_NAME, ICON_UPDATE,
UPDATE_SETTINGS, DEFAULT_SETTINGS)
from config import (CURRENCY_CACHE_AGE, CURRENCY_CACHE_NAME,
ICON_UPDATE,
UPDATE_SETTINGS, DEFAULT_SETTINGS,
BUILTIN_UNIT_DEFINITIONS,
CUSTOM_DEFINITIONS_FILENAME)

log = None

Expand All @@ -29,7 +34,7 @@
Q = ureg.Quantity


def convert(query):
def convert(query, decimal_places=2):
"""Parse query, calculate and return conversion result
Raises a `ValueError` if the query is not understood or is invalid (e.g.
Expand Down Expand Up @@ -87,10 +92,12 @@ def convert(query):
if to_unit is None:
raise ValueError('Unknown unit : %s' % q2)
conv = from_unit.to(to_unit)
# units = unicode(conv.units)
# if units.upper() in CURRENCIES:
# units = units.upper()
return '%0.2f %s' % (conv.magnitude, conv.units)
log.debug('%f %s' % (conv.magnitude, conv.units))

fmt = '%%0.%df %%s' % decimal_places
result = fmt % (conv.magnitude, conv.units)

return result


def main(wf):
Expand All @@ -107,18 +114,30 @@ def main(wf):
'Use query `workflow:update` to install the new version',
icon=ICON_UPDATE)

# Add custom units from workflow and user data
ureg.load_definitions(BUILTIN_UNIT_DEFINITIONS)
user_definitions = wf.datafile(CUSTOM_DEFINITIONS_FILENAME)

# User's custom units
if os.path.exists(user_definitions):
ureg.load_definitions(user_definitions)
else: # Copy template to data dir
shutil.copy(
wf.workflowfile('{}.sample'.format(CUSTOM_DEFINITIONS_FILENAME)),
wf.datafile(CUSTOM_DEFINITIONS_FILENAME))

# Load cached data
exchange_rates = wf.cached_data(CURRENCY_CACHE_NAME, max_age=0)

if exchange_rates: # Add exchange rates to conversion database
ureg.define('euros = [currency] = eur = EUR')
ureg.define('euro = [currency] = eur = EUR')
for abbr, rate in exchange_rates.items():
ureg.define('{0} = eur / {1} = {2}'.format(abbr, rate,
abbr.lower()))

if not wf.cached_data_fresh(CURRENCY_CACHE_NAME, CURRENCY_CACHE_AGE):
# Update currency rates
cmd = ['/usr/bin/python', wf.workflowfile('update_exchange_rates.py')]
cmd = ['/usr/bin/python', wf.workflowfile('currency.py')]
run_in_background('update', cmd)

if is_running('update'):
Expand All @@ -134,7 +153,9 @@ def main(wf):
conversion = None

try:
conversion = convert(query)
conversion = convert(query,
decimal_places=wf.settings.get('decimal_places',
2))
except UndefinedUnitError as err:
log.critical('Unknown unit : %s', err.unit_names)
error = 'Unknown unit : {}'.format(err.unit_names)
Expand Down
82 changes: 23 additions & 59 deletions src/currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,24 @@
from __future__ import print_function, unicode_literals

import csv
from datetime import timedelta
from itertools import izip_longest
import re

from workflow import (Workflow, web,
ICON_WARNING, ICON_INFO,
MATCH_ALL, MATCH_ALLCHARS)
from workflow import Workflow, web

from config import (CURRENCY_CACHE_NAME,
ICON_CURRENCY,
CURRENCY_CACHE_AGE,
REFERENCE_CURRENCY,
CURRENCIES,
YAHOO_BASE_URL,
SYMBOLS_PER_REQUEST)


wf = Workflow()
log = wf.logger
log = None

parse_yahoo_response = re.compile(r'{}(.+)=X'.format(REFERENCE_CURRENCY)).match


def human_timedelta(td):
output = []
d = {'day': td.days}
d['hour'], rem = divmod(td.seconds, 3600)
d['minute'], d['second'] = divmod(rem, 60)
for unit in ('day', 'hour', 'minute', 'second'):
i = d[unit]
if unit == 'second' and len(output):
# no seconds unless last update was < 1m ago
break
if i == 1:
output.append('1 %s' % unit)
elif i > 1:
output.append('%d %ss' % (i, unit))
output.append('ago')
return ' '.join(output)


def grouper(n, iterable, fillvalue=None):
"""Return iterable that groups ``iterable`` into groups of length ``n``
Expand Down Expand Up @@ -88,6 +66,7 @@ def load_yahoo_rates(symbols):
count -= 1
continue
parts.append('{}{}=X'.format(REFERENCE_CURRENCY, symbol))

query = ','.join(parts)
url = YAHOO_BASE_URL.format(query)

Expand All @@ -102,18 +81,22 @@ def load_yahoo_rates(symbols):
for row in csv.reader(lines):
if not row:
continue

name, rate = row
m = parse_yahoo_response(name)
if not m:

if not m: # Couldn't get symbol
log.error('Invalid currency : {}'.format(name))
ycount += 1
continue
symbol = m.group(1)
rate = float(rate)
if rate == 0:

if rate == 0: # Yahoo! returns 0.0 as rate for unsupported currencies
log.error('No exchange rate for : {}'.format(name))
ycount += 1
continue

rates[symbol] = rate
ycount += 1

Expand Down Expand Up @@ -141,39 +124,20 @@ def fetch_currency_rates():


def main(wf):
global log
log = wf.logger
query = ''
if len(wf.args):
query = wf.args[0]

currencies = CURRENCIES.items()

if query:
currencies = wf.filter(query, currencies,
key=lambda t: ' '.join(t),
match_on=MATCH_ALL ^ MATCH_ALLCHARS,
min_score=30)

# currencies = filter_currencies(query)
if not currencies:
wf.add_item('No matching currencies found',
valid=False, icon=ICON_WARNING)
else:
if not query: # Show last update time
td = timedelta(seconds=wf.cached_data_age(CURRENCY_CACHE_NAME))
# currencies_updated = datetime.now() - timedelta(seconds=age)
wf.add_item('Exchange rates updated %s' % human_timedelta(td),
valid=False, icon=ICON_INFO)
for name, abbr in currencies:
# wf.add_item(abbr, name, valid=False, icon='money.png')
wf.add_item('%s — %s' % (abbr, name),
'Use the 3-letter currency code in conversions',
valid=False, icon=ICON_CURRENCY)

wf.send_feedback()
return 0

log.debug('Fetching exchange rates from Yahoo! ...')

exchange_rates = wf.cached_data(CURRENCY_CACHE_NAME,
fetch_currency_rates,
CURRENCY_CACHE_AGE)

log.debug('Exchange rates updated.')

for currency, rate in exchange_rates.items():
wf.logger.debug('1 EUR = {0} {1}'.format(rate, currency))


if __name__ == '__main__':
wf = Workflow()
log = wf.logger
wf.run(main)
Loading

0 comments on commit 2318554

Please sign in to comment.