Feat: Decks page.

This commit is contained in:
2026-02-16 19:30:31 +00:00
parent 1cd9b7c976
commit 5661632540
75 changed files with 11850 additions and 536 deletions

View File

@@ -76,6 +76,7 @@ class Base():
FLAG_TEXT_COLOUR: ClassVar[str] = 'text_colour'
FLAG_URL: ClassVar[str] = 'url'
FLAG_USER: ClassVar[str] = 'authorisedUser' # 'user' already used
FLAG_VALUE: ClassVar[str] = 'value'
FLAG_VALUE_LOCAL_VAT_EXCL: ClassVar[str] = 'value_local_vat_excl'
FLAG_VALUE_LOCAL_VAT_INCL: ClassVar[str] = 'value_local_vat_incl'
FLAG_WEBSITE: ClassVar[str] = 'website'

View File

@@ -10,6 +10,7 @@ Feature: MTG Deck Business Object
# internal
from business_objects.base import Base
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
from business_objects.tcg.mtg_deck_commander_bracket import MTG_Deck_Commander_Bracket
import lib.argument_validation as av
from extensions import db
from helpers.helper_app import Helper_App
@@ -24,6 +25,7 @@ class MTG_Deck(SQLAlchemy_ABC, Base):
ATTR_COMMANDER_BRACKET_ID: ClassVar[str] = 'commander_bracket_id'
FLAG_DECK: ClassVar[str] = 'deck'
FLAG_IS_COMMANDER: ClassVar[str] = 'is_commander'
FLAG_STATISTICS: ClassVar[str] = 'statistics'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = ATTR_DECK_ID
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME
@@ -41,9 +43,13 @@ class MTG_Deck(SQLAlchemy_ABC, Base):
updated_last_by_user_id = db.Column(db.Integer)
change_set_id = db.Column(db.Integer)
# commander_bracket: MTG_Deck_Commander_Bracket
def __init__(self):
self.deck_id = 0
self.is_new = False
self.commander_bracket = None
self.statistics = None
super().__init__()
@classmethod
@@ -83,6 +89,7 @@ class MTG_Deck(SQLAlchemy_ABC, Base):
, self.FLAG_ACTIVE: self.active
, self.FLAG_CREATED_ON: self.created_on
, Base.ATTR_USER_ID: self.created_by_user_id
, self.FLAG_STATISTICS: self.statistics
}
return as_json

View File

