Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mbetter
MBetterc
Commits
0ca2b9bb
Commit
0ca2b9bb
authored
May 14, 2026
by
Your Name
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update 1.0.32
parent
2fd8202f
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
111 additions
and
200 deletions
+111
-200
MbetterClient.spec
MbetterClient.spec
+0
-45
player.py
mbetterclient/qt_player/player.py
+15
-100
admin_bet_details.html
.../web_dashboard/templates/dashboard/admin_bet_details.html
+5
-4
bet_details.html
...client/web_dashboard/templates/dashboard/bet_details.html
+5
-4
cashier_verify_bet.html
...web_dashboard/templates/dashboard/cashier_verify_bet.html
+2
-2
verify_bet.html
...rclient/web_dashboard/templates/dashboard/verify_bet.html
+2
-2
MbetterClient-1.0.31-linux-x86_64-README.txt
packages/MbetterClient-1.0.31-linux-x86_64-README.txt
+0
-43
test_reports_daily_alignment.py
test_reports_daily_alignment.py
+82
-0
No files found.
MbetterClient.spec
deleted
100644 → 0
View file @
2fd8202f
This diff is collapsed.
Click to expand it.
mbetterclient/qt_player/player.py
View file @
0ca2b9bb
...
...
@@ -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
()
...
...
mbetterclient/web_dashboard/templates/dashboard/admin_bet_details.html
View file @
0ca2b9bb
...
...
@@ -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 %}
mbetterclient/web_dashboard/templates/dashboard/bet_details.html
View file @
0ca2b9bb
...
...
@@ -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 %}
mbetterclient/web_dashboard/templates/dashboard/cashier_verify_bet.html
View file @
0ca2b9bb
...
...
@@ -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 %}
mbetterclient/web_dashboard/templates/dashboard/verify_bet.html
View file @
0ca2b9bb
...
...
@@ -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 %}
packages/MbetterClient-1.0.31-linux-x86_64-README.txt
deleted
100644 → 0
View file @
2fd8202f
# 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
test_reports_daily_alignment.py
View file @
0ca2b9bb
...
...
@@ -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
)
==
[]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment