app.model.strategy_definition module

Persistent model for user-authored strategies.

The legacy engine (see app.engine) loads BaseStrategy subclasses discovered at import time. A second path covers strategies expressed as a JSON AST and stored per-tenant in Postgres, so that game designers can create or extend strategies from the dashboard without touching Python code.

This model only stores the definition. Execution lives in the DSL interpreter. At this layer astJson is opaque to the engine and rows are addressable via the custom:<id> prefix on Games.strategyId / Tasks.strategyId (see the compat layer in app.services.strategy_service).

class app.model.strategy_definition.StrategyDefinitionType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: str, Enum

Discriminator describing how the row should be executed.

  • BUILT_IN – placeholder for promoting a registry class into the DB, currently unused but kept so the column is forward-compatible.

  • DSL_EXTEND – runs a built-in parent and wraps it with pre/post rules expressed in the AST.

  • DSL_FULL – fully replaces the parent; only the AST runs.

class app.model.strategy_definition.StrategyDefinitionStatus(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: str, Enum

Lifecycle states. Only PUBLISHED rows are eligible to execute in production; DRAFT and ARCHIVED are visible from the editor but ignored by the engine.

class app.model.strategy_definition.StrategyDefinition(*, id=<factory>, created_at=<factory>, updated_at=<factory>, apiKey_used=None, oauth_user_id=None, realmId=None, name, description=None, type, parentStrategyId=None, astJson=None, blocklyXml=None, version, status, createdBy=None, publishedAt=None, experimentTag=None)[source]

Bases: BaseModel

Tenant-scoped strategy authored from the dashboard.

Versioning rules (enforced by StrategyDefinitionService):
  • Editing a PUBLISHED row never overwrites it. A new row with version + 1 and status=DRAFT is created instead.

  • Publishing a draft transitions its status to PUBLISHED and archives any sibling row in the same (realmId, name) family that was previously published.

  • The (realmId, name, version) triple is unique so that the history can be walked deterministically.

Parameters:
  • id (Annotated[str, FieldInfoMetadata(primary_key=PydanticUndefined, nullable=PydanticUndefined, foreign_key=PydanticUndefined, ondelete=PydanticUndefined, unique=PydanticUndefined, index=PydanticUndefined, sa_type=UUID(), sa_column=PydanticUndefined, sa_column_args=PydanticUndefined, sa_column_kwargs={'primary_key': True, 'index': True})])

  • created_at (Annotated[datetime, FieldInfoMetadata(primary_key=PydanticUndefined, nullable=PydanticUndefined, foreign_key=PydanticUndefined, ondelete=PydanticUndefined, unique=PydanticUndefined, index=PydanticUndefined, sa_type=DateTime(timezone=True), sa_column=PydanticUndefined, sa_column_args=PydanticUndefined, sa_column_kwargs={'server_default': <sqlalchemy.sql.functions.now at 0x7fa8900484a0; now>})])

  • updated_at (Annotated[datetime, FieldInfoMetadata(primary_key=PydanticUndefined, nullable=PydanticUndefined, foreign_key=PydanticUndefined, ondelete=PydanticUndefined, unique=PydanticUndefined, index=PydanticUndefined, sa_type=DateTime(timezone=True), sa_column=PydanticUndefined, sa_column_args=PydanticUndefined, sa_column_kwargs={'server_default': <sqlalchemy.sql.functions.now at 0x7fa88fa2a840; now>, 'onupdate': <sqlalchemy.sql.functions.now at 0x7fa88fa2ad20; now>})])

  • apiKey_used (Annotated[str | None, FieldInfoMetadata(primary_key=PydanticUndefined, nullable=True, foreign_key=apikey.apiKey, ondelete=PydanticUndefined, unique=PydanticUndefined, index=PydanticUndefined, sa_type=PydanticUndefined, sa_column=PydanticUndefined, sa_column_args=PydanticUndefined, sa_column_kwargs=PydanticUndefined)])

  • oauth_user_id (Annotated[str | None, FieldInfoMetadata(primary_key=PydanticUndefined, nullable=True, foreign_key=oauthusers.provider_user_id, ondelete=PydanticUndefined, unique=PydanticUndefined, index=PydanticUndefined, sa_type=PydanticUndefined, sa_column=PydanticUndefined, sa_column_args=PydanticUndefined, sa_column_kwargs=PydanticUndefined)])

model_config: ClassVar[ConfigDict] = {'from_attributes': True, 'read_from_attributes': True, 'read_with_orm_mode': True, 'registry': PydanticUndefined, 'table': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].