Virgil Dupras avatar Virgil Dupras committed c54b69b

Initial Commit.

Comments (0)

Files changed (11)

+syntax: glob
+
+*.pyc
+.DS_Store
+hsutil = http://hg.hardcoded.net/hsutil
+1a237159f511ef98a95a5422ad66c3edf699f1e3 hsutil
+Copyright 2010 Hardcoded Software Inc. (http://www.hardcoded.net)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the name of Hardcoded Software Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Empty file added.

+#!/usr/bin/env python
+# Unit Name: currency_server.db
+# Created By: Virgil Dupras
+# Created On: 2008-04-20
+# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
+# 
+# This software is licensed under the "BSD" License as described in the "LICENSE" file, 
+# which should be included with this package. The terms are also available at 
+# http://www.hardcoded.net/licenses/bsd_license
+
+from datetime import date, datetime, timedelta
+import xml.etree.cElementTree as ET
+import xml.parsers.expat
+
+from hsutil.currency import Currency, RatesDB as RatesDBBase
+from hsutil import sqlite
+
+
+DB_PATH = '/var/currency_server/currency.db'
+
+
+class RatesDB(RatesDBBase):
+    """The RatesDB on the server side automatically updates itself using Bank of Canada's rates
+    
+    Bank of Canada uses n/a values for week-ends, holidays and future dates. We want to ignore those
+    values when importing.
+    """
+    def __init__(self, dbpath=DB_PATH):
+        RatesDBBase.__init__(self, sqlite.ThreadedConn(dbpath, False))
+    
+    def get_CAD_values(self, start, end, currency_code):
+        """Returns [(date, value)] for each CAD value the DB has for 'currency'.
+        
+        The values are in date order.
+        """
+        str_start = '%d%02d%02d' % (start.year, start.month, start.day)
+        str_end = '%d%02d%02d' % (end.year, end.month, end.day)
+        sql = "select date, rate from rates where date >= ? and date <= ? and currency = ?"
+        cur = self.con.execute(sql, [str_start, str_end, currency_code])
+        return [(datetime.strptime(date, '%Y%m%d').date(), rate) for (date, rate) in cur]
+    
+    def import_bank_of_canada_rates(self, source):
+        """Import rates from a Bank of Canada lookup xml file"""
+        root = ET.parse(source).getroot()
+        for observation in root.getiterator('Observation'):
+            currency_element = observation.find('Currency_name')
+            name = currency_element.text.strip()
+            # Some currency names have (), some not, but if we can't find it, try without the ()
+            if name not in Currency.by_name:
+                name = name.split('(')[0].strip() # Remove parenthesis after the name
+            currency_code = Currency(name=name).code
+            date_element = currency_element.find('Observation_date')
+            rate_element = currency_element.find('Observation_data')
+            try:
+                rate = float(rate_element.text.strip())
+            except ValueError: # probably n/a
+                continue
+            year, month, day = date_element.text.strip().split('-')
+            self.set_CAD_value(date(int(year), int(month), int(day)), currency_code, rate)
+#!/usr/bin/env python
+# Unit Name: currency_server.db_test
+# Created By: Virgil Dupras
+# Created On: 2008-04-20
+# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
+# 
+# This software is licensed under the "BSD" License as described in the "LICENSE" file, 
+# which should be included with this package. The terms are also available at 
+# http://www.hardcoded.net/licenses/bsd_license
+
+from datetime import date
+import threading
+
+from hsutil.path import Path
+from hsutil.testcase import TestCase as TestCaseBase
+
+from .db import RatesDB
+
+class TestCase(TestCaseBase):
+    @classmethod
+    def datadirpath(cls):
+        return Path(__file__)[:-1] + 'testdata'
+    
+
+class BOCDailyImport(TestCase):
+    """testdata/xml/bankofcanada_daily_rates_1_week.xml contains daily rates for a USD, EUR and PLN
+    for 1 week. To be successfully imported, the currency names have to be translated to code,
+    and stuff in parenthesis have to be ignored for the translation. The xml also contain some n/a
+    values that have to be handled.
+    """
+    def setUp(self):
+        self.db = RatesDB(':memory:')
+        xmlpath = self.filepath('bankofcanada_daily_rates_1_week.xml')
+        self.db.import_bank_of_canada_rates(unicode(xmlpath))
+    
+    def test_get_rates(self):
+        """Try a couple of rates and make sure they're correct"""
+        self.assertAlmostEqual(self.db.get_rate(date(2008, 4, 17), 'USD', 'CAD'), 1.0122)
+        self.assertAlmostEqual(self.db.get_rate(date(2008, 4, 21), 'USD', 'CAD'), 1.0060)
+        self.assertAlmostEqual(self.db.get_rate(date(2008, 4, 17), 'EUR', 'CAD'), 1.6103)
+        self.assertAlmostEqual(self.db.get_rate(date(2008, 4, 16), 'PLN', 'CAD'), 0.4675)
+    
+
+class DBWithDailyAndMeanRate(TestCase):
+    def setUp(self):
+        self.db = RatesDB(':memory:')
+        self.db.set_CAD_value(date(2008, 3, 25), 'USD', 1.0203)
+        self.db.set_CAD_value(date(2008, 3, 24), 'USD', 1.0123)
+    
+    def test_get_CAD_values(self):
+        """Returns the correct values, in the correct order"""
+        start = date(2008, 3, 20)
+        end = date(2008, 3, 25)
+        result = self.db.get_CAD_values(start, end, 'USD')
+        expected = [
+            (date(2008, 3, 24), 1.0123),
+            (date(2008, 3, 25), 1.0203),
+        ]
+        self.assertEqual(len(result), len(expected))
+        for pair1, pair2 in zip(result, expected):
+            self.assertEqual(pair1[0], pair2[0])
+            if pair1[1] is None:
+                self.assertTrue(pair2[1] is None)
+            else:
+                self.assertAlmostEqual(pair1[1], pair2[1])
+    
+    def test_get_CAD_values_threaded(self):
+        """get_CAD_values() can be called by different threads"""
+        def do():
+            mydate = date(2008, 3, 24)
+            result = self.db.get_CAD_values(mydate, mydate, 'USD')
+            self.assertEqual(result, [(mydate, 1.0123)])
+        threading.Thread(target=do).start()
+        threading.Thread(target=do).start()
+        self.jointhreads()
+    

fetch_boc_rates.py

+#!/usr/bin/env python
+# This script fetches today's rates from the Bank of Canada website
+# Created By: Eric Mc Sween
+# Created On: 2008-05-19
+# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
+# 
+# This software is licensed under the "BSD" License as described in the "LICENSE" file, 
+# which should be included with this package. The terms are also available at 
+# http://www.hardcoded.net/licenses/bsd_license
+
+import sys
+import re
+from datetime import date
+from urllib import urlencode, urlopen
+
+from currency_server.db import RatesDB
+
+
+# Open the db
+db = RatesDB()
+
+# Make the first query
+
+db_min, db_max = db.date_range('USD')
+start = db_max # We'll always refetch yesterday's data in case there was some missing.
+end = date.today()
+form_data = {
+    # Hidden
+    'cgi': 'famescript',
+    'pageName': 'exchange-look.html',
+    'famescript_formname': 'exchange-look.html',
+    'famescript_html_template': 'fxlook-nojs.html',
+    'ExchangeRates_frequency': 'business',
+    'ExchangeRates_daterange': '',
+    'ExchangeRates_replace_NC': 'Not available',
+    'ExchangeRates_replace_ND': 'Not available',
+    'ExchangeRates_replace_NA': 'Bank holiday',
+
+    # Select options
+    'show_xml': 'TRUE',
+
+    # Select dates
+    'ExchangeRates_dateclause': '=ExchangeRatesByDates',
+    'ExchangeRatesByDates_frequency': 'daily',
+    'ExchangeRatesByDates_daterange_start_Month': start.strftime('%b'),
+    'ExchangeRatesByDates_daterange_start_Day': start.day,
+    'ExchangeRatesByDates_daterange_start_Year': start.year,
+    'ExchangeRatesByDates_daterange_end_Month': end.strftime('%b'),
+    'ExchangeRatesByDates_daterange_end_Day': end.day,
+    'ExchangeRatesByDates_daterange_end_Year': end.year,
+
+    # Select currencies
+    'ExchangeRates': [
+        'IEXE0101',  # U.S. dollar (noon)
+        'IEXE2702',  # Argentine peso (floating rate)
+        'IEXE1601',  # Australian dollar
+        'IEXE6001',  # Bahamian dollar
+        'IEXE2801',  # Brazilian real
+        'IEXE4501',  # CFA franc
+        'IEXE4601',  # CFP franc
+        'IEXE2901',  # Chilean peso
+        'IEXE2201',  # Chinese renminbi (yuan)
+        'IEXE3901',  # Colombian peso
+        'IEXE6101',  # Croatian kuna
+        'IEXE2301',  # Czech Republic koruna
+        'IEXE0301',  # Danish krone
+        'IEXE4001',  # East Caribbean dollar
+        'EUROCAE01', # European Euro
+        'IEXE4101',  # Fiji dollar
+        'IEXE4702',  # Ghanaian cedi (new)
+        'IEXE6501',  # Guatemalan quetzal
+        'IEXE4301',  # Honduran lempira
+        'IEXE1401',  # Hong Kong dollar
+        'IEXE2501',  # Hungarian forint
+        'IEXE4401',  # Icelandic krona
+        'IEXE3001',  # Indian rupee
+        'IEXE2601',  # Indonesian rupiah
+        'IEXE5301',  # Israeli new shekel
+        'IEXE6401',  # Jamaican dollar
+        'IEXE0701',  # Japanese yen
+        'IEXE3201',  # Malaysian ringgit
+        'IEXE2001',  # Mexican peso
+        'IEXE4801',  # Moroccan dirham
+        'IEXE3801',  # Myanmar (Burma) kyat
+        'IEXE4901',  # Neth. Antilles florin
+        'IEXE1901',  # New Zealand dollar
+        'IEXE0901',  # Norwegian krone
+        'IEXE5001',  # Pakistan rupee
+        'IEXE5101',  # Panamanian balboa
+        'IEXE5201',  # Peruvian new sol
+        'IEXE3301',  # Philippine peso
+        'IEXE2401',  # Polish zloty
+        'IEXE6505',  # Romanian new leu
+        'IEXE2101',  # Russian rouble
+        'IEXE6504',  # Serbian dinar
+        'IEXE3701',  # Singapore dollar
+        'IEXE6201',  # Slovak koruna
+        'IEXE3401',  # South African rand
+        'IEXE3101',  # South Korean won
+        'IEXE5501',  # Sri Lanka rupee
+        'IEXE1001',  # Swedish krona
+        'IEXE1101',  # Swiss franc
+        'IEXE3501',  # Taiwanese new dollar
+        'IEXE3601',  # Thai baht
+        #'IEXE5601',  # Trinidad & Tobago dollar
+        'IEXE5701',  # Tunisian dinar
+        #'IEXE5802',  # Turkish new lira
+        'IEXE6506',  # U.A.E. dirham
+        'IEXE1201',  # U.K. pound sterling
+        'IEXE5902',  # Venezuelan bolivar fuerte
+        'IEXE6503',  # Vietnamese dong
+    ],
+}
+response = urlopen('http://www.bankofcanada.ca/cgi-bin/famecgi_fdps', urlencode(form_data, True))
+
+# Find the XML file's URL
+
+url_re = re.compile(r'/databank/client_output/.*\.xml')
+for line in response:
+    match = url_re.search(line)
+    if match:
+        xml_url = match.group()
+        break
+else:
+    sys.exit("Error: couldn't find the XML file's URL in the response: %s" % response.read())
+
+# Fetch the XML file
+
+xml_file = urlopen('http://www.bankofcanada.ca' + xml_url)
+db.import_bank_of_canada_rates(xml_file)

fetch_yahoo_rates.py

+#!/usr/bin/env python
+# This script fetches today's rates from Yahoo Finance for some currencies that are not at BOC
+# I have not yet figured how to fetch more than one currency from a single request.
+# Created By: Virgil Dupras
+# Created On: 2008-11-08
+# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
+# 
+# This software is licensed under the "BSD" License as described in the "LICENSE" file, 
+# which should be included with this package. The terms are also available at 
+# http://www.hardcoded.net/licenses/bsd_license
+
+import time
+from datetime import date
+from urllib import urlencode, urlopen
+
+from currency_server.db import RatesDB
+
+CURRENCIES_TO_FETCH = ['BHD', 'EGP']
+
+# Open the db
+db = RatesDB()
+
+def fetch_currency(currency):
+    # the result of this request is a single CSV line like this:
+    # "CADBHD=X",0.3173,"11/7/2008","5:11pm",N/A,N/A,N/A,N/A,N/A 
+    response = urlopen('http://download.finance.yahoo.com/d/quotes.csv?s=%sCAD=X&f=sl1d1t1c1ohgv&e=.csv' % currency)
+    content = response.read()
+    rate = float(content.split(',')[1])
+    db.set_CAD_value(date.today(), currency, rate)
+
+for currency in CURRENCIES_TO_FETCH:
+    fetch_currency(currency)
+    time.sleep(5)
+

modpythonhandler.py

+# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
+# 
+# This software is licensed under the "BSD" License as described in the "LICENSE" file, 
+# which should be included with this package. The terms are also available at 
+# http://www.hardcoded.net/licenses/bsd_license
+
+import sys
+import SimpleXMLRPCServer
+import logging
+from datetime import datetime
+
+from currency_server import db
+
+LOGFILE = '/var/currency_server/access.log'
+
+logging.basicConfig(filename=LOGFILE, level=logging.INFO, format='%(asctime)s %(message)s')
+
+def handler(req):
+    req.write(dispatcher._marshaled_dispatch(req.read()))
+    return 0 # apache.OK
+
+dispatcher = SimpleXMLRPCServer.SimpleXMLRPCDispatcher(True, 'utf-8')
+
+# Test method
+
+def hello():
+    return sys.version
+dispatcher.register_function(hello, 'hello')
+
+# Wrapper around the get_rates() method
+
+RATES_DB = db.RatesDB(db.DB_PATH)
+def get_CAD_values(start, end, currency):
+    logging.info('%s %s %s' % (start, end, currency))
+    start = datetime.strptime(start.value, "%Y%m%dT%H:%M:%S")
+    end = datetime.strptime(end.value, "%Y%m%dT%H:%M:%S")
+    return RATES_DB.get_CAD_values(start, end, currency)
+dispatcher.register_function(get_CAD_values, 'get_CAD_values')

testdata/bankofcanada_daily_rates_1_week.xml

+<?xml version="1.0" encoding="ISO-8859-1"?>
+<Currency xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Observation>
+    <Currency_name>
+        U.S. dollar (close)
+        <Observation_date>2008-04-16</Observation_date>
+        <Observation_data>1.0014</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        U.S. dollar (close)
+        <Observation_date>2008-04-17</Observation_date>
+        <Observation_data>1.0122</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        U.S. dollar (close)
+        <Observation_date>2008-04-18</Observation_date>
+        <Observation_data>1.0049</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        U.S. dollar (close)
+        <Observation_date>2008-04-21</Observation_date>
+        <Observation_data>1.0060</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        U.S. dollar (close)
+        <Observation_date>2008-04-22</Observation_date>
+        <Observation_data>na</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        European Euro
+        <Observation_date>2008-04-16</Observation_date>
+        <Observation_data>1.6018</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        European Euro
+        <Observation_date>2008-04-17</Observation_date>
+        <Observation_data>1.6103</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        European Euro
+        <Observation_date>2008-04-18</Observation_date>
+        <Observation_data>1.5873</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        European Euro
+        <Observation_date>2008-04-21</Observation_date>
+        <Observation_data>1.5987</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        European Euro
+        <Observation_date>2008-04-22</Observation_date>
+        <Observation_data>na</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        Polish zloty
+        <Observation_date>2008-04-16</Observation_date>
+        <Observation_data>0.4675</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        Polish zloty
+        <Observation_date>2008-04-17</Observation_date>
+        <Observation_data>0.4702</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        Polish zloty
+        <Observation_date>2008-04-18</Observation_date>
+        <Observation_data>0.4624</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        Polish zloty
+        <Observation_date>2008-04-21</Observation_date>
+        <Observation_data>0.4684</Observation_data>
+    </Currency_name>
+</Observation>
+<Observation>
+    <Currency_name>
+        Polish zloty
+        <Observation_date>2008-04-22</Observation_date>
+        <Observation_data>na</Observation_data>
+    </Currency_name>
+</Observation>
+</Currency>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.