Commit 0ca2b9bb authored by Your Name's avatar Your Name

Update 1.0.32

parent 2fd8202f
This diff is collapsed.
......@@ -557,9 +557,9 @@ class OverlayWebChannel(QObject):
return []
def _get_winning_outcomes_from_database(self, match_id: int) -> List[Dict[str, Any]]:
"""Get winning outcomes aggregated by outcome type for a match from database, including UNDER/OVER when they win"""
"""Get settled winning outcomes aggregated by outcome type for a match."""
try:
from ..database.models import BetDetailModel, MatchModel, MatchOutcomeModel, ExtractionStatsModel, ExtractionAssociationModel
from ..database.models import BetDetailModel, MatchModel
from sqlalchemy import func
logger.debug(f"QtWebChannel: _get_winning_outcomes_from_database called for match {match_id}")
......@@ -575,45 +575,7 @@ class OverlayWebChannel(QObject):
try:
logger.debug(f"QtWebChannel: Executing query for match {match_id}")
# First, check if match has result set (from extraction)
match = session.query(MatchModel).filter(MatchModel.id == match_id).first()
if match and match.result:
logger.debug(f"QtWebChannel: Match {match_id} has result set: {match.result}")
# Get winning outcomes from associations
winning_outcomes_query = session.query(ExtractionAssociationModel.outcome_name).filter(
ExtractionAssociationModel.extraction_result == match.result
).all()
winning_outcome_names = [outcome.outcome_name for outcome in winning_outcomes_query]
logger.debug(f"QtWebChannel: Found {len(winning_outcome_names)} winning outcomes from associations for result {match.result}: {winning_outcome_names}")
# Include under_over_result if available
if match.under_over_result:
winning_outcome_names.append(match.under_over_result)
logger.debug(f"QtWebChannel: Added under_over_result: {match.under_over_result}")
# Calculate amounts from pending bets
outcomes_data = []
for outcome_name in set(winning_outcome_names):
total_amount = session.query(func.sum(BetDetailModel.amount)).filter(
BetDetailModel.match_id == match_id,
BetDetailModel.outcome == outcome_name,
BetDetailModel.result == 'pending'
).scalar() or 0.0
outcome_data = {
'outcome': outcome_name,
'amount': float(total_amount)
}
outcomes_data.append(outcome_data)
logger.debug(f"QtWebChannel: Calculated amount for {outcome_name}: {total_amount}")
logger.debug(f"QtWebChannel: Retrieved {len(outcomes_data)} winning outcomes from match result: {outcomes_data}")
return outcomes_data
# Fallback: try to get winning outcomes from updated bet results
all_winning_outcomes_query = session.query(
settled_winning_outcomes_query = session.query(
BetDetailModel.outcome,
func.sum(BetDetailModel.win_amount).label('total_amount')
).join(MatchModel).filter(
......@@ -622,75 +584,28 @@ class OverlayWebChannel(QObject):
MatchModel.active_status == True
).group_by(BetDetailModel.outcome).all()
logger.debug(f"QtWebChannel: Query returned {len(all_winning_outcomes_query)} winning outcomes from bet results")
logger.debug(
f"QtWebChannel: Query returned {len(settled_winning_outcomes_query)} settled winning outcomes"
)
if all_winning_outcomes_query:
# Convert to dictionary format for JavaScript
if settled_winning_outcomes_query:
outcomes_data = []
for outcome_name, total_amount in all_winning_outcomes_query:
for outcome_name, total_amount in settled_winning_outcomes_query:
outcome_data = {
'outcome': outcome_name,
'amount': float(total_amount) if total_amount else 0.0
}
outcomes_data.append(outcome_data)
logger.debug(f"QtWebChannel: Retrieved {len(outcomes_data)} winning outcomes from bet results: {outcomes_data}")
logger.debug(
f"QtWebChannel: Retrieved {len(outcomes_data)} settled winning outcomes: {outcomes_data}"
)
return outcomes_data
# Fallback: No winning bets found, try to get from extraction results
logger.debug("QtWebChannel: No winning bets found, trying extraction results fallback")
# Get extraction stats for this match
extraction_stats = session.query(ExtractionStatsModel).filter(
ExtractionStatsModel.match_id == match_id
).first()
if not extraction_stats or not extraction_stats.actual_result:
logger.debug("QtWebChannel: No extraction stats found for match")
return []
extraction_result = extraction_stats.actual_result
logger.debug(f"QtWebChannel: Found extraction result: {extraction_result}")
# Get winning outcomes from extraction associations
winning_outcomes_query = session.query(ExtractionAssociationModel.outcome_name).filter(
ExtractionAssociationModel.extraction_result == extraction_result
).all()
winning_outcome_names = [outcome.outcome_name for outcome in winning_outcomes_query]
logger.debug(f"QtWebChannel: Found {len(winning_outcome_names)} winning outcomes from associations: {winning_outcome_names}")
# Get the match to check for under_over_result
match = session.query(MatchModel).filter(MatchModel.id == match_id).first()
under_over_result = match.under_over_result if match else None
logger.debug(f"QtWebChannel: Under/over result from match: {under_over_result}")
# Combine winning outcomes
all_winning_outcomes = set(winning_outcome_names)
if under_over_result:
all_winning_outcomes.add(under_over_result)
logger.debug(f"QtWebChannel: Combined winning outcomes: {all_winning_outcomes}")
# Calculate amounts for each winning outcome from pending bets
outcomes_data = []
for outcome_name in all_winning_outcomes:
# Get total amount of pending bets for this outcome
total_amount = session.query(func.sum(BetDetailModel.amount)).filter(
BetDetailModel.match_id == match_id,
BetDetailModel.outcome == outcome_name,
BetDetailModel.result == 'pending'
).scalar() or 0.0
outcome_data = {
'outcome': outcome_name,
'amount': float(total_amount)
}
outcomes_data.append(outcome_data)
logger.debug(f"QtWebChannel: Calculated amount for {outcome_name}: {total_amount}")
logger.debug(f"QtWebChannel: Retrieved {len(outcomes_data)} winning outcomes from extraction fallback: {outcomes_data}")
return outcomes_data
logger.debug(
f"QtWebChannel: No settled winning outcomes found for match {match_id}"
)
return []
finally:
session.close()
......
......@@ -281,7 +281,7 @@
</div>
<hr class="my-2">
<div class="text-center">
<strong class="text-success currency-amount" data-amount="{{ results.winnings|round(2) }}">Winnings: €{{ results.winnings|round(2) }}</strong>
<strong class="text-success currency-amount" data-amount="{{ results.winnings|round(2) }}">Settled Winnings: €{{ results.winnings|round(2) }}</strong>
</div>
</div>
</div>
......@@ -456,6 +456,7 @@
"amount": {{ detail.amount|round(2) }},
"odds": {{ detail.odds|round(2) }},
"potential_winning": {{ detail.potential_winning|round(2) }},
"settled_winning": {{ detail.settled_winning|round(2) }},
"result": "{{ detail.result }}"
}{% if not loop.last %},{% endif %}
{% endfor %}
......@@ -472,7 +473,7 @@ document.addEventListener('currencySettingsLoaded', function(event) {
document.querySelectorAll('.currency-amount').forEach(element => {
const amount = parseFloat(element.dataset.amount || 0);
const prefix = element.textContent.includes('Total:') ? 'Total: ' :
element.textContent.includes('Winnings:') ? 'Winnings: ' : '';
element.textContent.includes('Settled Winnings:') ? 'Settled Winnings: ' : '';
element.textContent = prefix + formatCurrency(amount);
});
});
......@@ -658,7 +659,7 @@ function generateReceiptHtml(betData) {
</div>
<div class="receipt-bet-line">
<span>ODDS: ${parseFloat(detail.odds).toFixed(2)}</span>
<span>POTENTIAL WIN: ${formatCurrency(parseFloat(detail.potential_winning))}</span>
<span>${detail.result === 'win' ? 'SETTLED WIN' : 'POTENTIAL WIN'}: ${formatCurrency(parseFloat(detail.result === 'win' ? detail.settled_winning : detail.potential_winning))}</span>
</div>
<div class="receipt-status">
STATUS: ${detail.result.toUpperCase()}
......@@ -1285,4 +1286,4 @@ function showNotification(message, type = 'info') {
}, 3000);
}
</script>
{% endblock %}
\ No newline at end of file
{% endblock %}
......@@ -281,7 +281,7 @@
</div>
<hr class="my-2">
<div class="text-center">
<strong class="text-success currency-amount" data-amount="{{ results.winnings|round(2) }}">Winnings: €{{ results.winnings|round(2) }}</strong>
<strong class="text-success currency-amount" data-amount="{{ results.winnings|round(2) }}">Settled Winnings: €{{ results.winnings|round(2) }}</strong>
</div>
</div>
</div>
......@@ -458,6 +458,7 @@
"amount": {{ detail.amount|round(2) }},
"odds": {{ detail.odds|round(2) }},
"potential_winning": {{ detail.potential_winning|round(2) }},
"settled_winning": {{ detail.settled_winning|round(2) }},
"result": "{{ detail.result }}"
}{% if not loop.last %},{% endif %}
{% endfor %}
......@@ -474,7 +475,7 @@ document.addEventListener('currencySettingsLoaded', function(event) {
document.querySelectorAll('.currency-amount').forEach(element => {
const amount = parseFloat(element.dataset.amount || 0);
const prefix = element.textContent.includes('Total:') ? 'Total: ' :
element.textContent.includes('Winnings:') ? 'Winnings: ' : '';
element.textContent.includes('Settled Winnings:') ? 'Settled Winnings: ' : '';
element.textContent = prefix + formatCurrency(amount);
});
});
......@@ -717,7 +718,7 @@ function generateReceiptHtml(betData) {
</div>
<div class="receipt-bet-line">
<span>ODDS: ${parseFloat(detail.odds).toFixed(2)}</span>
<span>POTENTIAL WIN: ${formatCurrency(parseFloat(detail.potential_winning))}</span>
<span>${detail.result === 'win' ? 'SETTLED WIN' : 'POTENTIAL WIN'}: ${formatCurrency(parseFloat(detail.result === 'win' ? detail.settled_winning : detail.potential_winning))}</span>
</div>
<div class="receipt-status">
STATUS: ${detail.result.toUpperCase()}
......@@ -1304,4 +1305,4 @@ function showNotification(message, type = 'info') {
}, 3000);
}
</script>
{% endblock %}
\ No newline at end of file
{% endblock %}
......@@ -661,7 +661,7 @@ function displayBetDetails(bet) {
${bet.results.winnings > 0 ? `
<div class="alert alert-success mt-3">
<i class="fas fa-trophy me-2"></i>
<strong>Potential Winnings: <span class="currency-amount" data-amount="${bet.results.winnings}">${formatCurrency(bet.results.winnings)}</span></strong>
<strong>Settled Winnings: <span class="currency-amount" data-amount="${bet.results.winnings}">${formatCurrency(bet.results.winnings)}</span></strong>
</div>
` : ''}
......@@ -758,4 +758,4 @@ function copyToClipboard(elementId) {
}, 2000);
}
</script>
{% endblock %}
\ No newline at end of file
{% endblock %}
......@@ -685,7 +685,7 @@ function displayBetDetails(bet) {
${bet.results.winnings > 0 ? `
<div class="alert alert-success mt-3">
<i class="fas fa-trophy me-2"></i>
<strong>Potential Winnings: <span class="currency-amount" data-amount="${bet.results.winnings}">${formatCurrency(bet.results.winnings)}</span></strong>
<strong>Settled Winnings: <span class="currency-amount" data-amount="${bet.results.winnings}">${formatCurrency(bet.results.winnings)}</span></strong>
</div>
` : ''}
......@@ -782,4 +782,4 @@ function copyToClipboard(elementId) {
}, 2000);
}
</script>
{% endblock %}
\ No newline at end of file
{% endblock %}
# MbetterClient v1.0.32
Cross-platform multimedia client application
## Installation
1. Extract this package to your desired location
2. Run the executable file
3. The application will create necessary configuration files on first run
## System Requirements
- **Operating System**: Linux 6.12.15-amd64
- **Architecture**: x86_64
- **Memory**: 512 MB RAM minimum, 1 GB recommended
- **Disk Space**: 100 MB free space
## Configuration
The application stores its configuration and database in:
- **Windows**: `%APPDATA%\MbetterClient`
- **macOS**: `~/Library/Application Support/MbetterClient`
- **Linux**: `~/.config/MbetterClient`
## Web Interface
By default, the web interface is available at: http://localhost:5001
Default login credentials:
- Username: admin
- Password: admin
**Please change the default password after first login.**
## Support
For support and documentation, please visit: https://git.nexlab.net/mbetter/mbetterc
## Version Information
- Version: 1.0.32
- Build Date: sissy
- Platform: Linux-6.12.15-amd64-x86_64-with-glibc2.42
......@@ -12,6 +12,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from mbetterclient.web_dashboard import routes as routes_module
from mbetterclient.web_dashboard.routes import api_bp, is_bet_detail_winning, is_settled_bet_detail_winning
from mbetterclient.qt_player.player import OverlayWebChannel
class DummyDetail:
......@@ -378,3 +379,84 @@ def test_match_details_includes_settled_winning_field(monkeypatch, isolated_api_
assert response.status_code == 200
payload = response.get_json()
assert payload['total'] == 0
def test_report_templates_label_settled_winnings():
from pathlib import Path
bet_details_template = Path('/working/mbetterc/mbetterclient/web_dashboard/templates/dashboard/bet_details.html').read_text()
admin_bet_details_template = Path('/working/mbetterc/mbetterclient/web_dashboard/templates/dashboard/admin_bet_details.html').read_text()
verify_template = Path('/working/mbetterc/mbetterclient/web_dashboard/templates/dashboard/verify_bet.html').read_text()
cashier_verify_template = Path('/working/mbetterc/mbetterclient/web_dashboard/templates/dashboard/cashier_verify_bet.html').read_text()
assert 'Settled Winnings' in bet_details_template
assert 'Settled Winnings' in admin_bet_details_template
assert 'Settled Winnings' in verify_template
assert 'Settled Winnings' in cashier_verify_template
def test_qt_overlay_winning_outcomes_use_settled_rows():
class AggregateQueryStub:
def __init__(self, rows):
self.rows = rows
def join(self, *args, **kwargs):
return self
def filter(self, *args, **kwargs):
return self
def group_by(self, *args, **kwargs):
return self
def all(self):
return self.rows
class OverlaySessionStub:
def __init__(self, rows):
self.rows = rows
def query(self, *models):
if len(models) == 2:
return AggregateQueryStub(self.rows)
return QueryStub([])
def close(self):
return None
overlay = OverlayWebChannel(
db_manager=DummyDbManager(OverlaySessionStub([('WIN1', 250.0), ('UNDER', 175.0)]))
)
assert overlay._get_winning_outcomes_from_database(10) == [
{'outcome': 'WIN1', 'amount': 250.0},
{'outcome': 'UNDER', 'amount': 175.0},
]
def test_qt_overlay_winning_outcomes_return_empty_without_settled_wins():
class AggregateQueryStub:
def join(self, *args, **kwargs):
return self
def filter(self, *args, **kwargs):
return self
def group_by(self, *args, **kwargs):
return self
def all(self):
return []
class OverlaySessionStub:
def query(self, *models):
if len(models) == 2:
return AggregateQueryStub()
return QueryStub([])
def close(self):
return None
overlay = OverlayWebChannel(db_manager=DummyDbManager(OverlaySessionStub()))
assert overlay._get_winning_outcomes_from_database(10) == []
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