yxcyc
This commit is contained in:
@@ -1173,6 +1173,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteMeasurement(id) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.confirm('Diesen Messpunkt wirklich loeschen?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSaving(true);
|
||||
setError('');
|
||||
setMessage('');
|
||||
try {
|
||||
await request(`${apiBase}/projects/${encodeURIComponent(projectKey)}/measurements/${encodeURIComponent(id)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
setMessage('Messpunkt geloescht.');
|
||||
await loadBootstrap(projectKey);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitMeasurementImport(event) {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
@@ -2272,7 +2297,7 @@
|
||||
panel('Messhistorie', 'Die letzten 10 Uploads inkl. Performance-Werten und OCR-Metadaten.', h('div', { className: 'mc-table-shell' }, [
|
||||
h('table', { key: 'table', className: 'mc-table' }, [
|
||||
h('thead', { key: 'thead' }, h('tr', null, [
|
||||
'Zeit', 'Coins', 'Kurs', 'Quelle', 'DOGE/Tag', 'Trend', 'Notiz'
|
||||
'Zeit', 'Coins', 'Kurs', 'Quelle', 'DOGE/Tag', 'Trend', 'Notiz', 'Aktion'
|
||||
].map((label) => h('th', { key: label }, label)))),
|
||||
h('tbody', { key: 'tbody' },
|
||||
measurements.slice(-10).reverse().map((row) => h('tr', { key: row.id }, [
|
||||
@@ -2283,6 +2308,12 @@
|
||||
h('td', { key: 'rate' }, fmtNumber(row.doge_per_day_interval, 4)),
|
||||
h('td', { key: 'trend' }, row.trend_label),
|
||||
h('td', { key: 'note' }, row.note || row.ocr_flags.join(', ') || '—'),
|
||||
h('td', { key: 'action' }, h('button', {
|
||||
type: 'button',
|
||||
className: 'mc-button mc-button--ghost',
|
||||
onClick: () => deleteMeasurement(row.id),
|
||||
disabled: saving,
|
||||
}, 'Loeschen')),
|
||||
]))
|
||||
),
|
||||
]),
|
||||
|
||||
@@ -181,6 +181,11 @@ final class Router
|
||||
Http::json(['data' => $this->createMeasurement($projectKey, Http::input())], 201);
|
||||
}
|
||||
|
||||
if (preg_match('~^measurements/(\d+)$~', $resource, $matches) && $method === 'DELETE') {
|
||||
$this->deleteMeasurement($projectKey, (int) $matches[1]);
|
||||
Http::json(['data' => ['deleted' => true]]);
|
||||
}
|
||||
|
||||
if ($resource === 'measurements-import' && $method === 'POST') {
|
||||
$this->repository()->ensureProject($projectKey);
|
||||
Http::json(['data' => $this->importMeasurements($projectKey, Http::input())], 201);
|
||||
@@ -1454,6 +1459,15 @@ final class Router
|
||||
];
|
||||
}
|
||||
|
||||
private function deleteMeasurement(string $projectKey, int $measurementId): void
|
||||
{
|
||||
if ($measurementId <= 0) {
|
||||
throw new ApiException('measurement_id ist ungueltig.', 422);
|
||||
}
|
||||
|
||||
$this->repository()->deleteMeasurement($projectKey, $measurementId);
|
||||
}
|
||||
|
||||
private function syncCurrencyCatalogForMeasurement(array $payload): void
|
||||
{
|
||||
$priceCurrency = strtoupper(trim((string) ($payload['price_currency'] ?? '')));
|
||||
|
||||
@@ -299,6 +299,10 @@ final class OcrService
|
||||
$price = null;
|
||||
$currency = null;
|
||||
$normalizedText = preg_replace('/[[:space:]]+/u', ' ', trim($rawText)) ?: '';
|
||||
$lines = array_values(array_filter(array_map(
|
||||
static fn (string $line): string => trim($line),
|
||||
preg_split('/\R/u', $rawText) ?: []
|
||||
), static fn (string $line): bool => $line !== ''));
|
||||
|
||||
if ($normalizedText === '') {
|
||||
$flags[] = 'ocr_raw_text_empty';
|
||||
@@ -338,7 +342,40 @@ final class OcrService
|
||||
$price = round((float) str_replace(',', '.', $priceMatch[1]), 8);
|
||||
}
|
||||
|
||||
$coinsCandidates = array_values(array_filter($decimalCandidates, static fn (array $item): bool => $item['value'] > 10 && $item['precision'] >= 4));
|
||||
if ($coinsTotal === null) {
|
||||
foreach ($lines as $line) {
|
||||
if (!preg_match('/MINING[- ]?GUTHABEN|MINING[- ]?BALANCE|GUTHABEN|BALANCE/i', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('/(\d+[.,]\d{4,8})/', $line, $lineCoinsMatch)) {
|
||||
$coinsTotal = round((float) str_replace(',', '.', $lineCoinsMatch[1]), 6);
|
||||
$flags[] = 'coins_from_balance_line';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($coinsTotal === null && preg_match('/(\d+[.,]\d{4,8})\s*(?:DOGE)?\s*(?:MINING[- ]?GUTHABEN|MINING[- ]?BALANCE|GUTHABEN|BALANCE)/i', $normalizedText, $coinsMatch)) {
|
||||
$coinsTotal = round((float) str_replace(',', '.', $coinsMatch[1]), 6);
|
||||
$flags[] = 'coins_from_balance_context';
|
||||
}
|
||||
|
||||
if ($coinsTotal === null) {
|
||||
$coinsCandidates = array_values(array_filter($decimalCandidates, static function (array $item) use ($price): bool {
|
||||
if ($item['precision'] < 4) {
|
||||
return false;
|
||||
}
|
||||
if ($item['value'] <= 0 || $item['value'] >= 1000000) {
|
||||
return false;
|
||||
}
|
||||
if ($price !== null && abs($item['value'] - $price) < 0.0000005) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}));
|
||||
|
||||
if ($coinsCandidates !== []) {
|
||||
usort($coinsCandidates, static function (array $a, array $b): int {
|
||||
return [$b['precision'], $b['value']] <=> [$a['precision'], $a['value']];
|
||||
@@ -350,6 +387,7 @@ final class OcrService
|
||||
} else {
|
||||
$flags[] = 'coins_missing';
|
||||
}
|
||||
}
|
||||
|
||||
$priceCandidates = array_values(array_filter(
|
||||
$decimalCandidates,
|
||||
|
||||
@@ -548,6 +548,32 @@ final class MiningRepository
|
||||
return is_array($row) ? $this->normalizeRow($row) : null;
|
||||
}
|
||||
|
||||
public function deleteMeasurement(string $projectKey, int $measurementId): void
|
||||
{
|
||||
if ($measurementId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$deleteRates = $this->pdo->prepare(
|
||||
'DELETE FROM ' . $this->table('measurement_rates') . '
|
||||
WHERE measurement_id = :measurement_id AND owner_sub = :owner_sub'
|
||||
);
|
||||
$deleteRates->execute([
|
||||
'measurement_id' => $measurementId,
|
||||
'owner_sub' => $this->ownerSub,
|
||||
]);
|
||||
|
||||
$deleteMeasurement = $this->pdo->prepare(
|
||||
'DELETE FROM ' . $this->table('measurements') . '
|
||||
WHERE id = :id AND project_key = :project_key AND owner_sub = :owner_sub'
|
||||
);
|
||||
$deleteMeasurement->execute([
|
||||
'id' => $measurementId,
|
||||
'project_key' => $projectKey,
|
||||
'owner_sub' => $this->ownerSub,
|
||||
]);
|
||||
}
|
||||
|
||||
public function replaceMeasurementRates(int $measurementId, string $projectKey, array $rates): void
|
||||
{
|
||||
$delete = $this->pdo->prepare('DELETE FROM ' . $this->table('measurement_rates') . ' WHERE measurement_id = :measurement_id AND owner_sub = :owner_sub');
|
||||
|
||||
Reference in New Issue
Block a user