Fix syntax errors in app/main/routes.py - Clean up duplicate and broken code

parent 79ef3b52
......@@ -8,6 +8,9 @@ from app import db, csrf
from app.upload.file_handler import get_file_upload_handler
from app.upload.fixture_parser import get_fixture_parser
from app.utils.security import require_admin, require_active_user
import pandas as pd
from io import BytesIO, StringIO
from flask import Response, make_response
logger = logging.getLogger(__name__)
......@@ -671,7 +674,7 @@ def admin_edit_user(user_id):
from app.models import User
user = User.query.get_or_404(user_id)
# Prevent editing the current admin user's admin status
# Prevent editing current admin user's admin status
if user.id == current_user.id and request.json.get('is_admin') == False:
return jsonify({'error': 'Cannot remove admin status from your own account'}), 400
......@@ -720,7 +723,7 @@ def admin_delete_user(user_id):
from app.models import User
user = User.query.get_or_404(user_id)
# Prevent deleting the current admin user
# Prevent deleting current admin user
if user.id == current_user.id:
return jsonify({'error': 'Cannot delete your own account'}), 400
......@@ -1125,7 +1128,7 @@ def create_api_token():
if existing_token:
return jsonify({'error': 'A token with this name already exists'}), 400
# Generate the token
# Generate token
api_token = current_user.generate_api_token(token_name)
logger.info(f"API token '{token_name}' created by user {current_user.username}")
......@@ -1148,7 +1151,7 @@ def delete_api_token(token_id):
try:
from app.models import APIToken
# Get the token (ensure it belongs to current user)
# Get token (ensure it belongs to current user)
token = APIToken.query.filter_by(
id=token_id,
user_id=current_user.id
......@@ -1177,7 +1180,7 @@ def revoke_api_token(token_id):
try:
from app.models import APIToken
# Get the token (ensure it belongs to current user)
# Get token (ensure it belongs to current user)
token = APIToken.query.filter_by(
id=token_id,
user_id=current_user.id
......@@ -1191,7 +1194,7 @@ def revoke_api_token(token_id):
logger.info(f"API token '{token.name}' revoked by user {current_user.username}")
return jsonify({
'message': f'API token "{token.name}" revoked successfully',
'message': f'API token "{token_name}" revoked successfully',
'token': token.to_dict()
}), 200
......@@ -1208,7 +1211,7 @@ def extend_api_token(token_id):
try:
from app.models import APIToken
# Get the token (ensure it belongs to current user)
# Get token (ensure it belongs to current user)
token = APIToken.query.filter_by(
id=token_id,
user_id=current_user.id
......@@ -1386,7 +1389,7 @@ def download_zip(match_id):
from app.models import Match
from flask import send_file, abort
# Get the match
# Get match
if current_user.is_admin:
match = Match.query.get_or_404(match_id)
else:
......@@ -1529,6 +1532,8 @@ def clients():
flash('Error loading clients', 'error')
return render_template('main/clients.html', clients=[])
# Reports Routes
@csrf.exempt
@bp.route('/reports')
@login_required
......@@ -1687,22 +1692,161 @@ def export_reports(query, export_format):
"""Export reports to various formats"""
try:
from app.models import ReportSync, Bet, ExtractionStats
import pandas as pd
from flask import Response
import io
# Get all reports matching the query
# Get all reports matching query
reports = query.all()
if not reports:
flash('No reports to export', 'warning')
return redirect(url_for('main.reports'))
@csrf.exempt
@bp.route('/sync-logs')
@login_required
@require_active_user
def sync_logs():
# Prepare data for export
export_data = []
for report in reports:
# Get bets and stats for this report
bets = Bet.query.filter_by(sync_id=report.id).all()
stats = ExtractionStats.query.filter_by(sync_id=report.id).all()
# Add summary row
export_data.append({
'Type': 'Summary',
'Sync ID': report.sync_id,
'Client ID': report.client_id,
'Sync Timestamp': report.sync_timestamp.strftime('%Y-%m-%d %H:%M:%S') if report.sync_timestamp else '',
'Date Range': report.date_range,
'Start Date': report.start_date.strftime('%Y-%m-%d') if report.start_date else '',
'End Date': report.end_date.strftime('%Y-%m-%d') if report.end_date else '',
'Total Payin': float(report.total_payin) if report.total_payin else 0.0,
'Total Payout': float(report.total_payout) if report.total_payout else 0.0,
'Net Profit': float(report.net_profit) if report.net_profit else 0.0,
'Total Bets': report.total_bets,
'Total Matches': report.total_matches,
'Bet UUID': '',
'Match ID': '',
'Outcome': '',
'Amount': '',
'Result': ''
})
# Add bet details
for bet in bets:
for detail in bet.details:
export_data.append({
'Type': 'Bet Detail',
'Sync ID': report.sync_id,
'Client ID': report.client_id,
'Sync Timestamp': report.sync_timestamp.strftime('%Y-%m-%d %H:%M:%S') if report.sync_timestamp else '',
'Date Range': report.date_range,
'Start Date': report.start_date.strftime('%Y-%m-%d') if report.start_date else '',
'End Date': report.end_date.strftime('%Y-%m-%d') if report.end_date else '',
'Total Payin': '',
'Total Payout': '',
'Net Profit': '',
'Total Bets': '',
'Total Matches': '',
'Bet UUID': bet.uuid,
'Match ID': detail.match_id,
'Outcome': detail.outcome,
'Amount': float(detail.amount) if detail.amount else 0.0,
'Result': detail.result
})
# Add extraction stats
for stat in stats:
export_data.append({
'Type': 'Extraction Stats',
'Sync ID': report.sync_id,
'Client ID': report.client_id,
'Sync Timestamp': report.sync_timestamp.strftime('%Y-%m-%d %H:%M:%S') if report.sync_timestamp else '',
'Date Range': report.date_range,
'Start Date': report.start_date.strftime('%Y-%m-%d') if report.start_date else '',
'End Date': report.end_date.strftime('%Y-%m-%d') if report.end_date else '',
'Total Payin': float(stat.total_amount_collected) if stat.total_amount_collected else 0.0,
'Total Payout': float(stat.total_redistributed) if stat.total_redistributed else 0.0,
'Net Profit': '',
'Total Bets': stat.total_bets,
'Total Matches': '',
'Bet UUID': '',
'Match ID': stat.match_id,
'Outcome': stat.actual_result,
'Amount': '',
'Result': stat.extraction_result
})
# Create DataFrame
df = pd.DataFrame(export_data)
# Export based on format
if export_format == 'csv':
output = StringIO()
df.to_csv(output, index=False)
output.seek(0)
return Response(
output.getvalue(),
mimetype='text/csv',
headers={'Content-Disposition': 'attachment; filename=reports_export.csv'}
)
elif export_format == 'xlsx':
output = BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Reports')
output.seek(0)
return Response(
output.getvalue(),
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers={'Content-Disposition': 'attachment; filename=reports_export.xlsx'}
)
elif export_format == 'pdf':
# For PDF, we'll create a simple HTML table and convert it
from weasyprint import HTML
import tempfile
# Create HTML table
html_table = df.to_html(index=False, classes='table table-striped table-bordered')
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
table {{ border-collapse: collapse; width: 100%; margin-bottom: 20px; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #4CAF50; color: white; }}
tr:nth-child(even) {{ background-color: #f2f2f2; }}
</style>
</head>
<body>
<h1>Reports Export</h1>
<p>Generated on: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}</p>
{html_table}
</body>
</html>
"""
# Generate PDF
pdf_bytes = HTML(string=html_content).write_pdf()
return Response(
pdf_bytes,
mimetype='application/pdf',
headers={'Content-Disposition': 'attachment; filename=reports_export.pdf'}
)
else:
flash('Invalid export format', 'error')
return redirect(url_for('main.reports'))
except Exception as e:
logger.error(f"Export reports error: {str(e)}")
flash('Error exporting reports', 'error')
return redirect(url_for('main.reports'))
@csrf.exempt
@bp.route('/sync-logs')
@login_required
@require_active_user
def sync_logs():
"""Sync logs page with filtering and search"""
try:
from app.models import ReportSyncLog
......@@ -1727,16 +1871,112 @@ def export_reports(query, export_format):
page = request.args.get('page', 1, type=int)
per_page = min(request.args.get('per_page', 50, type=int), 200)
# Base query
if current_user.is_admin:
query = ReportSyncLog.query
else:
# Non-admin users can only see logs from their own clients
from app.models import APIToken, ClientActivity
user_token_ids = [t.id for t in APIToken.query.filter_by(user_id=current_user.id).all()]
if user_token_ids:
client_ids = [c.rustdesk_id for c in ClientActivity.query.filter(
ClientActivity.api_token_id.in_(user_token_ids)
).all()]
if client_ids:
query = ReportSyncLog.query.filter(ReportSyncLog.client_id.in_(client_ids))
else:
query = ReportSyncLog.query.filter(ReportSyncLog.client_id == 'none')
else:
query = ReportSyncLog.query.filter(ReportSyncLog.client_id == 'none')
# Apply filters
if client_id_filter:
query = query.filter(ReportSyncLog.client_id == client_id_filter)
if operation_type_filter:
query = query.filter(ReportSyncLog.operation_type == operation_type_filter)
if status_filter:
query = query.filter(ReportSyncLog.status == status_filter)
if start_date_filter:
try:
start_date = datetime.strptime(start_date_filter, '%Y-%m-%d')
query = query.filter(ReportSyncLog.created_at >= start_date)
except ValueError:
pass
if end_date_filter:
try:
end_date = datetime.strptime(end_date_filter, '%Y-%m-%d')
# Include the entire end date
end_date = end_date.replace(hour=23, minute=59, second=59)
query = query.filter(ReportSyncLog.created_at <= end_date)
except ValueError:
pass
# Search functionality
if search_query:
search_pattern = f"%{search_query}%"
query = query.filter(
or_(
ReportSyncLog.sync_id.ilike(search_pattern),
ReportSyncLog.client_id.ilike(search_pattern),
ReportSyncLog.error_message.ilike(search_pattern)
)
)
# Sorting
if hasattr(ReportSyncLog, sort_by):
sort_column = getattr(ReportSyncLog, sort_by)
if sort_order == 'asc':
query = query.order_by(sort_column.asc())
else:
query = query.order_by(sort_column.desc())
else:
query = query.order_by(ReportSyncLog.created_at.desc())
# Pagination
logs_pagination = query.paginate(page=page, per_page=per_page, error_out=False)
# Get unique client IDs for filter dropdown
if current_user.is_admin:
all_client_ids = db.session.query(ReportSyncLog.client_id).distinct().all()
else:
if user_token_ids:
all_client_ids = db.session.query(ClientActivity.rustdesk_id).filter(
ClientActivity.api_token_id.in_(user_token_ids)
).distinct().all()
else:
all_client_ids = []
client_ids = [cid[0] for cid in all_client_ids if cid[0]]
return render_template('main/sync_logs.html',
logs=logs_pagination.items,
pagination=logs_pagination,
client_ids=client_ids,
filters={
'client_id': client_id_filter,
'operation_type': operation_type_filter,
'status': status_filter,
'search': search_query,
'start_date': start_date_filter,
'end_date': end_date_filter,
'sort_by': sort_by,
'sort_order': sort_order
})
except Exception as e:
logger.error(f"Sync logs page error: {str(e)}")
flash('Error loading sync logs', 'error')
return render_template('main/sync_logs.html', logs=[], pagination=None, client_ids=[])
def export_sync_logs(export_format):
"""Export sync logs to CSV, XLSX, or PDF"""
try:
from app.models import ReportSyncLog
from sqlalchemy import and_, or_
from flask import make_response, send_file
import pandas as pd
from io import BytesIO
from datetime import datetime
# Get filter parameters
client_id_filter = request.args.get('client_id', '').strip()
......@@ -1980,246 +2220,3 @@ def export_sync_logs(export_format):
logger.error(f"Export sync logs error: {str(e)}")
flash('Error exporting sync logs', 'error')
return redirect(url_for('main.sync_logs'))
\ No newline at end of file
# Base query
if current_user.is_admin:
query = ReportSyncLog.query
else:
# Non-admin users can only see logs from their own clients
from app.models import APIToken, ClientActivity
user_token_ids = [t.id for t in APIToken.query.filter_by(user_id=current_user.id).all()]
if user_token_ids:
client_ids = [c.rustdesk_id for c in ClientActivity.query.filter(
ClientActivity.api_token_id.in_(user_token_ids)
).all()]
if client_ids:
query = ReportSyncLog.query.filter(ReportSyncLog.client_id.in_(client_ids))
else:
query = ReportSyncLog.query.filter(ReportSyncLog.client_id == 'none')
else:
query = ReportSyncLog.query.filter(ReportSyncLog.client_id == 'none')
# Apply filters
if client_id_filter:
query = query.filter(ReportSyncLog.client_id == client_id_filter)
if operation_type_filter:
query = query.filter(ReportSyncLog.operation_type == operation_type_filter)
if status_filter:
query = query.filter(ReportSyncLog.status == status_filter)
if start_date_filter:
try:
start_date = datetime.strptime(start_date_filter, '%Y-%m-%d')
query = query.filter(ReportSyncLog.created_at >= start_date)
except ValueError:
pass
if end_date_filter:
try:
end_date = datetime.strptime(end_date_filter, '%Y-%m-%d')
# Include the entire end date
end_date = end_date.replace(hour=23, minute=59, second=59)
query = query.filter(ReportSyncLog.created_at <= end_date)
except ValueError:
pass
# Search functionality
if search_query:
search_pattern = f"%{search_query}%"
query = query.filter(
or_(
ReportSyncLog.sync_id.ilike(search_pattern),
ReportSyncLog.client_id.ilike(search_pattern),
ReportSyncLog.error_message.ilike(search_pattern)
)
)
# Sorting
if hasattr(ReportSyncLog, sort_by):
sort_column = getattr(ReportSyncLog, sort_by)
if sort_order == 'asc':
query = query.order_by(sort_column.asc())
else:
query = query.order_by(sort_column.desc())
else:
query = query.order_by(ReportSyncLog.created_at.desc())
# Pagination
logs_pagination = query.paginate(page=page, per_page=per_page, error_out=False)
# Get unique client IDs for filter dropdown
if current_user.is_admin:
all_client_ids = db.session.query(ReportSyncLog.client_id).distinct().all()
else:
if user_token_ids:
all_client_ids = db.session.query(ClientActivity.rustdesk_id).filter(
ClientActivity.api_token_id.in_(user_token_ids)
).distinct().all()
else:
all_client_ids = []
client_ids = [cid[0] for cid in all_client_ids if cid[0]]
return render_template('main/sync_logs.html',
logs=logs_pagination.items,
pagination=logs_pagination,
client_ids=client_ids,
filters={
'client_id': client_id_filter,
'operation_type': operation_type_filter,
'status': status_filter,
'search': search_query,
'start_date': start_date_filter,
'end_date': end_date_filter,
'sort_by': sort_by,
'sort_order': sort_order
})
except Exception as e:
logger.error(f"Sync logs page error: {str(e)}")
flash('Error loading sync logs', 'error')
return render_template('main/sync_logs.html', logs=[], pagination=None, client_ids=[])
# Prepare data for export
export_data = []
for report in reports:
# Get bets and stats for this report
bets = Bet.query.filter_by(sync_id=report.id).all()
stats = ExtractionStats.query.filter_by(sync_id=report.id).all()
# Add summary row
export_data.append({
'Type': 'Summary',
'Sync ID': report.sync_id,
'Client ID': report.client_id,
'Sync Timestamp': report.sync_timestamp.strftime('%Y-%m-%d %H:%M:%S') if report.sync_timestamp else '',
'Date Range': report.date_range,
'Start Date': report.start_date.strftime('%Y-%m-%d') if report.start_date else '',
'End Date': report.end_date.strftime('%Y-%m-%d') if report.end_date else '',
'Total Payin': float(report.total_payin) if report.total_payin else 0.0,
'Total Payout': float(report.total_payout) if report.total_payout else 0.0,
'Net Profit': float(report.net_profit) if report.net_profit else 0.0,
'Total Bets': report.total_bets,
'Total Matches': report.total_matches,
'Bet UUID': '',
'Match ID': '',
'Outcome': '',
'Amount': '',
'Result': ''
})
# Add bet details
for bet in bets:
for detail in bet.details:
export_data.append({
'Type': 'Bet Detail',
'Sync ID': report.sync_id,
'Client ID': report.client_id,
'Sync Timestamp': report.sync_timestamp.strftime('%Y-%m-%d %H:%M:%S') if report.sync_timestamp else '',
'Date Range': report.date_range,
'Start Date': report.start_date.strftime('%Y-%m-%d') if report.start_date else '',
'End Date': report.end_date.strftime('%Y-%m-%d') if report.end_date else '',
'Total Payin': '',
'Total Payout': '',
'Net Profit': '',
'Total Bets': '',
'Total Matches': '',
'Bet UUID': bet.uuid,
'Match ID': detail.match_id,
'Outcome': detail.outcome,
'Amount': float(detail.amount) if detail.amount else 0.0,
'Result': detail.result
})
# Add extraction stats
for stat in stats:
export_data.append({
'Type': 'Extraction Stats',
'Sync ID': report.sync_id,
'Client ID': report.client_id,
'Sync Timestamp': report.sync_timestamp.strftime('%Y-%m-%d %H:%M:%S') if report.sync_timestamp else '',
'Date Range': report.date_range,
'Start Date': report.start_date.strftime('%Y-%m-%d') if report.start_date else '',
'End Date': report.end_date.strftime('%Y-%m-%d') if report.end_date else '',
'Total Payin': float(stat.total_amount_collected) if stat.total_amount_collected else 0.0,
'Total Payout': float(stat.total_redistributed) if stat.total_redistributed else 0.0,
'Net Profit': '',
'Total Bets': stat.total_bets,
'Total Matches': '',
'Bet UUID': '',
'Match ID': stat.match_id,
'Outcome': stat.actual_result,
'Amount': '',
'Result': stat.extraction_result
})
# Create DataFrame
df = pd.DataFrame(export_data)
# Export based on format
if export_format == 'csv':
output = io.StringIO()
df.to_csv(output, index=False)
output.seek(0)
return Response(
output.getvalue(),
mimetype='text/csv',
headers={'Content-Disposition': 'attachment; filename=reports_export.csv'}
)
elif export_format == 'xlsx':
output = io.BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Reports')
output.seek(0)
return Response(
output.getvalue(),
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers={'Content-Disposition': 'attachment; filename=reports_export.xlsx'}
)
elif export_format == 'pdf':
# For PDF, we'll create a simple HTML table and convert it
from weasyprint import HTML
import tempfile
# Create HTML table
html_table = df.to_html(index=False, classes='table table-striped table-bordered')
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
table {{ border-collapse: collapse; width: 100%; margin-bottom: 20px; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #4CAF50; color: white; }}
tr:nth-child(even) {{ background-color: #f2f2f2; }}
</style>
</head>
<body>
<h1>Reports Export</h1>
<p>Generated on: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}</p>
{html_table}
</body>
</html>
"""
# Generate PDF
pdf_bytes = HTML(string=html_content).write_pdf()
return Response(
pdf_bytes,
mimetype='application/pdf',
headers={'Content-Disposition': 'attachment; filename=reports_export.pdf'}
)
else:
flash('Invalid export format', 'error')
return redirect(url_for('main.reports'))
except Exception as e:
logger.error(f"Export reports error: {str(e)}")
flash('Error exporting reports', 'error')
return redirect(url_for('main.reports'))
\ No newline at end of file
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