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
ec9fe6b6
Commit
ec9fe6b6
authored
Nov 25, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Again almost there...
parent
1c873f0c
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
189 additions
and
7 deletions
+189
-7
migrations.py
mbetterclient/database/migrations.py
+129
-0
routes.py
mbetterclient/web_dashboard/routes.py
+28
-3
fixture_details.html
...nt/web_dashboard/templates/dashboard/fixture_details.html
+16
-2
match_details.html
...ient/web_dashboard/templates/dashboard/match_details.html
+16
-2
No files found.
mbetterclient/database/migrations.py
View file @
ec9fe6b6
...
...
@@ -2421,6 +2421,134 @@ class Migration_031_AddWinningOutcomesFields(DatabaseMigration):
"""Remove winning_outcomes and under_over_result columns - SQLite doesn't support DROP COLUMN easily"""
logger
.
warning
(
"SQLite doesn't support DROP COLUMN - winning_outcomes and under_over_result columns will remain"
)
return
True
class
Migration_032_FixExtractionAssociationDefaults
(
DatabaseMigration
):
"""Fix incorrect defaults in extraction_associations table for KO1 and KO2"""
def
__init__
(
self
):
super
()
.
__init__
(
"032"
,
"Fix incorrect defaults in extraction_associations table for KO1 and KO2"
)
def
up
(
self
,
db_manager
)
->
bool
:
"""Update extraction associations to correct KO1 and KO2 defaults"""
try
:
with
db_manager
.
engine
.
connect
()
as
conn
:
# Remove incorrect associations
# Remove WIN1 and X1 from KO1
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'WIN1' AND extraction_result = 'KO1' AND is_default = 1
"""
))
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'X1' AND extraction_result = 'KO1' AND is_default = 1
"""
))
# Remove WIN2 and X2 from KO2
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'WIN2' AND extraction_result = 'KO2' AND is_default = 1
"""
))
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'X2' AND extraction_result = 'KO2' AND is_default = 1
"""
))
# Add correct associations
# Add WIN2 and X2 to KO1
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('WIN2', 'KO1', 1, datetime('now'), datetime('now'))
"""
))
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('X2', 'KO1', 1, datetime('now'), datetime('now'))
"""
))
# Add WIN1 and X1 to KO2
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('WIN1', 'KO2', 1, datetime('now'), datetime('now'))
"""
))
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('X1', 'KO2', 1, datetime('now'), datetime('now'))
"""
))
conn
.
commit
()
logger
.
info
(
"Fixed extraction association defaults for KO1 and KO2"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Failed to fix extraction association defaults: {e}"
)
return
False
def
down
(
self
,
db_manager
)
->
bool
:
"""Revert the extraction association changes"""
try
:
with
db_manager
.
engine
.
connect
()
as
conn
:
# Remove the corrected associations
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'WIN2' AND extraction_result = 'KO1' AND is_default = 1
"""
))
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'X2' AND extraction_result = 'KO1' AND is_default = 1
"""
))
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'WIN1' AND extraction_result = 'KO2' AND is_default = 1
"""
))
conn
.
execute
(
text
(
"""
DELETE FROM extraction_associations
WHERE outcome_name = 'X1' AND extraction_result = 'KO2' AND is_default = 1
"""
))
# Restore original incorrect associations
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('WIN1', 'KO1', 1, datetime('now'), datetime('now'))
"""
))
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('X1', 'KO1', 1, datetime('now'), datetime('now'))
"""
))
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('WIN2', 'KO2', 1, datetime('now'), datetime('now'))
"""
))
conn
.
execute
(
text
(
"""
INSERT OR IGNORE INTO extraction_associations
(outcome_name, extraction_result, is_default, created_at, updated_at)
VALUES ('X2', 'KO2', 1, datetime('now'), datetime('now'))
"""
))
conn
.
commit
()
logger
.
info
(
"Reverted extraction association defaults for KO1 and KO2"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Failed to revert extraction association defaults: {e}"
)
return
False
# Registry of all migrations in order
# Registry of all migrations in order
MIGRATIONS
:
List
[
DatabaseMigration
]
=
[
...
...
@@ -2455,6 +2583,7 @@ MIGRATIONS: List[DatabaseMigration] = [
Migration_029_ChangeMatchNumberToUniqueWithinFixture
(),
Migration_030_AddZipValidationStatus
(),
Migration_031_AddWinningOutcomesFields
(),
Migration_032_FixExtractionAssociationDefaults
(),
]
...
...
mbetterclient/web_dashboard/routes.py
View file @
ec9fe6b6
...
...
@@ -4,6 +4,7 @@ Flask routes for web dashboard
import
logging
import
time
import
json
from
datetime
import
datetime
,
date
from
flask
import
Blueprint
,
render_template
,
request
,
jsonify
,
redirect
,
url_for
,
flash
,
session
,
g
from
flask_login
import
login_required
,
current_user
,
login_user
,
logout_user
...
...
@@ -153,7 +154,18 @@ def bet_details(bet_id):
for
detail
in
bet_details
:
# Get match information
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
# Parse winning_outcomes JSON string if it exists
winning_outcomes
=
[]
if
match
and
match
.
winning_outcomes
:
try
:
if
isinstance
(
match
.
winning_outcomes
,
str
):
winning_outcomes
=
json
.
loads
(
match
.
winning_outcomes
)
elif
isinstance
(
match
.
winning_outcomes
,
list
):
winning_outcomes
=
match
.
winning_outcomes
except
(
json
.
JSONDecodeError
,
TypeError
):
winning_outcomes
=
[]
detail_dict
=
{
'id'
:
detail
.
id
,
'match_id'
:
detail
.
match_id
,
...
...
@@ -165,7 +177,8 @@ def bet_details(bet_id):
'fighter1_township'
:
match
.
fighter1_township
if
match
else
'Unknown'
,
'fighter2_township'
:
match
.
fighter2_township
if
match
else
'Unknown'
,
'venue_kampala_township'
:
match
.
venue_kampala_township
if
match
else
'Unknown'
,
'status'
:
match
.
status
if
match
else
'Unknown'
'status'
:
match
.
status
if
match
else
'Unknown'
,
'winning_outcomes'
:
winning_outcomes
}
if
match
else
None
}
...
...
@@ -591,6 +604,17 @@ def cashier_bet_details(bet_id):
# Get match information
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
# Parse winning_outcomes JSON string if it exists
winning_outcomes
=
[]
if
match
and
match
.
winning_outcomes
:
try
:
if
isinstance
(
match
.
winning_outcomes
,
str
):
winning_outcomes
=
json
.
loads
(
match
.
winning_outcomes
)
elif
isinstance
(
match
.
winning_outcomes
,
list
):
winning_outcomes
=
match
.
winning_outcomes
except
(
json
.
JSONDecodeError
,
TypeError
):
winning_outcomes
=
[]
detail_dict
=
{
'id'
:
detail
.
id
,
'match_id'
:
detail
.
match_id
,
...
...
@@ -602,7 +626,8 @@ def cashier_bet_details(bet_id):
'fighter1_township'
:
match
.
fighter1_township
if
match
else
'Unknown'
,
'fighter2_township'
:
match
.
fighter2_township
if
match
else
'Unknown'
,
'venue_kampala_township'
:
match
.
venue_kampala_township
if
match
else
'Unknown'
,
'status'
:
match
.
status
if
match
else
'Unknown'
'status'
:
match
.
status
if
match
else
'Unknown'
,
'winning_outcomes'
:
winning_outcomes
}
if
match
else
None
}
...
...
mbetterclient/web_dashboard/templates/dashboard/fixture_details.html
View file @
ec9fe6b6
...
...
@@ -308,16 +308,30 @@ function showError(message) {
function
formatWinningOutcomes
(
winningOutcomes
)
{
// Safely format winning outcomes, handling cases where it's not an array
let
outcomesArray
=
[];
if
(
Array
.
isArray
(
winningOutcomes
)
&&
winningOutcomes
.
length
>
0
)
{
return
winningOutcomes
.
join
(
', '
)
;
outcomesArray
=
winningOutcomes
;
}
else
if
(
winningOutcomes
&&
typeof
winningOutcomes
===
'string'
)
{
return
winningOutcomes
;
// Try to parse JSON string
try
{
const
parsed
=
JSON
.
parse
(
winningOutcomes
);
if
(
Array
.
isArray
(
parsed
)
&&
parsed
.
length
>
0
)
{
outcomesArray
=
parsed
;
}
else
{
return
winningOutcomes
;
// Return original string if not a valid array
}
}
catch
(
e
)
{
return
winningOutcomes
;
// Return original string if parsing fails
}
}
else
if
(
winningOutcomes
&&
typeof
winningOutcomes
===
'object'
)
{
// If it's an object, try to stringify it
return
JSON
.
stringify
(
winningOutcomes
);
}
else
{
return
'Not available'
;
}
return
outcomesArray
.
join
(
', '
);
}
function
renderFixtureDetails
(
fixture
,
matches
)
{
...
...
mbetterclient/web_dashboard/templates/dashboard/match_details.html
View file @
ec9fe6b6
...
...
@@ -293,16 +293,30 @@ function showError(message) {
function
formatWinningOutcomes
(
winningOutcomes
)
{
// Safely format winning outcomes, handling cases where it's not an array
let
outcomesArray
=
[];
if
(
Array
.
isArray
(
winningOutcomes
)
&&
winningOutcomes
.
length
>
0
)
{
return
winningOutcomes
.
join
(
', '
)
;
outcomesArray
=
winningOutcomes
;
}
else
if
(
winningOutcomes
&&
typeof
winningOutcomes
===
'string'
)
{
return
winningOutcomes
;
// Try to parse JSON string
try
{
const
parsed
=
JSON
.
parse
(
winningOutcomes
);
if
(
Array
.
isArray
(
parsed
)
&&
parsed
.
length
>
0
)
{
outcomesArray
=
parsed
;
}
else
{
return
winningOutcomes
;
// Return original string if not a valid array
}
}
catch
(
e
)
{
return
winningOutcomes
;
// Return original string if parsing fails
}
}
else
if
(
winningOutcomes
&&
typeof
winningOutcomes
===
'object'
)
{
// If it's an object, try to stringify it
return
JSON
.
stringify
(
winningOutcomes
);
}
else
{
return
''
;
}
return
outcomesArray
.
join
(
', '
);
}
function
updateMatchDetails
(
match
)
{
...
...
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