nenuscanner/db.py

256 lines
7.5 KiB
Python
Executable File

#!/usr/bin/env python
from enum import IntEnum
from datetime import datetime
from flask import g
import os
from os.path import join
import shutil
import sqlite3
from typing import Optional
if __name__ != '__main__':
from . import config
else:
import config
def get() -> sqlite3.Connection:
if 'db' not in g:
g.db = sqlite3.connect(
config.DATABASE_PATH,
detect_types=sqlite3.PARSE_DECLTYPES,
)
g.db.row_factory = sqlite3.Row
cur = g.db.cursor()
cur.execute('PRAGMA foreign_keys = on')
return g.db
def init(db):
with open('schema.sql', 'r') as f:
db.executescript(f.read())
def close():
db = g.pop('db', None)
if db is not None:
db.close()
def init_app(app):
app.teardown_appcontext(close)
app.cli.add_command(init)
class CalibrationState(IntEnum):
Empty = 0
HasData = 1
IsComputed = 2
IsValidated = 3
class Calibration:
@staticmethod
def select_args() -> str:
return 'id, state'
def __init__(self, calibration_id: int, state: int):
self.id = calibration_id
self.state = CalibrationState(state)
@staticmethod
def create(db: sqlite3.Connection) -> 'Calibration':
cur = db.cursor()
response = cur.execute(
'INSERT INTO calibration(state) VALUES (?) RETURNING ' + Calibration.select_args() + ';',
[int(CalibrationState.Empty)]
)
calibration = Calibration.from_row(response.fetchone())
if calibration is None:
return None
os.makedirs(join(config.CALIBRATION_DIR, str(calibration.id)))
return calibration
@staticmethod
def from_row(row: Optional['sqlite3.Row']) -> Optional['Calibration']:
if row is None:
return None
return Calibration(*row)
def save(self, db: sqlite3.Connection):
cur = db.cursor()
cur.execute(
'UPDATE calibration SET state = ? WHERE id = ?',
[int(self.state), self.id]
)
@staticmethod
def get_from_id(calibration_id: int, db: sqlite3.Connection) -> Optional['Calibration']:
cur = db.cursor()
response = cur.execute(
'SELECT ' + Calibration.select_args() + ' FROM calibration WHERE id = ?;',
[calibration_id]
)
return Calibration.from_row(response.fetchone())
@staticmethod
def get_last(db: sqlite3.Connection) -> Optional['Calibration']:
cur = db.cursor()
response = cur.execute(
'SELECT ' + Calibration.select_args() + ' FROM calibration WHERE state = 3 ORDER BY id DESC LIMIT 1;',
[]
)
return Calibration.from_row(response.fetchone())
Calibration.Dummy = Calibration(-1, CalibrationState.Empty)
class Acquisition:
@staticmethod
def select_args() -> str:
return 'id, calibration_id, object_id'
def __init__(self, acquisition_id: int, calibration_id: int, object_id: int):
self.id = object_id
self.calibration_id = calibration_id
self.object_id = object_id
def save(self, db: sqlite3.Connection):
cur = db.cursor()
cur.execute(
'UPDATE acquisition SET calibration_id = ?, object_id = ? WHERE id = ?',
[self.calibration_id, self.object_id, self.id]
)
@staticmethod
def from_row(row: Optional[sqlite3.Row]) -> Optional['Acquisition']:
if row is None:
return None
return Acquisition(*row)
@staticmethod
def get_from_id(acquisition_id: int, db: sqlite3.Connection) -> Optional['Acquisition']:
cur = db.cursor()
response = cur.execute(
'SELECT ' + Acquisition.select_args() + ' FROM acquisition WHERE id = ?;',
[acquisition_id]
)
return Acquisition.from_row(response.fetchone())
def calibration(self, db: sqlite3.Connection) -> Optional[Calibration]:
if self.calibration_id is None:
return None
return Calibration.get_from_id(self.calibration_id, db)
class Object:
@staticmethod
def select_args() -> str:
return 'id, name'
def __init__(self, object_id: int, name: str):
self.id = object_id
self.name = name
def save(self, db: sqlite3.Connection):
cur = db.cursor()
cur.execute(
'UPDATE object SET name = ? WHERE id = ?',
[self.name, self.id]
)
@staticmethod
def from_row(row: Optional[sqlite3.Row]) -> Optional['Object']:
if row is None:
return None
return Object(*row)
@staticmethod
def create(name: str, db: sqlite3.Connection) -> 'Object':
cur = db.cursor()
response = cur.execute(
'INSERT INTO object(name) VALUES (?) RETURNING ' + Object.select_args() + ';',
[name]
)
object = Object.from_row(response.fetchone())
os.makedirs(join(config.OBJECT_DIR, str(object.id)))
return object
@staticmethod
def get_from_id(object_id: int, db: sqlite3.Connection) -> Optional['Object']:
cur = db.cursor()
response = cur.execute(
'SELECT ' + Object.select_args() + ' FROM object WHERE id = ?;',
[object_id]
)
return Object.from_row(response.fetchone())
@staticmethod
def all(db: sqlite3.Connection) -> list['Object']:
cur = db.cursor()
response = cur.execute(
'SELECT ' + Object.select_args() + ' FROM object;',
[]
)
return list(map(Object.from_row, response.fetchall()))
def add_acquisition(self, calibration_id: int, db: sqlite3.Connection) -> Acquisition:
cur = db.cursor()
response = cur.execute(
'INSERT INTO acquisition(calibration_id, object_id) VALUES (?, ?) RETURNING ' + Acquisition.select_args() + ';',
[calibration_id, self.id]
)
return Acquisition.from_row(response.fetchone())
def main():
# Move current data to backup dir
if os.path.isdir(config.DATA_DIR):
# Ensure backup dir exists
os.makedirs(config.BACKUPS_DIR, exist_ok=True)
now = datetime.now()
dest = join(config.BACKUPS_DIR, f'{now.year}-{now.month:02}-{now.day:02}--{now.hour:02}-{now.minute:02}-{now.second:02}')
shutil.move(config.DATA_DIR, dest)
# Create new empty data dir
os.makedirs(config.DATA_DIR, exist_ok=True)
db = sqlite3.connect(
config.DATABASE_PATH,
detect_types=sqlite3.PARSE_DECLTYPES,
)
db.row_factory = sqlite3.Row
init(db)
# Create a new object
with db:
Object.create('Mon premier objet', db)
# calibration = Calibration.create(db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
# calibration = Calibration.create(db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
Object.create('Mon deuxième objet', db)
# calibration = Calibration.create(db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
# calibration = Calibration.create(db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
# object.add_acquisition(calibration.id, db)
if __name__ == '__main__':
main()