Skip to content

models

Module with database tables definitions.

Helper classes and utility functions for data management are defined here.

Modules:

Name Description
auth

Module with classes and functions used for authentication and password handling.

Classes:

Name Description
Credentials

Table with users credentials, used only if basic authentication is active.

Encrypted

Allows storing and retrieving password hashes using PasswordHash.

Flags

Table with global flags used by Data-Lunch.

Menu

Table with menu items.

Orders

Table with items that belongs to an order.

Password

Allows storing and retrieving password hashes using PasswordHash.

PrivilegedUsers

Table with user that have privileges (normal users and admin).

Stats

Table with number of users that ate a lunch, grouped by guest type.

Users

Table with users that placed an order.

Functions:

Name Description
create_database

Function to create the database through SQLAlchemy models.

create_engine

Factory function for SQLAlchemy engine.

create_exclusive_session

Factory function for database exclusive session.

create_session

Factory function for database session.

get_db_dialect

Return database type (postgresql, sqlite, etc.) based on the database object passed as input.

get_flag

Get the value of a flag.

session_add_with_upsert

Use an upsert statement for postgresql to add a new record to a table,

set_flag

Set a key,value pair inside flag table.

set_sqlite_pragma

Force foreign key constraints for sqlite connections.

Attributes:

Name Type Description
Data DeclarativeMeta

SQLAlchemy declarative base.

SCHEMA str

Schema name from environment (may be overridden by configuration files).

log Logger

Module logger.

metadata_obj MetaData

Database metadata (SQLAlchemy).

Data module-attribute

Data: DeclarativeMeta = declarative_base(
    metadata=metadata_obj
)

SQLAlchemy declarative base.

SCHEMA module-attribute

SCHEMA: str = get('DATA_LUNCH_DB_SCHEMA', None)

Schema name from environment (may be overridden by configuration files).

log module-attribute

log: Logger = getLogger(__name__)

Module logger.

metadata_obj module-attribute

metadata_obj: MetaData = MetaData(schema=SCHEMA)

Database metadata (SQLAlchemy).

Credentials

Bases: Data

Table with users credentials, used only if basic authentication is active.

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__tablename__ str

Name of the table.

password_encrypted Column

Encryped password.

password_hash Column

Hashed password.

user Column

Username.

Source code in dlunch/models.py
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
class Credentials(Data):
    """Table with users credentials, used only if basic authentication is active."""

    __tablename__: str = "credentials"
    """Name of the table."""
    user: Column = Column(
        String(100),
        primary_key=True,
        sqlite_on_conflict_primary_key="REPLACE",
    )
    """Username."""
    password_hash: Column = Column(Password(150), unique=False, nullable=False)
    """Hashed password."""
    password_encrypted: Column = Column(
        Encrypted(150),
        unique=False,
        nullable=True,
        default=None,
        server_default=None,
    )
    """Encryped password.

    Used only if basic authentication and guest users are both active."""

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<CREDENTIAL:{self.user}>"

    @validates("password_hash")
    def _validate_password(
        self, key: str, password: str
    ) -> auth.PasswordHash | None:
        """Function that validate password input.

        It converts string to auth.PasswordHash if necessary.

        Args:
            key (str): validated attribute name.
            password (str): hashed password to be validated.

        Returns:
            auth.PasswordHash | None: validated hashed password.
        """
        return getattr(type(self), key).type.validator(password)

    @validates("password_encrypted")
    def _validate_encrypted(
        self, key: str, password: str
    ) -> auth.PasswordEncrypt | None:
        """Function that validate encrypted input.

        It converts string to auth.PasswordEncrypt if necessary.

        Args:
            key (str): validated attribute name.
            password (str): encrypted password to be validated.

        Returns:
            auth.PasswordEncrypt | None: validated encrypted password.
        """
        return getattr(type(self), key).type.validator(password)

__tablename__ class-attribute instance-attribute

__tablename__: str = 'credentials'

Name of the table.

password_encrypted class-attribute instance-attribute

password_encrypted: Column = Column(
    Encrypted(150),
    unique=False,
    nullable=True,
    default=None,
    server_default=None,
)

Encryped password.

Used only if basic authentication and guest users are both active.

password_hash class-attribute instance-attribute

password_hash: Column = Column(
    Password(150), unique=False, nullable=False
)

Hashed password.

user class-attribute instance-attribute

user: Column = Column(
    String(100),
    primary_key=True,
    sqlite_on_conflict_primary_key="REPLACE",
)

Username.

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
804
805
806
807
808
809
810
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<CREDENTIAL:{self.user}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

Encrypted

Bases: TypeDecorator

Allows storing and retrieving password hashes using PasswordHash.

Methods:

Name Description
process_bind_param

Ensure the value is a PasswordEncrypt and then return the encrypted password.

process_result_value

Convert the hash to a PasswordEncrypt, if it's non-NULL.

validator

Provides a validator/converter used by @validates.

Attributes:

Name Type Description
impl String

Base column implementation.

