asdasd
This commit is contained in:
@@ -2268,17 +2268,15 @@
|
|||||||
]);
|
]);
|
||||||
}))
|
}))
|
||||||
: h('div', { className: 'mc-empty' }, 'Noch keine Wallet-Assets erkannt.')),
|
: h('div', { className: 'mc-empty' }, 'Noch keine Wallet-Assets erkannt.')),
|
||||||
panel('Wallet-Historie', 'Erkannte Wallet-Snapshots mit allen aus dem Screenshot gelesenen Assets.', h('div', { className: 'mc-table-shell' }, [
|
panel('Wallet-Historie', 'Die letzten 10 Wallet-Uploads mit allen aus dem Screenshot gelesenen Assets.', h('div', { className: 'mc-table-shell' }, [
|
||||||
h('table', { key: 'wallet-table', className: 'mc-table' }, [
|
h('table', { key: 'wallet-table', className: 'mc-table' }, [
|
||||||
h('thead', { key: 'thead' }, h('tr', null, [
|
h('thead', { key: 'thead' }, h('tr', null, [
|
||||||
'Zeit', 'Mining-Waehrung', 'Mining-Bestand', 'Quelle', 'Assets'
|
'Zeit', 'Quelle', 'Assets'
|
||||||
].map((label) => h('th', { key: label }, label)))),
|
].map((label) => h('th', { key: label }, label)))),
|
||||||
h('tbody', { key: 'tbody' },
|
h('tbody', { key: 'tbody' },
|
||||||
currentWalletSnapshots.length
|
currentWalletSnapshots.length
|
||||||
? currentWalletSnapshots.map((row) => h('tr', { key: row.id }, [
|
? currentWalletSnapshots.slice(0, 10).map((row) => h('tr', { key: row.id }, [
|
||||||
h('td', { key: 'measured' }, fmtDate(row.measured_at)),
|
h('td', { key: 'measured' }, fmtDate(row.measured_at)),
|
||||||
h('td', { key: 'currency' }, row.wallet_currency || currentSettings.crypto_currency || 'DOGE'),
|
|
||||||
h('td', { key: 'balance' }, row.wallet_balance !== null && row.wallet_balance !== undefined ? `${fmtNumber(row.wallet_balance, 8)} ${row.wallet_currency || ''}`.trim() : 'n/a'),
|
|
||||||
h('td', { key: 'source' }, row.source || 'manual'),
|
h('td', { key: 'source' }, row.source || 'manual'),
|
||||||
h('td', { key: 'assets' }, h('div', { className: 'mc-asset-list' }, Object.entries(row.balances_json || {}).map(([code, asset]) => {
|
h('td', { key: 'assets' }, h('div', { className: 'mc-asset-list' }, Object.entries(row.balances_json || {}).map(([code, asset]) => {
|
||||||
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
||||||
@@ -2293,7 +2291,7 @@
|
|||||||
]);
|
]);
|
||||||
}))),
|
}))),
|
||||||
]))
|
]))
|
||||||
: [h('tr', { key: 'empty' }, h('td', { colSpan: 5 }, 'Noch keine Wallet-Snapshots gespeichert.'))]
|
: [h('tr', { key: 'empty' }, h('td', { colSpan: 3 }, 'Noch keine Wallet-Snapshots gespeichert.'))]
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
])),
|
])),
|
||||||
|
|||||||
@@ -491,47 +491,50 @@ final class OcrService
|
|||||||
$flags[] = 'wallet_total_missing';
|
$flags[] = 'wallet_total_missing';
|
||||||
}
|
}
|
||||||
|
|
||||||
preg_match_all(
|
$assetRows = [];
|
||||||
'/([A-Z][A-Za-z0-9]+(?:\s+[A-Z][A-Za-z0-9]+)*)\s+(\d+(?:[.,]\d+)?)\s+([A-Z]{2,10})\s+(?:\d+(?:[.,]\d+)?)\s+USD\s+(\d+(?:[.,]\d+)?)\s+USD/u',
|
foreach ($lines as $lineIndex => $line) {
|
||||||
$normalizedText,
|
if (!preg_match('/(\d+(?:[.,]\d+)?)\s*([A-Z]{2,10})\b/u', $line, $match)) {
|
||||||
$assetRowMatches,
|
|
||||||
PREG_SET_ORDER
|
|
||||||
);
|
|
||||||
foreach ($assetRowMatches as $match) {
|
|
||||||
$balanceAmount = round((float) str_replace(',', '.', $match[2]), 10);
|
|
||||||
$assetCurrency = strtoupper((string) $match[3]);
|
|
||||||
$priceAmount = round((float) str_replace(',', '.', $match[4]), 8);
|
|
||||||
if ($balanceAmount <= 0 || $assetCurrency === '') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$balances[$assetCurrency] = [
|
|
||||||
'balance' => $balanceAmount,
|
|
||||||
'price_amount' => $priceAmount > 0 ? $priceAmount : null,
|
|
||||||
'price_currency' => $priceAmount > 0 ? 'USD' : null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
preg_match_all('/(\d+(?:[.,]\d+)?)\s*([A-Z]{2,10})\b/u', $normalizedText, $balanceMatches, PREG_SET_ORDER);
|
|
||||||
foreach ($balanceMatches as $match) {
|
|
||||||
$amount = round((float) str_replace(',', '.', $match[1]), 10);
|
$amount = round((float) str_replace(',', '.', $match[1]), 10);
|
||||||
$currency = strtoupper((string) $match[2]);
|
$currency = strtoupper((string) $match[2]);
|
||||||
if ($amount <= 0 || $currency === '' || in_array($currency, ['USD', 'EUR'], true)) {
|
if ($amount <= 0 || $currency === '' || in_array($currency, ['USD', 'EUR'], true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isset($balances[$currency])) {
|
|
||||||
$balances[$currency] = [
|
$assetRows[] = [
|
||||||
|
'index' => $lineIndex,
|
||||||
|
'currency' => $currency,
|
||||||
'balance' => $amount,
|
'balance' => $amount,
|
||||||
'price_amount' => null,
|
|
||||||
'price_currency' => null,
|
|
||||||
];
|
];
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$existingBalance = is_array($balances[$currency]) ? (float) ($balances[$currency]['balance'] ?? 0.0) : (float) $balances[$currency];
|
foreach ($assetRows as $assetIndex => $assetRow) {
|
||||||
if ($amount > $existingBalance) {
|
$currency = (string) $assetRow['currency'];
|
||||||
$balances[$currency]['balance'] = $amount;
|
$balanceAmount = (float) $assetRow['balance'];
|
||||||
|
$startIndex = (int) $assetRow['index'];
|
||||||
|
$endIndex = isset($assetRows[$assetIndex + 1]['index'])
|
||||||
|
? (int) $assetRows[$assetIndex + 1]['index']
|
||||||
|
: count($lines);
|
||||||
|
|
||||||
|
$usdCandidates = [];
|
||||||
|
for ($i = $startIndex; $i < $endIndex; $i++) {
|
||||||
|
if (!isset($lines[$i])) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
if (preg_match_all('/(\d+(?:[.,]\d+)?)\s*USD\b/u', $lines[$i], $usdMatches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($usdMatches as $usdMatch) {
|
||||||
|
$usdCandidates[] = round((float) str_replace(',', '.', $usdMatch[1]), 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$priceAmount = $this->pickWalletUnitPrice($balanceAmount, $usdCandidates);
|
||||||
|
$balances[$currency] = [
|
||||||
|
'balance' => $balanceAmount,
|
||||||
|
'price_amount' => $priceAmount,
|
||||||
|
'price_currency' => $priceAmount !== null ? 'USD' : null,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($walletCurrencyHint !== '' && array_key_exists($walletCurrencyHint, $balances)) {
|
if ($walletCurrencyHint !== '' && array_key_exists($walletCurrencyHint, $balances)) {
|
||||||
@@ -597,4 +600,49 @@ final class OcrService
|
|||||||
'score' => $score,
|
'score' => $score,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<float|int> $candidates
|
||||||
|
*/
|
||||||
|
private function pickWalletUnitPrice(float $balance, array $candidates): ?float
|
||||||
|
{
|
||||||
|
$candidates = array_values(array_filter(array_map(
|
||||||
|
static fn (mixed $value): float => round((float) $value, 8),
|
||||||
|
$candidates
|
||||||
|
), static fn (float $value): bool => $value > 0));
|
||||||
|
|
||||||
|
if ($balance <= 0 || $candidates === []) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (count($candidates) === 1) {
|
||||||
|
return $candidates[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$bestPrice = null;
|
||||||
|
$bestError = null;
|
||||||
|
$candidateCount = count($candidates);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $candidateCount; $i++) {
|
||||||
|
$priceCandidate = $candidates[$i];
|
||||||
|
for ($j = 0; $j < $candidateCount; $j++) {
|
||||||
|
if ($i === $j) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$totalCandidate = $candidates[$j];
|
||||||
|
$estimatedTotal = $balance * $priceCandidate;
|
||||||
|
$denominator = max(abs($totalCandidate), 0.00000001);
|
||||||
|
$error = abs($estimatedTotal - $totalCandidate) / $denominator;
|
||||||
|
if ($bestError === null || $error < $bestError) {
|
||||||
|
$bestError = $error;
|
||||||
|
$bestPrice = $priceCandidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bestPrice !== null && $bestError !== null && $bestError <= 0.2) {
|
||||||
|
return round($bestPrice, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return round($candidates[count($candidates) - 1], 8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user