gcp-hockey-results/motm_app/templates/device_tracking.html
2025-10-04 21:05:01 +08:00

231 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Device Tracking - HKFC Men's C Team MOTM System</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.tracking-section {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 2rem;
}
.pattern-warning {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1rem;
}
.device-id {
font-family: monospace;
font-size: 0.9em;
background-color: #e9ecef;
padding: 0.2rem 0.4rem;
border-radius: 0.25rem;
}
</style>
</head>
<body>
<div class="container mt-4">
<div class="row">
<div class="col-md-12">
<h1>Device Tracking Analysis</h1>
<p class="lead">Monitor voting patterns and detect potential duplicate voting</p>
<div class="mb-3">
<a href="/admin" class="btn btn-outline-primary">Back to Admin Dashboard</a>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- Analysis Controls -->
<div class="tracking-section">
<h3>Analysis Controls</h3>
<div class="row">
<div class="col-md-4">
<form method="POST">
<input type="hidden" name="action" value="analyze_patterns">
<button type="submit" class="btn btn-primary">Analyze Voting Patterns</button>
</form>
<small class="text-muted">Find devices with multiple votes</small>
</div>
<div class="col-md-4">
<a href="/admin/device-tracking" class="btn btn-outline-secondary">View Recent Votes</a>
<small class="text-muted">Show last 50 votes</small>
</div>
</div>
</div>
<!-- Pattern Analysis Results -->
{% if analysis_mode and patterns %}
<div class="tracking-section">
<h3>Voting Pattern Analysis</h3>
<p class="text-muted">Devices that have voted multiple times:</p>
{% if patterns %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>Device ID</th>
<th>Vote Count</th>
<th>Fixtures</th>
<th>MOTM Players</th>
<th>DotD Players</th>
<th>First Vote</th>
<th>Last Vote</th>
<th>IP Addresses</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for pattern in patterns %}
<tr>
<td><span class="device-id">{{ pattern.device_id }}</span></td>
<td>
<span class="badge bg-{{ 'danger' if pattern.vote_count > 3 else 'warning' }}">
{{ pattern.vote_count }}
</span>
</td>
<td>{{ pattern.fixtures_voted }}</td>
<td>{{ pattern.motm_players }}</td>
<td>{{ pattern.dotd_players }}</td>
<td>{{ pattern.first_vote.strftime('%Y-%m-%d %H:%M') if pattern.first_vote else 'N/A' }}</td>
<td>{{ pattern.last_vote.strftime('%Y-%m-%d %H:%M') if pattern.last_vote else 'N/A' }}</td>
<td>{{ pattern.ip_addresses }}</td>
<td>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="view_device_details">
<input type="hidden" name="device_id" value="{{ pattern.device_id }}">
<button type="submit" class="btn btn-sm btn-outline-info">Details</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if patterns|length > 0 %}
<div class="pattern-warning">
<h5><i class="bi bi-exclamation-triangle"></i> Pattern Analysis</h5>
<p class="mb-0">
<strong>Warning:</strong> {{ patterns|length }} device(s) have voted multiple times.
This could indicate duplicate voting or shared devices.
</p>
</div>
{% endif %}
{% else %}
<div class="alert alert-success">
<h5>No Suspicious Patterns Found</h5>
<p>All devices have voted only once per fixture.</p>
</div>
{% endif %}
</div>
{% endif %}
<!-- Device Details -->
{% if details_mode and device_details %}
<div class="tracking-section">
<h3>Device Details: <span class="device-id">{{ selected_device }}</span></h3>
<p class="text-muted">Complete voting history for this device:</p>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>Fixture Date</th>
<th>MOTM Vote</th>
<th>DotD Vote</th>
<th>IP Address</th>
<th>Vote Time</th>
</tr>
</thead>
<tbody>
{% for vote in device_details %}
<tr>
<td>{{ vote.fixture_date }}</td>
<td>{{ vote.motm_player_name }}</td>
<td>{{ vote.dotd_player_name }}</td>
<td>{{ vote.ip_address }}</td>
<td>{{ vote.vote_timestamp.strftime('%Y-%m-%d %H:%M:%S') if vote.vote_timestamp else 'N/A' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="mt-3">
<h5>Device Information</h5>
{% if device_details %}
<p><strong>User Agent:</strong> {{ device_details[0].user_agent[:100] }}{% if device_details[0].user_agent|length > 100 %}...{% endif %}</p>
<p><strong>Total Votes:</strong> {{ device_details|length }}</p>
<p><strong>Unique Fixtures:</strong> {{ device_details|map(attribute='fixture_date')|unique|list|length }}</p>
{% endif %}
</div>
</div>
{% endif %}
<!-- Recent Votes -->
{% if recent_votes and not analysis_mode and not details_mode %}
<div class="tracking-section">
<h3>Recent Votes</h3>
<p class="text-muted">Last 50 votes cast:</p>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>Device ID</th>
<th>Fixture Date</th>
<th>MOTM Vote</th>
<th>DotD Vote</th>
<th>IP Address</th>
<th>Vote Time</th>
</tr>
</thead>
<tbody>
{% for vote in recent_votes %}
<tr>
<td><span class="device-id">{{ vote.device_id }}</span></td>
<td>{{ vote.fixture_date }}</td>
<td>{{ vote.motm_player_name }}</td>
<td>{{ vote.dotd_player_name }}</td>
<td>{{ vote.ip_address }}</td>
<td>{{ vote.vote_timestamp.strftime('%Y-%m-%d %H:%M') if vote.vote_timestamp else 'N/A' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<!-- No Data Message -->
{% if not recent_votes and not patterns and not device_details %}
<div class="alert alert-info">
<h5>No Vote Data Available</h5>
<p>No votes have been cast yet, or the device tracking table is empty.</p>
</div>
{% endif %}
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>