Compare commits
14 Commits
1531a5dc77
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 969333998c | |||
| 2a8a44e80b | |||
| 7c3593d066 | |||
| c701a4ab9b | |||
| c468f51672 | |||
| bb8324ad1d | |||
| d84d62c79f | |||
| f50a9d0916 | |||
| 5f7b4102bc | |||
| 5fc1dc1b82 | |||
| 368038bdb9 | |||
| 75c295ce4b | |||
| dc819b1674 | |||
| b00c35f19b |
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.12.3
|
||||||
1
app/api/actions/__init__.py
Normal file
1
app/api/actions/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .install import install
|
||||||
18
app/api/actions/create_engine.py
Normal file
18
app/api/actions/create_engine.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from ...db.config.config import get_engine_configuration
|
||||||
|
from sqlalchemy import create_engine as sqlalchemy_create_engine
|
||||||
|
from functools import cache
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def create_engine(*, engine_string=None, echo=None):
|
||||||
|
logger.debug(f"create_engine {engine_string}")
|
||||||
|
if engine_string is None:
|
||||||
|
engine_string, echo = get_engine_configuration()
|
||||||
|
engine = sqlalchemy_create_engine(engine_string, echo=echo=="true", future=True) # TODO
|
||||||
|
Session = sessionmaker(engine)
|
||||||
|
session = Session()
|
||||||
|
return engine, session
|
||||||
17
app/api/actions/install.py
Normal file
17
app/api/actions/install.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
|
from ...schema.library.base import Base
|
||||||
|
from ...db.config.config import get_engine_configuration
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def install():
|
||||||
|
logger.info("Installing")
|
||||||
|
engine_string, echo = get_engine_configuration()
|
||||||
|
logger.debug(f"engine_string: {engine_string}")
|
||||||
|
engine = create_engine(engine_string, echo=echo=="true", future=True) # TODO
|
||||||
|
metadata = Base.metadata
|
||||||
|
metadata.create_all(engine)
|
||||||
|
logger.info("Installed")
|
||||||
|
return engine
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
from sqlalchemy import create_engine
|
|
||||||
|
|
||||||
from ....schema.config.base import Base
|
|
||||||
from ....db.config.config import get_engine_configuration
|
|
||||||
|
|
||||||
def install():
|
|
||||||
engine_string, echo = get_engine_configuration()
|
|
||||||
engine = create_engine(engine_string, echo=echo)
|
|
||||||
metadata = Base.metadata(engine)
|
|
||||||
metadata.create_all()
|
|
||||||
return engine
|
|
||||||
8
app/api/config/cruds/env/create.py
vendored
8
app/api/config/cruds/env/create.py
vendored
@@ -1,8 +0,0 @@
|
|||||||
import os
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
from .....schema.config.env import Env
|
|
||||||
|
|
||||||
def create(session:Session, env:Env):
|
|
||||||
session.add(env)
|
|
||||||
os.environ[env.key] = env.value
|
|
||||||
session.commit()
|
|
||||||
9
app/api/config/cruds/env/delete.py
vendored
9
app/api/config/cruds/env/delete.py
vendored
@@ -1,9 +0,0 @@
|
|||||||
import os
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from .....schema.config.env import Env
|
|
||||||
|
|
||||||
def delete(session:Session, env:Env):
|
|
||||||
session.delete(env)
|
|
||||||
delete(os.environ[env.key])
|
|
||||||
session.commit()
|
|
||||||
11
app/api/config/cruds/env/read.py
vendored
11
app/api/config/cruds/env/read.py
vendored
@@ -1,11 +0,0 @@
|
|||||||
import os
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
from sqlalchemy import select
|
|
||||||
|
|
||||||
from .....schema.config.env import Env
|
|
||||||
|
|
||||||
def read(session:Session, _id:int):
|
|
||||||
stmt = select(Env).where(Env.id == _id)
|
|
||||||
env:Env = session.scalars(stmt).one()
|
|
||||||
os.environ[env.key] = env.value
|
|
||||||
return env
|
|
||||||
8
app/api/config/cruds/env/update.py
vendored
8
app/api/config/cruds/env/update.py
vendored
@@ -1,8 +0,0 @@
|
|||||||
import os
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from .....schema.config.env import Env
|
|
||||||
|
|
||||||
def update(session:Session, env:Env):
|
|
||||||
os.environ[env.key] = env.value
|
|
||||||
session.commit()
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
from .....schema.config.library import LibraryConfig
|
|
||||||
|
|
||||||
def create(session:Session, library:LibraryConfig):
|
|
||||||
session.add(library)
|
|
||||||
session.commit()
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from .....schema.config.library import LibraryConfig
|
|
||||||
|
|
||||||
def delete(session:Session, library:LibraryConfig):
|
|
||||||
session.delete(library)
|
|
||||||
session.commit()
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
from sqlalchemy import select
|
|
||||||
|
|
||||||
from .....schema.config.library import LibraryConfig
|
|
||||||
|
|
||||||
def read(session:Session, _id:int):
|
|
||||||
stmt = select(LibraryConfig).where(LibraryConfig.id == _id)
|
|
||||||
return session.scalars(stmt).one()
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from .....schema.config.library import LibraryConfig
|
|
||||||
|
|
||||||
def update(session:Session, library:LibraryConfig):
|
|
||||||
session.commit()
|
|
||||||
4
app/api/cruds/base/__init__.py
Normal file
4
app/api/cruds/base/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .create import create
|
||||||
|
from .read import read, read_all
|
||||||
|
from .update import update
|
||||||
|
from .delete import delete
|
||||||
15
app/api/cruds/base/create.py
Normal file
15
app/api/cruds/base/create.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from ....schema.library.base import Base
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def create(session:Session, library:Base):
|
||||||
|
logger.debug("Add Library")
|
||||||
|
session.add(library)
|
||||||
|
logger.debug("Added Library")
|
||||||
|
session.commit()
|
||||||
|
logger.debug("Committed Library")
|
||||||
|
session.refresh(library)
|
||||||
|
logger.debug("Refreshed Library")
|
||||||
|
return library
|
||||||
7
app/api/cruds/base/delete.py
Normal file
7
app/api/cruds/base/delete.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from ....schema.library.base import Base
|
||||||
|
|
||||||
|
def delete(session:Session, library:Base):
|
||||||
|
session.delete(library)
|
||||||
|
session.commit()
|
||||||
12
app/api/cruds/base/read.py
Normal file
12
app/api/cruds/base/read.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
|
from ....schema.library.base import Base
|
||||||
|
|
||||||
|
def read(session:Session, _id:int, obj:Base):
|
||||||
|
stmt = select(obj).where(obj.id == _id)
|
||||||
|
return session.scalars(stmt).one()
|
||||||
|
|
||||||
|
def read_all(session:Session, obj:Base):
|
||||||
|
stmt = select(obj)
|
||||||
|
return session.scalars(stmt).fetchall() #TODO: Pagination
|
||||||
6
app/api/cruds/base/update.py
Normal file
6
app/api/cruds/base/update.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from ....schema.library.base import Base
|
||||||
|
|
||||||
|
def update(session:Session, library:Base):
|
||||||
|
session.commit()
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
from .....schema.library.book import Book
|
|
||||||
|
|
||||||
def create(session:Session, book:Book):
|
|
||||||
session.add(book)
|
|
||||||
session.commit()
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from .....schema.library.book import Book
|
|
||||||
|
|
||||||
def delete(session:Session, book:Book):
|
|
||||||
session.delete(book)
|
|
||||||
session.commit()
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
from sqlalchemy import select
|
|
||||||
|
|
||||||
from .....schema.library.book import Book
|
|
||||||
|
|
||||||
def read(session:Session, _id:int):
|
|
||||||
stmt = select(Book).where(Book.id == _id)
|
|
||||||
return session.scalars(stmt).one()
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from .....schema.library.book import Book
|
|
||||||
|
|
||||||
def update(session:Session, book:Book):
|
|
||||||
session.commit()
|
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
__version__ = "0.2.0.dev"
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
|
from .routes.api import api
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
app.register_blueprint(api)
|
||||||
36
app/config/__init__.py
Normal file
36
app/config/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import configparser
|
||||||
|
|
||||||
|
from .defaults import default_db_query
|
||||||
|
from .defaults import default_app_port, default_app_debug
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("config.ini")
|
||||||
|
logger.debug(f"config: {config.sections()}")
|
||||||
|
|
||||||
|
|
||||||
|
def save_config():
|
||||||
|
with open("config.ini", "w") as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
def check_config():
|
||||||
|
save = False
|
||||||
|
if not "DataBase" in config:
|
||||||
|
logger.debug("DataBase not found in Config")
|
||||||
|
config["DataBase"] = {
|
||||||
|
"query": default_db_query
|
||||||
|
}
|
||||||
|
save = True
|
||||||
|
if not "App" in config:
|
||||||
|
logger.debug("App not found in Config")
|
||||||
|
|
||||||
|
config["App"] = {
|
||||||
|
"port": default_app_port,
|
||||||
|
"debug": default_app_debug
|
||||||
|
}
|
||||||
|
save = True
|
||||||
|
if save: save_config()
|
||||||
|
|
||||||
|
check_config()
|
||||||
3
app/config/defaults.py
Normal file
3
app/config/defaults.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
default_db_query = "sqlite:///library.db"
|
||||||
|
default_app_port = 15012
|
||||||
|
default_app_debug = False
|
||||||
117
app/controller/__init__.py
Normal file
117
app/controller/__init__.py
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
|
from app.api.cruds.base import create, read, update, delete, read_all
|
||||||
|
from app.schema.library import Library
|
||||||
|
from ..db.config.config import get_engine_configuration
|
||||||
|
|
||||||
|
from sqlalchemy.exc import NoResultFound
|
||||||
|
|
||||||
|
from .exceptions import LibraryCreationException
|
||||||
|
from .exceptions import LibraryReadException
|
||||||
|
from .exceptions import LibraryUpdateException
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class LibraryController:
|
||||||
|
def __init__(self, library_id=None, *, engine_string=None, echo=False, engine=None):
|
||||||
|
if engine_string is None:
|
||||||
|
engine_string, echo = get_engine_configuration()
|
||||||
|
if engine is None:
|
||||||
|
self._engine = create_engine(engine_string, echo=echo=="true", future=True)
|
||||||
|
else:
|
||||||
|
self._engine = engine
|
||||||
|
self._Session = sessionmaker(bind=self.engine)
|
||||||
|
self._session = self._Session()
|
||||||
|
self._library = None
|
||||||
|
if library_id is not None:
|
||||||
|
self.read(library_id)
|
||||||
|
|
||||||
|
self._libraries = []
|
||||||
|
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.session.close()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *exc):
|
||||||
|
del(self)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
return self._library
|
||||||
|
|
||||||
|
@property
|
||||||
|
def engine(self):
|
||||||
|
return self._engine
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Session(self):
|
||||||
|
return self._Session
|
||||||
|
|
||||||
|
@property
|
||||||
|
def session(self):
|
||||||
|
return self._session
|
||||||
|
|
||||||
|
@property
|
||||||
|
def libraries(self):
|
||||||
|
if self._library and len(self._libraries) == 0:
|
||||||
|
self._libraries = [self._library]
|
||||||
|
return self._libraries
|
||||||
|
|
||||||
|
def set_library(self, _id):
|
||||||
|
libraries = filter(lambda x: x.get("id") == _id, self.libraries)
|
||||||
|
if len(libraries) == 1:
|
||||||
|
self._library = libraries[0]
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
#CRUDS
|
||||||
|
def create(self, library:Library):
|
||||||
|
try:
|
||||||
|
self._library = create(self.session, library)
|
||||||
|
except IntegrityError as e:
|
||||||
|
raise LibraryCreationException(
|
||||||
|
"Cannot create library",
|
||||||
|
f"{e.orig}",
|
||||||
|
"library",
|
||||||
|
str(library)
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def read(self, _id):
|
||||||
|
try:
|
||||||
|
self._library = read(self.session, _id, Library)
|
||||||
|
except NoResultFound as e:
|
||||||
|
raise LibraryReadException(
|
||||||
|
f"Cannot read Library with id {_id}",
|
||||||
|
f"{e}",
|
||||||
|
"library",
|
||||||
|
_id
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def read_all(self):
|
||||||
|
self._libraries = read_all(self.session, Library)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
try:
|
||||||
|
self.session.commit()
|
||||||
|
except IntegrityError as e:
|
||||||
|
raise LibraryUpdateException(
|
||||||
|
f"Cannot update Library",
|
||||||
|
f"{e}",
|
||||||
|
"library",
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
delete(self.session, self.data)
|
||||||
|
del(self)
|
||||||
|
|
||||||
5
app/controller/exceptions/__init__.py
Normal file
5
app/controller/exceptions/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .base import LibraryExceptionBase
|
||||||
|
from .exc_01000X_data import LibraryDataExection
|
||||||
|
from .exc_01001X_create import LibraryCreationException
|
||||||
|
from .exc_01002X_read import LibraryReadException
|
||||||
|
from .exc_01003X_update import LibraryUpdateException
|
||||||
26
app/controller/exceptions/base.py
Normal file
26
app/controller/exceptions/base.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
class LibraryExceptionBase(Exception):
|
||||||
|
def __init__(self, name, error, object_name, data, *, status_code=400):
|
||||||
|
self.code = "000000"
|
||||||
|
self.name = name
|
||||||
|
self.error = error
|
||||||
|
self.object = object_name
|
||||||
|
self.data = data
|
||||||
|
self.status_code = status_code
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"name": self.name,
|
||||||
|
"code": self.code,
|
||||||
|
"error": self.error,
|
||||||
|
"object": self.object,
|
||||||
|
"data": self.data,
|
||||||
|
"status_code": self.status_code
|
||||||
|
}
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"ERROR {self.code}: {self.error}"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.__class__}(code={self.code!r}, error={self.error!r}), " \
|
||||||
|
f"object={self.object!r}, data={self.data!r}, status_code={self._status_code})"
|
||||||
7
app/controller/exceptions/exc_01000X_data.py
Normal file
7
app/controller/exceptions/exc_01000X_data.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .base import LibraryExceptionBase
|
||||||
|
|
||||||
|
#010010
|
||||||
|
class LibraryDataExection(LibraryExceptionBase):
|
||||||
|
def __init__(self, name, error, object_name, data, *, status_code=400):
|
||||||
|
self.code = "010000"
|
||||||
|
super().__init__(name, error, object_name, data, status_code=status_code)
|
||||||
7
app/controller/exceptions/exc_01001X_create.py
Normal file
7
app/controller/exceptions/exc_01001X_create.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .base import LibraryExceptionBase
|
||||||
|
|
||||||
|
#010010
|
||||||
|
class LibraryCreationException(LibraryExceptionBase):
|
||||||
|
def __init__(self, name, error, object_name, data, *, status_code=400):
|
||||||
|
self.code = "010010"
|
||||||
|
super().__init__(name, error, object_name, data, status_code=status_code)
|
||||||
7
app/controller/exceptions/exc_01002X_read.py
Normal file
7
app/controller/exceptions/exc_01002X_read.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .base import LibraryExceptionBase
|
||||||
|
|
||||||
|
#010030
|
||||||
|
class LibraryReadException(LibraryExceptionBase):
|
||||||
|
def __init__(self, name, error, object_name, data, status_code=404):
|
||||||
|
super().__init__(name, error, object_name, data, status_code=status_code)
|
||||||
|
self.code = "010030"
|
||||||
7
app/controller/exceptions/exc_01003X_update.py
Normal file
7
app/controller/exceptions/exc_01003X_update.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .base import LibraryExceptionBase
|
||||||
|
|
||||||
|
#010020
|
||||||
|
class LibraryUpdateException(LibraryExceptionBase):
|
||||||
|
def __init__(self, name, error, object_name, data, status_code=404):
|
||||||
|
super().__init__(name, error, object_name, data, status_code=status_code)
|
||||||
|
self.code = "010020"
|
||||||
1
app/controller/functions/__init__.py
Normal file
1
app/controller/functions/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .update_item_key import update_item_key
|
||||||
21
app/controller/functions/update_item_key.py
Normal file
21
app/controller/functions/update_item_key.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from ..exceptions import LibraryDataExection
|
||||||
|
from ...schema.library import Base
|
||||||
|
|
||||||
|
def update_item_key(obj:Base, key, value):
|
||||||
|
if key == "id":
|
||||||
|
raise LibraryDataExection(
|
||||||
|
"id is not updatable",
|
||||||
|
"The key ID is not Updatable",
|
||||||
|
obj.__class__,
|
||||||
|
{key: value}
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
obj.__getattribute__(key)
|
||||||
|
except AttributeError:
|
||||||
|
raise LibraryDataExection(
|
||||||
|
f"{key} not in {obj.__class__}",
|
||||||
|
f"The key {key} is not in {obj.__class__}",
|
||||||
|
obj.__class__,
|
||||||
|
{key: value}
|
||||||
|
)
|
||||||
|
obj.__setattr__(key, value)
|
||||||
@@ -1 +1 @@
|
|||||||
from .engine import engine
|
from .config import get_engine_configuration
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
from ...config import config
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def get_engine_configuration():
|
def get_engine_configuration():
|
||||||
engine_string = os.getenv("DEV_URIA_BIBLIOGAME_DEBUG", False)
|
echo = os.getenv("DEV_URIA_BIBLIOGAME_DEBUG", "false")
|
||||||
echo = os.getenv("DEV_URIA_BIBLIOGAME_CONFIG_DB", "sqlite://")
|
engine_string = os.getenv("DEV_URIA_BIBLIOGAME_CONFIG_DB", config["DataBase"].get("query", "sqlite:///"))
|
||||||
|
logger.debug(f"engine_string, {engine_string}")
|
||||||
return (engine_string, echo)
|
return (engine_string, echo)
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from sqlalchemy import create_engine
|
|
||||||
from .config import engine_string, echo
|
|
||||||
|
|
||||||
engine = create_engine(engine_string, echo=echo)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from .engine import engine
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
engine_string = "sqlite://"
|
|
||||||
echo = True
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from sqlalchemy import create_engine
|
|
||||||
from .config import engine_string, echo
|
|
||||||
|
|
||||||
engine = create_engine(engine_string, echo=echo)
|
|
||||||
40
app/routes/__init__.py
Normal file
40
app/routes/__init__.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from flask import json, make_response
|
||||||
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
|
from .api import api
|
||||||
|
from ..controller.exceptions import LibraryExceptionBase
|
||||||
|
|
||||||
|
@api.errorhandler(LibraryExceptionBase)
|
||||||
|
def handle_exception(e):
|
||||||
|
"""Return JSON instead of HTML for HTTP errors."""
|
||||||
|
# start with the correct headers and status code from the error
|
||||||
|
response = make_response()
|
||||||
|
# replace the body with JSON
|
||||||
|
response.data = json.dumps({
|
||||||
|
"status": "error",
|
||||||
|
"code": e.code,
|
||||||
|
"status_code": e.status_code,
|
||||||
|
"name": e.name,
|
||||||
|
"error": e.error,
|
||||||
|
"data": e.data
|
||||||
|
})
|
||||||
|
response.content_type = "application/json"
|
||||||
|
response.status_code = e.status_code
|
||||||
|
return response
|
||||||
|
|
||||||
|
@api.errorhandler(HTTPException)
|
||||||
|
def handle_exception(e):
|
||||||
|
"""Return JSON instead of HTML for HTTP errors."""
|
||||||
|
# start with the correct headers and status code from the error
|
||||||
|
response = e.get_response()
|
||||||
|
# replace the body with JSON
|
||||||
|
response.data = json.dumps({
|
||||||
|
"status": "error",
|
||||||
|
"code": f"000{e.code}",
|
||||||
|
"status_code": e.code,
|
||||||
|
"name": e.name,
|
||||||
|
"error": e.description,
|
||||||
|
})
|
||||||
|
response.content_type = "application/json"
|
||||||
|
return response
|
||||||
|
|
||||||
6
app/routes/api/__init__.py
Normal file
6
app/routes/api/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from .blueprint import api
|
||||||
|
from .install import post_install
|
||||||
|
|
||||||
|
from .library import api_library
|
||||||
|
|
||||||
|
api.register_blueprint(api_library)
|
||||||
3
app/routes/api/blueprint.py
Normal file
3
app/routes/api/blueprint.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
api = Blueprint("api", __name__, url_prefix="/api")
|
||||||
24
app/routes/api/install.py
Normal file
24
app/routes/api/install.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from flask import request
|
||||||
|
|
||||||
|
from ...config import config, save_config
|
||||||
|
from ...api.actions.install import install
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
from .blueprint import api
|
||||||
|
|
||||||
|
@api.route("/install", methods=["POST"])
|
||||||
|
def post_install():
|
||||||
|
try:
|
||||||
|
body = request.json
|
||||||
|
except:
|
||||||
|
logger.debug("Installing with config.ini params")
|
||||||
|
else:
|
||||||
|
if body.get("query_string"):
|
||||||
|
config["DataBase"]["query"] = body.get("query_string")
|
||||||
|
save_config()
|
||||||
|
finally:
|
||||||
|
install()
|
||||||
|
return { "status": "ok" }, 200
|
||||||
|
|
||||||
5
app/routes/api/library/__init__.py
Normal file
5
app/routes/api/library/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .blueprint import api_library
|
||||||
|
from .create import create_library
|
||||||
|
from .read import read_libraries, read_library
|
||||||
|
from .update import update_library
|
||||||
|
from .delete import delete_library
|
||||||
3
app/routes/api/library/blueprint.py
Normal file
3
app/routes/api/library/blueprint.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
api_library = Blueprint("api_library", __name__, url_prefix="/library")
|
||||||
19
app/routes/api/library/create.py
Normal file
19
app/routes/api/library/create.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from flask import request
|
||||||
|
|
||||||
|
from .blueprint import api_library
|
||||||
|
|
||||||
|
from ....controller import LibraryController
|
||||||
|
from ....schema.library.library import Library
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@api_library.route("/", methods=["POST"])
|
||||||
|
def create_library():
|
||||||
|
|
||||||
|
data = request.json
|
||||||
|
|
||||||
|
with LibraryController() as controller:
|
||||||
|
lib = Library(**data)
|
||||||
|
library = controller.create(lib)
|
||||||
|
return { "status": "ok", "result": library.data.to_dict() }, 200
|
||||||
19
app/routes/api/library/delete.py
Normal file
19
app/routes/api/library/delete.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from flask import request
|
||||||
|
from sqlalchemy.exc import NoResultFound
|
||||||
|
|
||||||
|
from .blueprint import api_library
|
||||||
|
|
||||||
|
from ....controller import LibraryController
|
||||||
|
from ....schema.library.library import Library
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@api_library.route("/<_id>", methods=["DELETE"])
|
||||||
|
def delete_library(_id):
|
||||||
|
|
||||||
|
controller = LibraryController(_id)
|
||||||
|
|
||||||
|
controller.delete()
|
||||||
|
|
||||||
|
return { "status": "ok" }, 201
|
||||||
21
app/routes/api/library/read.py
Normal file
21
app/routes/api/library/read.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from flask import request
|
||||||
|
from sqlalchemy.exc import NoResultFound
|
||||||
|
|
||||||
|
from .blueprint import api_library
|
||||||
|
|
||||||
|
from ....controller import LibraryController
|
||||||
|
from ....schema.library.library import Library
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@api_library.route("/", methods=["GET"])
|
||||||
|
def read_libraries():
|
||||||
|
library = LibraryController()
|
||||||
|
library.read_all()
|
||||||
|
return { "status": "ok", "results": [lib.to_dict() for lib in library.libraries] }, 200
|
||||||
|
|
||||||
|
@api_library.route("/<_id>", methods=["GET"])
|
||||||
|
def read_library(_id):
|
||||||
|
library = LibraryController(_id)
|
||||||
|
return { "status": "ok", "result": library.data.to_dict() }, 200
|
||||||
27
app/routes/api/library/update.py
Normal file
27
app/routes/api/library/update.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from flask import request
|
||||||
|
from sqlalchemy.exc import IntegrityError, NoResultFound
|
||||||
|
|
||||||
|
from .blueprint import api_library
|
||||||
|
|
||||||
|
from ....controller import LibraryController
|
||||||
|
from ....controller.functions import update_item_key
|
||||||
|
from ....schema.library.library import Library
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@api_library.route("/<_id>", methods=["PATCH"])
|
||||||
|
def update_library(_id):
|
||||||
|
|
||||||
|
data:dict = request.json
|
||||||
|
controller = LibraryController(_id)
|
||||||
|
|
||||||
|
library = controller.data
|
||||||
|
for key, value in data.items():
|
||||||
|
update_item_key(library, key, value)
|
||||||
|
|
||||||
|
controller.update()
|
||||||
|
|
||||||
|
return { "status": "ok", "result": controller.data.to_dict() }, 200
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from sqlalchemy.orm import DeclarativeBase
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
|
||||||
pass
|
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
from .base import Base
|
from .base import Base
|
||||||
from .book import Book
|
from .env import Env
|
||||||
from .tag import Tag
|
from .tag import Tag
|
||||||
|
from .path import Path
|
||||||
|
from .library import Library
|
||||||
|
from .book import Book
|
||||||
|
from .book_tag import BookTag
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import String
|
from sqlalchemy import String, ForeignKey, Integer
|
||||||
from sqlalchemy.orm import Mapped
|
from sqlalchemy.orm import Mapped
|
||||||
from sqlalchemy.orm import mapped_column
|
from sqlalchemy.orm import mapped_column
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
@@ -10,15 +10,22 @@ from .base import Base
|
|||||||
class Book(Base):
|
class Book(Base):
|
||||||
__tablename__ = "book"
|
__tablename__ = "book"
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
name: Mapped[str] = mapped_column(String(255))
|
hash: Mapped[Optional[str]] = mapped_column(String(255))
|
||||||
publisher: Mapped[str] = mapped_column(String(255))
|
file_name: Mapped[Optional[str]] = mapped_column(String(65656))
|
||||||
notes: Mapped[str] = mapped_column(String(65656))
|
file_path: Mapped[Optional[str]] = mapped_column(String(65656))
|
||||||
classification: Mapped[int] = mapped_column(primary_key=True)
|
name: Mapped[str] = mapped_column(String(65656))
|
||||||
|
publisher: Mapped[Optional[str]] = mapped_column(String(65656))
|
||||||
|
notes: Mapped[Optional[str]] = mapped_column(String(65656))
|
||||||
|
classification: Mapped[Optional[int]] = mapped_column(Integer)
|
||||||
|
|
||||||
tags: Mapped[List["Tags"]] = relationship(
|
library_id: Mapped[int] = mapped_column(ForeignKey("library.id"))
|
||||||
back_populates="books", cascade="all, delete-orphan"
|
library: Mapped[int] = relationship("Library", back_populates="books")
|
||||||
|
|
||||||
|
tags: Mapped[List["BookTag"]] = relationship(
|
||||||
|
back_populates="book", cascade="all, delete-orphan"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \
|
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}, " \
|
||||||
" notes={self.notes!r}, classification={self.classification!r})"
|
f"notes={self.notes!r}, classification={self.classification!r}), " \
|
||||||
|
f"hash={self.hash!r}, file_name={self.file_name}, file_path={self.file_path})"
|
||||||
21
app/schema/library/book_tag.py
Normal file
21
app/schema/library/book_tag.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
|
from sqlalchemy import String, ForeignKey
|
||||||
|
from sqlalchemy.orm import Mapped
|
||||||
|
from sqlalchemy.orm import mapped_column
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
|
from .base import Base
|
||||||
|
|
||||||
|
class BookTag(Base):
|
||||||
|
__tablename__ = "book_tag_relation"
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
|
||||||
|
book_id: Mapped[int] = mapped_column(ForeignKey("book.id"))
|
||||||
|
book: Mapped[int] = relationship("Book", back_populates="tags")
|
||||||
|
|
||||||
|
tag_id: Mapped[int] = mapped_column(ForeignKey("tag.id"))
|
||||||
|
tag: Mapped[int] = relationship("Tag", back_populates="books")
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"BookTag(id={self.id!r}, book_id={self.book_id!r}, tag_id={self.tag_id!r}"
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import String
|
from sqlalchemy import String
|
||||||
|
from sqlalchemy import ForeignKey
|
||||||
from sqlalchemy.orm import Mapped
|
from sqlalchemy.orm import Mapped
|
||||||
from sqlalchemy.orm import mapped_column
|
from sqlalchemy.orm import mapped_column
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
@@ -13,9 +14,8 @@ class Env(Base):
|
|||||||
key: Mapped[str] = mapped_column(String(255))
|
key: Mapped[str] = mapped_column(String(255))
|
||||||
value: Mapped[str] = mapped_column(String(65656))
|
value: Mapped[str] = mapped_column(String(65656))
|
||||||
|
|
||||||
libraries: Mapped[List["Library"]] = relationship(
|
library_id: Mapped[int] = mapped_column(ForeignKey("library.id"))
|
||||||
back_populates="libraries", cascade="all, delete-orphan"
|
library: Mapped[int] = relationship("Library", back_populates="envs")
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \
|
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \
|
||||||
40
app/schema/library/library.py
Normal file
40
app/schema/library/library.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
|
from sqlalchemy import String
|
||||||
|
from sqlalchemy.orm import Mapped
|
||||||
|
from sqlalchemy.orm import mapped_column
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
|
from .base import Base
|
||||||
|
|
||||||
|
class Library(Base):
|
||||||
|
__tablename__ = "library"
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str] = mapped_column(String(255), unique=True)
|
||||||
|
notes: Mapped[Optional[str]] = mapped_column(String(65656))
|
||||||
|
|
||||||
|
envs: Mapped[List["Env"]] = relationship(
|
||||||
|
back_populates="library", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
|
||||||
|
paths: Mapped[List["Path"]] = relationship(
|
||||||
|
back_populates="library", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
|
||||||
|
books: Mapped[List["Book"]] = relationship(
|
||||||
|
back_populates="library", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
|
||||||
|
tags: Mapped[List["Tag"]] = relationship(
|
||||||
|
back_populates="library", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"name": self.name,
|
||||||
|
"notes": self.notes
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Library(id={self.id!r}, name={self.name!r}, notes={self.notes!r})"
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import String
|
from sqlalchemy import String
|
||||||
|
from sqlalchemy import ForeignKey
|
||||||
from sqlalchemy.orm import Mapped
|
from sqlalchemy.orm import Mapped
|
||||||
from sqlalchemy.orm import mapped_column
|
from sqlalchemy.orm import mapped_column
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from .base import Base
|
from .base import Base
|
||||||
|
|
||||||
class LibraryConfig(Base):
|
class Path(Base):
|
||||||
__tablename__ = "library_config"
|
__tablename__ = "path"
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
name: Mapped[str] = mapped_column(String(255))
|
path: Mapped[str] = mapped_column(String(65656))
|
||||||
notes: Mapped[str] = mapped_column(String(65656))
|
|
||||||
connection_string = Mapped[str] = mapped_column(String(65656))
|
|
||||||
|
|
||||||
env: Mapped[List["Env"]] = relationship(
|
library_id: Mapped[int] = mapped_column(ForeignKey("library.id"))
|
||||||
back_populates="env", cascade="all, delete-orphan"
|
library: Mapped[int] = relationship("Library", back_populates="paths")
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \
|
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import String
|
from sqlalchemy import String, ForeignKey
|
||||||
from sqlalchemy.orm import Mapped
|
from sqlalchemy.orm import Mapped
|
||||||
from sqlalchemy.orm import mapped_column
|
from sqlalchemy.orm import mapped_column
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
@@ -12,10 +12,12 @@ class Tag(Base):
|
|||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
name: Mapped[str] = mapped_column(String(255))
|
name: Mapped[str] = mapped_column(String(255))
|
||||||
|
|
||||||
books: Mapped[List["Book"]] = relationship(
|
books: Mapped[List["BookTag"]] = relationship(
|
||||||
back_populates="tags", cascade="all, delete-orphan"
|
back_populates="tag", cascade="all, delete-orphan"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
library_id: Mapped[int] = mapped_column(ForeignKey("library.id"))
|
||||||
|
library: Mapped[int] = relationship("Library", back_populates="tags")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \
|
return f"Tag(id={self.id!r}, name={self.name!r})"
|
||||||
" notes={self.notes!r}, classification={self.classification!r})"
|
|
||||||
7
config.ini
Normal file
7
config.ini
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[DataBase]
|
||||||
|
query = sqlite:///test.db
|
||||||
|
|
||||||
|
[App]
|
||||||
|
port = 15012
|
||||||
|
debug = True
|
||||||
|
|
||||||
14
main.py
14
main.py
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
from app.config import config
|
||||||
|
|
||||||
|
import logging
|
||||||
|
level = config["App"].get("debug", False) and logging.DEBUG or logging.INFO
|
||||||
|
logging.basicConfig(level=level)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
logger.info(f"Logging level set to {level}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(port=config["App"]["port"], debug=config["App"]["debug"])
|
||||||
5
pyproject.toml
Normal file
5
pyproject.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[tool.pytest.ini_options]
|
||||||
|
log_cli = true
|
||||||
|
log_cli_level = "DEBUG"
|
||||||
|
log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)"
|
||||||
|
log_cli_date_format = "%Y-%m-%d %H:%M:%S"
|
||||||
@@ -2,3 +2,4 @@ flask
|
|||||||
sqlalchemy
|
sqlalchemy
|
||||||
requests
|
requests
|
||||||
py-dotenv
|
py-dotenv
|
||||||
|
pytest
|
||||||
149
tests/test_controller.py
Normal file
149
tests/test_controller.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from sqlalchemy.exc import NoResultFound
|
||||||
|
|
||||||
|
from app.api.actions import install
|
||||||
|
from app.schema.library import Library, Tag, Book, BookTag, Path, Env
|
||||||
|
from app.controller import LibraryController
|
||||||
|
|
||||||
|
from app.controller.exceptions import LibraryReadException
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class TestController(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
os.environ["DEV_URIA_BIBLIOGAME_CONFIG_DB"] = "sqlite:///"
|
||||||
|
os.environ["DEV_URIA_BIBLIOGAME_DEBUG"] = "false"
|
||||||
|
self.engine = install()
|
||||||
|
|
||||||
|
self.library = LibraryController(engine = self.engine)
|
||||||
|
|
||||||
|
self.tags = [
|
||||||
|
Tag(
|
||||||
|
name="Foo"
|
||||||
|
),
|
||||||
|
Tag(
|
||||||
|
name="Bar"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.library.create(Library(
|
||||||
|
name="Library Test",
|
||||||
|
notes="My duckling library test",
|
||||||
|
paths=[
|
||||||
|
Path(
|
||||||
|
path="/home/ivan/Documentos/ttprpg"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
envs=[
|
||||||
|
Env(
|
||||||
|
key="ENVIRONMENT_VARIABLE",
|
||||||
|
value="Clearly an environment variable"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
books=[
|
||||||
|
Book(
|
||||||
|
name="Test book",
|
||||||
|
tags=[
|
||||||
|
BookTag(
|
||||||
|
tag=self.tags[0]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tags=self.tags
|
||||||
|
))
|
||||||
|
return super().setUp()
|
||||||
|
|
||||||
|
def test_install(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
library_string = str(library.data)
|
||||||
|
self.assertEqual(library_string, str(self.library.data))
|
||||||
|
|
||||||
|
def test_read_name(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
logger.debug(f"Name: {library.data.name}")
|
||||||
|
self.assertEqual(library.data.name, self.library.data.name)
|
||||||
|
self.assertEqual(library.data.name, "Library Test")
|
||||||
|
|
||||||
|
def test_read_notes(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
logger.debug(f"Notes: {library.data.notes}")
|
||||||
|
self.assertEqual(library.data.notes, self.library.data.notes)
|
||||||
|
self.assertEqual(library.data.notes, "My duckling library test")
|
||||||
|
|
||||||
|
def test_read_path(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
logger.debug(f"PATH: {library.data.paths[0].path}")
|
||||||
|
self.assertEqual(library.data.paths[0].path, self.library.data.paths[0].path)
|
||||||
|
self.assertEqual(library.data.paths[0].path, "/home/ivan/Documentos/ttprpg")
|
||||||
|
|
||||||
|
def test_read_env(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
logger.debug(f"ENV: {library.data.envs[0].key} - {library.data.envs[0].value}")
|
||||||
|
self.assertEqual(library.data.envs[0].key, self.library.data.envs[0].key)
|
||||||
|
self.assertEqual(library.data.envs[0].value, self.library.data.envs[0].value)
|
||||||
|
self.assertEqual(library.data.envs[0].key, "ENVIRONMENT_VARIABLE")
|
||||||
|
self.assertEqual(library.data.envs[0].value, "Clearly an environment variable")
|
||||||
|
|
||||||
|
def test_read_book(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
book = library.data.books[0]
|
||||||
|
logger.debug(f"BOOK: {book}")
|
||||||
|
self.assertEqual(book.name, self.library.data.books[0].name)
|
||||||
|
self.assertEqual(book.name, "Test book")
|
||||||
|
|
||||||
|
def test_read_tags(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
tags = library.data.tags
|
||||||
|
self.assertEqual(str(tags), str(self.library.data.tags))
|
||||||
|
self.assertEqual(str(tags[0]), str(self.tags[0]))
|
||||||
|
self.assertEqual(tags[0].name, self.tags[0].name)
|
||||||
|
self.assertEqual(tags[0].name, "Foo")
|
||||||
|
self.assertEqual(str(tags[1]), str(self.tags[1]))
|
||||||
|
self.assertEqual(tags[1].name, self.tags[1].name)
|
||||||
|
self.assertEqual(tags[1].name, "Bar")
|
||||||
|
|
||||||
|
def test_read_book_tags(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
book = library.data.books[0]
|
||||||
|
tags = library.data.tags
|
||||||
|
logger.debug(f"BOOK TAGS: {book.tags}")
|
||||||
|
self.assertEqual(str(book.tags[0].tag), str(self.tags[0]))
|
||||||
|
self.assertEqual(str(book.tags[0].tag), str(tags[0]))
|
||||||
|
self.assertEqual(book.tags[0].tag.name, tags[0].name)
|
||||||
|
self.assertEqual(book.tags[0].tag.name, "Foo")
|
||||||
|
self.assertNotEqual(book.tags[0].tag.name, "Bar")
|
||||||
|
|
||||||
|
def test_update_name(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
library.data.name = "Another Library"
|
||||||
|
library.update()
|
||||||
|
library1 = LibraryController(1, engine=self.engine)
|
||||||
|
self.assertEqual(library1.data.name, self.library.data.name)
|
||||||
|
self.assertNotEqual(library1.data.name, "Library Test")
|
||||||
|
self.assertEqual(library1.data.name, "Another Library")
|
||||||
|
|
||||||
|
def test_update_name(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
library.data.books[0].name = "Another Book on the shelf"
|
||||||
|
library.update()
|
||||||
|
|
||||||
|
library1 = LibraryController(1, engine=self.engine)
|
||||||
|
book = library1.data.books[0]
|
||||||
|
|
||||||
|
self.assertEqual(book.name, self.library.data.books[0].name)
|
||||||
|
self.assertNotEqual(book.name, "Test book")
|
||||||
|
self.assertEqual(book.name, "Another Book on the shelf")
|
||||||
|
|
||||||
|
def test_delete_library(self):
|
||||||
|
library = LibraryController(1, engine=self.engine)
|
||||||
|
library.delete()
|
||||||
|
|
||||||
|
self.assertRaises(LibraryReadException, LibraryController, 1, engine=self.engine)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
150
tests/test_db.py
Normal file
150
tests/test_db.py
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy.exc import NoResultFound
|
||||||
|
|
||||||
|
from app.api.actions import install
|
||||||
|
from app.api.cruds.base import create, read, update, delete
|
||||||
|
from app.schema.library import Library, Path, Env, Book, Tag, BookTag
|
||||||
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class TestDB(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
os.environ["DEV_URIA_BIBLIOGAME_CONFIG_DB"] = "sqlite:///"
|
||||||
|
os.environ["DEV_URIA_BIBLIOGAME_DEBUG"] = "false"
|
||||||
|
self.engine = install()
|
||||||
|
self.tags = [
|
||||||
|
Tag(
|
||||||
|
name="Foo"
|
||||||
|
),
|
||||||
|
Tag(
|
||||||
|
name="Bar"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.library = Library(
|
||||||
|
name="Library Test",
|
||||||
|
notes="My duckling library test",
|
||||||
|
paths=[
|
||||||
|
Path(
|
||||||
|
path="/home/ivan/Documentos/ttprpg"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
envs=[
|
||||||
|
Env(
|
||||||
|
key="ENVIRONMENT_VARIABLE",
|
||||||
|
value="Clearly an environment variable"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
books=[
|
||||||
|
Book(
|
||||||
|
name="Test book",
|
||||||
|
tags=[
|
||||||
|
BookTag(
|
||||||
|
tag=self.tags[0]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tags=self.tags
|
||||||
|
)
|
||||||
|
self.Session = sessionmaker(bind=self.engine)
|
||||||
|
self.session = self.Session()
|
||||||
|
create(self.session, self.library)
|
||||||
|
return super().setUp()
|
||||||
|
|
||||||
|
def test_install(self):
|
||||||
|
library_string = str(read(self.session, 1, Library))
|
||||||
|
self.assertEqual(library_string, str(self.library))
|
||||||
|
|
||||||
|
def test_read_name(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
logger.debug(f"Name: {library.name}")
|
||||||
|
self.assertEqual(library.name, self.library.name)
|
||||||
|
self.assertEqual(library.name, "Library Test")
|
||||||
|
|
||||||
|
def test_read_notes(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
logger.debug(f"Notes: {library.notes}")
|
||||||
|
self.assertEqual(library.notes, self.library.notes)
|
||||||
|
self.assertEqual(library.notes, "My duckling library test")
|
||||||
|
|
||||||
|
def test_read_path(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
logger.debug(f"PATH: {library.paths[0].path}")
|
||||||
|
self.assertEqual(library.paths[0].path, self.library.paths[0].path)
|
||||||
|
self.assertEqual(library.paths[0].path, "/home/ivan/Documentos/ttprpg")
|
||||||
|
|
||||||
|
def test_read_env(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
logger.debug(f"ENV: {library.envs[0].key} - {library.envs[0].value}")
|
||||||
|
self.assertEqual(library.envs[0].key, self.library.envs[0].key)
|
||||||
|
self.assertEqual(library.envs[0].value, self.library.envs[0].value)
|
||||||
|
self.assertEqual(library.envs[0].key, "ENVIRONMENT_VARIABLE")
|
||||||
|
self.assertEqual(library.envs[0].value, "Clearly an environment variable")
|
||||||
|
|
||||||
|
def test_read_book(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
book = library.books[0]
|
||||||
|
logger.debug(f"BOOK: {book}")
|
||||||
|
self.assertEqual(book.name, self.library.books[0].name)
|
||||||
|
self.assertEqual(book.name, "Test book")
|
||||||
|
|
||||||
|
def test_read_tags(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
tags = library.tags
|
||||||
|
self.assertEqual(tags, self.library.tags)
|
||||||
|
self.assertEqual(tags, self.tags)
|
||||||
|
self.assertEqual(str(tags[0]), str(self.tags[0]))
|
||||||
|
self.assertEqual(tags[0].name, self.tags[0].name)
|
||||||
|
self.assertEqual(tags[0].name, "Foo")
|
||||||
|
self.assertEqual(str(tags[1]), str(self.tags[1]))
|
||||||
|
self.assertEqual(tags[1].name, self.tags[1].name)
|
||||||
|
self.assertEqual(tags[1].name, "Bar")
|
||||||
|
|
||||||
|
def test_read_book_tags(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
book = library.books[0]
|
||||||
|
tags = library.tags
|
||||||
|
logger.debug(f"BOOK TAGS: {book.tags}")
|
||||||
|
self.assertEqual(str(book.tags[0].tag), str(self.tags[0]))
|
||||||
|
self.assertEqual(str(book.tags[0].tag), str(tags[0]))
|
||||||
|
self.assertEqual(book.tags[0].tag.name, tags[0].name)
|
||||||
|
self.assertEqual(book.tags[0].tag.name, "Foo")
|
||||||
|
self.assertNotEqual(book.tags[0].tag.name, "Bar")
|
||||||
|
|
||||||
|
def test_update_name(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
library.name = "Another Library"
|
||||||
|
update(self.session, library)
|
||||||
|
library1 = read(self.session, 1)
|
||||||
|
self.assertEqual(library1.name, self.library.name)
|
||||||
|
self.assertNotEqual(library1.name, "Library Test")
|
||||||
|
self.assertEqual(library1.name, "Another Library")
|
||||||
|
|
||||||
|
def test_update_name(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
library.books[0].name = "Another Book on the shelf"
|
||||||
|
update(self.session, library)
|
||||||
|
|
||||||
|
library1 = read(self.session, 1, Library)
|
||||||
|
book = library1.books[0]
|
||||||
|
|
||||||
|
self.assertEqual(book.name, self.library.books[0].name)
|
||||||
|
self.assertNotEqual(book.name, "Test book")
|
||||||
|
self.assertEqual(book.name, "Another Book on the shelf")
|
||||||
|
|
||||||
|
def test_delete_library(self):
|
||||||
|
library = read(self.session, 1, Library)
|
||||||
|
delete(self.session, library)
|
||||||
|
|
||||||
|
self.assertRaises(NoResultFound, read, self.session, 1, Library)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user