Source code in dlunch/models.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
class Encrypted(TypeDecorator):
    """Allows storing and retrieving password hashes using PasswordHash."""

    impl: String = String
    """Base column implementation."""

    def process_bind_param(
        self, value: auth.PasswordEncrypt | str | None, dialect
    ) -> str | None:
        """Ensure the value is a PasswordEncrypt and then return the encrypted password.

        Args:
            value (auth.PasswordEncrypt | str | None): input value (plain password or encrypted or `None` if empty)
            dialect (Any): dialect (not used).

        Returns:
            str | None: encrypted password or `None` if empty.
        """
        converted_value = self._convert(value)
        if converted_value:
            return converted_value.encrypted_password
        else:
            return None

    def process_result_value(
        self, value: str | None, dialect
    ) -> auth.PasswordEncrypt | None:
        """Convert the hash to a PasswordEncrypt, if it's non-NULL.

        Args:
            value (str | None): input value (plain password or encrypted or `None` if empty)
            dialect (Any): dialect (not used).

        Returns:
            auth.PasswordEncrypt | None: encrypted password as object or `None` (if nothing is passed as value).
        """
        if value is not None:
            return auth.PasswordEncrypt(value)

    def validator(
        self, password: auth.PasswordEncrypt | str | None
    ) -> auth.PasswordEncrypt | None:
        """Provides a validator/converter used by @validates.

        Args:
            password (auth.PasswordEncrypt | str | None): input value (plain password or encrypted or `None` if empty)

        Returns:
            auth.PasswordEncrypt | None: encrypted password as object or `None` (if nothing is passed as value).
        """
        return self._convert(password)

    def _convert(
        self, value: auth.PasswordEncrypt | str | None
    ) -> auth.PasswordEncrypt | None:
        """Returns a PasswordEncrypt from the given string.

        PasswordEncrypt instances or None values will return unchanged.
        Strings will be encrypted and the resulting PasswordEncrypt returned.
        Any other input will result in a TypeError.

        Args:
            value (auth.PasswordEncrypt | str | None): input value (plain password or encrypted or `None` if empty)

        Raises:
            TypeError: unknown type.

        Returns:
            auth.PasswordEncrypt | None: encrypted password as object or `None` (if nothing is passed as value).
        """
        if isinstance(value, auth.PasswordEncrypt):
            return value
        elif isinstance(value, str):
            return auth.PasswordEncrypt.from_str(value)
        elif value is not None:
            raise TypeError(
                f"Cannot initialize PasswordEncrypt from type '{type(value)}'"
            )

        # Reached only if value is None
        return None

impl class-attribute instance-attribute

impl: String = String

Base column implementation.

process_bind_param

process_bind_param(
    value: PasswordEncrypt | str | None, dialect
) -> str | None

Ensure the value is a PasswordEncrypt and then return the encrypted password.

Parameters:

Name Type Description Default
value PasswordEncrypt | str | None

input value (plain password or encrypted or None if empty)

required
dialect Any

dialect (not used).

required

Returns:

Type Description
str | None

encrypted password or None if empty.

Source code in dlunch/models.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def process_bind_param(
    self, value: auth.PasswordEncrypt | str | None, dialect
) -> str | None:
    """Ensure the value is a PasswordEncrypt and then return the encrypted password.

    Args:
        value (auth.PasswordEncrypt | str | None): input value (plain password or encrypted or `None` if empty)
        dialect (Any): dialect (not used).

    Returns:
        str | None: encrypted password or `None` if empty.
    """
    converted_value = self._convert(value)
    if converted_value:
        return converted_value.encrypted_password
    else:
        return None

process_result_value

process_result_value(
    value: str | None, dialect
) -> PasswordEncrypt | None

Convert the hash to a PasswordEncrypt, if it's non-NULL.

Parameters:

Name Type Description Default
value str | None

input value (plain password or encrypted or None if empty)

required
dialect Any

dialect (not used).

required

Returns:

Type Description
PasswordEncrypt | None

encrypted password as object or None (if nothing is passed as value).

Source code in dlunch/models.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def process_result_value(
    self, value: str | None, dialect
) -> auth.PasswordEncrypt | None:
    """Convert the hash to a PasswordEncrypt, if it's non-NULL.

    Args:
        value (str | None): input value (plain password or encrypted or `None` if empty)
        dialect (Any): dialect (not used).

    Returns:
        auth.PasswordEncrypt | None: encrypted password as object or `None` (if nothing is passed as value).
    """
    if value is not None:
        return auth.PasswordEncrypt(value)

validator

validator(
    password: PasswordEncrypt | str | None,
) -> PasswordEncrypt | None

Provides a validator/converter used by @validates.

Parameters:

Name Type Description Default
password PasswordEncrypt | str | None

input value (plain password or encrypted or None if empty)

required

Returns:

Type Description
PasswordEncrypt | None

encrypted password as object or None (if nothing is passed as value).

Source code in dlunch/models.py
210
211
212
213
214
215
216
217
218
219
220
221
def validator(
    self, password: auth.PasswordEncrypt | str | None
) -> auth.PasswordEncrypt | None:
    """Provides a validator/converter used by @validates.

    Args:
        password (auth.PasswordEncrypt | str | None): input value (plain password or encrypted or `None` if empty)

    Returns:
        auth.PasswordEncrypt | None: encrypted password as object or `None` (if nothing is passed as value).
    """
    return self._convert(password)

