Skip to content

Initialising the Database

Once the containers are running, the STELLA Server database must be initialized before the application can be used.

The initialization process creates the required database schema and populates the database with initial data.

Setting up Roles and Systems

Before initializing the database, the experimental systems and default users must be registered in the application seed logic.

This is done in the seed_db function located in web/app/commands.py

Step 1: Locate the seed_db Function

Open the commands.py file and find the seed_db function. This function is responsible for creating initial database entries such as roles, users and systems when the database is first initialized.

Step 2: Define User Roles

Ensure that the required roles are created. STELLA distinguishes between administrators, site owners and experiment participants.

Add or verify the following role definitions inside seed_db:

admin_role = Role(name="Admin")
participant_role = Role(name="Participant")
site_role = Role(name="Site")

Step 3: Create Default Users

Next, define the initial users associated with each role. These users are created using credentials provided via environment variables. Fallback values are used only if the variables are not set.

user_site_a = User(
    username="site",
    email=os.environ.get("SITE_MAIL") or "site@stella-project.org",
    role=site_role,
    password=os.environ.get("SITE_PASS") or "pass",
)

Each user represents a specific actor in the STELLA ecosystem:

  • Admin users manage the overall platform
  • Participant users contribute experimental systems
  • Site users represent deployed STELLA App installations

Step 4: Persist Roles and Users

Finally, add all created roles and users to the database session and commit them:

db.session.add_all(
    [
        admin_role,
        participant_role,
        site_role,
        user_admin,
        user_part_a,
        user_part_b,
        user_site_a,
    ]
)

These entries will be persisted when the database is initialized and seeded.

Step 5: Register Experimental Systems

In addition to users and roles, the STELLA App requires all experimental systems (rankers and recommenders) to be registered in the database before the application can be used.

Each experimental system is represented by a System database model. A system typically defines:

  • its operational status
  • a unique system name
  • the participant who contributed it
  • its type (RANK or REC)
  • how it was submitted (e.g. Docker-based)
  • a reference URL to its implementation
  • the site at which it is deployed
  • the submission date

Example system definitions:

# GESIS demo systems

gesis_rank_pyserini = System(
    status="running",
    name="gesis_rank_pyserini",
    participant_id=user_part_a.id,
    type="RANK",
    submitted="DOCKER",
    url="https://github.com/stella-project/gesis_rank_pyserini",
    site=user_site_a.id,
    submission_date=datetime.date(2019, 6, 10),
)

gesis_rank_pyserini_base = System(
    status="running",
    name="gesis_rank_pyserini_base",
    participant_id=user_part_a.id,
    type="RANK",
    submitted="DOCKER",
    url="https://github.com/stella-project/gesis_rank_pyserini_base",
    site=user_site_a.id,
    submission_date=datetime.date(2019, 6, 10),
)

gesis_rec_pyserini = System(
    status="running",
    name="gesis_rec_pyserini",
    participant_id=user_part_a.id,
    type="REC",
    submitted="DOCKER",
    url="https://github.com/stella-project/gesis_rec_pyserini",
    site=user_site_a.id,
    submission_date=datetime.date(2019, 6, 10),
)

gesis_rec_pyterrier = System(
    status="running",
    name="gesis_rec_pyterrier",
    participant_id=user_part_a.id,
    type="REC",
    submitted="DOCKER",
    url="https://github.com/stella-project/gesis_rec_pyterrier",
    site=user_site_a.id,
    submission_date=datetime.date(2019, 6, 10),
)

Each system must have a unique name, as this identifier is later referenced by the STELLA App configuration (for example in SYSTEMS_CONFIG).

Initialize and Seed the Database

Run the following commands to initialize and populate the database:

sudo docker exec -it stella-dev-server-1 flask init-db
sudo docker exec -it stella-dev-server-1 flask seed-db

Command Implementation

The database initialization and seeding logic is implemented in the following module:

init_db()

Use this function to setup a database with set of pre-registered users.

Source code in server/web/app/commands.py
def init_db():
    """Use this function to setup a database with set of pre-registered users."""
    db.create_all()

seed_db()

Use this function to setup a database with set of pre-registered users.

Source code in server/web/app/commands.py
def seed_db():
    """Use this function to setup a database with set of pre-registered users."""

    admin_role = Role(name="Admin")
    participant_role = Role(name="Participant")
    site_role = Role(name="Site")

    user_admin = User(
        username="stella-admin",
        email=os.environ.get("ADMIN_MAIL") or "admin@stella-project.org",
        role=admin_role,
        password=os.environ.get("ADMIN_PASS") or "pass",
    )

    user_part_a = User(
        username="LIVIVO",
        email=os.environ.get("EXPERIMENTER_MAIL") or "experimenter@stella-project.org",
        role=participant_role,
        password=os.environ.get("EXPERIMENTER_PASS") or "pass",
    )

    user_part_b = User(
        username="GESIS",
        email=os.environ.get("EXPERIMENTER_MAIL_REC")
        or "experimenter_rec@stella-project.org",
        role=participant_role,
        password=os.environ.get("EXPERIMENTER_PASS") or "pass",
    )

    user_site_a = User(
        username="site",
        email=os.environ.get("SITE_MAIL") or "site@stella-project.org",
        role=site_role,
        password=os.environ.get("SITE_PASS") or "pass",
    )

    db.session.add_all(
        [
            admin_role,
            participant_role,
            site_role,
            user_admin,
            user_part_a,
            user_part_b,
            user_site_a,
        ]
    )

    db.session.commit()

    # gesis demo systems:
    gesis_rank_pyserini = System(
        status="running",
        name="gesis_rank_pyserini",
        participant_id=user_part_a.id,
        type="RANK",
        submitted="DOCKER",
        url="https://github.com/stella-project/gesis_rank_pyserini",
        site=user_site_a.id,
        submission_date=datetime.date(2019, 6, 10),
    )

    gesis_rank_pyserini_base = System(
        status="running",
        name="gesis_rank_pyserini_base",
        participant_id=user_part_a.id,
        type="RANK",
        submitted="DOCKER",
        url="https://github.com/stella-project/gesis_rank_pyserini_base",
        site=user_site_a.id,
        submission_date=datetime.date(2019, 6, 10),
    )

    gesis_rec_pyserini = System(
        status="running",
        name="gesis_rec_pyserini",
        participant_id=user_part_a.id,
        type="REC",
        submitted="DOCKER",
        url="https://github.com/stella-project/gesis_rec_pyserini",
        site=user_site_a.id,
        submission_date=datetime.date(2019, 6, 10),
    )

    gesis_rec_pyterrier = System(
        status="running",
        name="gesis_rec_pyterrier",
        participant_id=user_part_a.id,
        type="REC",
        submitted="DOCKER",
        url="https://github.com/stella-project/gesis_rec_pyterrier",
        site=user_site_a.id,
        submission_date=datetime.date(2019, 6, 10),
    )


    db.session.add_all(
        [
            gesis_rank_pyserini,
            gesis_rank_pyserini_base,
            gesis_rec_pyserini,
            gesis_rec_pyterrier
        ]
    )

    db.session.commit()

Application Entry Point (Reference)

For context, the Flask application entry point used by these commands is defined here:

configure_logger(app)

Configure loggers.

Source code in app/web/app/app.py
def configure_logger(app):
    """Configure loggers."""
    if app.config["DEBUG"]:
        app.logger.handlers.clear()
        handler = logging.StreamHandler()
        handler.setLevel(logging.DEBUG)
        log_format = "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
        formatter = logging.Formatter(log_format)
        handler.setFormatter(formatter)
        app.logger.addHandler(handler)

        app.logger.info("Logging setup for debug complete.")

    else:
        del app.logger.handlers[:]
        gunicorn_logger = logging.getLogger("gunicorn.error")
        app.logger.handlers = gunicorn_logger.handlers
        app.logger.setLevel(gunicorn_logger.level)

        app.logger.info("Logging setup complete.")

create_app(config_name=None)

Create application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.

:param config_object: The configuration object or dictionary to use.

Source code in app/web/app/app.py
def create_app(config_name=None):
    """Create application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.

    :param config_object: The configuration object or dictionary to use.
    """
    config_name_environment = os.getenv("FLASK_CONFIG", "test")
    app = Flask(__name__.split(".")[0])

    # Load the default configuration
    app.config.from_object(config[config_name_environment])

    # If a test config or other config object is provided, load it
    if config_name:
        if isinstance(config_name, dict):
            app.config.update(config_name)
        else:
            app.config.from_object(config[config_name])

    if app.config["DEBUG"] and app.config["SENDFEEDBACK"] and not app.config["TESTING"]:
        print("Initializing and starting scheduler in app factory process")
        scheduler.init_app(app)
        scheduler.start()
        print("Scheduler started in app factory process")

    configure_logger(app)
    register_extensions(app)
    register_blueprints(app)
    register_commands(app)

    swagger_config = {
        "headers": [],
        "specs": [
            {
                "endpoint": "apispec",
                "route": "/apispec.json",
                "rule_filter": lambda rule: True,
                "model_filter": lambda tag: True,
            }
        ],
        "static_url_path": "/flasgger_static",
        "swagger_ui": True,
        "specs_route": "/docs/",
    }

    Swagger(app, config=swagger_config, template={
        "info": {
            "title": "STELLA API",
            "description": """API documentation for STELLA endpoints.
                    **Base URL:** /stella/api/v1
                    All endpoints are prefixed with this base path, except `/proxy`, which operates at the root level.
                    """,
            "version": "1.0.0",
        },
        "servers": [
        {"url": "/stella/api/v1", "description": "STELLA API base URL"},
        {"url": "/", "description": "Root-level proxy"},
    ],
    })

    return app

register_blueprints(app)

Register Flask blueprints.

Source code in app/web/app/app.py
def register_blueprints(app):
    """Register Flask blueprints."""
    app.register_blueprint(main_blueprint)
    app.register_blueprint(api_blueprint, url_prefix="/stella/api/v1")
    return None

register_commands(app)

Register Click commands.

Source code in app/web/app/app.py
def register_commands(app):
    """Register Click commands."""
    app.cli.add_command(init_db_command)
    app.cli.add_command(seed_db_command)
    app.cli.add_command(index_systems)

register_extensions(app)

Register Flask extensions.

Source code in app/web/app/app.py
def register_extensions(app):
    """Register Flask extensions."""
    db.init_app(app)
    migrate.init_app(app, db)
    bootstrap.init_app(app)
    return app