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/ flask_session/
git-version.txt git-version.txt
settings.py settings.py
*.db

70
app.py
View File

@ -9,7 +9,7 @@ from matplotlib.figure import Figure
import matplotlib.dates as mdates import matplotlib.dates as mdates
import base64 import base64
from tqdm import tqdm from tqdm import tqdm
import datetime
from io import BytesIO from io import BytesIO
import logging import logging
@ -58,6 +58,21 @@ app.config.from_object(__name__)
Session(app) 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 # $ flask routes -s rule
# INFO:root:Using EagleEyev3 version 0.0.17 # INFO:root:Using EagleEyev3 version 0.0.17
# Endpoint Methods Rule # Endpoint Methods Rule
@ -185,9 +200,30 @@ def login_callback():
oauth_object = een.login_tokens(code) oauth_object = een.login_tokens(code)
logging.debug(oauth_object) logging.debug(oauth_object)
# let's try resaving this to see if it fixs the missing access_token on first request after login # let's try resaving this to see if it fixs the missing access_token on first request after login
session['een'] = een 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')) return redirect(url_for('index'))
@ -213,6 +249,17 @@ def cameras():
logging.debug(een.get_list_of_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 = { values = {
"current_user": een.current_user, "current_user": een.current_user,
"cameras": een.cameras, "cameras": een.cameras,
@ -329,6 +376,25 @@ def camera_list_of_videos(esn=None):
camera = een.get_camera_by_id(esn) 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())) 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 = { values = {
"current_user": een.current_user, "current_user": een.current_user,
"camera": { 'id': camera.id, 'name': camera.name }, "camera": { 'id': camera.id, 'name': camera.name },
@ -348,4 +414,4 @@ def camera_list_of_videos(esn=None):
if __name__ == '__main__': 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}"