Skip to content

Commit

Permalink
add currency translation support
Browse files Browse the repository at this point in the history
  • Loading branch information
stephtr committed Nov 6, 2023
1 parent 644d1cf commit 078d74d
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 73 deletions.
28 changes: 14 additions & 14 deletions lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@ source ./emsdk_env.sh
mkdir -p ~/opt/src
cd ~/opt/src

wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.lz
tar xf gmp-6.1.2.tar.lz
cd gmp-6.1.2
emconfigure ./configure --disable-assembly --host none --enable-cxx --prefix=${HOME}/opt
wget https://gmplib.org/download/gmp/gmp-6.3.0.tar.lz
tar xf gmp-6.3.0.tar.lz
cd gmp-6.3.0
emconfigure ./configure --disable-assembly --host none --enable-cxx --prefix=${HOME}/opt # no, the "none" host is not obsolete.
make
make install
cd ..

wget https://www.mpfr.org/mpfr-current/mpfr-4.1.0.tar.xz
wget https://www.mpfr.org/mpfr-current/allpatches
tar xf mpfr-4.1.0.tar.xz
cd mpfr-4.1.0
patch -N -Z -p1 < ../allpatches
wget https://www.mpfr.org/mpfr-current/mpfr-4.2.1.tar.xz # if not found, use a newer version
# wget https://www.mpfr.org/mpfr-current/allpatches # if available
tar xf mpfr-4.2.1.tar.xz
cd mpfr-4.2.1
# patch -N -Z -p1 < ../allpatches
emconfigure ./configure --prefix=${HOME}/opt --with-gmp=${HOME}/opt
make
make install
cd ..

wget ftp://xmlsoft.org/libxml2/libxml2-git-snapshot.tar.gz
tar xf libxml2-git-snapshot.tar.gz
cd libxml2-2.9.12/
emconfigure ./configure --prefix=${HOME}/opt
cd libxml2-2.9.13/ # or whichever version is up to date
emconfigure ./configure --prefix=${HOME}/opt --disable-shared
make
make install
ln -s ${HOME}/opt/include/libxml2/libxml ${HOME}/opt/include/libxml
Expand All @@ -48,10 +48,10 @@ cd libqalculate
sed -i 's/PKG_CHECK_MODULES(LIBCURL, libcurl)/#PKG_CHECK_MODULES(LIBCURL, libcurl)/' configure
sed -i 's/PKG_CHECK_MODULES(ICU, icu-uc)/#PKG_CHECK_MODULES(ICU, icu-uc)/' configure
sed -i 's/PKG_CHECK_MODULES(LIBXML, libxml-2.0/#PKG_CHECK_MODULES(LIBXML, libxml-2.0/' configure
sed -i 's/$as_echo "#define HAVE_LIBCURL 1" >>confdefs.h/#$as_echo "#define HAVE_LIBCURL 1" >>confdefs.h/' configure
sed -i 's/$as_echo "#define HAVE_ICU 1" >>confdefs.h/#$as_echo "#define HAVE_ICU 1" >>confdefs.h/' configure
sed -i 's/#define HAVE_LIBCURL 1//' configure
sed -i 's/#define HAVE_ICU 1//' configure
sed -i 's/#define HAVE_PIPE2 1/#define HAVE_PIPE2 0/' configure
emconfigure ./configure --prefix=${HOME}/opt CPPFLAGS=-I${HOME}/opt/include LDFLAGS="-L${HOME}/opt/lib -lxml2" --without-libcurl --enable-compiled-definitions --disable-nls
emconfigure ./configure --prefix=${HOME}/opt CPPFLAGS=-I${HOME}/opt/include LDFLAGS="-L${HOME}/opt/lib -lxml2" --without-libcurl --enable-compiled-definitions --disable-nls --disable-shared
make
make install
cd ..
Expand Down
81 changes: 52 additions & 29 deletions lib/calc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,43 +41,75 @@ Calculation calculate(std::string calculation, int timeout = 500, int optionFlag
return ret;
}

struct VariableInfo
val getVariables()
{
std::string name;
std::string description;
std::string aliases;
};

