Ejegg has uploaded a new change for review.
https://gerrit.wikimedia.org/r/159347
Change subject: Add OANDA and ECB importers, update with fallthrough
......................................................................
Add OANDA and ECB importers, update with fallthrough
TODO: tests
Change-Id: I4dad864e210480fd963a2bdaec854567de632a3d
---
M sites/all/modules/exchange_rates/exchange_rates.info
M sites/all/modules/exchange_rates/exchange_rates.install
M sites/all/modules/exchange_rates/exchange_rates.module
A sites/all/modules/exchange_rates/retrievers/EcbRetriever.php
A sites/all/modules/exchange_rates/retrievers/ExchangeRateRetriever.php
A sites/all/modules/exchange_rates/retrievers/OandaRetriever.php
6 files changed, 191 insertions(+), 81 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/crm
refs/changes/47/159347/1
diff --git a/sites/all/modules/exchange_rates/exchange_rates.info
b/sites/all/modules/exchange_rates/exchange_rates.info
index 87cbe69..37b6d62 100644
--- a/sites/all/modules/exchange_rates/exchange_rates.info
+++ b/sites/all/modules/exchange_rates/exchange_rates.info
@@ -2,3 +2,6 @@
description = Downloads exchange rates and converts currencies.
core = 7.x
version = 2.x-dev
+files[] = retrievers/EcbRetriever.php
+files[] = retrievers/ExchangeRateRetriever.php
+files[] = retrievers/OandaRetriever.php
\ No newline at end of file
diff --git a/sites/all/modules/exchange_rates/exchange_rates.install
b/sites/all/modules/exchange_rates/exchange_rates.install
index d23be01..27cee24 100644
--- a/sites/all/modules/exchange_rates/exchange_rates.install
+++ b/sites/all/modules/exchange_rates/exchange_rates.install
@@ -11,7 +11,7 @@
'not null' => TRUE,
),
'value_in_usd' => array(
- 'description' => t('The timestamp of the last update.'),
+ 'description' => t('USD value of a single unit of the currency.'),
'type' => 'float',
'size' => 'big',
'unsigned' => TRUE,
diff --git a/sites/all/modules/exchange_rates/exchange_rates.module
b/sites/all/modules/exchange_rates/exchange_rates.module
index eba8b29..7cb2258 100644
--- a/sites/all/modules/exchange_rates/exchange_rates.module
+++ b/sites/all/modules/exchange_rates/exchange_rates.module
@@ -1,5 +1,8 @@
<?php
// FIXME: some functions begin with the incorrect prefix "exchange_rate_"
+use exchange_rates\OandaRetriever;
+use exchange_rates\EcbRetriever;
+use exchange_rates\ExchangeRateUpdateException;
function exchange_rates_menu() {
$items = array();
@@ -360,88 +363,43 @@
'ZMK', // Zambian Kwacha
'ZWD', // Zimbabwe Dollar
);
-
- // Construct the URL for the API call
- $currencyList = implode( '_', $currencies );
- $url =
'https://web-services.oanda.com/cgi-bin/fxml/fxml?fxmlrequest=%3Cconvert%3E%3Cclient_id%3E';
- $url .= 'WikimediaFoundation';
- $url .= '%3C/client_id%3E%3Cexpr%3EUSD%3C/expr%3E%3Cexch%3E';
- $url .= $currencyList;
- $url .= '%3C/exch%3E%3C/convert%3E';
-
- // Retrieve and parse the XML results
- $request = drupal_http_request( $url );
- $xml = $request->data;
- $p = xml_parser_create();
- $parseSuccess = xml_parse_into_struct( $p, $xml, $results, $index );
- xml_parser_free( $p );
-
- if ( $parseSuccess ) { // make sure parsing the XML was successful
before proceeding
- //date_default_timezone_set( 'America/Los_Angeles' );
- $datetime = '';
- if ( isset( $results[$index['DATE'][0]]['value'] ) ) {
- $datetime = $results[$index['DATE'][0]]['value']; //
get datatime from XML
- }
- $bankUpdateTimestamp = strtotime( $datetime ); // the bank's
timestamp for the rate
- variable_set( 'exchange_rates_bank_update',
$bankUpdateTimestamp ); // set as persistent drupal var
-
- // For each currency, get the values from the XML and update
the table
- $currencyCount = count( $index['EXCH'] );
- for ( $x = 0; $x < $currencyCount; $x++ ) {
- $currency = $results[$index['EXCH'][$x]]['value']; //
the currency code
- $valueInUsd = $results[$index['BID'][$x]]['value']; //
the value of 1 $currency in US dollars
- exchange_rates_update_rate( $currency, $valueInUsd,
$bankUpdateTimestamp ); // update the table
- }
- } else {
- // Use our back-up XML source. This will only set the rate for
the most common currencies.
- watchdog(
- 'exchange_rates',
- 'Using backup exchange rates source from the ECB',
- array(),
- WATCHDOG_WARNING
- );
- $url = 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml';
-
- // Retrieve and parse the XML results
- $request = drupal_http_request( $url );
- $xml = $request->data;
- $p = xml_parser_create();
- xml_parse_into_struct( $p, $xml, $results, $index );
- xml_parser_free( $p );
-
- // Get date and base USD rate
- $usdBase = 0;
- $date = '';
- foreach ( $index['CUBE'] as $valIndex ) {
- $current = $results[$valIndex];
- if ( $current['attributes']['CURRENCY'] == 'USD' &&
isset( $current['attributes']['RATE'] ) ) {
- $usdBase = $current['attributes']['RATE'];
- }
- if ( isset( $current['attributes']['TIME'] ) ) {
- $date = $current['attributes']['TIME'];
- }
- }
- $bankUpdateTimestamp = strtotime( $date . ' 00:00:00 GMT' );
- variable_set( 'exchange_rates_bank_update',
$bankUpdateTimestamp );
-
- // Table is based on EUR, so must insert manually if we
actually got anything
- if ( $usdBase !== 0 ) {
- exchange_rates_update_rate( 'EUR', $usdBase, $bankUpdateTimestamp
);
- // Calculate and insert remaining rates
- foreach ( $index['CUBE'] as $valIndex ) {
- $current = $results[$valIndex];
- if ( isset( $current['attributes']['CURRENCY'] ) && isset(
$current['attributes']['RATE'] ) ) {
- exchange_rates_update_rate(
- $current['attributes']['CURRENCY'],
- $usdBase / $current['attributes']['RATE'],
- $bankUpdateTimestamp
- );
- }
- }
- }
+ $retrievers = array();
+ $oanda_key = variable_get( 'exchange_rates_key_oanda', '' );
+ if ( $oanda_key === '' ) {
+ watchdog( 'exchange_rates', 'OANDA API key not set! Will fall
back to ECB', array(), WATCHDOG_ERROR );
+ } else {
+ $retrievers[] = new OandaRetriever( 'drupal_http_request',
$oanda_key );
}
-
+
+ $retrievers[] = new EcbRetriever( 'drupal_http_request' );
+ $result = null;
+
+ foreach ( $retrievers as $retriever ) {
+ try {
+ $result = $retriever->updateRates( $currencies );
+ break;
+ } catch ( ExchangeRateUpdateException $ex) {
+ watchdog( 'exchange_rates', $ex );
+ }
+ }
+ if ( $result === null ) {
+ watchdog( 'exchange_rates', 'Could not update exchange rates
from any provider!' );
+ return;
+ }
+
+ $date_set = false;
+ foreach( $result->rates as $code => $rate ) {
+ exchange_rates_update_rate( $code, $rate['value'],
$rate['date'] );
+ if ( !$date_set ) {
+ variable_set( 'exchange_rates_bank_update',
$rate['date'] );
+ $date_set = true;
+ }
+ }
+
+ if ( $result->quotesRemaining > -1 ) {
+ variable_set( 'exchange_rates_remaining_queries',
$result->quotesRemaining );
+ }
}
function exchange_rates_update_rate($currency, $value_in_usd, $bank_update) {
diff --git a/sites/all/modules/exchange_rates/retrievers/EcbRetriever.php
b/sites/all/modules/exchange_rates/retrievers/EcbRetriever.php
new file mode 100644
index 0000000..b4b92ba
--- /dev/null
+++ b/sites/all/modules/exchange_rates/retrievers/EcbRetriever.php
@@ -0,0 +1,53 @@
+<?php
+namespace exchange_rates;
+
+class EcbRetriever extends ExchangeRateRetriever {
+
+ public function updateRates( $currencies ) {
+ $url = 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml';
+
+ // Retrieve and parse the XML results
+ $request = call_user_func( $this->httpRequester, $url );
+ $xml = $request->data;
+ $p = xml_parser_create();
+ $results = array();
+ $index = array();
+ xml_parse_into_struct( $p, $xml, $results, $index );
+ xml_parser_free( $p );
+
+ // Get date and base USD rate
+ $usdBase = 0;
+ $date = '';
+ foreach ( $index['CUBE'] as $valIndex ) {
+ $current = $results[$valIndex];
+ if ( $current['attributes']['CURRENCY'] == 'USD' &&
isset( $current['attributes']['RATE'] ) ) {
+ $usdBase = $current['attributes']['RATE'];
+ }
+ if ( isset( $current['attributes']['TIME'] ) ) {
+ $date = $current['attributes']['TIME'];
+ }
+ }
+ $bankUpdateTimestamp = strtotime( $date . ' 00:00:00 GMT' );
+ $result = new ExchangeRateUpdateResult();
+
+ // Table is based on EUR, so must insert manually if we
actually got anything
+ if ( $usdBase !== 0 ) {
+ $result->rates['EUR'] = array(
+ 'value' => $usdBase,
+ 'date' => $bankUpdateTimestamp
+ );
+
+ // Calculate and insert remaining rates
+ foreach ( $index['CUBE'] as $valIndex ) {
+ $current = $results[$valIndex];
+ if ( isset( $current['attributes']['CURRENCY'] ) && isset(
$current['attributes']['RATE'] ) ) {
+ $result->rates[$current['attributes']['CURRENCY']] = array(
+ 'value' => $usdBase /
$current['attributes']['RATE'],
+ 'date' => $bankUpdateTimestamp
+ );
+ }
+ }
+ }
+ return $result;
+ }
+}
diff --git
a/sites/all/modules/exchange_rates/retrievers/ExchangeRateRetriever.php
b/sites/all/modules/exchange_rates/retrievers/ExchangeRateRetriever.php
new file mode 100644
index 0000000..0876d64
--- /dev/null
+++ b/sites/all/modules/exchange_rates/retrievers/ExchangeRateRetriever.php
@@ -0,0 +1,41 @@
+<?php
+namespace exchange_rates;
+
+use \Exception;
+use \InvalidArgumentException;
+
+abstract class ExchangeRateRetriever {
+ protected $httpRequester;
+
+ /**
+ * @param callable $httpRequester - either drupal_http_request or a fake
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $httpRequester ) {
+ if ( !is_callable( $httpRequester ) ) {
+ throw new InvalidArgumentException( 'httpRequester
should be callable' );
+ }
+ $this->httpRequester = $httpRequester;
+ }
+
+ /**
+ * Retrieve updated rates using $this->httpRequester
+ * @param array $currencies - list of currency codes to update
+ * @return ExchangeRateUpdateResult
+ */
+ abstract function updateRates( $currencies );
+}
+
+class ExchangeRateUpdateResult {
+ /**
+ * @var array key is currency code, value is array with two keys:
+ * 'value' = USD value of a single unit, 'date' = UTC timestamp
+ */
+ public $rates;
+ /**
+ * @var int number of quotes remaining
+ */
+ public $quotesRemaining = -1;
+}
+
+class ExchangeRateUpdateException extends Exception {}
diff --git a/sites/all/modules/exchange_rates/retrievers/OandaRetriever.php
b/sites/all/modules/exchange_rates/retrievers/OandaRetriever.php
new file mode 100644
index 0000000..ce56028
--- /dev/null
+++ b/sites/all/modules/exchange_rates/retrievers/OandaRetriever.php
@@ -0,0 +1,55 @@
+<?php
+namespace exchange_rates;
+
+class OandaRetriever extends ExchangeRateRetriever {
+
+ protected $key;
+ protected $endpoint = 'https://web-services.oanda.com/rates/api';
+
+ public function __construct( $httpRequester, $key ) {
+ parent::__construct($httpRequester);
+ $this->key = $key;
+ }
+
+ public function updateRates( $currencies ) {
+ $params = array(
+ 'fields' => 'midpoint',
+ 'decimal_places' => 'all',
+ );
+ $url = $this->endpoint .
+ '/v1/rates/USD.json?' .
+ http_build_query( $params ) .
+ '"e=' . implode ( '"e=', $currencies );
+
+ $response = call_user_func(
+ $this->httpRequester,
+ $url,
+ array(
+ 'headers' => array(
+ 'Authorization' => 'Bearer ' .
$this->key,
+ ),
+ )
+ );
+ if ( $response->code != 200 ) {
+ throw new ExchangeRateUpdateException( "OANDA API
endpoint returned code {$response->code}" );
+ }
+ $result = new ExchangeRateUpdateResult();
+ if ( array_key_exists( 'x-rate-limit-remaining',
$response->headers ) ) {
+ $remaining =
$response->headers['x-rate-limit-remaining'];
+ if ( is_numeric( $remaining ) ) {
+ $result->quotesRemaining = (int) $remaining;
+ }
+ }
+ $json = json_decode( $response->data );
+ if ( $json === null ) {
+ throw new ExchangeRateUpdateException( "OANDA response
was null or invalid JSON. Data: {$response->data}" );
+ }
+ foreach ( $json->quotes as $code => $quote ) {
+ $result->rates[$code] = array(
+ 'value' => $quote->midpoint,
+ 'date' => strtotime( $quote->date )
+ );
+ }
+ return $result;
+ }
+}
--
To view, visit https://gerrit.wikimedia.org/r/159347
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4dad864e210480fd963a2bdaec854567de632a3d
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/fundraising/crm
Gerrit-Branch: master
Gerrit-Owner: Ejegg <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits