-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Basic read/write functionality for database #23
Changes from 12 commits
578344e
7a92be5
db02c65
fd1900c
9a8016d
89f30e7
9a65bc0
3ab6e9c
4d20e87
d6ce916
ba8b3bd
fb57794
c14ce42
24fcbd5
c9240cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,122 @@ | ||
"""Data routines that can be integrated into main control flow.""" | ||
"""Module for interacting with SQLite database""" | ||
|
||
import sqlite3 | ||
from typing import Any | ||
|
||
from datetime import datetime | ||
from typing import Any, NamedTuple, Optional | ||
from importlib import resources | ||
|
||
from pydbml import PyDBML | ||
|
||
DATABASE_DEFINITION = resources.files("data.resources").joinpath("database.dbml") | ||
DATABASE_RESOURCE = resources.files("data.resources").joinpath("database.db") | ||
RESOURCES = resources.files("data.resources") | ||
DATABASE_DEFINITION = RESOURCES.joinpath("database.dbml") | ||
DATABASE_RESOURCE = RESOURCES.joinpath("database.db") | ||
|
||
|
||
class User(NamedTuple): | ||
"""Represents a user record in the SQLite database""" | ||
|
||
id_: Optional[int] | ||
|
||
|
||
class Posture(NamedTuple): | ||
"""Represents a posture record in the SQLite database""" | ||
|
||
id_: Optional[int] | ||
user_id: int | ||
prop_good: float | ||
prop_in_frame: float | ||
period_start: datetime | ||
period_end: datetime | ||
|
||
|
||
def init_database() -> None: | ||
"""Initialise SQLite database if it does not already exist""" | ||
# Open connection with database | ||
# Check if database exists | ||
with resources.as_file(DATABASE_RESOURCE) as database_file: | ||
if database_file.is_file(): | ||
return | ||
|
||
parsed = PyDBML(DATABASE_DEFINITION) | ||
init_script = parsed.sql | ||
|
||
connection = sqlite3.connect(database_file) | ||
parsed = PyDBML(DATABASE_DEFINITION) | ||
init_script = parsed.sql | ||
|
||
# Run init script | ||
with connection: | ||
with _connect() as connection: | ||
cursor = connection.cursor() | ||
cursor.executescript(init_script) | ||
connection.commit() | ||
|
||
|
||
def create_user() -> int: | ||
"""Creates a new user in the database. | ||
|
||
Returns: | ||
The id of the new user. | ||
""" | ||
with _connect() as connection: | ||
cursor = connection.cursor() | ||
cursor.execute("INSERT INTO user DEFAULT VALUES;") | ||
result = cursor.execute("SELECT last_insert_rowid() FROM user;") | ||
user_id = result.fetchone()[0] | ||
connection.commit() | ||
|
||
return user_id | ||
|
||
|
||
def save_posture(posture: Posture | tuple) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm should this just be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking @avg-lebesgue-enjoyer and @polypies73 might not want to worry about using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it'd be too much more work, wouldn't you just wrap a tuple in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah okay I agree, I will typehint just |
||
"""Stores the posture record in the database. | ||
|
||
Args: | ||
posture: The posture record to save. | ||
""" | ||
if posture[0] is not None: | ||
raise ValueError("Posture record id must be None") | ||
|
||
with _connect() as connection: | ||
cursor = connection.cursor() | ||
cursor.execute( | ||
"INSERT INTO posture VALUES (?, ?, ?, ?, ?, ?);", | ||
posture, | ||
) | ||
connection.commit() | ||
|
||
|
||
def get_users(num: int = 10) -> list[User]: | ||
""" | ||
Args: | ||
num: Number of user to retrieve | ||
|
||
Returns: | ||
num users from the database. | ||
""" | ||
with _connect() as connection: | ||
cursor = connection.cursor() | ||
result = cursor.execute("SELECT * FROM user LIMIT ?", (num,)) | ||
return [User(*record) for record in result.fetchall()] | ||
|
||
|
||
def get_postures(num: int = 10) -> list[Posture]: | ||
""" | ||
Args: | ||
num: Number of posture records to retrieve | ||
|
||
Returns: | ||
num posture records from the database. | ||
""" | ||
with _connect() as connection: | ||
cursor = connection.cursor() | ||
result = cursor.execute("SELECT * FROM posture LIMIT ?", (num,)) | ||
return [Posture(*record) for record in result.fetchall()] | ||
|
||
|
||
def get_schema_info() -> list[list[tuple[Any]]]: | ||
"""Column information on all tables in database. | ||
|
||
Returns: | ||
(list[list[tuple[Any]]]): Outer list contains table information, inner list contains column | ||
Outer list contains table information, inner list contains column | ||
information tuples. | ||
""" | ||
with resources.as_file(DATABASE_RESOURCE) as database_file: | ||
connection = sqlite3.connect(database_file) | ||
|
||
with connection: | ||
with _connect() as connection: | ||
cursor = connection.cursor() | ||
result = cursor.execute("SELECT name FROM sqlite_schema WHERE type='table'") | ||
tables = result.fetchall() | ||
|
@@ -50,3 +128,10 @@ def get_schema_info() -> list[list[tuple[Any]]]: | |
table_schemas.append(table_schema) | ||
|
||
return table_schemas | ||
|
||
|
||
def _connect() -> sqlite3.Connection: | ||
with resources.as_file(DATABASE_RESOURCE) as database_file: | ||
return sqlite3.connect( | ||
database_file, detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the underscore suffix notation mean here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its to avoid name clashes with the inbuilt
id()
function.