Flags

Bases: Data

Table with global flags used by Data-Lunch.

'No more orders' flag and guest override flags are stored here.

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

clear_guest_override

Clear 'guest_override' flags and return deleted rows

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__tablename__ str

Name of the table.

id Column

Flag ID (name).

value Column

Flag value.

Source code in dlunch/models.py
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
class Flags(Data):
    """Table with global flags used by Data-Lunch.

    'No more orders' flag and guest override flags are stored here.
    """

    __tablename__: str = "flags"
    """Name of the table."""
    id: Column = Column(
        String(50),
        primary_key=True,
        nullable=False,
        sqlite_on_conflict_primary_key="REPLACE",
    )
    """Flag ID (name)."""
    value: Column = Column(Boolean, nullable=False)
    """Flag value."""

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def clear_guest_override(self, config: DictConfig) -> int:
        """Clear 'guest_override' flags and return deleted rows

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(
                delete(self).where(self.id.like("%_guest_override"))
            )
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows (guest override) from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<FLAG:{self.id} - value:{self.value}>"

__tablename__ class-attribute instance-attribute

__tablename__: str = 'flags'

Name of the table.

id class-attribute instance-attribute

id: Column = Column(
    String(50),
    primary_key=True,
    nullable=False,
    sqlite_on_conflict_primary_key="REPLACE",
)

Flag ID (name).

value class-attribute instance-attribute

value: Column = Column(Boolean, nullable=False)

Flag value.

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
659
660
661
662
663
664
665
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<FLAG:{self.id} - value:{self.value}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

clear_guest_override classmethod

clear_guest_override(config: DictConfig) -> int

Clear 'guest_override' flags and return deleted rows

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
@classmethod
def clear_guest_override(self, config: DictConfig) -> int:
    """Clear 'guest_override' flags and return deleted rows

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(
            delete(self).where(self.id.like("%_guest_override"))
        )
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows (guest override) from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

Menu

Bases: Data

Table with menu items.

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__tablename__ str

Name of the table.

id Column

Menu item ID.

item Column

Item name.

orders DeclarativeMeta

Orders connected to each menu item.

