chto-ty-takoe-na-baze-kakih/src/dashboard/templates/sources.html
Elshat 4a928c10d6 feat: manual CSV/HTML upload for sources without public API (Arbuz/Sharyn)
- src/pipeline/upload.py: parse_csv/parse_html → normalize→categorize→validate→upsert
- POST /api/upload (multipart) + form on /sources
- low-confidence records → review queue, not analytics
- +3 tests (16 passed, 1 skipped)
2026-09-24 07:13:33 +00:00

58 lines
3.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<section class="kt-ai-hero" data-align="left" style="padding-top:24px;padding-bottom:16px">
<h1 style="font-size:32px;margin:0">Источники</h1>
<p class="kt-ai-hero-sub">Стекляшка из sources.yaml + живой статус (последний успех, количество записей, ошибка).</p>
</section>
<div class="panel">
<table class="data">
<thead><tr>
<th>Ид</th><th>Название</th><th>Тир</th>
<th>Последний успех</th><th>Записей</th><th>Статус</th>
<th>Последняя ошибка</th><th>Дата</th>
</tr></thead>
<tbody>{{ SOURCES_HTML }}</tbody>
</table>
</div>
<div class="panel">
<h3>Ручная загрузка файла (Arbuz.kz, Sharyn и аналоги без API)</h3>
<p class="muted" style="font-size:13px">
CSV со столбцами <code>product, price, unit, currency, region, as_of</code> (русские/английские названия
столбцов узнаются) или HTML-таблица (имя в 1-й колонке, цена во 2-й). Запись проходит через
тот же пайплайн: нормализация → категоризация → валидация. Низкая уверенность — в очередь на проверку,
не в аналитику.
</p>
<form id="upload-form" style="display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-top:8px">
<label><span class="muted" style="font-size:12px">Источник (id, например arbuz)</span>
<input name="source_id" required style="width:100%;height:34px;padding:0 10px;border-radius:8px;border:1px solid var(--kt-ai-border);background:var(--kt-ai-surface);font-size:14px"></label>
<label><span class="muted" style="font-size:12px">Название источника</span>
<input name="source_name" style="width:100%;height:34px;padding:0 10px;border-radius:8px;border:1px solid var(--kt-ai-border);background:var(--kt-ai-surface);font-size:14px"></label>
<label><span class="muted" style="font-size:12px">Ссылка на страницу (для прозрачности)</span>
<input name="source_url" style="width:100%;height:34px;padding:0 10px;border-radius:8px;border:1px solid var(--kt-ai-border);background:var(--kt-ai-surface);font-size:14px"></label>
<label><span class="muted" style="font-size:12px">Дата цен (YYYY-MM-DD, если в файле нет)</span>
<input name="as_of" style="width:100%;height:34px;padding:0 10px;border-radius:8px;border:1px solid var(--kt-ai-border);background:var(--kt-ai-surface);font-size:14px"></label>
<label style="grid-column:1/-1"><span class="muted" style="font-size:12px">Файл (.csv / .html)</span>
<input type="file" name="file" accept=".csv,.html,.htm,.txt" required style="width:100%;padding:6px;font-size:14px"></label>
<div style="grid-column:1/-1">
<button type="submit" class="kt-ai-btn" data-variant="primary">Загрузить и проанализировать</button>
<span id="upload-result" class="muted" style="font-size:13px;margin-left:10px"></span>
</div>
</form>
</div>
<script>
document.getElementById('upload-form').addEventListener('submit', function(e) {
e.preventDefault();
const f = new FormData(e.target);
const out = document.getElementById('upload-result');
out.textContent = 'Загружаю…';
fetch('api/upload', { method: 'POST', body: f }).then(r => r.json()).then(res => {
if (!res.ok) { out.textContent = 'Ошибка: ' + res.error; return; }
out.innerHTML = 'Готово: записано <b>' + res.loaded + '</b>, в карантине <b>' +
res.quarantined + '</b>, пропущено ' + res.skipped +
(res.total ? ' / всего ' + res.total : '') +
' строк. Источник: ' + res.source_id;
}).catch(err => { out.textContent = 'Ошибка сети: ' + err; });
});
</script>