std::vector<VariableInfo> getVariables()
{
std::vector<VariableInfo> variables;
auto variables = val::array();
for (auto &variable : calc.variables)
{
if (!variable->isKnown() || variable->isHidden())
continue;

VariableInfo info;
info.name = variable->preferredDisplayName(true, true).name;
info.description = variable->title(false, true);
auto info = val::object();
info.set("name", variable->preferredDisplayName(true, true).name);
info.set("description", variable->title(false, true));
auto nameCount = variable->countNames();
auto aliases = val::array();
if (nameCount < 1)
{
info.aliases = variable->preferredDisplayName(true, true).name;
aliases.call<void>("push", variable->preferredDisplayName(true, true).name);
}
else
{
for (size_t i = 1; i <= nameCount; i++)
{
info.aliases += variable->getName(i).name;
if (i < nameCount)
info.aliases += "\t";
aliases.call<void>("push", variable->getName(i).name);
}
}
variables.push_back(info);
info.set("aliases", aliases);
variables.call<void>("push", info);
}
return variables;
}

int updateCurrencyValues(const val &currencyData, std::string baseCurrency, bool showWarning)
{
int errorCode = 0;

auto u1 = CALCULATOR->getActiveUnit(baseCurrency);
if (u1 != calc.u_euro)
{
return 1;
}

for (int i = 0; i < currencyData["length"].as<int>(); i++)
{
emscripten::val data = currencyData[i];
auto name = data["name"].as<std::string>();
auto value = data["value"].as<std::string>();
auto u2 = calculator->getActiveUnit(name);
if (!u2)
{
u2 = calc.addUnit(new AliasUnit(_("Currency"), name, "", "", "", calc.u_euro, "1", 1, "", false, true));
}
else if (!u2->isCurrency())
{
errorCode = 2;
continue;
}

((AliasUnit *)u2)->setBaseUnit(u1);
((AliasUnit *)u2)->setExpression(value);
u2->setApproximate();
u2->setPrecision(-2);
u2->setChanged(false);
}

calc.setExchangeRatesWarningEnabled(showWarning);
calc.loadGlobalCurrencies();

return errorCode;
}

int main()
{
calc.loadGlobalDefinitions();
Expand All @@ -94,12 +126,12 @@ int main()

std::string info()
{
return "libqalculate by Hanna Knutsson, compiled by Stephan Troyer";
return "libqalculate by Hanna Knutsson, wrapped & compiled by Stephan Troyer";
}

int version()
{
return 3;
return 4;
}

EMSCRIPTEN_BINDINGS(Calculator)
Expand All @@ -109,6 +141,7 @@ EMSCRIPTEN_BINDINGS(Calculator)
function("version", &version);
function("getVariables", &getVariables);
function("set_option", &set_option);
function("updateCurrencyValues", &updateCurrencyValues);
}

EMSCRIPTEN_BINDINGS(calculation)
Expand All @@ -119,13 +152,3 @@ EMSCRIPTEN_BINDINGS(calculation)
.property("output", &Calculation::output)
.property("messages", &Calculation::messages);
}

EMSCRIPTEN_BINDINGS(variableInfo)
{
class_<VariableInfo>("VariableInfo")
.constructor<>()
.property("name", &VariableInfo::name)
.property("description", &VariableInfo::description)
.property("aliases", &VariableInfo::aliases);
register_vector<VariableInfo>("vector<VariableInfo>");
}
39 changes: 34 additions & 5 deletions website/src/lib/calculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
calculate,
initializeCalculationModule,
setOption,
updateCurrencyValues,
} from './calculatorModule';
import { History } from './history';
import { Settings } from './settings';
Expand All @@ -19,6 +20,12 @@ export interface Calculation {
bookmarkName?: string;
}

export interface CurrencyData {
base: string;
date: string;
rates: Record<string, string>;
}

/** Parses the status message coming from Web Assembly */
function parseCalculationMessages(messagesString: string): {
messages: string[];
Expand Down Expand Up @@ -102,15 +109,15 @@ export class Calculator {
};
}

private pendingCalculationOnceLoaded: string | null = null;
#pendingCalculationOnceLoaded: string | null = null;

submitCalculation(input: string) {
const isSetCommand = input.startsWith('set ');
if (!isSetCommand) {
this.submittedListeners.forEach((l) => l(input));
}
if (!this.isLoaded) {
this.pendingCalculationOnceLoaded = input;
this.#pendingCalculationOnceLoaded = input;
return;
}

Expand All @@ -121,6 +128,23 @@ export class Calculator {
}
}

#pendingCurrencyData: CurrencyData | null = null;

updateCurrencyData(data: CurrencyData) {
if (!this.isLoaded) {
this.#pendingCurrencyData = data;
return;
}
updateCurrencyValues(
Object.entries(data.rates).map(([name, value]) => ({
name,
value,
})),
data.base,
new Date(data.date),
);
}

