asdasd
This commit is contained in:
@@ -2268,17 +2268,15 @@
|
||||
]);
|
||||
}))
|
||||
: 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('thead', { key: 'thead' }, h('tr', null, [
|
||||
'Zeit', 'Mining-Waehrung', 'Mining-Bestand', 'Quelle', 'Assets'
|
||||
'Zeit', 'Quelle', 'Assets'
|
||||
].map((label) => h('th', { key: label }, label)))),
|
||||
h('tbody', { key: 'tbody' },
|
||||
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: '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: 'assets' }, h('div', { className: 'mc-asset-list' }, Object.entries(row.balances_json || {}).map(([code, 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';
|
||||
}
|
||||
|
||||
preg_match_all(
|
||||
'/([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',
|
||||
$normalizedText,
|
||||
$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 === '') {
|
||||
$assetRows = [];
|
||||
foreach ($lines as $lineIndex => $line) {
|
||||
if (!preg_match('/(\d+(?:[.,]\d+)?)\s*([A-Z]{2,10})\b/u', $line, $match)) {
|
||||
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);
|
||||
$currency = strtoupper((string) $match[2]);
|
||||
if ($amount <= 0 || $currency === '' || in_array($currency, ['USD', 'EUR'], true)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($balances[$currency])) {
|
||||
$balances[$currency] = [
|
||||
'balance' => $amount,
|
||||
'price_amount' => null,
|
||||
'price_currency' => null,
|
||||
];
|
||||
continue;
|
||||
|
||||
$assetRows[] = [
|
||||
'index' => $lineIndex,
|
||||
'currency' => $currency,
|
||||
'balance' => $amount,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($assetRows as $assetIndex => $assetRow) {
|
||||
$currency = (string) $assetRow['currency'];
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$existingBalance = is_array($balances[$currency]) ? (float) ($balances[$currency]['balance'] ?? 0.0) : (float) $balances[$currency];
|
||||
if ($amount > $existingBalance) {
|
||||
$balances[$currency]['balance'] = $amount;
|
||||
}
|
||||
$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)) {
|
||||
@@ -597,4 +600,49 @@ final class OcrService
|
||||
'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