templates license and results bottom bar

parent 0a38c586
Collecting flask-cors
Using cached flask_cors-6.0.1-py3-none-any.whl.metadata (5.3 kB)
Requirement already satisfied: flask>=0.9 in ./venv/lib/python3.13/site-packages (from flask-cors) (3.1.2)
Requirement already satisfied: Werkzeug>=0.7 in ./venv/lib/python3.13/site-packages (from flask-cors) (3.1.3)
Requirement already satisfied: blinker>=1.9.0 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (1.9.0)
Requirement already satisfied: click>=8.1.3 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (8.3.1)
Requirement already satisfied: itsdangerous>=2.2.0 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (2.2.0)
Requirement already satisfied: jinja2>=3.1.2 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (3.1.6)
Requirement already satisfied: markupsafe>=2.1.1 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (3.0.3)
Using cached flask_cors-6.0.1-py3-none-any.whl (13 kB)
Installing collected packages: flask-cors
Successfully installed flask-cors-6.0.1
......@@ -280,6 +280,113 @@ class OverlayWebChannel(QObject):
logger.error(f"QtWebChannel: Full traceback: {traceback.format_exc()}")
return json.dumps([])
@pyqtSlot(result=str)
def getLicenseText(self) -> str:
"""Provide license text to JavaScript via WebChannel"""
try:
logger.info("QtWebChannel: Getting license text from database")
if not self.db_manager:
logger.error("QtWebChannel: Database manager not available")
return json.dumps({"license_text": "License text not available - database not connected"})
# Get license text from database
license_text = self._get_license_text_from_database()
logger.debug(f"QtWebChannel: Retrieved license text from database")
return json.dumps({"license_text": license_text})
except Exception as e:
logger.error(f"QtWebChannel: Failed to get license text: {e}")
return json.dumps({"license_text": "License text not available - error occurred"})
@pyqtSlot(result=str)
def getCompletedMatches(self) -> str:
"""Provide last 3 completed matches to JavaScript via WebChannel"""
try:
logger.info("QtWebChannel: Getting completed matches from database")
if not self.db_manager:
logger.error("QtWebChannel: Database manager not available")
return json.dumps([])
# Get completed matches from database
completed_matches = self._get_completed_matches_from_database()
logger.debug(f"QtWebChannel: Retrieved {len(completed_matches)} completed matches from database")
return json.dumps(completed_matches)
except Exception as e:
logger.error(f"QtWebChannel: Failed to get completed matches: {e}")
return json.dumps([])
def _get_license_text_from_database(self) -> str:
"""Get license text from database configuration"""
try:
if not self.db_manager:
logger.error("Database manager not available for getting license text")
return "License text not available - database not connected"
# Get license text from configuration
license_text = self.db_manager.get_config_value('license_text', 'License text not configured')
logger.debug(f"Retrieved license text from database: {license_text}")
return license_text
except Exception as e:
logger.error(f"Failed to get license text from database: {e}")
return "License text not available - database error"
def _get_completed_matches_from_database(self) -> Optional[List[Dict[str, Any]]]:
"""Get last 3 completed matches from database"""
try:
from ..database.models import MatchModel
from datetime import datetime
# Use the database manager passed to this channel
if not self.db_manager:
logger.error("Database manager not initialized")
return []
session = self.db_manager.get_session()
try:
# Get last 3 completed matches (status = 'done' and result is not null)
# Ordered by end_time descending (most recent first)
completed_matches = session.query(MatchModel).filter(
MatchModel.status == 'done',
MatchModel.result.isnot(None),
MatchModel.end_time.isnot(None)
).order_by(MatchModel.end_time.desc()).limit(3).all()
if not completed_matches:
logger.debug("No completed matches found")
return []
matches_data = []
for match in completed_matches:
match_data = {
'id': match.id,
'match_number': match.match_number,
'fighter1_township': match.fighter1_township,
'fighter2_township': match.fighter2_township,
'venue_kampala_township': match.venue_kampala_township,
'result': match.result,
'under_over_result': match.under_over_result,
'end_time': match.end_time.isoformat() if match.end_time else None
}
matches_data.append(match_data)
logger.debug(f"Retrieved {len(matches_data)} completed matches from database")
return matches_data
finally:
session.close()
except Exception as e:
logger.error(f"Failed to get completed matches from database: {e}")
return []
def _get_fixture_data_from_games_thread(self) -> Optional[List[Dict[str, Any]]]:
"""Get fixture data from the games thread"""
try:
......
......@@ -256,25 +256,41 @@
let currentMessage = 'Waiting for game to start...';
let currentIcon = '🥊';
let licenseText = 'Loading license text...';
let licenseTextFromData = '';
// Function to update overlay data (called by Qt WebChannel)
function updateOverlayData(data) {
console.log('Received text overlay data:', data);
overlayData = data || {};
if (data && data.title) {
let contentChanged = false;
if (data && data.title && data.title !== currentTitle) {
currentTitle = data.title;
contentChanged = true;
}
if (data && data.message) {
if (data && data.message && data.message !== currentMessage) {
currentMessage = data.message;
contentChanged = true;
}
if (data && data.icon) {
if (data && data.icon && data.icon !== currentIcon) {
currentIcon = data.icon;
contentChanged = true;
}
// Handle license text from overlay data
if (data && data.license_text) {
licenseTextFromData = data.license_text;
licenseText = licenseTextFromData;
updateLicenseDisplay();
}
// Only update message display and restart animations if content actually changed
if (contentChanged) {
updateMessageDisplay();
}
updateMessageDisplay();
}
// Update the message display
......@@ -292,21 +308,35 @@
restartAnimations();
}
// Fetch license text from API
// Fetch license text from WebChannel
async function fetchLicenseText() {
try {
// Try to fetch from the web dashboard API
const response = await fetch('http://127.0.0.1:5001/api/config/license-text');
if (response.ok) {
const data = await response.json();
if (data.success) {
licenseText = data.license_text || '';
updateLicenseDisplay();
// Only fetch from WebChannel if we don't have license text from overlay data yet
if (!licenseTextFromData || licenseTextFromData.trim() === '') {
// Try to get license text from Qt WebChannel
if (window.overlay && window.overlay.getLicenseText) {
const licenseTextData = await window.overlay.getLicenseText();
if (licenseTextData) {
// Parse the JSON response to extract license_text
try {
const licenseData = JSON.parse(licenseTextData);
if (licenseData.license_text) {
licenseText = licenseData.license_text;
updateLicenseDisplay();
console.log('License text loaded from WebChannel:', licenseText);
} else {
console.log('No license_text found in WebChannel response');
}
} catch (parseError) {
console.log('Failed to parse license text JSON from WebChannel:', parseError);
// Keep default text if parsing fails
}
}
}
}
} catch (error) {
console.log('Could not fetch license text from API, using default');
// Keep default text if API is not available
console.log('Could not fetch license text from WebChannel, using default');
// Keep default text if WebChannel is not available
}
}
......@@ -347,28 +377,79 @@
document.addEventListener('DOMContentLoaded', function() {
console.log('Text message overlay initialized');
updateMessageDisplay();
fetchLicenseText();
// Setup WebChannel communication
setupWebChannel();
});
// Qt WebChannel initialization (when available)
if (typeof QWebChannel !== 'undefined') {
new QWebChannel(qt.webChannelTransport, function(channel) {
console.log('WebChannel initialized for text message overlay');
// Connect to overlay object if available
if (channel.objects.overlay) {
channel.objects.overlay.dataChanged.connect(function(data) {
// Setup WebChannel communication
function setupWebChannel() {
// Check if WebChannel is already set up by overlay.js
if (window.overlay) {
console.log('WebChannel already set up by overlay.js');
// Listen for data updates from Python
if (window.overlay.dataUpdated) {
window.overlay.dataUpdated.connect(function(data) {
console.log('Received data update from Python:', data);
updateOverlayData(data);
});
// Get initial data
if (channel.objects.overlay.getCurrentData) {
channel.objects.overlay.getCurrentData(function(data) {
updateOverlayData(data);
}
// Wait for WebChannel to be ready before fetching license text
waitForWebChannelReady(() => {
fetchLicenseText(); // Fetch license text now that WebChannel is ready
});
return;
}
// Fallback: setup WebChannel if overlay.js didn't do it
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
try {
new QWebChannel(qt.webChannelTransport, function(channel) {
console.log('WebChannel connected successfully (fallback)');
// Connect to overlay object
window.overlay = channel.objects.overlay;
// Listen for data updates from Python
if (window.overlay && window.overlay.dataUpdated) {
window.overlay.dataUpdated.connect(function(data) {
console.log('Received data update from Python (fallback):', data);
updateOverlayData(data);
});
}
// Wait for WebChannel to be ready before fetching license text
waitForWebChannelReady(() => {
fetchLicenseText(); // Fetch license text now that WebChannel is ready
});
}
});
} catch (e) {
console.log('Failed to setup WebChannel:', e);
}
});
} else {
console.log('WebChannel not available, using default settings');
}
}
// Wait for WebChannel to be ready
function waitForWebChannelReady(callback, maxWait = 10000) {
const startTime = Date.now();
const checkReady = () => {
if (window.overlay && window.overlay.getLicenseText) {
console.log('WebChannel ready for license text fetch');
callback();
} else if (Date.now() - startTime > maxWait) {
console.warn('WebChannel not ready after max wait time, proceeding anyway');
callback();
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
}
// Export functions for external use
......@@ -377,13 +458,13 @@
</script>
<!--
IMPORTANT: When creating or editing custom templates, always maintain these two script tags:
IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
2. overlay://overlay.js - Required for overlay functionality and data updates
These scripts enable communication between the Qt application and the overlay template.
Without them, the template will not receive data updates or function properly.
NOTE: When editing this template or creating new ones, never remove these script sources!
The overlay:// custom scheme ensures JavaScript files work for both built-in and uploaded templates.
-->
......
......@@ -143,32 +143,23 @@
.results-bottom-bar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
left: 20px;
right: 20px;
background: rgba(0, 123, 255, 0.85);
border-radius: 15px;
padding: 15px 20px;
max-width: 90%;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.1);
color: white;
font-size: 24px;
text-align: left;
text-align: center;
z-index: 1001;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.results-title {
font-weight: bold;
margin-bottom: 10px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.match-result {
font-size: 20px;
margin: 5px 0;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.fixtures-title {
color: white;
......@@ -417,7 +408,6 @@
<!-- Results Bottom Bar -->
<div class="results-bottom-bar" id="resultsBottomBar" style="display: none;">
<div class="results-title">RESULTS of the last 3 matches:</div>
<div id="resultsContent">
<!-- Match results will be populated by JavaScript -->
</div>
......@@ -822,21 +812,32 @@
];
}
// Fetch license text from API
// Fetch license text from WebChannel
async function fetchLicenseText() {
try {
// Try to fetch from the web dashboard API
const response = await fetch('http://127.0.0.1:5001/api/config/license-text');
if (response.ok) {
const data = await response.json();
if (data.success) {
licenseText = data.license_text || '';
updateLicenseDisplay();
// Try to get license text from Qt WebChannel
if (window.overlay && window.overlay.getLicenseText) {
const licenseTextData = await window.overlay.getLicenseText();
if (licenseTextData) {
// Parse the JSON response to extract license_text
try {
const licenseData = JSON.parse(licenseTextData);
if (licenseData.license_text) {
licenseText = licenseData.license_text;
updateLicenseDisplay();
console.log('License text loaded from WebChannel:', licenseText);
} else {
console.log('No license_text found in WebChannel response');
}
} catch (parseError) {
console.log('Failed to parse license text JSON from WebChannel:', parseError);
// Keep default text if parsing fails
}
}
}
} catch (error) {
console.log('Could not fetch license text from API, using default');
// Keep default text if API is not available
console.log('Could not fetch license text from WebChannel, using default');
// Keep default text if WebChannel is not available
}
}
......@@ -1090,7 +1091,19 @@
try {
debugTime('Fetching last match results');
// Try to fetch from web API if webServerBaseUrl is available
// Try WebChannel first (same as license text)
if (window.overlay && typeof window.overlay.getCompletedMatches === 'function') {
const completedMatchesJson = await window.overlay.getCompletedMatches();
const completedMatches = JSON.parse(completedMatchesJson);
if (completedMatches && completedMatches.length > 0) {
// Take last 3 matches (already ordered by end_time desc in WebChannel)
const lastMatches = completedMatches.slice(0, 3);
renderMatchResults(lastMatches);
return;
}
}
// Fallback: try to fetch from web API if webServerBaseUrl is available
if (webServerBaseUrl) {
const response = await fetch(`${webServerBaseUrl}/api/fixtures`);
if (response.ok) {
......@@ -1125,17 +1138,6 @@
}
}
// Fallback: try WebChannel if available
if (window.overlay && typeof window.overlay.getCompletedMatches === 'function') {
const completedMatchesJson = await window.overlay.getCompletedMatches();
const completedMatches = JSON.parse(completedMatchesJson);
if (completedMatches && completedMatches.length > 0) {
const lastMatches = completedMatches.slice(-3); // Last 3 matches
renderMatchResults(lastMatches);
return;
}
}
// No results available
debugTime('No match results available');
document.getElementById('resultsBottomBar').style.display = 'none';
......@@ -1151,9 +1153,9 @@
const resultsContent = document.getElementById('resultsContent');
if (!resultsContent) return;
let resultsHTML = '';
let resultsText = 'RESULTS: ';
matches.forEach(match => {
matches.forEach((match, index) => {
const matchNumber = match.match_number || match.id || 'N/A';
const fighter1 = match.fighter1_township || match.fighter1 || 'Fighter 1';
const fighter2 = match.fighter2_township || match.fighter2 || 'Fighter 2';
......@@ -1162,10 +1164,13 @@
const resultText = underOver ? `${result} (${underOver})` : result;
resultsHTML += `<div class="match-result">Match #${matchNumber}:: ${fighter1} vs ${fighter2}: ${resultText}</div>`;
resultsText += `Match #${matchNumber}: ${fighter1} vs ${fighter2} = ${resultText}`;
if (index < matches.length - 1) {
resultsText += ' | ';
}
});
resultsContent.innerHTML = resultsHTML;
resultsContent.textContent = resultsText;
document.getElementById('resultsBottomBar').style.display = 'block';
debugTime(`Rendered ${matches.length} match results`);
......@@ -1179,9 +1184,6 @@
// Setup WebChannel first
setupWebChannel();
// Fetch license text
fetchLicenseText();
// Show loading message initially
document.getElementById('fixturesContent').style.display = 'none';
document.getElementById('noMatches').style.display = 'none';
......@@ -1190,9 +1192,12 @@
debugTime('UI initialized - Loading message displayed');
// Wait briefly for WebChannel to connect
setTimeout(() => {
console.log('🔍 DEBUG: Starting fixture data fetch via WebChannel');
// Wait for WebChannel to be ready before fetching data
waitForWebChannelReady(() => {
console.log('🔍 DEBUG: WebChannel ready, starting data fetch');
// Fetch license text now that WebChannel is ready
fetchLicenseText();
// Fetch fixtures data via WebChannel
debugTime('Fetching fixtures data via WebChannel');
......@@ -1213,8 +1218,27 @@
debugTime('Data was received before 5 second timeout');
}
}, 5000);
}, 50); // Wait 50ms for WebChannel setup
});
});
// Wait for WebChannel to be ready
function waitForWebChannelReady(callback, maxWait = 10000) {
const startTime = Date.now();
const checkReady = () => {
if (window.overlay && window.overlay.getLicenseText) {
console.log('🔍 DEBUG: WebChannel ready for license text fetch');
callback();
} else if (Date.now() - startTime > maxWait) {
console.warn('🔍 DEBUG: WebChannel not ready after max wait time, proceeding anyway');
callback();
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
}
</script>
......
This diff is collapsed.
#!/usr/bin/env python3
"""
Test script to check API endpoints for debugging fixtures overlay
"""
import requests
import json
import sys
from pathlib import Path
def test_fixtures_api():
"""Test the fixtures API endpoint"""
print("Testing fixtures API endpoint...")
# Try different possible URLs
urls_to_test = [
"http://127.0.0.1:5001/api/fixtures",
"http://localhost:5001/api/fixtures",
"http://127.0.0.1:5000/api/fixtures",
"http://localhost:5000/api/fixtures"
]
for url in urls_to_test:
print(f"\nTrying URL: {url}")
try:
response = requests.get(url, timeout=5)
print(f"Status Code: {response.status_code}")
if response.status_code == 200:
try:
data = response.json()
print("Response data:")
print(json.dumps(data, indent=2))
if 'success' in data and data['success']:
print(f"✅ SUCCESS: Found {data.get('total', 0)} fixtures")
return True
else:
print("❌ API returned success=false")
return False
except json.JSONDecodeError as e:
print(f"❌ Invalid JSON response: {e}")
print(f"Raw response: {response.text[:500]}...")
return False
else:
print(f"❌ HTTP Error: {response.status_code}")
print(f"Response: {response.text[:200]}...")
except requests.exceptions.ConnectionError:
print("❌ Connection refused - server not running")
except requests.exceptions.Timeout:
print("❌ Request timed out")
except Exception as e:
print(f"❌ Error: {e}")
print("\n❌ All URLs failed - API server may not be running")
return False
def test_web_dashboard_status():
"""Test if web dashboard is running"""
print("\nTesting web dashboard status...")
urls_to_test = [
"http://127.0.0.1:5001/",
"http://localhost:5001/",
"http://127.0.0.1:5000/",
"http://localhost:5000/"
]
for url in urls_to_test:
print(f"Trying URL: {url}")
try:
response = requests.get(url, timeout=5)
print(f"Status Code: {response.status_code}")
if response.status_code == 200:
print("✅ Web dashboard is running")
return True
except Exception as e:
print(f"❌ Error: {e}")
print("❌ Web dashboard not accessible")
return False
def main():
print("=== MbetterClient API Test ===")
# Test web dashboard first
web_running = test_web_dashboard_status()
if not web_running:
print("\n⚠️ Web dashboard is not running. Make sure to start the web server first.")
print("Run: python main.py --web-only")
sys.exit(1)
# Test fixtures API
api_working = test_fixtures_api()
if api_working:
print("\n✅ API test completed successfully")
else:
print("\n❌ API test failed")
print("\nTroubleshooting tips:")
print("1. Make sure the web server is running")
print("2. Check if the database has fixture data")
print("3. Check the web server logs for errors")
print("4. Verify the fixtures endpoint is properly configured")
if __name__ == "__main__":
main()
\ 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