Fix duplicate Flask route and add Statistics link to admin navbar

- Remove duplicate start_games function in routes.py that was causing Flask route conflict
- Add Statistics page link to admin submenu in navbar with chart-bar icon
parent ac7e676c
......@@ -798,6 +798,24 @@ def change_password():
return render_template('auth/change_password.html')
@main_bp.route('/statistics')
@login_required
def statistics():
"""Statistics dashboard page"""
try:
if not current_user.is_admin:
flash("Admin access required", "error")
return redirect(url_for('main.index'))
return render_template('dashboard/statistics.html',
user=current_user,
page_title="Statistics")
except Exception as e:
logger.error(f"Statistics page error: {e}")
flash("Error loading statistics", "error")
return render_template('errors/500.html'), 500
# API routes
@api_bp.route('/status')
def system_status():
......@@ -4651,50 +4669,6 @@ def mark_admin_bet_paid(bet_id):
logger.error(f"API mark admin bet paid error: {e}")
@api_bp.route('/cashier/start-games', methods=['POST'])
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def start_games():
"""Start games for the first fixture - send START_GAME message to message bus"""
try:
from ..core.message_bus import MessageBuilder, MessageType
# Send START_GAME message to the message bus
# The games thread will handle finding and activating the appropriate matches
start_game_message = MessageBuilder.start_game(
sender="web_dashboard",
fixture_id=None # Let the games thread find the first fixture with pending matches
)
# Publish the message to the message bus
if api_bp.message_bus:
success = api_bp.message_bus.publish(start_game_message)
if success:
logger.info("START_GAME message sent to message bus")
return jsonify({
"success": True,
"message": "Start games request sent to games thread",
"fixture_id": None # Will be determined by games thread
})
else:
logger.error("Failed to publish START_GAME message to message bus")
return jsonify({
"success": False,
"error": "Failed to send start games request"
}), 500
else:
logger.error("Message bus not available")
return jsonify({
"success": False,
"error": "Message bus not available"
}), 500
except Exception as e:
logger.error(f"API start games error: {e}")
return jsonify({"error": str(e)}), 500
# Barcode Settings API routes
@api_bp.route('/barcode-settings')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
......@@ -4872,6 +4846,147 @@ def set_qrcode_settings():
return jsonify({"error": str(e)}), 500
# Statistics API endpoints
@api_bp.route('/statistics')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_statistics():
"""Get extraction statistics with filtering"""
try:
from ..database.models import ExtractionStatsModel
from datetime import datetime, timedelta
# Get filter parameters
date_range = request.args.get('date_range', 'month')
fixture_id = request.args.get('fixture_id', '').strip()
result_filter = request.args.get('result', '').strip()
session = api_bp.db_manager.get_session()
try:
# Build query with filters
query = session.query(ExtractionStatsModel)
# Date range filter
now = datetime.utcnow()
if date_range == 'today':
start_date = now.replace(hour=0, minute=0, second=0, microsecond=0)
query = query.filter(ExtractionStatsModel.match_datetime >= start_date)
elif date_range == 'week':
start_date = now - timedelta(days=7)
query = query.filter(ExtractionStatsModel.match_datetime >= start_date)
elif date_range == 'month':
start_date = now - timedelta(days=30)
query = query.filter(ExtractionStatsModel.match_datetime >= start_date)
# 'all' doesn't need filtering
# Fixture ID filter
if fixture_id:
query = query.filter(ExtractionStatsModel.fixture_id.ilike(f'%{fixture_id}%'))
# Result filter
if result_filter:
query = query.filter(ExtractionStatsModel.actual_result == result_filter)
# Get statistics ordered by most recent first
stats = query.order_by(ExtractionStatsModel.match_datetime.desc()).all()
# Calculate summary
total_matches = len(stats)
total_collected = sum(stat.total_amount_collected for stat in stats)
total_redistributed = sum(stat.total_redistributed for stat in stats)
net_profit = total_collected - total_redistributed
summary = {
'total_matches': total_matches,
'total_collected': float(total_collected),
'total_redistributed': float(total_redistributed),
'net_profit': float(net_profit)
}
# Convert stats to dict
stats_data = []
for stat in stats:
stat_dict = {
'id': stat.id,
'match_id': stat.match_id,
'fixture_id': stat.fixture_id,
'match_datetime': stat.match_datetime.isoformat(),
'total_bets': stat.total_bets,
'total_amount_collected': float(stat.total_amount_collected),
'total_redistributed': float(stat.total_redistributed),
'actual_result': stat.actual_result,
'extraction_result': stat.extraction_result,
'cap_applied': stat.cap_applied,
'cap_percentage': float(stat.cap_percentage) if stat.cap_percentage else None,
'under_bets': stat.under_bets,
'under_amount': float(stat.under_amount),
'over_bets': stat.over_bets,
'over_amount': float(stat.over_amount)
}
stats_data.append(stat_dict)
return jsonify({
"success": True,
"summary": summary,
"statistics": stats_data,
"total": len(stats_data)
})
finally:
session.close()
except Exception as e:
logger.error(f"API get statistics error: {e}")
return jsonify({"error": str(e)}), 500
@api_bp.route('/statistics/<int:stats_id>')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_statistics_details(stats_id):
"""Get detailed information for a specific statistics record"""
try:
from ..database.models import ExtractionStatsModel
session = api_bp.db_manager.get_session()
try:
stat = session.query(ExtractionStatsModel).filter_by(id=stats_id).first()
if not stat:
return jsonify({"error": "Statistics record not found"}), 404
stat_dict = {
'id': stat.id,
'match_id': stat.match_id,
'fixture_id': stat.fixture_id,
'match_datetime': stat.match_datetime.isoformat(),
'total_bets': stat.total_bets,
'total_amount_collected': float(stat.total_amount_collected),
'total_redistributed': float(stat.total_redistributed),
'actual_result': stat.actual_result,
'extraction_result': stat.extraction_result,
'cap_applied': stat.cap_applied,
'cap_percentage': float(stat.cap_percentage) if stat.cap_percentage else None,
'under_bets': stat.under_bets,
'under_amount': float(stat.under_amount),
'over_bets': stat.over_bets,
'over_amount': float(stat.over_amount),
'result_breakdown': stat.result_breakdown,
'created_at': stat.created_at.isoformat(),
'updated_at': stat.updated_at.isoformat()
}
return jsonify({
"success": True,
"statistics": stat_dict
})
finally:
session.close()
except Exception as e:
logger.error(f"API get statistics details error: {e}")
return jsonify({"error": str(e)}), 500
@api_bp.route('/barcode/<uuid:bet_id>')
def generate_bet_barcode(bet_id):
"""Generate barcode image for bet verification - no authentication required"""
......
......@@ -142,6 +142,9 @@
<li><a class="dropdown-item" href="{{ url_for('main.logs') }}">
<i class="fas fa-file-alt me-1"></i>Logs
</a></li>
<li><a class="dropdown-item" href="{{ url_for('main.statistics') }}">
<i class="fas fa-chart-bar me-1"></i>Statistics
</a></li>
</ul>
</li>
{% endif %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment