Define Classes and create the SQLite database

This module defines the classes we use to represent the PKM workflow.

We use from __future__ import annotations to support forward references in type hints. To be precise in the @classmethod we create to keep track of all instances of the class.

Using Pydantic with MiniDataAPI and SQLite

We want to use Pydantic Dataclasses to enable typechecking and validation. We also want to use the Dataclasses with the MiniDataAPI to create the tables in the SQLite database. But SQLite only has datatypes: NULL, INTEGER, REAL, TEXT, and BLOB. So no list or any of the Dataclass(Enum) types we use.

To be able to use both Pydanctic and the MiniDataAPI we will do two things:

  1. Define a Pydantic Dataclass with the correct datatypes and a Dataclass that has the same fields as the Pydantic Dataclass, but with datatypes that can be used with SQLite.
  2. We add @field_serializer methods to the Pydantic Dataclass that convert the fields to JSON strings when we use the method .model_dump() on the instance of the Pydantic Dataclass. These serialised JSON strings can then be added to the SQLite database.
  3. We also use the @field_validator decorator to convert the JSON strings back to the correct datatypes when we load the data from the SQLite database back into the Pydantic Dataclass.

This way we can:

  • create instances with the Pydantic Dataclass to have easy typechecking and validation.
  • convert this instances to MiniDataAPI and SQLite friendly datatypes using .model_dump() on the instance, that we can then add to the database.
  • load the data from the SQLite database back into the Pydantic Dataclass.

The exact implementation can be found below where the Classes are defined.

Track instances of classes

We also want to keep track of the instances available for each class. Therefore we need some higher order magic.

  • a list in the class to store the instances
  • a init method to add the instance to the list
  • a classmethod to get the list of instances

We can’t just add a _instances = [] statement to the Class, because Pydantic will then assume it is a model field (private attribute). We need to tell Pydantic to ignore the _instances class variable as a model field and treat is as a class variable. Therefore we need to import ClassVar from typing and use it to type the _instances variable.

Enum Classes

First we define the possible values of the different variables that are available in the classes. We use the module enum to define Enumerations. We use this to bind the possible values to a variable name, making the code more readable and maintainable.


source

OrganizationSystem

 OrganizationSystem (value, names=None, module=None, qualname=None,
                     type=None, start=1, boundary=None)

How tools organize and structure information.


source

PhaseQuality

 PhaseQuality (value, names=None, module=None, qualname=None, type=None,
               start=1, boundary=None)

Quality rating for how well a tool performs in each phase.


source

Phase

 Phase (value, names=None, module=None, qualname=None, type=None, start=1,
        boundary=None)

The five phases of the PKM workflow.


source

Method

 Method (value, names=None, module=None, qualname=None, type=None,
         start=1, boundary=None)

How actions are performed - manually or automatically.


source

InformationType

 InformationType (value, names=None, module=None, qualname=None,
                  type=None, start=1, boundary=None)

Information content types that flow through the PKM workflow.

Phase("refine")
<Phase.REFINE: 'refine'>

PKM Workflow Classes

Next we create a dataclass for each item we need to be present in the PKM workflow.

Pydantic Dataclasses

Used for typechecking.

When creating a new instance for an InformationItem the toolflow must be given as a list of Tool objects. The typechecking makes sure that any Tool object mentioned in the toolfow list, does exist as an actual Tool instance. So make sure to first create all the Tool instances that are needed for an InformationItem, before creating the InformationItem instance.

Tip

I had some serious trouble getting the Pydantic dataclass validations to work. One of the issues is described above and is about SQLite not supporting all datatypes. A second major issue is that the Pydantic Dataclasses reference each other. The InformationItem references the Tool in the toolflow field. I would also be convenient to store all the InformationItems that can be used with a certain Tool, but in that case we would create a circular reference between InformationItem and Tool.

We decided to remove the information_items list from Tool. When we need to get all the InformationItems that are supported by a Tool we can write a Python function or do a SQL-query on the SQLite database.

But then we are left with the fact that we want a list of Tools that exist. These are the options considered:

  • toolflow: list[Tool]
  • toolflow: list[Tool.name]
  • toolflow: list[str]

The last option is used in combination with validation to ensure each string is a valid Tool.name.

Here’s why this is the best approach:

  • Clean serialization (no complex object embedding)
  • Human-readable in the database
  • Type safety through validation
  • Easy to query

The same goes for the Improvement class and the field tool.


source

Tool

 Tool (name:str, organization_system:list[__main__.OrganizationSystem],
       phase_quality:__main__.PhaseQualityData, collect:str|None=None,
       retrieve:str|None=None, consume:str|None=None,
       extract:str|None=None, refine:str|None=None)

*!!! abstract “Usage Documentation” Models

A base class for creating Pydantic models.

Attributes: class_vars: The names of the class variables defined on the model. private_attributes: Metadata about the private attributes of the model. signature: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.*

source

PhaseQualityData

 PhaseQualityData (collect:__main__.PhaseQuality,
                   retrieve:__main__.PhaseQuality,
                   consume:__main__.PhaseQuality,
                   extract:__main__.PhaseQuality,
                   refine:__main__.PhaseQuality)

*!!! abstract “Usage Documentation” Models

A base class for creating Pydantic models.

Attributes: class_vars: The names of the class variables defined on the model. private_attributes: Metadata about the private attributes of the model. signature: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.*

source

PhaseToolflowData

 PhaseToolflowData (collect:Union[str,list[str],tuple[str],NoneType],
                    retrieve:Union[str,list[str],tuple[str],NoneType],
                    consume:Union[str,list[str],tuple[str],NoneType],
                    extract:Union[str,list[str],tuple[str],NoneType],
                    refine:Union[str,list[str],tuple[str],NoneType])

*!!! abstract “Usage Documentation” Models

A base class for creating Pydantic models.

Attributes: class_vars: The names of the class variables defined on the model. private_attributes: Metadata about the private attributes of the model. signature: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.*

source

PhaseMethodData

 PhaseMethodData (collect:__main__.Method|None,
                  retrieve:__main__.Method|None,
                  consume:__main__.Method|None,
                  extract:__main__.Method|None,
                  refine:__main__.Method|None)

*!!! abstract “Usage Documentation” Models

A base class for creating Pydantic models.

Attributes: class_vars: The names of the class variables defined on the model. private_attributes: Metadata about the private attributes of the model. signature: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.*

source

InformationItem

 InformationItem (name:str, info_type:__main__.InformationType,
                  method:__main__.PhaseMethodData,
                  toolflow:__main__.PhaseToolflowData)

*!!! abstract “Usage Documentation” Models

A base class for creating Pydantic models.

Attributes: class_vars: The names of the class variables defined on the model. private_attributes: Metadata about the private attributes of the model. signature: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.*

source

Improvement

 Improvement (title:str, what:str, why:str, prio:int, tool:str,
              phase:__main__.Phase)

*!!! abstract “Usage Documentation” Models

A base class for creating Pydantic models.

Attributes: class_vars: The names of the class variables defined on the model. private_attributes: Metadata about the private attributes of the model. signature: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.*

Test creating instances

def test_phase_quality_data():
    pqd = PhaseQualityData(collect=PhaseQuality.GREAT, retrieve=PhaseQuality.BAD, consume=PhaseQuality.OK, extract=PhaseQuality.NA, refine=PhaseQuality.GREAT)
    test_eq(pqd.collect, PhaseQuality.GREAT)
    test_eq(pqd.retrieve, PhaseQuality.BAD)

def test_tool_creation():
    tool = Tool(name="TestTool", organization_system=[OrganizationSystem.TAGS], phase_quality=PhaseQualityData(collect=PhaseQuality.GREAT, retrieve=PhaseQuality.BAD, consume=PhaseQuality.OK, extract=PhaseQuality.NA, refine=PhaseQuality.GREAT))
    test_eq(tool.name, "TestTool")
    test_eq(tool.slug, "testtool")
    test_eq(tool.phase_quality.collect, PhaseQuality.GREAT)

def test_tool_flatten():
    tool = Tool(name="TestTool", organization_system=[OrganizationSystem.TAGS], phase_quality=PhaseQualityData(collect=PhaseQuality.GREAT, retrieve=PhaseQuality.BAD, consume=PhaseQuality.OK, extract=PhaseQuality.NA, refine=PhaseQuality.GREAT))
    flat = tool.flatten_for_db()
    test_eq(flat['collect_quality'], 'great')
    test_eq(flat['retrieve_quality'], 'bad')
    test_eq(flat['name'], 'TestTool')

def test_information_item():
    methods = PhaseMethodData(collect=Method.MANUAL, retrieve=None, consume=None, extract=None, refine=None)
    tools = PhaseToolflowData(collect="Reader", retrieve="Recall", consume=None, extract=None, refine=None)
    item = InformationItem(name="Test Article", info_type=InformationType.WEB_ARTICLE, method=methods, toolflow=tools)
    test_eq(item.method.collect, Method.MANUAL)
    test_eq(item.toolflow.collect, "Reader")

def test_information_item_flatten():
    methods = PhaseMethodData(collect=Method.MANUAL, retrieve=None, consume=None, extract=None, refine=None)
    tools = PhaseToolflowData(collect=["Reader", "Recall"], retrieve="Recall", consume=None, extract=None, refine=None)
    item = InformationItem(name="Test Article", info_type=InformationType.WEB_ARTICLE, method=methods, toolflow=tools)
    flat = item.flatten_for_db()
    test_eq(flat['collect_method'], 'manual')
    test_eq(flat['retrieve_method'], None)
    test_eq(flat['collect_toolflow'], '["Reader", "Recall"]')
    test_eq(flat['retrieve_toolflow'], 'Recall')

def test_improvement():
    imp = Improvement(title="Fix Search", what="Better search in Reader", why="Current search is bad", prio=1, tool="testtool", phase=Phase.RETRIEVE)
    test_eq(imp.title, "Fix Search")
    test_eq(imp.phase, Phase.RETRIEVE)
    test_eq(imp.flatten_for_db()['phase'], 'retrieve')
test_phase_quality_data()
test_tool_creation()
test_tool_flatten()
test_information_item()
test_information_item_flatten()
test_improvement()
Tool.get_instances()
{'testtool': Tool(name='TestTool', organization_system=[<OrganizationSystem.TAGS: 'tags'>], phase_quality=PhaseQualityData(collect=<PhaseQuality.GREAT: 'great'>, retrieve=<PhaseQuality.BAD: 'bad'>, consume=<PhaseQuality.OK: 'ok'>, extract=<PhaseQuality.NA: 'na'>, refine=<PhaseQuality.GREAT: 'great'>), collect=None, retrieve=None, consume=None, extract=None, refine=None, slug='testtool')}
InformationItem.get_instances()
{'test_article': InformationItem(name='Test Article', info_type=<InformationType.WEB_ARTICLE: 'web_article'>, method=PhaseMethodData(collect=<Method.MANUAL: 'manual'>, retrieve=None, consume=None, extract=None, refine=None), toolflow=PhaseToolflowData(collect=['Reader', 'Recall'], retrieve='Recall', consume=None, extract=None, refine=None), slug='test_article')}
Improvement.get_instances()
{'fix_search': Improvement(title='Fix Search', what='Better search in Reader', why='Current search is bad', prio=1, tool='testtool', phase=<Phase.RETRIEVE: 'retrieve'>, slug='fix_search')}

SQLite database

Create and connect

Connect to the database in the main.py. We should also enable foreign key constraints. These are disabled by default in Sqlite.

For testing purposes in this module we will use db = database(":memory:") to create an in-memory database.


source

create_db

 create_db (loc='static/infoflow.db')
Tip

We can add foreign key constraints to the tables using the transform method from sqlite_utils.

inf_tbl.transform(add_foreign_keys=[("<field_name>", "<table_name_to_connect>", "<field_name_in_table_to_connect>")])

But for now we won’t use foreign key constraints.

Tests and usage examples

db, inf_tbl, tool_tbl, imp_tbl = create_db(":memory:")
inf_tbl.columns
[Column(cid=0, name='id', type='INTEGER', notnull=0, default_value=None, is_pk=1),
 Column(cid=1, name='slug', type='TEXT', notnull=0, default_value=None, is_pk=0),
 Column(cid=2, name='name', type='TEXT', notnull=0, default_value=None, is_pk=0),
 Column(cid=3, name='info_type', type='TEXT', notnull=0, default_value=None, is_pk=0),
 Column(cid=4, name='method', type='TEXT', notnull=0, default_value=None, is_pk=0),
 Column(cid=5, name='toolflow', type='TEXT', notnull=0, default_value=None, is_pk=0)]

Add data to the database

Add the previously created instances to the SQLite tables

Improvement.get_instances()['improvement_a'].model_dump()
{'title': 'improvement_a',
 'what': 'gras',
 'why': 'dus',
 'prio': 0,
 'tool': 'reader',
 'phase': 'collect',
 'slug': 'improvement_a'}

Add a single instance to the SQLite table

imp_tbl.insert(Improvement.get_instances()['improvement_a'].model_dump())
inf_tbl.insert(InformationItem.get_instances()['infoitem_a'].model_dump())
InformationItemDB(id=1, slug='infoitem_a', name='infoitem_a', info_type='book', method='["manual"]', toolflow='[["reader", "obsidian"], "obsidian", "reader", "obsidian", "reader"]')

Add multiple instances to the SQLite table

tool_tbl.insert_all([t.model_dump() for t in Tool.get_instances().values()])
<Table tool_db (id, slug, name, organization_system, phase_quality, collect, retrieve, consume, extract, refine)>
[r for r in tool_tbl.rows]
[{'id': 1,
  'slug': 'reader',
  'name': 'reader',
  'organization_system': '["tags"]',
  'phase_quality': '["great", "ok", "ok", "ok", "ok"]',
  'collect': None,
  'retrieve': None,
  'consume': None,
  'extract': None,
  'refine': None},
 {'id': 2,
  'slug': 'obsidian',
  'name': 'obsidian',
  'organization_system': '["tags"]',
  'phase_quality': '["great", "bad", "bad", "bad", "bad"]',
  'collect': None,
  'retrieve': None,
  'consume': None,
  'extract': None,
  'refine': None}]

Retrieve data from the database

Now retrieve the info from the database as intances from the Pydantic Dataclass

Method 1:

db.t
improvement_db, information_item_db, tool_db
type(db.t)
fastlite.core._TablesGetter
db.t.tool_db()
[{'id': 1,
  'slug': 'reader',
  'name': 'reader',
  'organization_system': '["tags"]',
  'phase_quality': '["great", "ok", "ok", "ok", "ok"]',
  'collect': None,
  'retrieve': None,
  'consume': None,
  'extract': None,
  'refine': None},
 {'id': 2,
  'slug': 'obsidian',
  'name': 'obsidian',
  'organization_system': '["tags"]',
  'phase_quality': '["great", "bad", "bad", "bad", "bad"]',
  'collect': None,
  'retrieve': None,
  'consume': None,
  'extract': None,
  'refine': None}]
reader_back_from_sqlite_1 = Tool(**db.t.tool_db()[0])
reader_back_from_sqlite_1
Tool(name='reader', organization_system=[<OrganizationSystem.TAGS: 'tags'>], phase_quality=[<PhaseQuality.GREAT: 'great'>, <PhaseQuality.OK: 'ok'>, <PhaseQuality.OK: 'ok'>, <PhaseQuality.OK: 'ok'>, <PhaseQuality.OK: 'ok'>], collect=None, retrieve=None, consume=None, extract=None, refine=None, slug='reader')

Method 2:

tool_tbl()
[ToolDB(id=1, slug='reader', name='reader', organization_system='["tags"]', phase_quality='["great", "ok", "ok", "ok", "ok"]', collect=None, retrieve=None, consume=None, extract=None, refine=None),
 ToolDB(id=2, slug='obsidian', name='obsidian', organization_system='["tags"]', phase_quality='["great", "bad", "bad", "bad", "bad"]', collect=None, retrieve=None, consume=None, extract=None, refine=None)]
tool_tbl()[0]
ToolDB(id=1, slug='reader', name='reader', organization_system='["tags"]', phase_quality='["great", "ok", "ok", "ok", "ok"]', collect=None, retrieve=None, consume=None, extract=None, refine=None)
reader_back_from_sqlite_2 = Tool(**tool_tbl()[0].__dict__)
reader_back_from_sqlite_2
Tool(name='reader', organization_system=[<OrganizationSystem.TAGS: 'tags'>], phase_quality=[<PhaseQuality.GREAT: 'great'>, <PhaseQuality.OK: 'ok'>, <PhaseQuality.OK: 'ok'>, <PhaseQuality.OK: 'ok'>, <PhaseQuality.OK: 'ok'>], collect=None, retrieve=None, consume=None, extract=None, refine=None, slug='reader')

source

instns_to_db

 instns_to_db (db_tbl, cls_instns)

Add all instances from a given Class to the given database table