@ -1,5 +1,5 @@
from sqlalchemy import \
from sqlalchemy import \
Boolean , Column , DateTime , ForeignKey , Integer , String , Text , UniqueConstraint , \
Boolean , Column , DateTime , ForeignKey , Integer , String , Text , UniqueConstraint , Enum , \
text , func , \
text , func , \
create_engine , inspect , select
create_engine , inspect , select
from sqlalchemy . engine import Engine
from sqlalchemy . engine import Engine
@ -14,6 +14,8 @@ from sqlalchemy.sql.sqltypes import Numeric
from typing import Any , Optional , List , Tuple
from typing import Any , Optional , List , Tuple
import secrets
import secrets
import string
import string
from enum import Enum as PythonEnum , auto
import hra . config as config
import hra . config as config
import hra . game
import hra . game
@ -26,6 +28,33 @@ _engine: Optional[Engine] = None
_session : Optional [ Session ] = None
_session : Optional [ Session ] = None
flask_db : Any = None
flask_db : Any = None
class MOEnum ( str , PythonEnum ) :
""" MOEnum je varianta PythonEnum, ve které se automaticky přidělované
hodnoty jmenují stejně jako klíče a funguje serializace do JSONu . """
def _generate_next_value_ ( name , start , count , last_values ) :
return name
@classmethod
def choices ( enum ) - > List [ Tuple [ str , str ] ] :
out = [ ]
for item in enum :
out . append ( ( item . name , item . friendly_name ( ) ) )
return out
def friendly_name ( self ) - > str :
return str ( self )
@classmethod
def coerce ( enum , name ) :
if isinstance ( name , enum ) :
return name
try :
return enum [ name ]
except KeyError :
raise ValueError ( name )
def get_engine ( ) - > Engine :
def get_engine ( ) - > Engine :
global _engine
global _engine
if _engine is None :
if _engine is None :
@ -62,31 +91,63 @@ class User(Base):
def print ( self ) :
def print ( self ) :
return self . username + ( " (org) " if self . org else " " )
return self . username + ( " (org) " if self . org else " " )
class BigData ( Base ) :
__tablename__ = ' bigdata '
id = Column ( Integer , primary_key = True )
data = Column ( JSONB , nullable = False )
def get_big_data ( id ) :
return get_session ( ) . query ( BigData ) . filter_by ( id = id ) . one_or_none ( ) . data
def new_big_data ( d ) :
o = BigData ( data = d )
get_session ( ) . add ( o )
get_session ( ) . flush ( )
return o . id
class StepMode ( MOEnum ) :
none = auto ( )
org = auto ( )
user = auto ( )
automatic = auto ( )
class Game ( Base ) :
class Game ( Base ) :
__tablename__ = ' games '
__tablename__ = ' games '
game_id = Column ( Integer , primary_key = True )
game_id = Column ( Integer , primary_key = True )
configuration = Column ( JSONB , nullable = False )
name = Column ( String ( 80 ) , nullable = True )
configuration = Column ( Integer , ForeignKey ( ' bigdata.id ' ) , nullable = False )
step_mode = Column ( Enum ( StepMode , name = ' step_mode ' ) , nullable = False , default = StepMode . none )
step_every_s = Column ( Integer , nullable = False , default = 60 )
game_mode = Column ( String ( 80 ) , nullable = False )
game_mode = Column ( String ( 80 ) , nullable = False )
teams_count = Column ( Integer , nullable = False )
teams_count = Column ( Integer , nullable = False )
working_on_next_state = Column ( Boolean , nullable = False , default = True )
working_on_next_state = Column ( Boolean , nullable = False , default = True )
current_round = Column ( Integer , default = - 1 )
current_round = Column ( Integer , default = - 1 )
def get_configuration ( self ) :
return get_big_data ( self . configuration )
def current_state ( self , none_if_working = True ) - > Optional [ ' State ' ] :
def current_state ( self , none_if_working = True ) - > Optional [ ' State ' ] :
if none_if_working and self . working_on_next_state is True :
if none_if_working and self . working_on_next_state is True :
return None
return None
return get_session ( ) . query ( State ) . filter_by ( game_id = self . game_id , round = self . current_round ) . order_by ( State . round . desc ( ) ) . first ( )
return get_session ( ) . query ( State ) . filter_by ( game_id = self . game_id , round = self . current_round ) . one_or_none ( )
def get_logic ( self ) - > ' hra.game.Logic ' :
def get_logic ( self ) - > ' hra.game.Logic ' :
return hra . game . logic_by_mode [ self . game_mode ] ( self . teams_count , self . configuration )
return hra . game . logic_by_mode [ self . game_mode ] ( self . teams_count , self . get_ configuration( ) )
def lock ( self ) - > ' Game ' :
def lock ( self ) - > ' Game ' :
ses = get_session ( )
ses = get_session ( )
ses . expire_all ( )
ses . expire_all ( )
return ses . query ( Game ) . filter_by ( game_id = self . game_id ) . with_for_update ( ) . first ( )
return ses . query ( Game ) . filter_by ( game_id = self . game_id ) . with_for_update ( ) . first ( )
def print ( self ) :
def print ( self ) - > str :
return f " { self . game_id } : <name> "
name = self . name
if not name :
name = " <name> "
return f " { self . game_id } : { name } "
class Team ( Base ) :
class Team ( Base ) :
@ -108,9 +169,13 @@ class Team(Base):
class State ( Base ) :
class State ( Base ) :
__tablename__ = ' states '
__tablename__ = ' states '
create_time = Column ( DateTime , nullable = False )
game_id = Column ( Integer , ForeignKey ( ' games.game_id ' ) , primary_key = True , nullable = False )
game_id = Column ( Integer , ForeignKey ( ' games.game_id ' ) , primary_key = True , nullable = False )
round = Column ( Integer , primary_key = True )
round = Column ( Integer , primary_key = True )
state = Column ( JSONB )
state = Column ( Integer , ForeignKey ( ' bigdata.id ' ) , nullable = False )
def get_state ( self ) :
return get_big_data ( self . state )
game = relationship ( ' Game ' , primaryjoin = ' State.game_id == Game.game_id ' )
game = relationship ( ' Game ' , primaryjoin = ' State.game_id == Game.game_id ' )
@ -120,6 +185,11 @@ class Move(Base):
game_id = Column ( Integer , ForeignKey ( ' games.game_id ' ) , primary_key = True , nullable = False )
game_id = Column ( Integer , ForeignKey ( ' games.game_id ' ) , primary_key = True , nullable = False )
round = Column ( Integer , primary_key = True )
round = Column ( Integer , primary_key = True )
team_id = Column ( Integer , primary_key = True )
team_id = Column ( Integer , primary_key = True )
move = Column ( JSONB )
move = Column ( Integer , ForeignKey ( ' bigdata.id ' ) , nullable = True )
def get_move ( self ) :
if self . move is None :
return None
return get_big_data ( self . move )
game = relationship ( ' Game ' , primaryjoin = ' Move.game_id == Game.game_id ' )
game = relationship ( ' Game ' , primaryjoin = ' Move.game_id == Game.game_id ' )