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 ) .
+                       '&quote=' . implode ( '&quote=', $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

Reply via email to