WIP, most basic version is writing data to database

database
Mark Cotton 2023-09-06 16:49:42 -05:00
parent b6c35e015e
commit baf32f857d
3 changed files with 271 additions and 3 deletions

2
.gitignore vendored
View File

@ -10,5 +10,5 @@ my_settings.py
flask_session/
git-version.txt
settings.py
*.db

70
app.py
View File

@ -9,7 +9,7 @@ from matplotlib.figure import Figure
import matplotlib.dates as mdates
import base64
from tqdm import tqdm
import datetime
from io import BytesIO
import logging
@ -58,6 +58,21 @@ app.config.from_object(__name__)
Session(app)
from flask_sqlalchemy import SQLAlchemy
from models import *
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db.init_app(app)
with app.app_context():
db.create_all()
# $ flask routes -s rule
# INFO:root:Using EagleEyev3 version 0.0.17
# Endpoint Methods Rule
@ -186,8 +201,29 @@ def login_callback():
logging.debug(oauth_object)
# let's try resaving this to see if it fixs the missing access_token on first request after login
session['een'] = een
een.get_current_user()
try:
user = User.query.filter(User.email == een.current_user['email']).first()
if user is None:
user = User(account_id=een.current_user['accountId'],\
user_id=een.current_user['id'],\
email=een.current_user['email'],\
timezone=een.current_user['timeZone']['timeZone'],\
access_token=een.access_token,\
refresh_token=een.refresh_token,\
last_login=None)
user.save()
except Exception as e:
logging.warn(e)
return redirect(url_for('index'))
@ -213,6 +249,17 @@ def cameras():
logging.debug(een.get_list_of_cameras())
for c in een.cameras:
try:
check_camera = Camera.query.filter(Camera.device_id == c.id).first()
if check_camera is None:
new_camera = Camera(device_id=c.id,\
timezone=None, \
name=c.name)
new_camera.save()
except Exception as e:
logging.warn(e)
values = {
"current_user": een.current_user,
"cameras": een.cameras,
@ -329,6 +376,25 @@ def camera_list_of_videos(esn=None):
camera = een.get_camera_by_id(esn)
logging.debug(camera.get_list_of_videos(start_timestamp=een.time_before(hours=24), end_timestamp=een.time_now()))
db_camera = Camera.query.filter(Camera.device_id == esn).first()
if db_camera:
for v in camera.videos:
try:
check_video = Download.query.filter(Download.camera_id == db_camera.id)\
.filter(Download.url == v['mp4Url'])\
.first()
if check_video is None:
new_video = Download(url=v['mp4Url'],\
camera_id=db_camera.id,\
start_timestamp=datetime.fromisoformat(v['startTimestamp']),\
end_timestamp=datetime.fromisoformat(v['endTimestamp']),\
status='new',
error=None)
new_video.save()
except Exception as e:
logging.warn(f"saving videos to db: {e}")
values = {
"current_user": een.current_user,
"camera": { 'id': camera.id, 'name': camera.name },
@ -348,4 +414,4 @@ def camera_list_of_videos(esn=None):
if __name__ == '__main__':
app.run(host=config.server_host, port=config.server_port)
app.run(host=config['server_host'], port=config['server_port'])

202
models.py Normal file
View File

@ -0,0 +1,202 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import exc
from settings import *
db = SQLAlchemy()
__all__ = ['db', 'save_row', 'delete_row', 'User', 'Camera', 'Download' ]
#
# helper function to reduce repeated code
#
def commit_or_rollback(db, obj):
try:
db.session.add(obj)
db.session.commit()
except exc.IntegrityError as e:
print(f"caught an exception {e}")
db.session.rollback()
except exc.OperationalError as e:
print(f"caught an exception {e}")
db.session.rollback()
def delete_or_rollback(db, obj):
try:
db.session.delete(obj)
db.session.commit()
except exc.IntegrityError as e:
print(f"caught an exception {e}")
db.session.rollback()
except exc.OperationalError as e:
print(f"caught an exception {e}")
db.session.rollback()
def save_row(obj):
commit_or_rollback(db, obj)
def delete_row(obj):
delete_or_rollback(db, obj)
# trying out making my own BaseModel, follow this blogpost
# https://dev.to/chidioguejiofor/making-sqlalchemy-models-simpler-by-creating-a-basemodel-3m9c
class BaseModel(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
created = db.Column(db.DateTime, server_default=db.func.now())
updated = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now())
def before_save(self, *args, **kwargs):
pass
def after_save(self, *args, **kwargs):
pass
def save(self, commit=True):
self.before_save()
db.session.add(self)
if commit:
try:
db.session.commit()
except Exception as e:
db.session.rollback()
raise e
self.after_save()
def before_update(self, *args, **kwargs):
pass
def after_update(self, *args, **kwargs):
pass
def update(self, *args, **kwargs):
self.before_update(*args, **kwargs)
db.session.commit()
self.after_update(*args, **kwargs)
def delete(self, commit=True):
db.session.delete(self)
if commit:
db.session.commit()
@classmethod
def eager(cls, *args):
cols = [orm.joinedload(arg) for arg in args]
return cls.query.options(*cols)
@classmethod
def before_bulk_create(cls, iterable, *args, **kwargs):
pass
@classmethod
def after_bulk_create(cls, model_objs, *args, **kwargs):
pass
@classmethod
def bulk_create(cls, iterable, *args, **kwargs):
cls.before_bulk_create(iterable, *args, **kwargs)
model_objs = []
for data in iterable:
if not isinstance(data, cls):
data = cls(**data)
model_objs.append(data)
db.session.bulk_save_objects(model_objs)
if kwargs.get('commit', True) is True:
db.session.commit()
cls.after_bulk_create(model_objs, *args, **kwargs)
return model_objs
@classmethod
def bulk_create_or_none(cls, iterable, *args, **kwargs):
try:
return cls.bulk_create(iterable, *args, **kwargs)
except exc.IntegrityError as e:
db.session.rollback()
return None
class User(BaseModel):
'''
The User object stores basic data from EagleEye API along with cameras they have access to, and stores the access/refresh tokens.
'''
account_id = db.Column(db.String(8), nullable=True)
user_id = db.Column(db.String(8), nullable=True)
email = db.Column(db.String(), nullable=True)
timezone = db.Column(db.String(), nullable=True)
access_token = db.Column(db.String(), nullable=True)
refresh_token = db.Column(db.String(), nullable=True)
last_login = db.Column(db.DateTime, nullable=True)
cameras = db.relationship('Camera', backref=db.backref('user', lazy=True))
def __repr__(self):
return f"[User] {email}"
def to_dict(self):
return {
"account_id": self.account_id,
"user_id": self.user_id,
"email": self.email,
"timezone": self.timezone,
"access_token": self.access_token,
"refresh_token": self.refresh_token,
"last_login": self.last_login,
"cameras": self.cameras
}
class Camera(BaseModel):
'''
The Camera object holds the list of recordings (Downloads) and some basics about the device
'''
device_id = db.Column(db.String(8), nullable=True)
timezone = db.Column(db.String(), nullable=True)
name = db.Column(db.String(), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
downloads = db.relationship('Download', backref=db.backref('camera', lazy=True))
def __repr__(self):
return f"[Camera] {device_id} {name}"
def get_newest_download(self, status=None):
return None
def get_oldest_download(self, status=None):
return None
class Download(BaseModel):
'''
Downloads are the actual recording that should be downloaded.
It keeps its own state/progress/errors so it can be reported on later.
Downloads belong to a camera which by extension belongs to a user
'''
url = db.Column(db.String(), nullable=True)
camera_id = db.Column(db.Integer, db.ForeignKey('camera.id'), nullable=True)
start_timestamp = db.Column(db.DateTime, nullable=True)
end_timestamp = db.Column(db.DateTime, nullable=True)
status = db.Column(db.String(), nullable=True)
error = db.Column(db.String(), nullable=True)
def __repr__(self):
return f"[Download] {id} {camera_id} {status} {error}"