[gnome-calculator/wip/currency-conversion-plugins: 18/19] Merge branch 'wip/async-currency' into wip/currency-conversion-plugins




commit a343f06616f09e4c90fba974e21ff367404ad2a8
Merge: 6385d4d6 38fb8875
Author: Robert Roth <robert roth off gmail com>
Date:   Thu Jan 14 14:51:33 2021 +0200

    Merge branch 'wip/async-currency' into wip/currency-conversion-plugins

 lib/currency-provider.vala | 50 +++++++++++++++++++++++++++++++++++++++++-----
 lib/currency.vala          |  2 --
 2 files changed, 45 insertions(+), 7 deletions(-)
---
diff --cc lib/currency-provider.vala
index 1136867a,00000000..8dde4386
mode 100644,000000..100644
--- a/lib/currency-provider.vala
+++ b/lib/currency-provider.vala
@@@ -1,337 -1,0 +1,377 @@@
 +public interface CurrencyProvider : Object {
 +
 +    public signal void updated ();
 +
-     public abstract void update_rates ();
++    public abstract void update_rates (bool asyncLoad = false);
 +
 +    public abstract void set_refresh_interval (int interval);
 +}
 +
 +abstract class AbstractCurrencyProvider : Object, CurrencyProvider {
 +
 +    public abstract string rate_filepath {owned get ;}
 +
 +    public abstract string rate_source_url {get;}
 +
 +    public abstract string source_name {get;}
 +
 +    public int refresh_interval { get; set; }
 +
-     public override void set_refresh_interval (int interval) {
++    public void set_refresh_interval (int interval) {
++        loaded = false;
 +        this.refresh_interval = interval;
++        update_rates ();
 +    }
 +
 +    private bool loading;
 +    private bool loaded;
 +    private List<Currency> currencies;
 +
-     public void update_rates () {
++    public void update_rates (bool asyncLoad = true) {
++        debug ("Updating %s rates ".printf(source_name));
++
 +        if (loading || loaded) return;
 +
++        debug ("Checking %s rates ".printf(source_name));
++
 +        if (!file_needs_update (rate_filepath, refresh_interval )) return;
 +
++        debug ("Loading %s rates ".printf(source_name));
++
 +        loading = true;
 +
-         download_file.begin (rate_source_url, rate_filepath, source_name);
++        if (asyncLoad)
++            download_file_async.begin (rate_source_url, rate_filepath, source_name);
++        else {
++            download_file_sync (rate_source_url, rate_filepath, source_name);
++            do_load_rates ();
++        }
++            
++
 +    }
 +
 +    protected Currency? get_currency (string name)
 +    {
 +        return CurrencyManager.get_default ().get_currency (name);
 +    }
 +
 +    protected virtual void do_load_rates () {
++        debug ("Loaded %s rates ".printf(source_name));
 +        loaded = true;
 +        updated ();
 +    }
 +
 +    /* A file needs to be redownloaded if it doesn't exist, or is too old.
 +     * When an error occur, it probably won't hurt to try to download again.
 +     */
 +    private bool file_needs_update (string filename, double max_age)
 +    {
 +        if (max_age == 0)
 +            return false;
 +
 +        if (!FileUtils.test (filename, FileTest.IS_REGULAR))
 +            return true;
 +
 +        var buf = Posix.Stat ();
 +        if (Posix.stat (filename, out buf) == -1)
 +            return true;
 +
 +        var modify_time = buf.st_mtime;
 +        var now = time_t ();
 +        if (now - modify_time > max_age)
 +            return true;
 +
 +        return false;
 +    }
 +
-     private async void download_file (string uri, string filename, string source)
++    private async void download_file_sync (string uri, string filename, string source)
++    {
++
++        var directory = Path.get_dirname (filename);
++        DirUtils.create_with_parents (directory, 0755);
++
++        var dest = File.new_for_path (filename);
++        var session = new Soup.Session ();
++        var message = new Soup.Message ("GET", uri);
++        try
++        {
++            var bodyinput = session.send (message);
++            var output = dest.replace (null, false, FileCreateFlags.REPLACE_DESTINATION);
++            output.splice (bodyinput, OutputStreamSpliceFlags.CLOSE_SOURCE | 
OutputStreamSpliceFlags.CLOSE_TARGET);
++            loading = false;
++            do_load_rates ();
++            debug ("%s rates updated", source);
++        }
++        catch (Error e)
++        {
++            warning ("Couldn't download %s currency rate file: %s", source, e.message);
++        }
++    }
++    
++    private async void download_file_async (string uri, string filename, string source)
 +    {
 +
 +        var directory = Path.get_dirname (filename);
 +        DirUtils.create_with_parents (directory, 0755);
 +
 +        var dest = File.new_for_path (filename);
 +        var session = new Soup.Session ();
 +        var message = new Soup.Message ("GET", uri);
 +        try
 +        {
 +            var bodyinput = yield session.send_async (message);
 +            var output = yield dest.replace_async (null, false, FileCreateFlags.REPLACE_DESTINATION, 
Priority.DEFAULT);
 +            yield output.splice_async (bodyinput,
 +                                       OutputStreamSpliceFlags.CLOSE_SOURCE | 
OutputStreamSpliceFlags.CLOSE_TARGET,
 +                                       Priority.DEFAULT);
 +            loading = false;
 +            do_load_rates ();
 +            debug ("%s rates updated", source);
 +        }
 +        catch (Error e)
 +        {
 +            warning ("Couldn't download %s currency rate file: %s", source, e.message);
 +        }
 +    }
 +
 +}
 +
 +class ImfCurrencyProvider : AbstractCurrencyProvider {
 +    public override string rate_filepath { owned get {
 +        return Path.build_filename (Environment.get_user_cache_dir (), "gnome-calculator", "rms_five.xls"); 
} }
 +
 +    public override string rate_source_url { get {
 +        return "https://www.imf.org/external/np/fin/data/rms_five.aspx?tsvflag=Y";; } }
 +
 +    public override string source_name { get { return "IMF";} }
 +
 +    private HashTable <string, string> get_name_map () {
 +        HashTable <string, string> name_map = new HashTable <string, string> (str_hash, str_equal);
 +        name_map.insert ("Euro", "EUR");
 +        name_map.insert ("Japanese yen", "JPY");
 +        name_map.insert ("U.K. pound", "GBP");
 +        name_map.insert ("U.S. dollar", "USD");
 +        name_map.insert ("Algerian dinar", "DZD");
 +        name_map.insert ("Australian dollar", "AUD");
 +        name_map.insert ("Bahrain dinar", "BHD");
 +        name_map.insert ("Bangladeshi taka", "BDT");
 +        name_map.insert ("Botswana pula", "BWP");
 +        name_map.insert ("Brazilian real", "BRL");
 +        name_map.insert ("Brunei dollar", "BND");
 +        name_map.insert ("Canadian dollar", "CAD");
 +        name_map.insert ("Chilean peso", "CLP");
 +        name_map.insert ("Chinese yuan", "CNY");
 +        name_map.insert ("Colombian peso", "COP");
 +        name_map.insert ("Czech koruna", "CZK");
 +        name_map.insert ("Danish krone", "DKK");
 +        name_map.insert ("Hungarian forint", "HUF");
 +        name_map.insert ("Icelandic krona", "ISK");
 +        name_map.insert ("Indian rupee", "INR");
 +        name_map.insert ("Indonesian rupiah", "IDR");
 +        name_map.insert ("Iranian rial", "IRR");
 +        name_map.insert ("Israeli New Shekel", "ILS");
 +        name_map.insert ("Kazakhstani tenge", "KZT");
 +        name_map.insert ("Korean won", "KRW");
 +        name_map.insert ("Kuwaiti dinar", "KWD");
 +        name_map.insert ("Libyan dinar", "LYD");
 +        name_map.insert ("Malaysian ringgit", "MYR");
 +        name_map.insert ("Mauritian rupee", "MUR");
 +        name_map.insert ("Mexican peso", "MXN");
 +        name_map.insert ("Nepalese rupee", "NPR");
 +        name_map.insert ("New Zealand dollar", "NZD");
 +        name_map.insert ("Norwegian krone", "NOK");
 +        name_map.insert ("Omani rial", "OMR");
 +        name_map.insert ("Pakistani rupee", "PKR");
 +        name_map.insert ("Peruvian sol", "PEN");
 +        name_map.insert ("Philippine peso", "PHP");
 +        name_map.insert ("Polish zloty", "PLN");
 +        name_map.insert ("Qatari riyal", "QAR");
 +        name_map.insert ("Russian ruble", "RUB");
 +        name_map.insert ("Saudi Arabian riyal", "SAR");
 +        name_map.insert ("Singapore dollar", "SGD");
 +        name_map.insert ("South African rand", "ZAR");
 +        name_map.insert ("Sri Lankan rupee", "LKR");
 +        name_map.insert ("Swedish krona", "SEK");
 +        name_map.insert ("Swiss franc", "CHF");
 +        name_map.insert ("Thai baht", "THB");
 +        name_map.insert ("Trinidadian dollar", "TTD");
 +        name_map.insert ("Tunisian dinar", "TND");
 +        name_map.insert ("U.A.E. dirham", "AED");
 +        name_map.insert ("Uruguayan peso", "UYU");
 +        name_map.insert ("Bolivar Fuerte", "VEF");
 +        return name_map;
 +    }
 +
 +    protected override void do_load_rates ()
 +    {
 +        var name_map = get_name_map ();
 +
 +        string data;
 +        try
 +        {
 +            FileUtils.get_contents (rate_filepath, out data);
 +        }
 +        catch (Error e)
 +        {
 +            warning ("Failed to read exchange rates: %s", e.message);
 +            return;
 +        }
 +
 +        var lines = data.split ("\n", 0);
 +
 +        var in_data = false;
 +        foreach (var line in lines)
 +        {
 +            line = line.chug ();
 +
 +            /* Start after first blank line, stop on next */
 +            if (line == "")
 +            {
 +                if (!in_data)
 +                {
 +                   in_data = true;
 +                   continue;
 +                }
 +                else
 +                   break;
 +            }
 +            if (!in_data)
 +                continue;
 +
 +            var tokens = line.split ("\t", 0);
 +            if (tokens[0] != "Currency")
 +            {
 +                int value_index;
 +                for (value_index = 1; value_index < tokens.length; value_index++)
 +                {
 +                    var value = tokens[value_index].chug ();
 +                    if (value != "")
 +                        break;
 +                }
 +
 +                if (value_index < tokens.length)
 +                {
 +                    var symbol = name_map.lookup (tokens[0]);
 +                    if (symbol != null)
 +                    {
 +                        var c = get_currency (symbol);
 +                        var value = mp_set_from_string (tokens[value_index]);
 +                        /* Use data if we have a valid value */
 +                        if (c == null && value != null)
 +                        {
 +                            debug ("Using IMF rate of %s for %s", tokens[value_index], symbol);
 +                            c = CurrencyManager.get_default ().add_currency (symbol, source_name);
 +                            value = value.reciprocal ();
 +                            if (c != null)
 +                                c.set_value (value);
 +                        }
 +                    }
 +                    else
 +                        warning ("Unknown currency '%s'", tokens[0]);
 +                }
 +            }
 +        }
 +        base.do_load_rates ();
 +    }
 +}
 +
 +
 +class EcbCurrencyProvider : AbstractCurrencyProvider {
 +    public override string rate_filepath { owned get {
 +        return Path.build_filename (Environment.get_user_cache_dir (), "gnome-calculator", 
"eurofxref-daily.xml"); } }
 +
 +    public override string rate_source_url { get {
 +        return "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";; } }
 +
 +    public override string source_name { get { return "ECB";} }
 +
 +    protected override void do_load_rates ()
 +    {
 +        /* Scale rates to the EUR value */
 +        var eur_rate = get_currency ("EUR");
 +        if (eur_rate == null)
 +        {
 +            warning ("Cannot use ECB rates as don't have EUR rate");
 +            return;
 +        }
 +
 +        /* Set some fixed rates */
 +        set_ecb_fixed_rate ("BDT", "0.0099", eur_rate);
 +        set_ecb_fixed_rate ("RSD", "0.0085", eur_rate);
 +        set_ecb_fixed_rate ("EEK", "0.06391", eur_rate);
 +        set_ecb_fixed_rate ("CFA", "0.00152449", eur_rate);
 +
 +        Xml.Parser.init ();
 +        var document = Xml.Parser.read_file (rate_filepath);
 +        if (document == null)
 +        {
 +            warning ("Couldn't parse ECB rate file %s", rate_filepath);
 +            return;
 +        }
 +
 +        var xpath_ctx = new Xml.XPath.Context (document);
 +        if (xpath_ctx == null)
 +        {
 +            warning ("Couldn't create XPath context");
 +            return;
 +        }
 +
 +        xpath_ctx.register_ns ("xref", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";);
 +        var xpath_obj = xpath_ctx.eval_expression ("//xref:Cube[@currency][@rate]");
 +        if (xpath_obj == null)
 +        {
 +            warning ("Couldn't create XPath object");
 +            return;
 +        }
 +        var len = (xpath_obj->nodesetval != null) ? xpath_obj->nodesetval->length () : 0;
 +        for (var i = 0; i < len; i++)
 +        {
 +            var node = xpath_obj->nodesetval->item (i);
 +
 +            if (node->type == Xml.ElementType.ELEMENT_NODE)
 +                set_ecb_rate (node, eur_rate);
 +
 +            /* Avoid accessing removed elements */
 +            if (node->type != Xml.ElementType.NAMESPACE_DECL)
 +                node = null;
 +        }
 +
 +        Xml.Parser.cleanup ();
 +        base.do_load_rates ();
 +    }
 +
 +    private void set_ecb_rate (Xml.Node node, Currency eur_rate)
 +    {
 +        string? name = null, value = null;
 +
 +        for (var attribute = node.properties; attribute != null; attribute = attribute->next)
 +        {
 +            var n = (Xml.Node*) attribute;
 +            if (attribute->name == "currency")
 +                name = n->get_content ();
 +            else if (attribute->name == "rate")
 +                value = n->get_content ();
 +        }
 +
 +        /* Use data if value and no rate currently defined */
 +        if (name != null && value != null && get_currency (name) == null)
 +        {
 +            debug ("Using ECB rate of %s for %s", value, name);
 +            var c = CurrencyManager.get_default ().add_currency (name, source_name);
 +            var r = mp_set_from_string (value);
 +            var v = eur_rate.get_value ();
 +            v = v.multiply (r);
 +            c.set_value (v);
 +        }
 +    }
 +
 +    private void set_ecb_fixed_rate (string name, string value, Currency eur_rate)
 +    {
 +        debug ("Using ECB fixed rate of %s for %s", value, name);
 +        var c = CurrencyManager.get_default ().add_currency (name, source_name + "#fixed");
 +        var r = mp_set_from_string (value);
 +        var v = eur_rate.get_value ();
 +        v = v.divide (r);
 +        c.set_value (v);
 +    }
 +}
diff --cc lib/currency.vala
index 9afb0438,17862fd2..03b031d0
--- a/lib/currency.vala
+++ b/lib/currency.vala
@@@ -14,18 -17,16 +14,16 @@@ public class CurrencyManager : Objec
  {
      private List<Currency> currencies;
  
 -    protected int _refresh_interval;
 +    private List<CurrencyProvider> providers;
  
 -    public int refresh_interval {
 -        get {
 -            return _refresh_interval;
 -        }
 -        set {
 -            loaded_rates = false;
 +    private int _refresh_interval;
 +    public int refresh_interval { get { return _refresh_interval;}
 +        set
 +        {
-             loaded_rates = false;
              _refresh_interval = value;
 -            download_rates (false);
 +            foreach (var p in default_currency_manager.providers) {
 +                p.set_refresh_interval(_refresh_interval);
 +            }
-             download_rates ();
          }
      }
  


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]