Refactor reports

parent ccb5153f
This diff is collapsed.
......@@ -925,7 +925,7 @@ def api_reports_sync():
start_time = time.time()
try:
from app.models import ReportSync, Bet, BetDetail, ExtractionStats, APIToken, ReportSyncLog
from app.models import ReportSync, Bet, BetDetail, ExtractionStats, APIToken, ReportSyncLog, MatchReport, ClientActivity
from app.auth.jwt_utils import validate_api_token, extract_token_from_request
from datetime import datetime
import uuid as uuid_lib
......@@ -1266,6 +1266,72 @@ def api_reports_sync():
stats_count += 1
stats_new += 1
# Create MatchReport records for comprehensive match-level data
match_reports_count = 0
for stats_data in data['extraction_stats']:
# Get client token name
client_activity = ClientActivity.query.filter_by(rustdesk_id=data['client_id']).first()
client_token_name = client_activity.api_token.name if client_activity and client_activity.api_token else 'Unknown'
# Calculate winning/losing/pending bets from bet details
match_id = stats_data['match_id']
winning_bets = 0
losing_bets = 0
pending_bets = 0
# Query all bet details for this match
from sqlalchemy import func
bet_details_query = db.session.query(
BetDetail.result,
func.count(BetDetail.id).label('count')
).join(Bet).filter(
Bet.client_id == data['client_id'],
BetDetail.match_id == match_id
).group_by(BetDetail.result)
for result, count in bet_details_query.all():
if result == 'won':
winning_bets = count
elif result == 'lost':
losing_bets = count
elif result == 'pending':
pending_bets = count
# Calculate balance (payin - payout)
total_payin = stats_data['total_amount_collected']
total_payout = stats_data['total_redistributed']
balance = total_payin - total_payout
# Create MatchReport record
match_report = MatchReport(
sync_id=report_sync.id,
client_id=data['client_id'],
client_token_name=client_token_name,
match_id=match_id,
match_number=stats_data.get('match_number', 0),
fixture_id=stats_data['fixture_id'],
match_datetime=datetime.fromisoformat(stats_data['match_datetime']),
total_bets=stats_data['total_bets'],
winning_bets=winning_bets,
losing_bets=losing_bets,
pending_bets=pending_bets,
total_payin=total_payin,
total_payout=total_payout,
balance=balance,
actual_result=stats_data['actual_result'],
extraction_result=stats_data['extraction_result'],
cap_applied=stats_data.get('cap_applied', False),
cap_percentage=stats_data.get('cap_percentage'),
cap_compensation_balance=cap_compensation_balance,
under_bets=stats_data.get('under_bets', 0),
under_amount=stats_data.get('under_amount', 0.00),
over_bets=stats_data.get('over_bets', 0),
over_amount=stats_data.get('over_amount', 0.00),
result_breakdown=stats_data.get('result_breakdown')
)
db.session.add(match_report)
match_reports_count += 1
# Commit all changes
db.session.commit()
......
"""
Migration script to add match_reports table for comprehensive match-level reporting
"""
def upgrade():
"""Add match_reports table"""
from app import db
from app.models import MatchReport
# Create the table
db.create_all()
print("✓ match_reports table created successfully")
def downgrade():
"""Remove match_reports table"""
from app import db
from sqlalchemy import text
# Drop the table
db.session.execute(text("DROP TABLE IF EXISTS match_reports"))
db.session.commit()
print("✓ match_reports table dropped successfully")
if __name__ == '__main__':
upgrade()
\ No newline at end of file
This diff is collapsed.
......@@ -1089,4 +1089,88 @@ class ReportSyncLog(db.Model):
}
def __repr__(self):
return f'<ReportSyncLog {self.sync_id} {self.operation_type} {self.status}>'
\ No newline at end of file
return f'<ReportSyncLog {self.sync_id} {self.operation_type} {self.status}>'
class MatchReport(db.Model):
"""Comprehensive match-level report data"""
__tablename__ = 'match_reports'
id = db.Column(db.Integer, primary_key=True)
sync_id = db.Column(db.Integer, db.ForeignKey('report_syncs.id'), nullable=False, index=True)
client_id = db.Column(db.String(255), nullable=False, index=True)
client_token_name = db.Column(db.String(255), nullable=False, index=True)
# Match identification
match_id = db.Column(db.Integer, nullable=False, index=True)
match_number = db.Column(db.Integer, nullable=False)
fixture_id = db.Column(db.String(255), nullable=False, index=True)
match_datetime = db.Column(db.DateTime, nullable=False, index=True)
# Betting statistics
total_bets = db.Column(db.Integer, default=0)
winning_bets = db.Column(db.Integer, default=0)
losing_bets = db.Column(db.Integer, default=0)
pending_bets = db.Column(db.Integer, default=0)
# Financial data
total_payin = db.Column(db.Numeric(15, 2), default=0.00)
total_payout = db.Column(db.Numeric(15, 2), default=0.00)
balance = db.Column(db.Numeric(15, 2), default=0.00)
# Match result
actual_result = db.Column(db.String(50), nullable=False)
extraction_result = db.Column(db.String(50), nullable=False)
# CAP information
cap_applied = db.Column(db.Boolean, default=False)
cap_percentage = db.Column(db.Numeric(5, 2))
cap_compensation_balance = db.Column(db.Numeric(15, 2), default=0.00)
# Detailed breakdown
under_bets = db.Column(db.Integer, default=0)
under_amount = db.Column(db.Numeric(15, 2), default=0.00)
over_bets = db.Column(db.Integer, default=0)
over_amount = db.Column(db.Numeric(15, 2), default=0.00)
result_breakdown = db.Column(db.JSON)
# Metadata
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
sync = db.relationship('ReportSync', backref='match_reports', lazy='select')
def to_dict(self):
"""Convert to dictionary for JSON serialization"""
return {
'id': self.id,
'sync_id': self.sync_id,
'client_id': self.client_id,
'client_token_name': self.client_token_name,
'match_id': self.match_id,
'match_number': self.match_number,
'fixture_id': self.fixture_id,
'match_datetime': self.match_datetime.isoformat() if self.match_datetime else None,
'total_bets': self.total_bets,
'winning_bets': self.winning_bets,
'losing_bets': self.losing_bets,
'pending_bets': self.pending_bets,
'total_payin': float(self.total_payin) if self.total_payin else 0.0,
'total_payout': float(self.total_payout) if self.total_payout else 0.0,
'balance': float(self.balance) if self.balance else 0.0,
'actual_result': self.actual_result,
'extraction_result': self.extraction_result,
'cap_applied': self.cap_applied,
'cap_percentage': float(self.cap_percentage) if self.cap_percentage else None,
'cap_compensation_balance': float(self.cap_compensation_balance) if self.cap_compensation_balance else 0.0,
'under_bets': self.under_bets,
'under_amount': float(self.under_amount) if self.under_amount else 0.0,
'over_bets': self.over_bets,
'over_amount': float(self.over_amount) if self.over_amount else 0.0,
'result_breakdown': self.result_breakdown,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
def __repr__(self):
return f'<MatchReport {self.client_token_name} match={self.match_id} balance={self.balance}>'
\ No newline at end of file
This diff is collapsed.
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