Source code in dlunch/models.py
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
class Menu(Data):
    """Table with menu items."""

    __tablename__: str = "menu"
    """Name of the table."""
    id: Column = Column(
        Integer, Identity(start=1, cycle=True), primary_key=True
    )
    """Menu item ID."""
    item: Column = Column(String(250), unique=False, nullable=False)
    """Item name."""
    orders: DeclarativeMeta = relationship(
        "Orders",
        back_populates="menu_item",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    """Orders connected to each menu item."""

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<MENU_ITEM:{self.id} - {self.item}>"

__tablename__ class-attribute instance-attribute

__tablename__: str = 'menu'

Name of the table.

id class-attribute instance-attribute

id: Column = Column(
    Integer, Identity(start=1, cycle=True), primary_key=True
)

Menu item ID.

item class-attribute instance-attribute

item: Column = Column(
    String(250), unique=False, nullable=False
)

Item name.

orders class-attribute instance-attribute

orders: DeclarativeMeta = relationship(
    "Orders",
    back_populates="menu_item",
    cascade="all, delete-orphan",
    passive_deletes=True,
)

Orders connected to each menu item.

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
317
318
319
320
321
322
323
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<MENU_ITEM:{self.id} - {self.item}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

Orders

Bases: Data

Table with items that belongs to an order.

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__tablename__ str

Name of the table.

id Column

Order ID.

menu_item DeclarativeMeta

Menu items connected to each order (see menu table).

menu_item_id Column

ID of the menu item included in the order.

note Column

Note field attached to the order.

user Column

User that placed the order.

user_record DeclarativeMeta

User connected to this order.

Source code in dlunch/models.py
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
class Orders(Data):
    """Table with items that belongs to an order."""

    __tablename__: str = "orders"
    """Name of the table."""
    id: Column = Column(
        Integer, Identity(start=1, cycle=True), primary_key=True
    )
    """Order ID."""
    user: Column = Column(
        String(100),
        ForeignKey("users.id", ondelete="CASCADE"),
        index=True,
        nullable=False,
    )
    """User that placed the order."""
    user_record: DeclarativeMeta = relationship(
        "Users", back_populates="orders", uselist=False
    )
    """User connected to this order."""
    menu_item_id: Column = Column(
        Integer,
        ForeignKey("menu.id", ondelete="CASCADE"),
        nullable=False,
    )
    """ID of the menu item included in the order."""
    menu_item: DeclarativeMeta = relationship("Menu", back_populates="orders")
    """Menu items connected to each order (see `menu` table)."""
    note: Column = Column(String(300), unique=False, nullable=True)
    """Note field attached to the order."""

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<ORDER:{self.user}, {self.menu_item.item}>"

__tablename__ class-attribute instance-attribute

__tablename__: str = 'orders'

Name of the table.

id class-attribute instance-attribute

id: Column = Column(
    Integer, Identity(start=1, cycle=True), primary_key=True
)

Order ID.

menu_item class-attribute instance-attribute

menu_item: DeclarativeMeta = relationship(
    "Menu", back_populates="orders"
)

Menu items connected to each order (see menu table).

menu_item_id class-attribute instance-attribute

menu_item_id: Column = Column(
    Integer,
    ForeignKey("menu.id", ondelete="CASCADE"),
    nullable=False,
)

ID of the menu item included in the order.

note class-attribute instance-attribute

note: Column = Column(
    String(300), unique=False, nullable=True
)

Note field attached to the order.

user class-attribute instance-attribute

user: Column = Column(
    String(100),
    ForeignKey("users.id", ondelete="CASCADE"),
    index=True,
    nullable=False,
)

User that placed the order.

user_record class-attribute instance-attribute

user_record: DeclarativeMeta = relationship(
    "Users", back_populates="orders", uselist=False
)

User connected to this order.

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
398
399
400
401
402
403
404
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<ORDER:{self.user}, {self.menu_item.item}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

Password

Bases: TypeDecorator

Allows storing and retrieving password hashes using PasswordHash.

Methods:

Name Description
process_bind_param

Ensure the value is a PasswordHash and then return its hash.

process_result_value

Convert the hash to a PasswordHash, if it's non-NULL.

validator

Provides a validator/converter used by @validates.

Attributes:

Name Type Description
impl String

Base column implementation.

Source code in dlunch/models.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
class Password(TypeDecorator):
    """Allows storing and retrieving password hashes using PasswordHash."""

    impl: String = String
    """Base column implementation."""

    def process_bind_param(
        self, value: auth.PasswordHash | str | None, dialect
    ) -> str:
        """Ensure the value is a PasswordHash and then return its hash.

        Args:
            value (auth.PasswordHash | str): input value (plain password or hash, or `None` if empty).
            dialect (Any): dialect (not used).

        Returns:
            str: password hash.
        """
        return self._convert(value).hashed_password

    def process_result_value(
        self, value: str | None, dialect
    ) -> auth.PasswordHash | None:
        """Convert the hash to a PasswordHash, if it's non-NULL.

        Args:
            value (str | None): password hash (or `None` if empty).
            dialect (Any): dialect (not used).

        Returns:
            auth.PasswordHash | None: hashed password as object or `None` (if nothing is passed as value).
        """
        if value is not None:
            return auth.PasswordHash(value)

    def validator(
        self, password: auth.PasswordHash | str | None
    ) -> auth.PasswordHash | None:
        """Provides a validator/converter used by @validates.

        Args:
            password (auth.PasswordHash | str | None): input value (plain password or hash or `None` if empty).

        Returns:
            auth.PasswordHash | None: hashed password as object or `None` (if nothing is passed as value).
        """
        return self._convert(password)

    def _convert(
        self, value: auth.PasswordHash | str | None
    ) -> auth.PasswordHash | None:
        """Returns a PasswordHash from the given string.

        PasswordHash instances or None values will return unchanged.
        Strings will be hashed and the resulting PasswordHash returned.
        Any other input will result in a TypeError.

        Args:
            value (auth.PasswordHash | str | None): input value (plain password or hash or `None` if empty).

        Raises:
            TypeError: unknown type.

        Returns:
            auth.PasswordHash | None: hashed password as object or `None` (if nothing is passed as value).
        """
        if isinstance(value, auth.PasswordHash):
            return value
        elif isinstance(value, str):
            return auth.PasswordHash.from_str(value)
        elif value is not None:
            raise TypeError(
                f"Cannot initialize PasswordHash from type '{type(value)}'"
            )

        # Reached only if value is None
        return None

impl class-attribute instance-attribute

impl: String = String

Base column implementation.

process_bind_param

process_bind_param(
    value: PasswordHash | str | None, dialect
) -> str

Ensure the value is a PasswordHash and then return its hash.

Parameters:

Name Type Description Default
value PasswordHash | str

input value (plain password or hash, or None if empty).

required
dialect Any

dialect (not used).

required

Returns:

Type Description
str

password hash.

Source code in dlunch/models.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def process_bind_param(
    self, value: auth.PasswordHash | str | None, dialect
) -> str:
    """Ensure the value is a PasswordHash and then return its hash.

    Args:
        value (auth.PasswordHash | str): input value (plain password or hash, or `None` if empty).
        dialect (Any): dialect (not used).

    Returns:
        str: password hash.
    """
    return self._convert(value).hashed_password

process_result_value

process_result_value(
    value: str | None, dialect
) -> PasswordHash | None

Convert the hash to a PasswordHash, if it's non-NULL.

Parameters:

Name Type Description Default
value str | None

password hash (or None if empty).

required
dialect Any

dialect (not used).

required

Returns:

Type Description
PasswordHash | None

hashed password as object or None (if nothing is passed as value).

Source code in dlunch/models.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def process_result_value(
    self, value: str | None, dialect
) -> auth.PasswordHash | None:
    """Convert the hash to a PasswordHash, if it's non-NULL.

    Args:
        value (str | None): password hash (or `None` if empty).
        dialect (Any): dialect (not used).

    Returns:
        auth.PasswordHash | None: hashed password as object or `None` (if nothing is passed as value).
    """
    if value is not None:
        return auth.PasswordHash(value)

validator

validator(
    password: PasswordHash | str | None,
) -> PasswordHash | None

Provides a validator/converter used by @validates.

Parameters:

Name Type Description Default
password PasswordHash | str | None

input value (plain password or hash or None if empty).

required

Returns:

Type Description
PasswordHash | None

hashed password as object or None (if nothing is passed as value).

Source code in dlunch/models.py
127
128
129
130
131
132
133
134
135
136
137
138
def validator(
    self, password: auth.PasswordHash | str | None
) -> auth.PasswordHash | None:
    """Provides a validator/converter used by @validates.

    Args:
        password (auth.PasswordHash | str | None): input value (plain password or hash or `None` if empty).

    Returns:
        auth.PasswordHash | None: hashed password as object or `None` (if nothing is passed as value).
    """
    return self._convert(password)

PrivilegedUsers

Bases: Data

Table with user that have privileges (normal users and admin).

If enabled guests are all the authenticated users that do not belong to this table (see config key auth.authorize_guest_users and basic_auth.guest_user)

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__tablename__ str

Name of the table.

admin Column

Admin flag (true if admin).

user Column

User name.

Source code in dlunch/models.py
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
class PrivilegedUsers(Data):
    """Table with user that have privileges (normal users and admin).

    If enabled guests are all the authenticated users that do not belong to this table
    (see config key `auth.authorize_guest_users` and `basic_auth.guest_user`)
    """

    __tablename__: str = "privileged_users"
    """Name of the table."""
    user: Column = Column(
        String(100),
        primary_key=True,
        sqlite_on_conflict_primary_key="REPLACE",
    )
    """User name."""
    admin: Column = Column(
        Boolean, nullable=False, default=False, server_default=sql_false()
    )
    """Admin flag (true if admin)."""

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<PRIVILEGED_USER:{self.id}>"

__tablename__ class-attribute instance-attribute

__tablename__: str = 'privileged_users'

Name of the table.

admin class-attribute instance-attribute

admin: Column = Column(
    Boolean,
    nullable=False,
    default=False,
    server_default=false(),
)

Admin flag (true if admin).

user class-attribute instance-attribute

user: Column = Column(
    String(100),
    primary_key=True,
    sqlite_on_conflict_primary_key="REPLACE",
)

User name.

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
730
731
732
733
734
735
736
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<PRIVILEGED_USER:{self.id}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

Stats

Bases: Data

Table with number of users that ate a lunch, grouped by guest type.

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__table_args__ tuple

Table arguments, used to create primary key (ON CONFLICT options for composite

__tablename__ str

Name of the table.

date Column

Day to which the statistics refers to.

guest Column

Different kind of guests are identified by the value defined in config files

hungry_people Column

Number of people that ate in a certain day.

Source code in dlunch/models.py
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
class Stats(Data):
    """Table with number of users that ate a lunch, grouped by guest type."""

    # Primary key handled with __table_args__ because ON CONFLICT for composite
    # primary key is available only with __table_args__
    __tablename__: str = "stats"
    """Name of the table."""
    __table_args__: tuple = (
        PrimaryKeyConstraint(
            "date", "guest", name="stats_pkey", sqlite_on_conflict="REPLACE"
        ),
    )
    """Table arguments, used to create primary key (ON CONFLICT options for composite
    primary key is available only with __table_args__)."""
    date: Column = Column(
        Date,
        nullable=False,
        server_default=func.current_date(),
    )
    """Day to which the statistics refers to."""
    guest: Column = Column(
        String(20),
        nullable=True,
        default="NotAGuest",
        server_default="NotAGuest",
    )
    """Different kind of guests are identified by the value defined in config files
    (see config key `panel.guest_types`).
    'NotAGuest' is the value used for locals.
    """
    hungry_people: Column = Column(
        Integer, nullable=False, default=0, server_default="0"
    )
    """Number of people that ate in a certain day.
    different kind of guests are identified by the value in guest column.
    """

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<STAT:{self.id} - HP:{self.hungry_people} - HG:{self.hungry_guests}>"

__table_args__ class-attribute instance-attribute

__table_args__: tuple = PrimaryKeyConstraint(
    "date",
    "guest",
    name="stats_pkey",
    sqlite_on_conflict="REPLACE",
)

Table arguments, used to create primary key (ON CONFLICT options for composite primary key is available only with table_args).

__tablename__ class-attribute instance-attribute

__tablename__: str = 'stats'

Name of the table.

date class-attribute instance-attribute

date: Column = Column(
    Date, nullable=False, server_default=current_date()
)

Day to which the statistics refers to.

guest class-attribute instance-attribute

guest: Column = Column(
    String(20),
    nullable=True,
    default="NotAGuest",
    server_default="NotAGuest",
)

Different kind of guests are identified by the value defined in config files (see config key panel.guest_types). 'NotAGuest' is the value used for locals.

hungry_people class-attribute instance-attribute

hungry_people: Column = Column(
    Integer, nullable=False, default=0, server_default="0"
)

Number of people that ate in a certain day. different kind of guests are identified by the value in guest column.

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
567
568
569
570
571
572
573
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<STAT:{self.id} - HP:{self.hungry_people} - HG:{self.hungry_guests}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

Users

Bases: Data

Table with users that placed an order.

Methods:

Name Description
__repr__

Simple object representation.

clear

Clear table and return deleted rows.

read_as_df

Read table as pandas DataFrame.

Attributes:

Name Type Description
__tablename__ str

Name of the table.

guest Column

Guest flag (true if guest).

id Column

User ID.

lunch_time Column

User selected lunch time.

orders DeclarativeMeta

Orders connected to each user.

takeaway Column

Takeaway flag (true if takeaway).

Source code in dlunch/models.py
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
class Users(Data):
    """Table with users that placed an order."""

    __tablename__: str = "users"
    """Name of the table."""
    id: Column = Column(
        String(100),
        primary_key=True,
        nullable=False,
    )
    """User ID."""
    guest: Column = Column(
        String(20),
        nullable=False,
        default="NotAGuest",
        server_default="NotAGuest",
    )
    """Guest flag (true if guest)."""
    lunch_time: Column = Column(String(7), index=True, nullable=False)
    """User selected lunch time."""
    takeaway: Column = Column(
        Boolean, nullable=False, default=False, server_default=sql_false()
    )
    """Takeaway flag (true if takeaway)."""
    orders: DeclarativeMeta = relationship(
        "Orders",
        back_populates="user_record",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    """Orders connected to each user."""

    @classmethod
    def clear(self, config: DictConfig) -> int:
        """Clear table and return deleted rows.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            int: deleted rows.
        """

        session = create_session(config)
        with session:
            # Clean menu
            num_rows_deleted = session.execute(delete(self))
            session.commit()
            log.info(
                f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
            )

        return num_rows_deleted.rowcount

    @classmethod
    def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
        """Read table as pandas DataFrame.

        Args:
            config (DictConfig): Hydra configuration dictionary.

        Returns:
            pd.DataFrame: dataframe with table content.
        """
        df = pd.read_sql_table(
            self.__tablename__,
            create_engine(config=config),
            schema=config.db.get("schema", SCHEMA),
            **kwargs,
        )

        return df

    def __repr__(self) -> str:
        """Simple object representation.

        Returns:
            str: string representation.
        """
        return f"<USER:{self.id}>"

__tablename__ class-attribute instance-attribute

__tablename__: str = 'users'

Name of the table.

guest class-attribute instance-attribute

guest: Column = Column(
    String(20),
    nullable=False,
    default="NotAGuest",
    server_default="NotAGuest",
)

Guest flag (true if guest).

id class-attribute instance-attribute

id: Column = Column(
    String(100), primary_key=True, nullable=False
)

User ID.

lunch_time class-attribute instance-attribute

lunch_time: Column = Column(
    String(7), index=True, nullable=False
)

User selected lunch time.

orders class-attribute instance-attribute

orders: DeclarativeMeta = relationship(
    "Orders",
    back_populates="user_record",
    cascade="all, delete-orphan",
    passive_deletes=True,
)

Orders connected to each user.

takeaway class-attribute instance-attribute

takeaway: Column = Column(
    Boolean,
    nullable=False,
    default=False,
    server_default=false(),
)

Takeaway flag (true if takeaway).

__repr__

__repr__() -> str

Simple object representation.

Returns:

Type Description
str

string representation.

Source code in dlunch/models.py
480
481
482
483
484
485
486
def __repr__(self) -> str:
    """Simple object representation.

    Returns:
        str: string representation.
    """
    return f"<USER:{self.id}>"

clear classmethod

clear(config: DictConfig) -> int

Clear table and return deleted rows.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
int

deleted rows.

Source code in dlunch/models.py
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
@classmethod
def clear(self, config: DictConfig) -> int:
    """Clear table and return deleted rows.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        int: deleted rows.
    """

    session = create_session(config)
    with session:
        # Clean menu
        num_rows_deleted = session.execute(delete(self))
        session.commit()
        log.info(
            f"deleted {num_rows_deleted.rowcount} rows from table '{self.__tablename__}'"
        )

    return num_rows_deleted.rowcount

read_as_df classmethod

read_as_df(config: DictConfig, **kwargs) -> DataFrame

Read table as pandas DataFrame.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
DataFrame

dataframe with table content.

Source code in dlunch/models.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
@classmethod
def read_as_df(self, config: DictConfig, **kwargs) -> pd.DataFrame:
    """Read table as pandas DataFrame.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        pd.DataFrame: dataframe with table content.
    """
    df = pd.read_sql_table(
        self.__tablename__,
        create_engine(config=config),
        schema=config.db.get("schema", SCHEMA),
        **kwargs,
    )

    return df

create_database

create_database(
    config: DictConfig, add_basic_auth_users=False
) -> None

Function to create the database through SQLAlchemy models.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required
add_basic_auth_users bool

set to true to add admin and guest users. These users are of interest only if 'basic authentication' is selected. Defaults to False.

False
Source code in dlunch/models.py
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
def create_database(config: DictConfig, add_basic_auth_users=False) -> None:
    """Function to create the database through SQLAlchemy models.

    Args:
        config (DictConfig): Hydra configuration dictionary.
        add_basic_auth_users (bool, optional): set to true to add admin and guest users.
            These users are of interest only if 'basic authentication' is selected.
            Defaults to False.
    """
    # Create directory if missing
    log.debug("create 'shared_data' folder")
    pathlib.Path(config.db.shared_data_folder).mkdir(exist_ok=True)

    # In case the database is not ready use a retry mechanism
    @tenacity.retry(
        retry=tenacity.retry_if_exception_type(OperationalError),
        wait=tenacity.wait_fixed(config.db.create_retries.wait),
        stop=(
            tenacity.stop_after_delay(config.db.create_retries.stop.delay)
            | tenacity.stop_after_attempt(
                config.db.create_retries.stop.attempts
            )
        ),
    )
    def create_database_with_retries(config: DictConfig) -> None:
        engine = create_engine(config)
        Data.metadata.create_all(engine)

    # Create tables
    log.debug(f"attempt database creation: {config.db.attempt_creation}")
    if config.db.attempt_creation:
        create_database_with_retries(config)

        # Retries stats
        log.debug(
            f"create database attempts: {create_database_with_retries.retry.statistics}"
        )

    # If requested add users for basic auth (admin and guest)
    if add_basic_auth_users:
        log.debug("add basic auth standard users")
        # If no user exist create the default admin
        session = create_session(config)

        with session:
            # Check if admin exists
            if session.get(Credentials, "admin") is None:
                # Add authorization and credentials for admin
                auth.add_privileged_user(
                    user="admin",
                    is_admin=True,
                    config=config,
                )
                auth.add_user_hashed_password(
                    user="admin",
                    password="admin",
                    config=config,
                )
            # Check if guest exists
            if (
                session.get(Credentials, "guest") is None
            ) and config.basic_auth.guest_user:
                # Add only credentials for guest (guest users are not included
                # in privileged_users table)
                auth.add_user_hashed_password(
                    user="guest",
                    password="guest",
                    config=config,
                )

create_engine

create_engine(config: DictConfig) -> Engine

Factory function for SQLAlchemy engine.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
Engine

SQLAlchemy engine.

Source code in dlunch/models.py
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
def create_engine(config: DictConfig) -> Engine:
    """Factory function for SQLAlchemy engine.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        Engine: SQLAlchemy engine.
    """
    engine = hydra.utils.instantiate(config.db.engine)

    # Change schema with change_execution_options
    # If schema exist in config.db it will override the schema selected through
    # the environment variable
    if "schema" in config.db:
        engine.update_execution_options(
            schema_translate_map={SCHEMA: config.db.schema}
        )

    return engine

create_exclusive_session

create_exclusive_session(config: DictConfig) -> Session

Factory function for database exclusive session. Database is locked until the transaction is completed (to be used to avoid race conditions).

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
Session

SQLAlchemy exclusive session.

Note

This function is not used inside Data-Lunch code.

Source code in dlunch/models.py
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
def create_exclusive_session(config: DictConfig) -> Session:
    """Factory function for database exclusive session.
    Database is locked until the transaction is completed (to be used to avoid
    race conditions).

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        Session: SQLAlchemy exclusive session.

    Note:
        This function is not used inside Data-Lunch code.

    """
    engine = create_engine(config)

    # Alter begin statement
    @event.listens_for(engine, "connect")
    def do_connect(dbapi_connection, connection_record):
        # disable pysqlite's emitting of the BEGIN statement entirely.
        # also stops it from emitting COMMIT before any DDL.
        dbapi_connection.isolation_level = None

    @event.listens_for(engine, "begin")
    def do_begin(conn):
        # Emit exclusive BEGIN
        conn.exec_driver_sql("BEGIN EXCLUSIVE")

    session = Session(engine)

    return session

create_session

create_session(config: DictConfig) -> Session

Factory function for database session.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required

Returns:

Type Description
Session

SQLAlchemy session.

Source code in dlunch/models.py
933
934
935
936
937
938
939
940
941
942
943
944
945
def create_session(config: DictConfig) -> Session:
    """Factory function for database session.

    Args:
        config (DictConfig): Hydra configuration dictionary.

    Returns:
        Session: SQLAlchemy session.
    """
    engine = create_engine(config)
    session = Session(engine)

    return session

get_db_dialect

get_db_dialect(
    db_obj: Session | Connection | Connection,
) -> str

Return database type (postgresql, sqlite, etc.) based on the database object passed as input. If a session is passed, the database type is set based on an internal map (see models._DBTYPE_MAP).

Parameters:

Name Type Description Default
db_obj Session | Connection | Connection

session or connection object.

required

Raises:

Type Description
TypeError

db_obj shall be a session or a connection object.

Returns:

Type Description
str

database dialect.

Source code in dlunch/models.py
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
def get_db_dialect(
    db_obj: Session | ConnectionSqlite | ConnectionPostgresql,
) -> str:
    """Return database type (postgresql, sqlite, etc.) based on the database object passed as input.
    If a session is passed, the database type is set based on an internal map (see `models._DBTYPE_MAP`).

    Args:
        db_obj (Session | ConnectionSqlite | ConnectionPostgresql): session or connection object.

    Raises:
        TypeError: db_obj shall be a session or a connection object.

    Returns:
        str: database dialect.
    """
    if isinstance(db_obj, Session):
        dialect = db_obj.bind.dialect.name
    elif isinstance(db_obj, ConnectionSqlite) or isinstance(
        db_obj, ConnectionPostgresql
    ):
        module = db_obj.__class__.__module__.split(".", 1)[0]
        dialect = _MODULE_TO_DIALECT_MAP[module]
    else:
        raise TypeError("db_obj should be a session or connection object")

    return dialect

get_flag

get_flag(
    config: DictConfig,
    id: str,
    value_if_missing: bool | None = None,
) -> bool | None

Get the value of a flag. Optionally select the values to return if the flag is missing (default to None).

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required
id str

flag id (name).

required
value_if_missing bool | None

value to return if the flag does not exist. Defaults to None.

None

Returns:

Type Description
bool | None

flag value.

Source code in dlunch/models.py
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
def get_flag(
    config: DictConfig, id: str, value_if_missing: bool | None = None
) -> bool | None:
    """Get the value of a flag.
    Optionally select the values to return if the flag is missing (default to None).

    Args:
        config (DictConfig): Hydra configuration dictionary.
        id (str): flag id (name).
        value_if_missing (bool | None, optional): value to return if the flag does not exist. Defaults to None.

    Returns:
        bool | None: flag value.
    """

    session = create_session(config)
    flag = session.get(Flags, id)
    if flag is None:
        value = value_if_missing
    else:
        value = flag.value
    return value

session_add_with_upsert

session_add_with_upsert(
    session: Session,
    constraint: str,
    new_record: Stats | Flags,
) -> None

Use an upsert statement for postgresql to add a new record to a table, a simple session add otherwise.

Parameters:

Name Type Description Default
session Session

SQLAlchemy session object.

required
constraint str

constraint used for upsert (usually the primary key)

required
new_record Stats | Flags

table resord (valid tables are stats or flags)

required
Source code in dlunch/models.py
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
def session_add_with_upsert(
    session: Session, constraint: str, new_record: Stats | Flags
) -> None:
    """Use an upsert statement for postgresql to add a new record to a table,
    a simple session add otherwise.

    Args:
        session (Session): SQLAlchemy session object.
        constraint (str): constraint used for upsert (usually the primary key)
        new_record (Stats | Flags): table resord (valid tables are `stats` or `flags`)
    """
    # Use an upsert for postgresql (for sqlite an 'on conflict replace' is set
    # on table, so session.add is fine)
    if get_db_dialect(session) == "postgresql":
        insert_statement = postgresql_upsert(new_record.__table__).values(
            {
                column.name: getattr(new_record, column.name)
                for column in new_record.__table__.c
                if getattr(new_record, column.name, None) is not None
            }
        )
        upsert_statement = insert_statement.on_conflict_do_update(
            constraint=constraint,
            set_={
                column.name: getattr(insert_statement.excluded, column.name)
                for column in insert_statement.excluded
            },
        )
        session.execute(upsert_statement)
    else:
        session.add(new_record)

set_flag

set_flag(config: DictConfig, id: str, value: bool) -> None

Set a key,value pair inside flag table.

Parameters:

Name Type Description Default
config DictConfig

Hydra configuration dictionary.

required
id str

flag id (name).

required
value bool

flag value.

required
Source code in dlunch/models.py
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
def set_flag(config: DictConfig, id: str, value: bool) -> None:
    """Set a key,value pair inside `flag` table.

    Args:
        config (DictConfig): Hydra configuration dictionary.
        id (str): flag id (name).
        value (bool): flag value.
    """

    session = create_session(config)

    with session:
        # Write the selected flag (it will be overwritten if exists)
        new_flag = Flags(id=id, value=value)

        # Use an upsert for postgresql, a simple session add otherwise
        session_add_with_upsert(
            session=session, constraint="flags_pkey", new_record=new_flag
        )

        session.commit()

set_sqlite_pragma

set_sqlite_pragma(dbapi_connection, connection_record)

Force foreign key constraints for sqlite connections.

Parameters:

Name Type Description Default
dbapi_connection Any

connection to database. Shall have a cursor method.

required
connection_record Any

connection record (not used).

required
Source code in dlunch/models.py
77
78
79
80
81
82
83
84
85
86
87
88
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    """Force foreign key constraints for sqlite connections.

    Args:
        dbapi_connection (Any): connection to database. Shall have a `cursor` method.
        connection_record (Any): connection record (not used).
    """
    if get_db_dialect(dbapi_connection) == "sqlite":
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON;")
        cursor.close()