@@ -104,21 +104,19 @@ class Parameters_MTG_Deck_Commander_Bracket(Get_Many_Parameters_Base):
get_inactive_commander_bracket: bool
commander_bracket_ids: str
commander_bracket_names: str
user_ids: str
require_all_id_filters_met: bool
require_any_id_filters_met: bool
require_all_non_id_filters_met: bool
require_any_non_id_filters_met: bool
@classmethod
def get_default(cls, user_id_session):
def get_default(cls):
return cls(
get_all_commander_bracket = True
, get_inactive_commander_bracket = False
, commander_bracket_ids = ''
, commander_bracket_names = ''
, user_ids = str(user_id_session)
, require_all_id_filters_met = True
, require_all_id_filters_met = False
, require_any_id_filters_met = True
, require_all_non_id_filters_met = False
, require_any_non_id_filters_met = True
@@ -127,12 +125,11 @@ class Parameters_MTG_Deck_Commander_Bracket(Get_Many_Parameters_Base):
@classmethod
def from_json(cls, json):
return cls(
get_all_commander_bracket = json.get('a_get_all_commander_bracket', False)
get_all_commander_bracket = json.get('a_get_all_commander_bracket', True)
, get_inactive_commander_bracket = json.get('a_get_inactive_commander_bracket', False)
, commander_bracket_ids = json.get('a_commander_bracket_ids', '')
, commander_bracket_names = json.get('a_commander_bracket_names', '')
, user_ids = json.get('a_user_ids', '')
, require_all_id_filters_met = json.get('a_require_all_id_filters_met', True)
, require_all_id_filters_met = json.get('a_require_all_id_filters_met', False)
, require_any_id_filters_met = json.get('a_require_any_id_filters_met', True)
, require_all_non_id_filters_met = json.get('a_require_all_non_id_filters_met', False)
, require_any_non_id_filters_met = json.get('a_require_any_non_id_filters_met', True)
@@ -144,7 +141,6 @@ class Parameters_MTG_Deck_Commander_Bracket(Get_Many_Parameters_Base):
, 'a_get_inactive_commander_bracket': self.get_inactive_commander_bracket
, 'a_commander_bracket_ids': self.commander_bracket_ids
, 'a_commander_bracket_names': self.commander_bracket_names
, 'a_user_ids': self.user_ids
, 'a_require_all_id_filters_met': self.require_all_id_filters_met
, 'a_require_any_id_filters_met': self.require_any_id_filters_met
, 'a_require_all_non_id_filters_met': self.require_all_non_id_filters_met

View File

@@ -27,8 +27,9 @@ class MTG_Game_Round_Player_Damage(SQLAlchemy_ABC, Base):
ATTR_RECEIVED_FROM_COMMANDER_PLAYER_ID: ClassVar[str] = 'received_from_commander_player_id'
FLAG_COMMANDER_DEATHS: ClassVar[str] = 'commander_deaths'
FLAG_DAMAGE: ClassVar[str] = 'damage'
FLAG_HEALTH_CHANGE: ClassVar[str] = 'health_change'
FLAG_IS_ELIMINATED: ClassVar[str] = 'is_eliminated'
FLAG_LIFE_GAIN: ClassVar[str] = 'life_gain'
FLAG_LIFE_LOSS: ClassVar[str] = 'life_loss'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = ATTR_DAMAGE_ID
NAME_ATTR_OPTION_TEXT: ClassVar[str] = ATTR_DAMAGE_ID
@@ -39,7 +40,8 @@ class MTG_Game_Round_Player_Damage(SQLAlchemy_ABC, Base):
round_id = db.Column(db.Integer)
player_id = db.Column(db.Integer)
received_from_commander_player_id = db.Column(db.Integer)
health_change = db.Column(db.Integer)
life_gain = db.Column(db.Integer)
life_loss = db.Column(db.Integer)
commander_deaths = db.Column(db.Integer)
is_eliminated = db.Column(db.Boolean)
active = db.Column(db.Boolean)
@@ -62,12 +64,13 @@ class MTG_Game_Round_Player_Damage(SQLAlchemy_ABC, Base):
damage.round_id = query_row[1]
damage.player_id = query_row[2]
damage.received_from_commander_player_id = query_row[3]
damage.health_change = query_row[4]
damage.commander_deaths = query_row[5]
damage.is_eliminated = av.input_bool(query_row[6], cls.FLAG_IS_ELIMINATED, _m)
damage.active = av.input_bool(query_row[7], cls.FLAG_ACTIVE, _m)
damage.created_on = query_row[8]
damage.created_by_user_id = query_row[9]
damage.life_gain = query_row[4]
damage.life_loss = query_row[5]
damage.commander_deaths = query_row[6]
damage.is_eliminated = av.input_bool(query_row[7], cls.FLAG_IS_ELIMINATED, _m)
damage.active = av.input_bool(query_row[8], cls.FLAG_ACTIVE, _m)
damage.created_on = query_row[9]
damage.created_by_user_id = query_row[10]
return damage
@classmethod
@@ -79,7 +82,8 @@ class MTG_Game_Round_Player_Damage(SQLAlchemy_ABC, Base):
damage.round_id = json.get(cls.ATTR_ROUND_ID, None)
damage.player_id = json.get(cls.ATTR_PLAYER_ID, None)
damage.received_from_commander_player_id = json.get(cls.ATTR_RECEIVED_FROM_COMMANDER_PLAYER_ID, None)
damage.health_change = json.get(cls.FLAG_HEALTH_CHANGE, 0)
damage.life_gain = json.get(cls.FLAG_LIFE_GAIN, 0)
damage.life_loss = json.get(cls.FLAG_LIFE_LOSS, 0)
damage.commander_deaths = json.get(cls.FLAG_COMMANDER_DEATHS, 0)
damage.is_eliminated = json.get(cls.FLAG_IS_ELIMINATED, False)
damage.active = av.input_bool(json.get(cls.FLAG_ACTIVE, True), cls.FLAG_ACTIVE, _m)
@@ -94,7 +98,8 @@ class MTG_Game_Round_Player_Damage(SQLAlchemy_ABC, Base):
, self.ATTR_ROUND_ID: self.round_id
, self.ATTR_PLAYER_ID: self.player_id
, self.ATTR_RECEIVED_FROM_COMMANDER_PLAYER_ID: self.received_from_commander_player_id
, self.FLAG_HEALTH_CHANGE: self.health_change
, self.FLAG_LIFE_GAIN: self.life_gain
, self.FLAG_LIFE_LOSS: self.life_loss
, self.FLAG_COMMANDER_DEATHS: self.commander_deaths
, self.FLAG_IS_ELIMINATED: self.is_eliminated
, self.FLAG_ACTIVE: self.active
@@ -110,7 +115,8 @@ class MTG_Game_Round_Player_Damage(SQLAlchemy_ABC, Base):
{self.ATTR_ROUND_ID}: {self.round_id}
{self.ATTR_PLAYER_ID}: {self.player_id}
{self.ATTR_RECEIVED_FROM_COMMANDER_PLAYER_ID}: {self.received_from_commander_player_id}
{self.FLAG_HEALTH_CHANGE}: {self.health_change}
{self.FLAG_LIFE_GAIN}: {self.life_gain}
{self.FLAG_LIFE_LOSS}: {self.life_loss}
{self.FLAG_COMMANDER_DEATHS}: {self.commander_deaths}
{self.FLAG_IS_ELIMINATED}: {self.is_eliminated}
{self.FLAG_ACTIVE}: {self.active}
@@ -127,7 +133,8 @@ class MTG_Game_Round_Player_Damage_Temp(db.Model, Base):
round_id = db.Column(db.Integer)
player_id = db.Column(db.Integer)
received_from_commander_player_id = db.Column(db.Integer)
health_change = db.Column(db.Integer)
life_gain = db.Column(db.Integer)
life_loss = db.Column(db.Integer)
commander_deaths = db.Column(db.Integer)
is_eliminated = db.Column(db.Boolean)
active = db.Column(db.Boolean)
@@ -145,7 +152,8 @@ class MTG_Game_Round_Player_Damage_Temp(db.Model, Base):
temp.round_id = damage.round_id
temp.player_id = damage.player_id
temp.received_from_commander_player_id = damage.received_from_commander_player_id
temp.health_change = damage.health_change
temp.life_gain = damage.life_gain
temp.life_loss = damage.life_loss
temp.commander_deaths = damage.commander_deaths
temp.is_eliminated = damage.is_eliminated
temp.active = damage.active

View File

@@ -0,0 +1,287 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Statistic Business Object
"""
# internal
from business_objects.base import Base
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
import lib.argument_validation as av
from extensions import db
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar
from sqlalchemy import Uuid, Interval
from sqlalchemy.types import Text, Boolean
class Statistic(SQLAlchemy_ABC, Base):
ATTR_ENTITY_RECORD_ID: ClassVar[str] = 'entity_record_id'
ATTR_STATISTIC_ID: ClassVar[str] = 'statistic_id'
FLAG_ENTITY_TYPE_CODE: ClassVar[str] = 'entity_type_code'
FLAG_IS_BOOL: ClassVar[str] = 'is_bool'
FLAG_IS_FLOAT: ClassVar[str] = 'is_float'
FLAG_IS_INTERVAL: ClassVar[str] = 'is_interval'
FLAG_IS_TEXT: ClassVar[str] = 'is_text'
FLAG_IS_TIMESTAMP: ClassVar[str] = 'is_timestamp'
FLAG_VALUE_BOOL: ClassVar[str] = 'value_bool'
FLAG_VALUE_FLOAT: ClassVar[str] = 'value_float'
FLAG_VALUE_INTERVAL: ClassVar[str] = 'value_interval'
FLAG_VALUE_TEXT: ClassVar[str] = 'value_text'
FLAG_VALUE_TIMESTAMP: ClassVar[str] = 'value_timestamp'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = ATTR_STATISTIC_ID
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME
__tablename__ = 'tcg_statistic'
__table_args__ = { 'extend_existing': True }
statistic_id = db.Column(db.Integer, primary_key=True)
entity_type_code = db.Column(db.Text) # replace with lookup table later
entity_record_id = db.Column(db.Text)
name = db.Column(db.Text)
value_bool = db.Column(db.Boolean)
value_float = db.Column(db.Float)
value_interval = db.Column(Interval)
value_text = db.Column(db.Text)
value_timestamp = db.Column(db.DateTime)
is_bool = db.Column(db.Boolean)
is_float = db.Column(db.Boolean)
is_interval = db.Column(db.Boolean)
is_text = db.Column(db.Boolean)
is_timestamp = db.Column(db.Boolean)
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by_user_id = db.Column(db.Integer)
updated_last_on = db.Column(db.DateTime)
updated_last_by_user_id = db.Column(db.Integer)
change_set_id = db.Column(db.Integer)
def __init__(self):
self.statistic_id = 0
self.is_new = False
super().__init__()
@classmethod
def from_db_statistic(cls, query_row):
_m = f'{cls.__qualname__}.from_db_statistic'
statistic = cls()
statistic.statistic_id = query_row[0]
statistic.entity_type_code = query_row[1]
statistic.entity_record_id = query_row[2]
statistic.name = query_row[3]
statistic.value_bool = query_row[4]
statistic.value_float = query_row[5]
statistic.value_interval = query_row[6]
statistic.value_text = query_row[7]
statistic.value_timestamp = query_row[8]
statistic.is_bool = query_row[9]
statistic.is_float = query_row[10]
statistic.is_interval = query_row[11]
statistic.is_text = query_row[12]
statistic.is_timestamp = query_row[13]
statistic.display_order = query_row[14]
statistic.active = av.input_bool(query_row[15], cls.FLAG_ACTIVE, _m)
statistic.created_on = query_row[16]
statistic.created_by_user_id = query_row[17]
return statistic
@classmethod
def from_json(cls, json):
_m = f'{cls.__qualname__}.from_json'
statistic = cls()
if json is None: return statistic
statistic.statistic_id = json.get(cls.ATTR_STATISTIC_ID, -1)
statistic.entity_type_code = json.get(cls.FLAG_ENTITY_TYPE_CODE, None)
statistic.entity_record_id = json.get(cls.ATTR_ENTITY_RECORD_ID, None)
statistic.name = json.get(cls.FLAG_NAME, '')
statistic.value_bool = json.get(cls.FLAG_VALUE_BOOL, None)
statistic.value_float = json.get(cls.FLAG_VALUE_FLOAT, None)
statistic.value_interval = json.get(cls.FLAG_VALUE_INTERVAL, None)
statistic.value_text = json.get(cls.FLAG_VALUE_TEXT, None)
statistic.value_timestamp = json.get(cls.FLAG_VALUE_TIMESTAMP, None)
statistic.is_bool = json.get(cls.FLAG_IS_BOOL, False)
statistic.is_float = json.get(cls.FLAG_IS_FLOAT, False)
statistic.is_interval = json.get(cls.FLAG_IS_INTERVAL, False)
statistic.is_text = json.get(cls.FLAG_IS_TEXT, False)
statistic.is_timestamp = json.get(cls.FLAG_IS_TIMESTAMP, False)
statistic.display_order = json.get(cls.FLAG_DISPLAY_ORDER, -1)
statistic.active = av.input_bool(json.get(cls.FLAG_ACTIVE, True), cls.FLAG_ACTIVE, _m)
statistic.created_on = json.get(cls.FLAG_CREATED_ON, None)
statistic.created_by_user_id = json.get(Base.ATTR_USER_ID, None)
return statistic
def to_json(self):
as_json = {
**self.get_shared_json_attributes(self)
, self.ATTR_STATISTIC_ID: self.statistic_id
, self.FLAG_ENTITY_TYPE_CODE: self.entity_type_code
, self.ATTR_ENTITY_RECORD_ID: self.entity_record_id
, self.FLAG_NAME: self.name
, self.FLAG_VALUE_BOOL: self.value_bool
, self.FLAG_VALUE_FLOAT: self.value_float
, self.FLAG_VALUE_INTERVAL: self.value_interval
, self.FLAG_VALUE_TEXT: self.value_text
, self.FLAG_VALUE_TIMESTAMP: self.value_timestamp
, self.FLAG_IS_BOOL: self.is_bool
, self.FLAG_IS_FLOAT: self.is_float
, self.FLAG_IS_INTERVAL: self.is_interval
, self.FLAG_IS_TEXT: self.is_text
, self.FLAG_IS_TIMESTAMP: self.is_timestamp
, self.FLAG_DISPLAY_ORDER: self.display_order
, self.FLAG_ACTIVE: self.active
, self.FLAG_CREATED_ON: self.created_on
, Base.ATTR_USER_ID: self.created_by_user_id
}
return as_json
def __repr__(self):
return f'''
{self.__class__.__name__}(
{self.ATTR_STATISTIC_ID}: {self.statistic_id}
{self.FLAG_ENTITY_TYPE_CODE}: {self.entity_type_code}
{self.ATTR_ENTITY_RECORD_ID}: {self.entity_record_id}
{self.FLAG_NAME}: {self.name}
{self.FLAG_VALUE_BOOL}: {self.value_bool}
{self.FLAG_VALUE_FLOAT}: {self.value_float}
{self.FLAG_VALUE_INTERVAL}: {self.value_interval}
{self.FLAG_VALUE_TEXT}: {self.value_text}
{self.FLAG_VALUE_TIMESTAMP}: {self.value_timestamp}
{self.FLAG_IS_BOOL}: {self.is_bool}
{self.FLAG_IS_FLOAT}: {self.is_float}
{self.FLAG_IS_INTERVAL}: {self.is_interval}
{self.FLAG_IS_TEXT}: {self.is_text}
{self.FLAG_IS_TIMESTAMP}: {self.is_timestamp}
{self.FLAG_DISPLAY_ORDER}: {self.display_order}
{self.FLAG_ACTIVE}: {self.active}
{self.FLAG_CREATED_ON}: {self.created_on}
{Base.ATTR_USER_ID}: {self.created_by_user_id}
)
'''
def get_formatted_value(self):
if self.is_bool:
return self.value_bool
elif self.is_float:
return self.value_float
elif self.is_interval:
return self.value_interval
elif self.is_text:
return self.value_text
elif self.is_timestamp:
return self.value_timestamp
else:
return None
class Statistic_Temp(db.Model, Base):
__tablename__ = 'tcg_statistic_temp'
__table_args__ = { 'extend_existing': True }
temp_id = db.Column(db.Integer, primary_key=True)
statistic_id = db.Column(db.Integer)
entity_type_code = db.Column(db.Text)
entity_record_id = db.Column(db.Integer)
name = db.Column(db.Integer)
value_bool = db.Column(db.Boolean)
value_float = db.Column(db.Float)
value_interval = db.Column(Interval)
value_text = db.Column(db.Text)
value_timestamp = db.Column(db.DateTime)
is_bool = db.Column(db.Boolean)
is_float = db.Column(db.Boolean)
is_interval = db.Column(db.Boolean)
is_text = db.Column(db.Boolean)
is_timestamp = db.Column(db.Boolean)
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
guid = db.Column(Uuid)
def __init__(self):
self.entity_record_name = None
super().__init__()
@classmethod
def from_statistic(cls, statistic, guid):
_m = 'Statistic_Temp.from_statistic'
temp = cls()
temp.statistic_id = statistic.statistic_id
temp.entity_type_code = statistic.entity_type_code
temp.entity_record_id = statistic.entity_record_id
temp.name = statistic.name
temp.value_bool = statistic.value_bool
temp.value_float = statistic.value_float
temp.value_interval = statistic.value_interval
temp.value_text = statistic.value_text
temp.value_timestamp = statistic.value_timestamp
temp.is_bool = statistic.is_bool
temp.is_float = statistic.is_float
temp.is_interval = statistic.is_interval
temp.is_text = statistic.is_text
temp.is_timestamp = statistic.is_timestamp
temp.display_order = statistic.display_order
temp.active = statistic.active
temp.created_on = statistic.created_on
temp.guid = guid
return temp
class Parameters_Statistic(Get_Many_Parameters_Base):
get_all_statistic: bool
get_inactive_statistic: bool
statistic_ids: str
entity_type_codes: str
entity_record_ids: str
require_all_id_filters_met: bool
require_any_id_filters_met: bool
@classmethod
def get_default(cls):
return cls(
get_all_statistic = True
, get_inactive_statistic = False
, statistic_ids = ''
, entity_type_codes = ''
, entity_record_ids = ''
, require_all_id_filters_met = True
, require_any_id_filters_met = True
)
@classmethod
def from_json(cls, json):
return cls(
get_all_statistic = json.get('a_get_all_statistic', False)
, get_inactive_statistic = json.get('a_get_inactive_statistic', False)
, statistic_ids = json.get('a_statistic_ids', '')
, entity_type_codes = json.get('a_entity_type_codes', '')
, entity_record_ids = json.get('a_entity_record_ids', '')
, require_all_id_filters_met = json.get('a_require_all_id_filters_met', True)
, require_any_id_filters_met = json.get('a_require_any_id_filters_met', True)
)
def to_json(self):
return {
'a_get_all_statistic': self.get_all_statistic
, 'a_get_inactive_statistic': self.get_inactive_statistic
, 'a_statistic_ids': self.statistic_ids
, 'a_entity_type_codes': self.entity_type_codes
, 'a_entity_record_ids': self.entity_record_ids
, 'a_require_all_id_filters_met': self.require_all_id_filters_met
, 'a_require_any_id_filters_met': self.require_any_id_filters_met
}
@staticmethod
def get_type_hints():
return {
'a_get_all_statistic': Boolean
, 'a_get_inactive_statistic': Boolean
, 'a_statistic_ids': Text
, 'a_entity_type_codes': Text
, 'a_entity_record_ids': Text
, 'a_require_all_id_filters_met': Boolean
, 'a_require_any_id_filters_met': Boolean
}