Database

Helpers

flashcards_core.database.init_db(database_path: str = 'sqlite:////home/runner/work/flashcards-core/flashcards-core/docs/sqlite_dev.db', connect_args: Mapping[str, Any] = {'check_same_thread': False})

Initializes the database connection. Creates an SQLAlchemy engine, makes sure all tables exist, and returns a sessionmaker that can be used to generate a database connection.

Note: the default connect_args is needed only for SQLite.

Parameters
  • database_path – The database URL. Can be used to specify the database type with the protocol (‘sqlite:///’, ‘postgres:///’, …)

  • connect_args – other arguments to pass to the SQLAlchemy engine. See SQLAlchemy documentation for sqlalchemy.create_engine()

Returns

a sessionmaker, a function that can be called to return a Session object.

Example usage:

from sqlalchemy.orm import Session
from flashcards_core.database import init_db

# Initialize the database connection
sessionmaker = init_db()
session: Session = sessionmaker()
fact = Fact.create(session=session, value="A fact", format="text")

CRUD Base class

class flashcards_core.database.crud.CrudOperations

Bases: object

classmethod create(session: sqlalchemy.orm.session.Session, **kwargs)

Create a new model object with the given kwargs. Check the model to understand what you can give as **kwargs.

Parameters

session – the session (see flashcards_core.database:init_session()).

Returns

the new model object.

async classmethod create_async(session: sqlalchemy.orm.session.Session, **kwargs)

Create a new model object with the given kwargs. Check the model to understand what you can give as **kwargs (asyncio-friendly).

Parameters

session – the session (see flashcards_core.database:init_session()).

Returns

the new model object.

classmethod delete(session: sqlalchemy.orm.session.Session, object_id: int) None

Delete a model object.

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • object_id – the ID of the model object to delete.

Returns

None.

Raises

ObjectNotFoundException if no object with the given ID was found in the database.

async classmethod delete_async(session: sqlalchemy.orm.session.Session, object_id: int) None

Delete a model object (asyncio-friendly).

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • object_id – the ID of the model object to delete.

Returns

None.

Raises

ObjectNotFoundException if no object with the given ID was found in the database.

classmethod get_all(session: sqlalchemy.orm.session.Session, offset: int = 0, limit: int = 100) List

Returns a list of all the model objects available in the DB, or a subset of them.

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • offset – for pagination, index at which to start returning values.

  • limit – for pagination, maximum number of elements to return.

Returns

List of model objects.

async classmethod get_all_async(session: sqlalchemy.orm.session.Session, offset: int = 0, limit: int = 100) List[Any]

Returns a list of all the model objects available in the DB, or a subset of them (asyncio-friendly).

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • offset – for pagination, index at which to start returning values.

  • limit – for pagination, maximum number of elements to return.

Returns

List of model objects.

classmethod get_one(session: sqlalchemy.orm.session.Session, object_id: int) Optional

Returns the model object corresponding to the given ID.

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • object_id – the ID of the model object to return.

Returns

the matching model object.

async classmethod get_one_async(session: sqlalchemy.orm.session.Session, object_id: int) Optional[Any]

Returns the model object corresponding to the given ID (asyncio-friendly).

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • object_id – the ID of the model object to return.

Returns

the matching model object.

classmethod update(session: sqlalchemy.orm.session.Session, object_id: int, **kwargs)

Modify the model object with the given values. Check the model to understand what you can give as **kwargs.

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • object_id – the ID of the model object to update.

Returns

the updated model object.

Raises

ObjectNotFoundException if no model object with the given ID was found in the database.

async classmethod update_async(session: sqlalchemy.orm.session.Session, object_id: int, **kwargs)

Modify the model object with the given values. Check the model to understand what you can give as **kwargs (asyncio-friendly).

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • object_id – the ID of the model object to update.

Returns

the updated model object.

Raises

ObjectNotFoundException if no model object with the given ID was found in the database.

Export Utils

flashcards_core.database.exporter.DEFAULT_EXCLUDE_FIELDS = {'cards': ['deck']}

Default fields not to follow for related objects discovery. See export_to_json() for more info

flashcards_core.database.exporter.export_to_dict(session: sqlalchemy.orm.session.Session, objects_to_export: List[sqlalchemy.orm.decl_api.Base], exclude_fields: Optional[Mapping[str, List[str]]] = None, _hierarchy: Optional[Mapping[str, Any]] = None) Mapping[str, Any]

Exports the given objects to a dictionary, which can be easily dumped into a standard format like JSON or YAML.

Note that, by default, all relationships are followed, except for the following ones:

  • Exporting a Card won’t export its Deck.

  • Exporting a Fact will not export the Cards it’s included in.

These default can be overridden by providing a value to the exclude_fields attribute. It expect a mapping of a tablename (like ‘cards’) and a list of string with the name of the columns that should not be checked for potential related objects to export. Its default value looks like {'cards': ['deck']} (facts don’t have references to the cards they’re included in, so you need a query to find them).

Remember to pass an empty dictionary to exclude_fields to really exclude no fields; passing None will instruct this function to apply the default exclusion list.

The list of objects to export can be a mixture of several subclasses of SQLAlchemy’s Base class. In the output they will be categorized by table name.

Example output where only one Deck object was passed:

{
    'decks': {
        1: {
            'name': 'Test Deck',
            'description': 'A deck for tests',
            'algorithm': 'random',
            'parameters': {
                'unseen_first': true
            },
            'state': {
                'last_reviewed_card': 2
            }
        }
    },
    'cards': {
        1: {
            'deck_id': 1,
            'question_id': 1,
            'answer_id': 2
        }
    },
    'facts': {
        1: {
            'value': 'A question',
            'format': 'text'
        },
        2: {
            'value': 'An answer',
            'format': 'text'
        }
    },
    'reviews': {
        1: {
            'card_id': 1,
            'result': True,
            'algorithm': 'random',
            'datetime': '2021-01-01 12:00:00'
        }
    },
    'tags': {
        1: {
            'name': 'test-tag-1'
        },
        2: {
            'name': 'test-tag-2'
        }
    }
    'decktags': {
        (1, 1),
        (1, 2)
    }
}

If more Decks were passed, the overall structure would be the same, as well as if the list was made of mixed objects.

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • objects_to_export – a list of objects to export. They should be subclasses of any class defined in flashcards_core.database.models.

  • exclude_fields – If any of the model object columns should not be followed, they should be added here. Note that these exclusions apply to all the objects of this type discovered by following other relationships. The default value is set to {'cards': ['deck']} (see above).

  • _hierarchy – internal, used to pass the already built hierarchy through recursive calls.

Returns

a definition of all the objects required to reconstruct the database hierarchy the objects were taken from.

flashcards_core.database.exporter.export_to_json(session: sqlalchemy.orm.session.Session, objects_to_export: List[sqlalchemy.orm.decl_api.Base], exclude_fields: Optional[Mapping[str, List[str]]] = None, **json_kwargs) str

Exports the given objects into a JSON string. Simple wrapper around export_to_dict() that performs some normalization (like UUIDs to string, set to list, etc…)

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • objects_to_export – a list of objects to export. They should be subclasses of any class defined in flashcards_core.database.models.

  • json_kwargs – any parameter you may wish to pass to json.dumps()

flashcards_core.database.exporter.hierarchy_to_json(obj)

JSON serializer for objects not serializable by default, like dates, sets, UUIDs.

This is the ‘default’ method of the JSON encoder, see export_to_json.

Import Utils

flashcards_core.database.importer.datetime_hook(json_dict)
flashcards_core.database.importer.import_from_dict(session: sqlalchemy.orm.session.Session, hierarchy: Mapping[str, Any], stop_on_error=False) None

Create objects in the database from the data contained in the dictionary. Note that the keys must be strings, not UUID objects.

Example of valid input:

{
    'decks': {
        1: {
            'name': 'Test Deck',
            'description': 'A deck for tests',
            'algorithm': 'random',
            'parameters': {
                'unseen_first': true
            },
            'state': {
                'last_reviewed_card': 2
            }
        }
    },
    'cards': {
        1: {
            'deck_id': 1,
            'question_id': 1,
            'answer_id': 2
        }
    },
    'facts': {
        1: {
            'value': 'A question',
            'format': 'text'
        },
        2: {
            'value': 'An answer',
            'format': 'text'
        }
    },
    'reviews': {
        1: {
            'card_id': 1,
            'result': True,
            'algorithm': 'random',
            'datetime': '2021-01-01 12:00:00'
        }
    },
    'tags': {
        1: {
            'name': 'test-tag-1'
        },
        2: {
            'name': 'test-tag-2'
        }
    }
    'decktags': {
        (1, 1),
        (1, 2)
    }
}
Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • hierarchy – a dictionary containing all the data of the objects to import. See above or export_to_dict() for more info.

  • stop_on_error – if an Integrity error is raised, stop instead of skipping the object.

Returns

None

flashcards_core.database.importer.import_from_json(session: sqlalchemy.orm.session.Session, json_string: str, stop_on_error=False, **json_kwargs) None

Import the objects from their JSON representation. Simple wrapper around import_from_dict().

Parameters
  • session – the session (see flashcards_core.database:init_session()).

  • json_string – the string containing the data of the objects to import. It should have been created with export_to_json() or match the same schema.

  • json_kwargs – any parameter you may wish to pass to json.loads()

flashcards_core.database.importer.import_to_associative_table(session: sqlalchemy.orm.session.Session, table: sqlalchemy.sql.schema.Table, tablename: str, entities: dict, stop_on_error: bool) None
flashcards_core.database.importer.import_to_table(session: sqlalchemy.orm.session.Session, table: sqlalchemy.sql.schema.Table, tablename: str, entities: dict, stop_on_error: bool) None