constructor() {
if (typeof window !== 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Expand All @@ -129,9 +153,14 @@ export class Calculator {

this.settings.apply();

if (this.pendingCalculationOnceLoaded) {
this.submitCalculation(this.pendingCalculationOnceLoaded);
this.pendingCalculationOnceLoaded = null;
if (this.#pendingCurrencyData) {
this.updateCurrencyData(this.#pendingCurrencyData);
this.#pendingCurrencyData = null;
}

if (this.#pendingCalculationOnceLoaded) {
this.submitCalculation(this.#pendingCalculationOnceLoaded);
this.#pendingCalculationOnceLoaded = null;
}
this.loadedListeners.forEach((l) => l());
});
Expand Down
53 changes: 44 additions & 9 deletions website/src/lib/calculatorModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export interface VariableDefinition {
aliases: string[];
}

export interface CurrencyDataset {
name: string;
value: number;
}

let Version: number | undefined;
export function version(): number {
if (!Version) Version = Module.version?.() ?? 1;
Expand Down Expand Up @@ -47,6 +52,21 @@ export function setOption(option: string): boolean {
return Module.set_option(option);
}

export function updateCurrencyValues(
data: CurrencyDataset[],
baseCurrency: string,
updateDate: Date,
): boolean {
if (version() < 4) return false;
return (
Module.updateCurrencyValues(
data,
baseCurrency,
+new Date() - +new Date(updateDate) > 7 * 24 * 3600 * 1000, // 7 days
) === 0
);
}

export function info(): string {
return Module.info();
}
Expand Down Expand Up @@ -86,14 +106,29 @@ export async function initializeCalculationModule() {

if (Module.getVariables) {
// parse the variables supported by libqalculate
const vars = Module.getVariables();
Variables = wasmVectorToArray(vars)
.map((v: any) => ({
name: v.name as string,
description: v.description as string,
aliases: (v.aliases as string).split('\t'),
}))
.filter((v) => !['true', 'false', 'undefined'].includes(v.name));
vars.delete();
const vars = Module.getVariables() as Array<{
name: string;
description: string;
aliases: string[];
}>;
if (version() < 4) {
Variables = wasmVectorToArray<any>(vars)
.map((v: any) => ({
name: v.name,
description: v.description,
aliases: v.aliases.split('\t'),
}))
.filter(
(v) =>
!['true', 'false', 'undefined'].includes(
v.name as string,
),
);
(vars as any).delete();
} else {
Variables = vars.filter(
(v) => !['true', 'false', 'undefined'].includes(v.name),
);
}
}
}
2 changes: 1 addition & 1 deletion website/src/lib/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function getOS():
if (navigator.userAgent.includes('Linux')) return 'linux';
}

export function wasmVectorToArray(vector: any) {
export function wasmVectorToArray<T>(vector: any): T[] {
const array = [];
for (let i = 0; i < vector.size(); i++) {
array.push(vector.get(i));
Expand Down
17 changes: 12 additions & 5 deletions website/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,21 @@
});
});
});
navigator.serviceWorker?.addEventListener(
'message',
(event: MessageEvent<{ type: string }>) => {
if (event.data?.type === 'reload') {
window.location.reload();
(event: MessageEvent<{ type: string, data?: any }>) => {
switch (event.data?.type) {
case 'reload': window.location.reload(); break;
case 'updateCurrencyData': calculator.updateCurrencyData(event.data?.data); break;
default: throw new Error(`Unknown message ${event.data?.type}`);
}
},
);
fetch('/api/getCurrencyData').then(async (response) =>
calculator.updateCurrencyData(await response.json())
);
}
let isTouchScreen = false;
function touchstart() {
Expand All @@ -85,7 +92,7 @@
<svelte:window on:touchstart={touchstart} />

<div class="content" class:isTouchScreen>
<a href="/" class="mainLink"><h1>Qalculator</h1></a>
<a href="/" class="mainLink"><h1>Qalculator.xyz</h1></a>
<CalculatorWidget
{calculator}
bind:selectCalculation={$selectCalculation}
Expand All @@ -109,7 +116,7 @@
>
<p class="update">
<FontAwesomeIcon icon={faArrowRotateRight} />
An update is available, click to restart Qalculator.
An update is available, click to restart Qalculator.xyz.
</p>
</button>
{/if}
Expand Down

0 comments on commit 078d74d

Please sign in to comment.