Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterd
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
MBetterd
Commits
cc4ddd48
Commit
cc4ddd48
authored
Feb 02, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement MatchReport update for incremental sync
parent
e69c15ea
Changes
8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
863 additions
and
85 deletions
+863
-85
routes.py
app/api/routes.py
+104
-43
migrations.py
app/database/migrations.py
+114
-1
allow_null_results.py
app/database/migrations/allow_null_results.py
+127
-0
routes.py
app/main/routes.py
+44
-36
models.py
app/models.py
+4
-4
match_detail.html
app/templates/main/match_detail.html
+2
-1
test_matchreport_update.py
test_matchreport_update.py
+294
-0
test_simple_matchreport_update.py
test_simple_matchreport_update.py
+174
-0
No files found.
app/api/routes.py
View file @
cc4ddd48
This diff is collapsed.
Click to expand it.
app/database/migrations.py
View file @
cc4ddd48
...
...
@@ -1121,6 +1121,116 @@ class Migration_014_AddAccumulatedShortfallAndCapPercentage(Migration):
def
can_rollback
(
self
)
->
bool
:
return
True
class
Migration_015_AllowNullResults
(
Migration
):
"""Allow NULL values for actual_result and extraction_result columns in extraction_stats and match_reports tables for incomplete matches"""
def
__init__
(
self
):
super
()
.
__init__
(
"015"
,
"Allow NULL values for actual_result and extraction_result columns in extraction_stats and match_reports tables"
)
def
up
(
self
):
"""Allow NULL values for actual_result and extraction_result columns"""
try
:
inspector
=
inspect
(
db
.
engine
)
# Check extraction_stats columns
extraction_stats_columns
=
{
col
[
'name'
]:
col
[
'nullable'
]
for
col
in
inspector
.
get_columns
(
'extraction_stats'
)}
if
'actual_result'
in
extraction_stats_columns
:
if
extraction_stats_columns
[
'actual_result'
]
is
False
:
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN actual_result VARCHAR(50) NULL
"""
))
conn
.
commit
()
logger
.
info
(
"Updated actual_result column to allow NULL in extraction_stats table"
)
else
:
logger
.
info
(
"actual_result column already allows NULL in extraction_stats table"
)
if
'extraction_result'
in
extraction_stats_columns
:
if
extraction_stats_columns
[
'extraction_result'
]
is
False
:
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN extraction_result VARCHAR(50) NULL
"""
))
conn
.
commit
()
logger
.
info
(
"Updated extraction_result column to allow NULL in extraction_stats table"
)
else
:
logger
.
info
(
"extraction_result column already allows NULL in extraction_stats table"
)
# Check match_reports columns
match_reports_columns
=
{
col
[
'name'
]:
col
[
'nullable'
]
for
col
in
inspector
.
get_columns
(
'match_reports'
)}
if
'actual_result'
in
match_reports_columns
:
if
match_reports_columns
[
'actual_result'
]
is
False
:
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN actual_result VARCHAR(50) NULL
"""
))
conn
.
commit
()
logger
.
info
(
"Updated actual_result column to allow NULL in match_reports table"
)
else
:
logger
.
info
(
"actual_result column already allows NULL in match_reports table"
)
if
'extraction_result'
in
match_reports_columns
:
if
match_reports_columns
[
'extraction_result'
]
is
False
:
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN extraction_result VARCHAR(50) NULL
"""
))
conn
.
commit
()
logger
.
info
(
"Updated extraction_result column to allow NULL in match_reports table"
)
else
:
logger
.
info
(
"extraction_result column already allows NULL in match_reports table"
)
logger
.
info
(
"Migration 015 completed successfully"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Migration 015 failed: {str(e)}"
)
raise
def
down
(
self
):
"""Revert actual_result and extraction_result columns to NOT NULL"""
try
:
# Revert extraction_stats columns
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN actual_result VARCHAR(50) NOT NULL
"""
))
conn
.
execute
(
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN extraction_result VARCHAR(50) NOT NULL
"""
))
conn
.
commit
()
# Revert match_reports columns
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN actual_result VARCHAR(50) NOT NULL
"""
))
conn
.
execute
(
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN extraction_result VARCHAR(50) NOT NULL
"""
))
conn
.
commit
()
logger
.
info
(
"Migration 015 rolled back successfully"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Rollback of migration 015 failed: {str(e)}"
)
raise
def
can_rollback
(
self
)
->
bool
:
return
True
class
MigrationManager
:
"""Manages database migrations and versioning"""
...
...
@@ -1140,6 +1250,7 @@ class MigrationManager:
Migration_012_AddMatchNumberToBetsAndStats
(),
Migration_013_CreateMatchReportsTable
(),
Migration_014_AddAccumulatedShortfallAndCapPercentage
(),
Migration_015_AllowNullResults
(),
]
def
ensure_version_table
(
self
):
...
...
@@ -1314,3 +1425,5 @@ def run_migrations():
def
get_migration_status
():
"""Get migration status"""
return
migration_manager
.
get_migration_status
()
app/database/migrations/allow_null_results.py
0 → 100644
View file @
cc4ddd48
"""
Migration to allow NULL values for actual_result and extraction_result columns
in extraction_stats and match_reports tables for incomplete matches
"""
from
app
import
db
def
upgrade
():
"""Allow NULL values for actual_result and extraction_result columns"""
try
:
# Check if columns allow NULL in extraction_stats
inspector
=
db
.
inspect
(
db
.
engine
)
extraction_stats_columns
=
{
col
[
'name'
]:
col
[
'nullable'
]
for
col
in
inspector
.
get_columns
(
'extraction_stats'
)}
if
'actual_result'
in
extraction_stats_columns
:
if
extraction_stats_columns
[
'actual_result'
]
is
False
:
# Alter actual_result column to allow NULL
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN actual_result VARCHAR(50) NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Updated actual_result column to allow NULL in extraction_stats table"
)
else
:
print
(
"✓ actual_result column already allows NULL in extraction_stats table"
)
if
'extraction_result'
in
extraction_stats_columns
:
if
extraction_stats_columns
[
'extraction_result'
]
is
False
:
# Alter extraction_result column to allow NULL
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN extraction_result VARCHAR(50) NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Updated extraction_result column to allow NULL in extraction_stats table"
)
else
:
print
(
"✓ extraction_result column already allows NULL in extraction_stats table"
)
# Check if columns allow NULL in match_reports
match_reports_columns
=
{
col
[
'name'
]:
col
[
'nullable'
]
for
col
in
inspector
.
get_columns
(
'match_reports'
)}
if
'actual_result'
in
match_reports_columns
:
if
match_reports_columns
[
'actual_result'
]
is
False
:
# Alter actual_result column to allow NULL
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN actual_result VARCHAR(50) NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Updated actual_result column to allow NULL in match_reports table"
)
else
:
print
(
"✓ actual_result column already allows NULL in match_reports table"
)
if
'extraction_result'
in
match_reports_columns
:
if
match_reports_columns
[
'extraction_result'
]
is
False
:
# Alter extraction_result column to allow NULL
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN extraction_result VARCHAR(50) NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Updated extraction_result column to allow NULL in match_reports table"
)
else
:
print
(
"✓ extraction_result column already allows NULL in match_reports table"
)
print
(
"
\n
✓ Migration completed successfully!"
)
except
Exception
as
e
:
db
.
session
.
rollback
()
print
(
f
"✗ Error updating columns: {str(e)}"
)
raise
def
downgrade
():
"""Revert actual_result and extraction_result columns to NOT NULL"""
try
:
# Revert extraction_stats columns
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN actual_result VARCHAR(50) NOT NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Reverted actual_result column to NOT NULL in extraction_stats table"
)
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE extraction_stats
MODIFY COLUMN extraction_result VARCHAR(50) NOT NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Reverted extraction_result column to NOT NULL in extraction_stats table"
)
# Revert match_reports columns
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN actual_result VARCHAR(50) NOT NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Reverted actual_result column to NOT NULL in match_reports table"
)
with
db
.
engine
.
connect
()
as
conn
:
conn
.
execute
(
db
.
text
(
"""
ALTER TABLE match_reports
MODIFY COLUMN extraction_result VARCHAR(50) NOT NULL
"""
))
db
.
session
.
commit
()
print
(
"✓ Reverted extraction_result column to NOT NULL in match_reports table"
)
print
(
"
\n
✓ Downgrade completed successfully!"
)
except
Exception
as
e
:
db
.
session
.
rollback
()
print
(
f
"✗ Error reverting columns: {str(e)}"
)
raise
if
__name__
==
'__main__'
:
from
app
import
create_app
app
=
create_app
()
with
app
.
app_context
():
print
(
"Running migration: Allow NULL values for actual_result and extraction_result"
)
print
(
"="
*
70
)
upgrade
()
print
(
"="
*
70
)
app/main/routes.py
View file @
cc4ddd48
...
...
@@ -200,7 +200,7 @@ def matches():
@
login_required
@
require_active_user
def
fixture_detail
(
fixture_id
):
"""Fixture detail page showing all matches in
the
fixture"""
"""Fixture detail page showing all matches in fixture"""
try
:
from
app.models
import
Match
,
FileUpload
...
...
@@ -224,7 +224,7 @@ def fixture_detail(fixture_id):
'created_by'
:
matches
[
0
]
.
created_by
}
# Get associated uploads for
the
fixture
# Get associated uploads for fixture
match_ids
=
[
m
.
id
for
m
in
matches
]
uploads
=
FileUpload
.
query
.
filter
(
FileUpload
.
match_id
.
in_
(
match_ids
))
.
all
()
if
match_ids
else
[]
...
...
@@ -337,7 +337,7 @@ def update_match_outcomes(match_id):
try
:
from
app.models
import
Match
,
MatchOutcome
# Get
the
match
# Get match
if
current_user
.
is_admin
:
match
=
Match
.
query
.
get_or_404
(
match_id
)
else
:
...
...
@@ -353,7 +353,7 @@ def update_match_outcomes(match_id):
# Update or create outcomes
for
column_name
,
float_value
in
outcomes_data
.
items
():
try
:
# Validate
the
float value
# Validate float value
float_val
=
float
(
float_value
)
# Find existing outcome or create new one
...
...
@@ -406,13 +406,13 @@ def delete_match_outcome(match_id, outcome_id):
try
:
from
app.models
import
Match
,
MatchOutcome
# Get
the
match to verify ownership
# Get match to verify ownership
if
current_user
.
is_admin
:
match
=
Match
.
query
.
get_or_404
(
match_id
)
else
:
match
=
Match
.
query
.
filter_by
(
id
=
match_id
,
created_by
=
current_user
.
id
)
.
first_or_404
()
# Get
the
outcome
# Get outcome
outcome
=
MatchOutcome
.
query
.
filter_by
(
id
=
outcome_id
,
match_id
=
match_id
...
...
@@ -1407,7 +1407,7 @@ def download_zip(match_id):
flash
(
'ZIP file not found on disk'
,
'error'
)
abort
(
404
)
# Log
the
download
# Log download
logger
.
info
(
f
"ZIP file downloaded: {match.zip_filename} by user {current_user.username}"
)
return
send_file
(
zip_path
,
as_attachment
=
True
,
download_name
=
match
.
zip_filename
)
...
...
@@ -1654,7 +1654,7 @@ def reports():
if
export_format
:
return
export_reports
(
query
,
export_format
)
# Aggregate data by client for
the
selected period using MatchReport
# Aggregate data by client for selected period using MatchReport
# Build base query with filters
base_query
=
MatchReport
.
query
...
...
@@ -1717,7 +1717,7 @@ def reports():
client_aggregates
[
client_id
][
'losing_bets'
]
+=
report
.
losing_bets
client_aggregates
[
client_id
][
'pending_bets'
]
+=
report
.
pending_bets
# Use
the
most recent CAP balance and accumulated shortfall for this client
# Use most recent CAP balance and accumulated shortfall for this client
if
report
.
match_datetime
>=
client_aggregates
[
client_id
][
'last_match_timestamp'
]:
client_aggregates
[
client_id
][
'cap_balance'
]
=
float
(
report
.
cap_compensation_balance
)
if
report
.
cap_compensation_balance
else
0.0
client_aggregates
[
client_id
][
'accumulated_shortfall'
]
=
float
(
report
.
accumulated_shortfall
)
if
report
.
accumulated_shortfall
else
0.0
...
...
@@ -1760,7 +1760,6 @@ def reports():
total_balance
=
total_payin
-
total_payout
cap_balance
=
clients_list
[
0
][
'cap_balance'
]
if
clients_list
else
0.0
accumulated_shortfall
=
clients_list
[
0
][
'accumulated_shortfall'
]
if
clients_list
else
0.0
accumulated_shortfall
=
clients_list
[
0
][
'accumulated_shortfall'
]
if
clients_list
else
0.0
# Pagination
total_clients
=
len
(
clients_list
)
...
...
@@ -2129,7 +2128,7 @@ def sync_logs():
if
end_date_filter
:
try
:
end_date
=
datetime
.
strptime
(
end_date_filter
,
'
%
Y-
%
m-
%
d'
)
# Include
the
entire end date
# Include entire end date
end_date
=
end_date
.
replace
(
hour
=
23
,
minute
=
59
,
second
=
59
)
query
=
query
.
filter
(
ReportSyncLog
.
created_at
<=
end_date
)
except
ValueError
:
...
...
@@ -2439,6 +2438,7 @@ def export_sync_logs(export_format):
except
Exception
as
e
:
logger
.
error
(
f
"Export sync logs error: {str(e)}"
)
flash
(
'Error exporting sync logs'
,
'error'
)
return
redirect
(
url_for
(
'main.sync_logs'
))
@
csrf
.
exempt
@
bp
.
route
(
'/client-report/<client_id>'
)
...
...
@@ -2534,23 +2534,34 @@ def client_report_detail(client_id):
if
end_date
:
query
=
query
.
filter
(
MatchReport
.
match_datetime
<=
end_date
)
# Get all matching match reports for this client with pagination
# Get all matching match reports for this client (without pagination for totals)
all_match_reports
=
query
.
all
()
# Get paginated match reports for display
match_reports_pagination
=
query
.
order_by
(
MatchReport
.
match_datetime
.
desc
())
.
paginate
(
page
=
page
,
per_page
=
per_page
,
error_out
=
False
)
match_reports
=
match_reports_pagination
.
items
# Calculate totals from
match reports
total_payin
=
sum
(
float
(
r
.
total_payin
)
for
r
in
match_reports
if
r
.
total_payin
)
total_payout
=
sum
(
float
(
r
.
total_payout
)
for
r
in
match_reports
if
r
.
total_payout
)
# Calculate totals from
ALL match reports (not just paginated)
total_payin
=
sum
(
float
(
r
.
total_payin
)
for
r
in
all_
match_reports
if
r
.
total_payin
)
total_payout
=
sum
(
float
(
r
.
total_payout
)
for
r
in
all_
match_reports
if
r
.
total_payout
)
total_balance
=
total_payin
-
total_payout
total_bets
=
sum
(
r
.
total_bets
for
r
in
match_reports
)
total_matches
=
len
(
match_reports
)
winning_bets
=
sum
(
r
.
winning_bets
for
r
in
match_reports
)
losing_bets
=
sum
(
r
.
losing_bets
for
r
in
match_reports
)
pending_bets
=
sum
(
r
.
pending_bets
for
r
in
match_reports
)
cap_balance
=
float
(
match_reports
[
0
]
.
cap_compensation_balance
)
if
match_reports
and
match_reports
[
0
]
.
cap_compensation_balance
else
0.0
accumulated_shortfall
=
float
(
match_reports
[
0
]
.
accumulated_shortfall
)
if
match_reports
and
match_reports
[
0
]
.
accumulated_shortfall
else
0.0
total_bets
=
sum
(
r
.
total_bets
for
r
in
all_match_reports
)
total_matches
=
len
(
all_match_reports
)
winning_bets
=
sum
(
r
.
winning_bets
for
r
in
all_match_reports
)
losing_bets
=
sum
(
r
.
losing_bets
for
r
in
all_match_reports
)
pending_bets
=
sum
(
r
.
pending_bets
for
r
in
all_match_reports
)
# Use most recent CAP balance and accumulated shortfall from all reports
if
all_match_reports
:
# Sort by match_datetime descending to get most recent
sorted_reports
=
sorted
(
all_match_reports
,
key
=
lambda
x
:
x
.
match_datetime
or
datetime
.
min
,
reverse
=
True
)
cap_balance
=
float
(
sorted_reports
[
0
]
.
cap_compensation_balance
)
if
sorted_reports
[
0
]
.
cap_compensation_balance
else
0.0
accumulated_shortfall
=
float
(
sorted_reports
[
0
]
.
accumulated_shortfall
)
if
sorted_reports
[
0
]
.
accumulated_shortfall
else
0.0
else
:
cap_balance
=
0.0
accumulated_shortfall
=
0.0
# Get client token name
client_activity
=
ClientActivity
.
query
.
filter_by
(
rustdesk_id
=
client_id
)
.
first
()
...
...
@@ -2593,6 +2604,7 @@ def client_report_detail(client_id):
end_date
=
end_date_filter
if
'end_date_filter'
in
locals
()
else
''
,
start_time
=
start_time_filter
if
'start_time_filter'
in
locals
()
else
''
,
end_time
=
end_time_filter
if
'end_time_filter'
in
locals
()
else
''
))
@
csrf
.
exempt
@
bp
.
route
(
'/client-report/<client_id>/match/<int:match_id>'
)
@
login_required
...
...
@@ -2681,13 +2693,7 @@ def match_report_detail(client_id, match_id):
flash
(
'Access denied to this client'
,
'error'
)
return
redirect
(
url_for
(
'main.reports'
))
# Apply date filters
if
start_date
:
query
=
query
.
filter
(
MatchReport
.
match_datetime
>=
start_date
)
if
end_date
:
query
=
query
.
filter
(
MatchReport
.
match_datetime
<=
end_date
)
# Get the match report
# Get match report (don't apply date filters - we want specific match)
match_report
=
query
.
first
()
if
not
match_report
:
...
...
@@ -2731,6 +2737,8 @@ def match_report_detail(client_id, match_id):
except
Exception
as
e
:
logger
.
error
(
f
"Match report detail error: {str(e)}"
)
flash
(
'Error loading match report details'
,
'error'
)
return
redirect
(
url_for
(
'main.match_report_detail'
,
client_id
=
client_id
,
match_id
=
match_id
))
@
csrf
.
exempt
@
bp
.
route
(
'/client-report/<client_id>/match/<int:match_id>/bet/<bet_uuid>'
)
@
login_required
...
...
@@ -2764,7 +2772,7 @@ def bet_detail(client_id, match_id, bet_uuid):
flash
(
'Access denied to this client'
,
'error'
)
return
redirect
(
url_for
(
'main.reports'
))
# Get
the
bet
# Get bet
bet
=
Bet
.
query
.
filter_by
(
client_id
=
client_id
,
match_id
=
match_id
,
uuid
=
bet_uuid
)
.
first
()
if
not
bet
:
...
...
app/models.py
View file @
cc4ddd48
...
...
@@ -980,8 +980,8 @@ class ExtractionStats(db.Model):
total_bets
=
db
.
Column
(
db
.
Integer
,
nullable
=
False
)
total_amount_collected
=
db
.
Column
(
db
.
Numeric
(
15
,
2
),
nullable
=
False
)
total_redistributed
=
db
.
Column
(
db
.
Numeric
(
15
,
2
),
nullable
=
False
)
actual_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Fals
e
)
extraction_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Fals
e
)
actual_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Tru
e
)
extraction_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Tru
e
)
cap_applied
=
db
.
Column
(
db
.
Boolean
,
default
=
False
)
cap_percentage
=
db
.
Column
(
db
.
Numeric
(
5
,
2
))
accumulated_shortfall
=
db
.
Column
(
db
.
Numeric
(
15
,
2
),
default
=
0.00
)
...
...
@@ -1126,8 +1126,8 @@ class MatchReport(db.Model):
balance
=
db
.
Column
(
db
.
Numeric
(
15
,
2
),
default
=
0.00
)
# Match result
actual_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Fals
e
)
extraction_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Fals
e
)
actual_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Tru
e
)
extraction_result
=
db
.
Column
(
db
.
String
(
50
),
nullable
=
Tru
e
)
# CAP information
cap_applied
=
db
.
Column
(
db
.
Boolean
,
default
=
False
)
...
...
app/templates/main/match_detail.html
View file @
cc4ddd48
...
...
@@ -38,7 +38,7 @@
background-color
:
rgba
(
255
,
255
,
255
,
0.1
);
}
.container
{
max-width
:
1
200px
;
max-width
:
1
00%
;
margin
:
2rem
auto
;
padding
:
0
2rem
;
}
...
...
@@ -47,6 +47,7 @@
padding
:
2rem
;
border-radius
:
8px
;
box-shadow
:
0
2px
10px
rgba
(
0
,
0
,
0
,
0.1
);
max-width
:
100%
;
}
.btn
{
padding
:
8px
16px
;
...
...
test_matchreport_update.py
0 → 100644
View file @
cc4ddd48
This diff is collapsed.
Click to expand it.
test_simple_matchreport_update.py
0 → 100644
View file @
cc4ddd48
"""
Simple test to verify MatchReport update logic without database connection
This tests the core logic directly without requiring a running MySQL server
"""
from
datetime
import
datetime
,
timedelta
import
uuid
import
sys
import
os
# Add the project root to Python path
sys
.
path
.
insert
(
0
,
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
)))
def
test_matchreport_logic
():
"""Test MatchReport update logic"""
print
(
"="
*
80
)
print
(
"Testing MatchReport Update Logic"
)
print
(
"="
*
80
)
# Simulate what happens in a real sync
print
(
"
\n
1. Simulating first sync (full sync)..."
)
# First, let's simulate existing match reports before incremental sync
existing_match_reports
=
[
{
'id'
:
1
,
'client_id'
:
'test_client_001'
,
'match_id'
:
123
,
'sync_id'
:
'sync_1'
,
'total_bets'
:
10
,
'total_payin'
:
2000.00
,
'total_payout'
:
1500.00
,
'winning_bets'
:
3
,
'losing_bets'
:
5
,
'pending_bets'
:
2
,
'cap_compensation_balance'
:
0.00
}
]
# Print existing state
print
(
f
" Existing MatchReport count: {len(existing_match_reports)}"
)
for
mr
in
existing_match_reports
:
print
(
f
" - Match {mr['match_id']}: {mr['total_bets']} bets, "
f
"{mr['total_payin']:.2f} payin, {mr['total_payout']:.2f} payout"
)
# New sync data (incremental) with updated information
print
(
"
\n
2. Processing incremental sync with updated data..."
)
sync_data
=
{
'sync_id'
:
f
'sync_2_{uuid.uuid4()}'
,
'client_id'
:
'test_client_001'
,
'sync_timestamp'
:
(
datetime
.
utcnow
()
+
timedelta
(
minutes
=
10
))
.
isoformat
(),
'date_range'
:
'all'
,
'start_date'
:
(
datetime
.
utcnow
()
-
timedelta
(
days
=
7
))
.
isoformat
(),
'end_date'
:
datetime
.
utcnow
()
.
isoformat
(),
'bets'
:
[
{
'uuid'
:
str
(
uuid
.
uuid4
()),
'fixture_id'
:
'fixture_test_001'
,
'bet_datetime'
:
(
datetime
.
utcnow
()
-
timedelta
(
hours
=
2
))
.
isoformat
(),
'paid'
:
False
,
'paid_out'
:
False
,
'total_amount'
:
600.00
,
'bet_count'
:
1
,
'details'
:
[
{
'match_id'
:
123
,
'match_number'
:
1
,
'outcome'
:
'WIN1'
,
'amount'
:
600.00
,
'win_amount'
:
0.00
,
'result'
:
'pending'
}
]
}
],
'extraction_stats'
:
[
{
'match_id'
:
123
,
'fixture_id'
:
'fixture_test_001'
,
'match_datetime'
:
(
datetime
.
utcnow
()
-
timedelta
(
hours
=
1
))
.
isoformat
(),
'total_bets'
:
15
,
# Updated
'total_amount_collected'
:
2500.00
,
# Updated
'total_redistributed'
:
1800.00
,
# Updated
'actual_result'
:
'WIN1'
,
'extraction_result'
:
'WIN1'
,
'cap_applied'
:
False
,
'under_bets'
:
7
,
'under_amount'
:
1200.00
,
'over_bets'
:
8
,
'over_amount'
:
1300.00
,
'result_breakdown'
:
{
'WIN1'
:
{
'bets'
:
4
,
'amount'
:
800.00
},
'X1'
:
{
'bets'
:
3
,
'amount'
:
600.00
},
'WIN2'
:
{
'bets'
:
8
,
'amount'
:
1100.00
}
}
}
],
'cap_compensation_balance'
:
100.00
,
'summary'
:
{
'total_payin'
:
2500.00
,
'total_payout'
:
1800.00
,
'net_profit'
:
700.00
,
'total_bets'
:
15
,
'total_matches'
:
1
}
}
# Let's apply our fix logic
print
(
"
\n
3. Applying the fix..."
)
updated_match_reports
=
[]
for
stats_data
in
sync_data
[
'extraction_stats'
]:
match_id
=
stats_data
[
'match_id'
]
client_id
=
sync_data
[
'client_id'
]
# Check if MatchReport exists for this client and match
existing
=
None
for
mr
in
existing_match_reports
:
if
mr
[
'client_id'
]
==
client_id
and
mr
[
'match_id'
]
==
match_id
:
existing
=
mr
break
if
existing
:
print
(
f
" Existing MatchReport found for match {match_id}"
)
print
(
f
" Updating from {existing['sync_id']} to {sync_data['sync_id']}"
)
existing
[
'sync_id'
]
=
sync_data
[
'sync_id'
]
existing
[
'total_bets'
]
=
stats_data
[
'total_bets'
]
existing
[
'total_payin'
]
=
stats_data
[
'total_amount_collected'
]
existing
[
'total_payout'
]
=
stats_data
[
'total_redistributed'
]
existing
[
'cap_compensation_balance'
]
=
sync_data
[
'cap_compensation_balance'
]
# Recalculate balance
existing
[
'balance'
]
=
existing
[
'total_payin'
]
-
existing
[
'total_payout'
]
print
(
f
" Updated: {existing['total_bets']} bets, "
f
"{existing['total_payin']:.2f} payin, {existing['total_payout']:.2f} payout, "
f
"balance: {existing['balance']:.2f}"
)
updated_match_reports
.
append
(
existing
)
else
:
print
(
f
" Creating new MatchReport for match {match_id}"
)
new_mr
=
{
'id'
:
len
(
existing_match_reports
)
+
1
,
'client_id'
:
client_id
,
'match_id'
:
match_id
,
'sync_id'
:
sync_data
[
'sync_id'
],
'total_bets'
:
stats_data
[
'total_bets'
],
'total_payin'
:
stats_data
[
'total_amount_collected'
],
'total_payout'
:
stats_data
[
'total_redistributed'
],
'cap_compensation_balance'
:
sync_data
[
'cap_compensation_balance'
],
'balance'
:
stats_data
[
'total_amount_collected'
]
-
stats_data
[
'total_redistributed'
]
}
updated_match_reports
.
append
(
new_mr
)
# Verify the results
print
(
"
\n
4. Verifying results..."
)
print
(
f
" MatchReport count: {len(updated_match_reports)}"
)
assert
len
(
updated_match_reports
)
==
1
,
"Should not create duplicate"
updated_report
=
updated_match_reports
[
0
]
assert
updated_report
[
'sync_id'
]
==
sync_data
[
'sync_id'
],
"Sync ID should be updated"
assert
updated_report
[
'total_bets'
]
==
15
,
"Total bets should be updated"
assert
updated_report
[
'total_payin'
]
==
2500.00
,
"Total payin should be updated"
assert
updated_report
[
'total_payout'
]
==
1800.00
,
"Total payout should be updated"
assert
updated_report
[
'cap_compensation_balance'
]
==
100.00
,
"Cap compensation balance should be updated"
assert
updated_report
[
'balance'
]
==
700.00
,
"Balance should be recalculated"
print
(
"
\n
✓ SUCCESS: MatchReport logic works correctly!"
)
print
(
f
" - No duplicate MatchReport created"
)
print
(
f
" - Existing record updated with new values"
)
print
(
f
" - All fields properly modified"
)
if
__name__
==
"__main__"
:
test_matchreport_logic
()
\ No newline at end of file
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