Skip to content
Snippets Groups Projects
Verified Commit 1d240327 authored by David Beniamine's avatar David Beniamine
Browse files

ORM working with a bit of documentation

parent 7f41aa0f
No related branches found
No related tags found
1 merge request!2Draft:Generic api v1
This commit is part of merge request !2. Comments created here will be created in the context of that merge request.
......@@ -5,3 +5,4 @@ venv
.env
*.sw?
/.idea/
doc/
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import mapped_column
from sqlalchemy import Integer, JSON
from sqlalchemy import select
import json
from Mirador_backend.utils.database import db
class BaseModel(DeclarativeBase):
query = db.db_session.query_property()
"""A generic model class
"""
def create():
session = db.db_session
"""Access the db session"""
fillable = []
"""Field that can be overriden by self.fill()"""
id = mapped_column(Integer, primary_key=True)
"""Identifier of the object"""
def create_tables():
"""Creates all tables for all models"""
BaseModel.metadata.create_all(db.engine)
@classmethod
def query(cls, stmt):
"""Execute the given stmt on the db session
positionnal arguments:
stmt -- the statement to execute
"""
return cls.session.execute(stmt)
@classmethod
def find(cls, id):
"""Returns the model having the given id or none
positionnal arguments:
id -- the id of the model
side effect: add the returned objects to the session
returns: cls or throw an Error
"""
stmt = select(cls).where(cls.id == id)
obj = cls.query(stmt).scalar_one()
cls.session.add(obj)
return obj
@classmethod
def filter(cls, where=None):
"""Returns all model matching the where clause
positionnal arguments:
where -- a valid where close
returns: list(cls)
"""
stmt = select(cls)
if where is not None:
stmt = stmt.where(where)
# Add objects to the session
objects = cls.query(stmt).scalars().fetchall()
if objects is not []:
cls.session.add_all(objects)
return objects
@classmethod
def all(cls):
"""Returns all model matching the where clause
positionnal arguments:
where -- a valid where close
side effect: add the returned objects to the session
returns: list(cls)
"""
return cls.filter()
def delete(self):
"""Deletes the current model from the database
side effect: commits the session
returns: boolean
"""
self.session.delete(self)
self.session.commit()
pass
def is_dirty(self):
"""Checks if the object needs to be saved"""
return self in self.session.dirty
def fill(self, data):
"""Fills the object with data
only fillable field are filled, all other are ignored silently
positionnal arguments:
data dictionnary of data to fill the model with
"""
for k, v in data.items():
if k in self.fillable:
setattr(self, k, v)
if self.is_dirty():
return self.save()
return False
def create(self, data):
"""Creates an object from the given data
side effect: commits the session
positionnal arguments:
data dictionnary of data to fill the model with
"""
self.fill(data)
self.session.add(self)
self.session.commit()
return self
def save(self):
"""Updates the model in database
side effect: commits the session
returns: boolean has the model been updated
"""
if not self.is_dirty():
return False
self.session.commit()
return self
class MiradorResource(BaseModel):
"""A MiradorResource : manifest or catalog"""
__tablename__ = "mirador_resource"
"""The database table holding the objects"""
id = mapped_column(Integer, primary_key=True)
content = mapped_column(JSON)
"""JSON content of the resource"""
def __init__(self, content=None):
self.content = content
fillable = ['content']
def __repr__(self):
"""Returns the string representation of the object"""
return f'<Manifest {self.id} content {json.dumps(self.content)}>'
......@@ -18,10 +18,14 @@ class SimpleResponse(Schema):
class ResourceGroup(MethodResource, Resource):
Model = BaseModel
def get_queryset(self):
return self.Model.all()
def get(self):
return self.Model.query.filter().all()
return self.get_queryset()
def post(self):
Model.
# TODO
return {'method': 'post', 'mode': 'group'}
......@@ -36,8 +40,11 @@ class ResourceGroup(MethodResource, Resource):
class ResourceItem(MethodResource, Resource):
Model = BaseModel
def get_queryset(self, id):
return self.Model.find(id)
def get(self, id):
return self.Model.query.filter(self.Model.id == id).first()
return self.get_queryset(id)
def post(self, id):
# TODO
......@@ -52,8 +59,7 @@ class ResourceItem(MethodResource, Resource):
return {'method': 'patch', 'id': id, 'mode': 'item'}
def delete(self, id):
# TODO
return {'method': 'delete', 'id': id, 'mode': 'item'}
return Model.find(id).delete()
def genDoc(descr, tags):
doc(description=f'Get {descr} by id', tags=tags)(ResourceItem.get)
......
......@@ -15,6 +15,9 @@ usage() {
echo -e "COMMANDS\n"
echo "bash"
echo -e "\t opens a bash shell on the flask container"
echo "gen_doc"
echo -e "\t generates the documentation"
echo "help"
echo "help"
echo -e "\t display this helps and quit"
echo "mysql"
......@@ -48,6 +51,15 @@ case $action in
"bash")
$cmd bash
;;
"gen_doc")
mkdir -p doc
$cmd pdoc -o doc Mirador_backend
if [ ! -z "$(which xdg-open)" ]; then
xdg-open doc/index.html
else
echo "You can now open doc/index.html in your browser to see the doc"
fi
;;
"help")
usage
;;
......@@ -57,7 +69,7 @@ case $action in
set +x
;;
"mysql_init")
$cmd python -c 'from Mirador_backend.models import BaseModel; BaseModel.create()'
$cmd python -c 'from Mirador_backend.models import BaseModel; BaseModel.create_tables()'
;;
"shell")
$cmd ipython
......
......@@ -10,6 +10,7 @@ services:
MYSQL_USER:
MYSQL_PASSWORD:
MYSQL_DATABASE:
DOC_PORT:
volumes:
- .:/app
......
#!/bin/bash
pip install -e .
if [ "$ENV" == "dev" ]; then
dev="[dev]"
fi
pip install -e .$dev
exec flask --debug --app Mirador_backend.app run --host $HOST
......@@ -39,10 +39,15 @@ setup(
# 1.1.x is incompatible with mariadb connector already in python:3.10 docker image
"mariadb>=1.0.11,<1.1.0",
"python-dotenv",
# For a nice shell
"ipython",
],
extras_require={
'dev': [
'ipython',
'pdoc'
]
},
setup_requires=[""],
tests_require=["unittest"],
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment