Skip to content

Commit

Permalink
Feature/currency symbols (#922)
Browse files Browse the repository at this point in the history
* feature: group thousands using the comma character, and allow replacing currency codes with currency symbols

Signed-off-by: F.N. Claessen <felix@seita.nl>

* docs: add usage note and rationale to docstring

Signed-off-by: F.N. Claessen <felix@seita.nl>

* docs: address review comments

Signed-off-by: F.N. Claessen <felix@seita.nl>

* docs: changelog entry

Signed-off-by: F.N. Claessen <felix@seita.nl>

---------

Signed-off-by: F.N. Claessen <felix@seita.nl>
  • Loading branch information
Flix6x committed Dec 12, 2023
1 parent 8824476 commit 763889d
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 2 deletions.
1 change: 1 addition & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Infrastructure / Support
----------------------

* New documentation section on constructing a flex model for :abbr:`V2G (vehicle-to-grid)` [see `PR #885 <https://github.com/FlexMeasures/flexmeasures/pull/885>`_]
* Allow charts in plugins to show currency codes (such as EUR) as currency symbols (€) [see `PR #922 <https://github.com/FlexMeasures/flexmeasures/pull/922>`_]

Bugfixes
-----------
Expand Down
50 changes: 48 additions & 2 deletions flexmeasures/ui/static/js/flexmeasures.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,28 @@ function submit_sensor_type() {
/* Quantities incl. units
* Usage:
* {
* 'format': [<d3-format>, <sensor unit>],
* 'format': [<d3-format>, <sensor unit>, <optional preference to show currency symbol instead of currency code>],
* 'formatType': 'quantityWithUnitFormat'
* }
* The use of currency symbols, such as the euro sign (€), should be reserved for use in graphics.
* See, for example, https://publications.europa.eu/code/en/en-370303.htm
* The rationale behind this is that they are often ambiguous.
* For example, both the Australian dollar (AUD) and the United States dollar (USD) map to the dollar sign ($).
*/
vega.expressionFunction('quantityWithUnitFormat', function(datum, params) {
return d3.format(params[0])(datum) + " " + params[1];
const formatDef = {
"decimal": ".",
"thousands": ",",
"grouping": [3],
};
const locale = d3.formatLocale(formatDef);
// The third element on param allows choosing to show the currency symbol (true) or the currency name (false)
if (params.length > 2 && params[2] === true){
return locale.format(params[0])(datum) + " " + convertCurrencyCodeToSymbol(params[1]);
}
else {
return d3.format(params[0])(datum) + " " + params[1];
}
});

/*
Expand Down Expand Up @@ -359,3 +375,33 @@ vega.expressionFunction('timezoneFormat', function(date, params) {
const tzDate = new Date(0,0,0,0,Math.abs(tzOffsetNumber));
return `${ tzOffsetNumber > 0 ? '-' : '+'}${("" + tzDate.getHours()).padStart(2, '0')}:${("" + tzDate.getMinutes()).padStart(2, '0')}` + ' (' + timezoneString + ')';
});

/*
* Convert any currency codes in the unit to currency symbols.
* This relies on the currencyToSymbolMap imported from currency-symbol-map/map.js
*/
const convertCurrencyCodeToSymbol = (unit) => {
return replaceMultiple(unit, currencySymbolMap);
};

/**
* Replaces multiple substrings in a given string based on a provided mapping object.
*
* @param {string} str - The input string in which replacements will be performed.
* @param {Object} mapObj - An object where keys are substrings to be replaced, and values are their corresponding replacements.
* @returns {string} - A new string with the specified substitutions applied.
*
* @example
* // Replace currency codes with symbols in the given string
* const inputString = "The price is 50 EUR/MWh, and 30 AUD/MWh.";
* const currencyMapping = { EUR: '€', AUD: '$' };
* const result = replace_multiple(inputString, currencyMapping);
* // The result will be "The price is 50 €/MWh, and 30 $/MWh."
*/
function replaceMultiple(str, mapObj){
// Create a regular expression pattern using the keys of the mapObj joined with "|" (OR) to match any of the substrings.
let regex = new RegExp(Object.keys(mapObj).join("|"),"g");
// Use the regular expression to replace matched substrings with their corresponding values from the mapObj.
// The "g" flag makes the replacement global (replaces all occurrences), and it is case-sensitive by default.
return str.replace(regex, matched => mapObj[matched]);
}
4 changes: 4 additions & 0 deletions flexmeasures/ui/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,10 @@
<script src="https://cdn.jsdelivr.net/npm/vega@{{ js_versions.vega }}"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@{{ js_versions.vegalite }}"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@{{ js_versions.vegaembed }}"></script>
{# Workaround for loading a NodeJS module without NodeJS #}
<script>var module = { exports: {} };</script>
<script src="https://cdn.jsdelivr.net/npm/currency-symbol-map@{{ js_versions.currencysymbolmap }}/map.js"></script>
<script>const currencySymbolMap = module.exports;</script>
{% endif %}

<!-- Custom scripts -->
Expand Down
1 change: 1 addition & 0 deletions flexmeasures/utils/config_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class Config(object):
vega="5.22.1",
vegaembed="6.21.0",
vegalite="5.5.0", # "5.6.0" has a problematic bar chart: see our sensor page and https://github.com/vega/vega-lite/issues/8496
currencysymbolmap="5.1.0",
# todo: expand with other js versions used in FlexMeasures
)

Expand Down

0 comments on commit 763889d

Please sign in to comment.