Files
hotels/simple_web.py
Фёдор 0cf3297290 Проект аудита отелей: основные скрипты и документация
- Краулеры: smart_crawler.py, regional_crawler.py
- Аудит: audit_orel_to_excel.py, audit_chukotka_to_excel.py
- РКН проверка: check_rkn_registry.py, recheck_unclear_rkn.py
- Отчёты: create_orel_horizontal_report.py
- Обработка: process_all_hotels_embeddings.py
- Документация: README.md, DB_SCHEMA_REFERENCE.md
2025-10-16 10:52:09 +03:00

174 lines
7.1 KiB
Python
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.

#!/usr/bin/env python3
"""
Упрощенный веб-интерфейс без сложных БД запросов
"""
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import uvicorn
app = FastAPI(title="Система аудита отелей")
@app.get("/")
async def root():
return HTMLResponse("""
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Аудит Отелей - Общественный Контроль</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; }
h1 { color: #2c3e50; text-align: center; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
.stat-card { background: #3498db; color: white; padding: 20px; border-radius: 8px; text-align: center; }
.stat-number { font-size: 2em; font-weight: bold; }
.stat-label { font-size: 0.9em; opacity: 0.9; }
.regions { margin-top: 30px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #34495e; color: white; }
.btn { background: #e74c3c; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; }
.btn:hover { background: #c0392b; }
.btn-small { background: #27ae60; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 0.8em; }
.btn-small:hover { background: #229954; }
</style>
</head>
<body>
<div class="container">
<h1>🏨 Система аудита отелей</h1>
<div class="stats">
<div class="stat-card">
<div class="stat-number" id="total-hotels">33,773</div>
<div class="stat-label">Всего отелей</div>
</div>
<div class="stat-card">
<div class="stat-number" id="crawled-sites">115</div>
<div class="stat-label">Спарсено сайтов</div>
</div>
<div class="stat-card">
<div class="stat-number" id="audited-hotels">239</div>
<div class="stat-label">Проведено аудитов</div>
</div>
<div class="stat-card">
<div class="stat-number" id="avg-score">11.0</div>
<div class="stat-label">Средний балл</div>
</div>
</div>
<div class="regions">
<h2>📊 Статус по регионам</h2>
<table>
<thead>
<tr>
<th>Регион</th>
<th>Отелей</th>
<th>Спарсено</th>
<th>Проверено</th>
<th>Средний балл</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
<tr>
<td>Камчатский край</td>
<td>159</td>
<td>82</td>
<td>159</td>
<td>10.8/20</td>
<td><button class="btn-small" onclick="downloadAudit('Камчатский край')">📥 Скачать</button></td>
</tr>
<tr>
<td>Орловская область</td>
<td>68</td>
<td>29</td>
<td>68</td>
<td>10.6/20</td>
<td><button class="btn-small" onclick="downloadAudit('Орловская область')">📥 Скачать</button></td>
</tr>
<tr>
<td>Чукотский автономный округ</td>
<td>12</td>
<td>4</td>
<td>12</td>
<td>11.7/20</td>
<td><span style="color: #999;">Старый аудит</span></td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top: 30px; text-align: center;">
<button class="btn" onclick="location.reload()">🔄 Обновить</button>
<button class="btn" onclick="alert('Полная версия: http://localhost:8888')">🔗 Полная версия</button>
</div>
</div>
<script>
function downloadAudit(regionName) {
const url = `/api/audit/download/${encodeURIComponent(regionName)}`;
const link = document.createElement('a');
link.href = url;
link.download = `audit_${regionName.replace(/\\s+/g, '_')}.xlsx`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
</body>
</html>
""")
@app.get("/api/stats")
async def get_stats():
"""Упрощенная статистика"""
return {
"total_hotels": 33773,
"crawled_sites": 115,
"audited_hotels": 239,
"avg_score": 11.0,
"regions": [
{"region_name": "Камчатский край", "total_hotels": 159, "crawled": 82, "audited": 159, "avg_score": 10.8},
{"region_name": "Орловская область", "total_hotels": 68, "crawled": 29, "audited": 68, "avg_score": 10.6},
{"region_name": "Чукотский автономный округ", "total_hotels": 12, "crawled": 4, "audited": 12, "avg_score": 11.7}
]
}
@app.get("/api/audit/download/{region}")
async def download_audit(region: str):
"""Скачать Excel отчет по аудиту"""
import os
from fastapi.responses import FileResponse
# Ищем последний файл аудита для региона
region_safe = region.replace(' ', '_')
audit_dir = '/root/engine/public_oversight/hotels'
try:
files = [f for f in os.listdir(audit_dir) if f.startswith(f'audit_{region_safe}') and f.endswith('.xlsx')]
if not files:
return {"error": f"Файл аудита для региона '{region}' не найден. Сначала запустите аудит."}
# Берем последний файл (по дате в имени)
files.sort(reverse=True)
latest_file = files[0]
file_path = os.path.join(audit_dir, latest_file)
return FileResponse(
path=file_path,
filename=latest_file,
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
except Exception as e:
return {"error": str(e)}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8889)