quick fix 2
This commit is contained in:
271
webserver/app.py
Normal file
271
webserver/app.py
Normal file
@ -0,0 +1,271 @@
|
||||
from flask import Flask, jsonify, send_from_directory, make_response, request
|
||||
import os
|
||||
import pyodbc
|
||||
|
||||
app = Flask(__name__, static_folder='web', static_url_path='')
|
||||
|
||||
|
||||
def get_access_db_path():
|
||||
# Database file next to this script
|
||||
base = os.path.dirname("C:/Users/marek_yrlsweo/Documents/furrystatus/bot/webserver")
|
||||
return os.path.join(base, 'playcount.accdb')
|
||||
|
||||
|
||||
def read_access_db(max_rows=1000):
|
||||
db_path = get_access_db_path()
|
||||
if not os.path.exists(db_path):
|
||||
return {'error': 'database file not found', 'path': db_path}
|
||||
|
||||
conn_str = (
|
||||
r'Driver={Microsoft Access Driver (*.mdb, *.accdb)};'
|
||||
f'DBQ={db_path};'
|
||||
)
|
||||
|
||||
try:
|
||||
conn = pyodbc.connect(conn_str, autocommit=True)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# list user tables
|
||||
tables = []
|
||||
for row in cursor.tables():
|
||||
ttype = getattr(row, 'TABLE_TYPE', None) or row[3]
|
||||
tname = getattr(row, 'TABLE_NAME', None) or row[2]
|
||||
if ttype and ttype.upper() == 'TABLE':
|
||||
# skip system tables
|
||||
if tname.startswith('MSys'):
|
||||
continue
|
||||
tables.append(tname)
|
||||
|
||||
data = {}
|
||||
for table in tables:
|
||||
try:
|
||||
cursor.execute(f'SELECT * FROM [{table}]')
|
||||
cols = [column[0] for column in cursor.description] if cursor.description else []
|
||||
rows = []
|
||||
for i, r in enumerate(cursor.fetchmany(max_rows)):
|
||||
rows.append({cols[j]: r[j] for j in range(len(cols))})
|
||||
data[table] = rows
|
||||
except Exception as e:
|
||||
data[table] = {'error': str(e)}
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return {'tables': tables, 'data': data}
|
||||
except Exception as e:
|
||||
return {'error': 'pyodbc connection failed', 'detail': str(e)}
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return send_from_directory(app.static_folder, 'main.html')
|
||||
|
||||
|
||||
@app.route('/danebota.html')
|
||||
def danebota_html():
|
||||
return send_from_directory(app.static_folder, 'danebota.html')
|
||||
|
||||
|
||||
@app.route('/api/playcounts')
|
||||
def api_playcounts():
|
||||
result = read_access_db()
|
||||
resp = make_response(jsonify(result))
|
||||
# allow simple fetches from the same host or other origins during development
|
||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||
return resp
|
||||
|
||||
|
||||
def _coerce_int(v):
|
||||
try:
|
||||
if v is None:
|
||||
return 0
|
||||
if isinstance(v, (int,)):
|
||||
return v
|
||||
# handle Decimal, floats and numeric strings
|
||||
return int(float(v))
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
|
||||
def _find_key(keys, candidates):
|
||||
# keys: iterable of strings (actual column names)
|
||||
# candidates: list of substrings to search for (lowercase)
|
||||
low_map = {k.lower(): k for k in keys}
|
||||
for cand in candidates:
|
||||
for lk, orig in low_map.items():
|
||||
if cand in lk:
|
||||
return orig
|
||||
return None
|
||||
|
||||
|
||||
def compute_summary_from_readdata(read_result):
|
||||
# read_result expected to have 'data' mapping table->rows
|
||||
if not read_result or 'data' not in read_result:
|
||||
return {'error': 'no data available'}
|
||||
|
||||
data = read_result['data']
|
||||
servers = {} # server -> {'total': int, 'tracks': {track: int}}
|
||||
total_plays = 0
|
||||
|
||||
# candidate substrings to detect columns
|
||||
server_candidates = ['server', 'guild', 'serverid', 'guildid']
|
||||
track_candidates = ['track', 'song', 'file', 'title', 'name']
|
||||
playcount_candidates = ['playcount', 'plays', 'count', 'play_count', 'times']
|
||||
|
||||
for table, rows in (data.items() if isinstance(data, dict) else []):
|
||||
if not isinstance(rows, list):
|
||||
continue
|
||||
if len(rows) == 0:
|
||||
continue
|
||||
# determine column names from first row
|
||||
keys = list(rows[0].keys())
|
||||
k_server = _find_key(keys, server_candidates)
|
||||
k_track = _find_key(keys, track_candidates)
|
||||
k_play = _find_key(keys, playcount_candidates)
|
||||
|
||||
for r in rows:
|
||||
# fallback values
|
||||
server_val = r.get(k_server) if k_server else None
|
||||
track_val = r.get(k_track) if k_track else None
|
||||
play_val = r.get(k_play) if k_play else None
|
||||
|
||||
# If server not present, lump under 'unknown'
|
||||
server_key = str(server_val) if server_val is not None else 'unknown'
|
||||
track_key = str(track_val) if track_val is not None else '(unknown track)'
|
||||
plays = _coerce_int(play_val)
|
||||
|
||||
if server_key not in servers:
|
||||
servers[server_key] = {'total': 0, 'tracks': {}}
|
||||
servers[server_key]['total'] += plays
|
||||
servers[server_key]['tracks'][track_key] = servers[server_key]['tracks'].get(track_key, 0) + plays
|
||||
total_plays += plays
|
||||
|
||||
return {
|
||||
'total_servers': len(servers),
|
||||
'total_plays': total_plays,
|
||||
'servers': servers,
|
||||
}
|
||||
|
||||
|
||||
@app.route('/api/playcounts/summary')
|
||||
def api_playcounts_summary():
|
||||
read = read_access_db(max_rows=10000)
|
||||
if 'error' in read:
|
||||
resp = make_response(jsonify(read))
|
||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||
return resp
|
||||
|
||||
summary = compute_summary_from_readdata(read)
|
||||
# Try to read bot_status.json (written by bot.py) to get accurate guild count
|
||||
try:
|
||||
status_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'bot_status.json')
|
||||
if os.path.exists(status_path):
|
||||
import json
|
||||
with open(status_path, 'r', encoding='utf-8') as f:
|
||||
bot_status = json.load(f)
|
||||
# override total_servers if available
|
||||
if isinstance(bot_status, dict) and 'guild_count' in bot_status:
|
||||
summary['bot_status'] = bot_status
|
||||
summary['total_servers'] = bot_status.get('guild_count', summary.get('total_servers', 0))
|
||||
except Exception:
|
||||
# non-fatal; continue with original summary
|
||||
pass
|
||||
resp = make_response(jsonify(summary))
|
||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||
return resp
|
||||
|
||||
def write_to_db(table_name, column1_name, value1, column2_name, value2):
|
||||
db_path = get_access_db_path()
|
||||
if not os.path.exists(db_path):
|
||||
return {'success': False, 'error': f'Database file not found: {db_path}'}
|
||||
|
||||
conn_str = (
|
||||
r'Driver={Microsoft Access Driver (*.mdb, *.accdb)};'
|
||||
f'DBQ={db_path};'
|
||||
)
|
||||
|
||||
try:
|
||||
# Uwaga: Użycie parametrów zapytania (znak ?) jest kluczowe dla
|
||||
# bezpieczeństwa (ochrona przed SQL injection)
|
||||
conn = pyodbc.connect(conn_str)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Budowanie zapytania INSERT
|
||||
# W Access należy użyć nawiasów kwadratowych dla nazw kolumn,
|
||||
# szczególnie jeśli zawierają spacje lub znaki specjalne
|
||||
sql_query = f"INSERT INTO [{table_name}] ([{column1_name}], [{column2_name}]) VALUES (?, ?)"
|
||||
|
||||
# Wykonanie zapytania z danymi
|
||||
cursor.execute(sql_query, value1, value2)
|
||||
|
||||
# Zatwierdzenie zmian
|
||||
conn.commit()
|
||||
|
||||
# Zamknięcie połączenia
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return {'success': True, 'message': 'Data inserted successfully.'}
|
||||
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'pyodbc write failed: {str(e)}'}
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
# ... (funkcja read_access_db - bez zmian) ...
|
||||
# ... (trasy index, danebota_html, api_playcounts - bez zmian) ...
|
||||
# ... (funkcje _coerce_int, _find_key, compute_summary_from_readdata - bez zmian) ...
|
||||
# ... (trasa api_playcounts_summary - bez zmian) ...
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# NOWA TRASA: Strona HTML dla sugestii
|
||||
# ----------------------------------------------------------------------
|
||||
@app.route('/sugestie.html')
|
||||
def sugestie_html():
|
||||
# Zakładając, że plik sugestie.html znajduje się w folderze 'web'
|
||||
return send_from_directory(app.static_folder, 'sugestie.html')
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# NOWA TRASA API: Odbieranie danych z JavaScript
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
@app.route('/api/sugestie', methods=['POST'])
|
||||
def api_sugestie():
|
||||
# Wymagane jest, aby dane były przesyłane metodą POST w formacie JSON
|
||||
if not request.is_json:
|
||||
return make_response(jsonify({'success': False, 'error': 'Missing JSON in request'}), 400)
|
||||
|
||||
data = request.get_json()
|
||||
|
||||
# 1. Zmienna 1 jest nadal obowiązkowa
|
||||
zmienna1 = data.get('zmienna1')
|
||||
|
||||
# 2. Zmienna 2 jest teraz nieobowiązkowa (ustawiamy pusty ciąg, jeśli brak)
|
||||
zmienna2 = data.get('zmienna2', '') # <-- Zmiana: ustawiamy domyślną wartość
|
||||
|
||||
|
||||
# Sprawdzenie, czy obowiązkowa zmienna1 jest obecna
|
||||
if zmienna1 is None: # Zmieniono warunek na sprawdzenie tylko zmiennej1
|
||||
return make_response(jsonify({'success': False, 'error': 'Missing required variable: zmienna1'}), 400)
|
||||
|
||||
# UWAGA: Nazwy tabeli i kolumn muszą być zgodne z bazą Access!
|
||||
TABLE_NAME = "Sugestie" # Zmień na faktyczną nazwę tabeli
|
||||
COLUMN1_NAME = "NazwaUtworu" # Zmień na faktyczną nazwę kolumny
|
||||
COLUMN2_NAME = "Link" # Zmień na faktyczną nazwę kolumny
|
||||
|
||||
# Wywołanie funkcji zapisu do bazy danych
|
||||
# Wartość zmienna2 (pusty ciąg lub podana) zostanie przekazana
|
||||
result = write_to_db(TABLE_NAME, COLUMN1_NAME, zmienna1, COLUMN2_NAME, zmienna2)
|
||||
|
||||
resp = make_response(jsonify(result))
|
||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||
return resp
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Development server
|
||||
app.run(host='100.100.23.87', port=5000, debug=False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user