daemon to use newer db access api
This commit is contained in:
parent
022f4a90a7
commit
6589f71052
@ -112,6 +112,7 @@ Roadmap
|
||||
- ~~Standardize on API ingest~~ done
|
||||
- Display and retrieval of images from the abstracted image store
|
||||
- Database support
|
||||
- Get the web UI code (daemon.py) using the same db access method as the api
|
||||
- Support any connection URI sqlalchemy is happy with
|
||||
- Tune certain databases if their uri is detected (sqlite and threads lol)
|
||||
- ~~Cache~~ done
|
||||
|
@ -79,7 +79,6 @@ class GfapiAdapter(StorageAdapter):
|
||||
pass # TODO gluster storage backend
|
||||
|
||||
|
||||
# This is largely duplicated from library.py, but written with intent for later refactoring to support abstract storage.
|
||||
class LibraryManager(object):
|
||||
def __init__(self, storage):
|
||||
assert isinstance(storage, StorageAdapter)
|
||||
@ -98,7 +97,8 @@ class PhotosApiV1(object):
|
||||
|
||||
@cherrypy.expose
|
||||
def index(self):
|
||||
yield f"<plaintext>hello, this is the api. my database is: {db}\n"
|
||||
cherrypy.response.headers["Content-type"] = "text/plain"
|
||||
return "Hello! This is the Photolib V1 API.\n"
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -13,6 +13,7 @@ from photoapp.dbutils import SAEnginePlugin, SATool
|
||||
import math
|
||||
from urllib.parse import urlparse
|
||||
from photoapp.utils import mime2ext, auth, require_auth, photo_auth_filter, slugify
|
||||
from photoapp.dbutils import db
|
||||
|
||||
|
||||
APPROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
|
||||
@ -45,14 +46,13 @@ class PhotosWeb(object):
|
||||
"""
|
||||
Return a dict containing variables expected to be on every page
|
||||
"""
|
||||
s = self.session()
|
||||
# all tags / albums with photos visible under the current auth context
|
||||
tagq = s.query(Tag).join(TagItem).join(PhotoSet)
|
||||
tagq = db.query(Tag).join(TagItem).join(PhotoSet)
|
||||
if not auth():
|
||||
tagq = tagq.filter(PhotoSet.status == PhotoStatus.public)
|
||||
tagq = tagq.filter(Tag.is_album == False).order_by(Tag.name).all() # pragma: manual auth
|
||||
|
||||
albumq = s.query(Tag).join(TagItem).join(PhotoSet)
|
||||
albumq = db.query(Tag).join(TagItem).join(PhotoSet)
|
||||
if not auth():
|
||||
albumq = albumq.filter(PhotoSet.status == PhotoStatus.public)
|
||||
albumq = albumq.filter(Tag.is_album == True).order_by(Tag.name).all() # pragma: manual auth
|
||||
@ -64,15 +64,8 @@ class PhotosWeb(object):
|
||||
"auth": auth(),
|
||||
"PhotoStatus": PhotoStatus
|
||||
}
|
||||
s.close()
|
||||
return ret
|
||||
|
||||
def session(self):
|
||||
"""
|
||||
Get a database session
|
||||
"""
|
||||
return self.library.session()
|
||||
|
||||
@cherrypy.expose
|
||||
def index(self):
|
||||
"""
|
||||
@ -85,10 +78,9 @@ class PhotosWeb(object):
|
||||
"""
|
||||
/feed - main photo feed - show photos sorted by date, newest first
|
||||
"""
|
||||
s = self.session()
|
||||
page, pgsize = int(page), int(pgsize)
|
||||
total_sets = photo_auth_filter(s.query(func.count(PhotoSet.id))).first()[0]
|
||||
images = photo_auth_filter(s.query(PhotoSet)).order_by(PhotoSet.date.desc()). \
|
||||
total_sets = photo_auth_filter(db.query(func.count(PhotoSet.id))).first()[0]
|
||||
images = photo_auth_filter(db.query(PhotoSet)).order_by(PhotoSet.date.desc()). \
|
||||
offset(pgsize * page).limit(pgsize).all()
|
||||
yield self.render("feed.html", images=[i for i in images], page=page, pgsize=int(pgsize), total_sets=total_sets)
|
||||
|
||||
@ -97,12 +89,11 @@ class PhotosWeb(object):
|
||||
"""
|
||||
/stats - show server statistics
|
||||
"""
|
||||
s = self.session()
|
||||
images = photo_auth_filter(s.query(func.count(PhotoSet.uuid),
|
||||
func.strftime('%Y', PhotoSet.date).label('year'),
|
||||
func.strftime('%m', PhotoSet.date).label('month'))). \
|
||||
images = photo_auth_filter(db.query(func.count(PhotoSet.uuid),
|
||||
func.strftime('%Y', PhotoSet.date).label('year'),
|
||||
func.strftime('%m', PhotoSet.date).label('month'))). \
|
||||
group_by('year', 'month').order_by(desc('year'), desc('month')).all()
|
||||
tsize = photo_auth_filter(s.query(func.sum(Photo.size)).join(PhotoSet)).scalar() # pragma: manual auth
|
||||
tsize = photo_auth_filter(db.query(func.sum(Photo.size)).join(PhotoSet)).scalar() # pragma: manual auth
|
||||
yield self.render("monthly.html", images=images, tsize=tsize)
|
||||
|
||||
@cherrypy.expose
|
||||
@ -112,8 +103,7 @@ class PhotosWeb(object):
|
||||
the given tag.
|
||||
TODO using so many coordinates is slow in the browser. dedupe them somehow.
|
||||
"""
|
||||
s = self.session()
|
||||
query = photo_auth_filter(s.query(PhotoSet)).filter(PhotoSet.lat != 0, PhotoSet.lon != 0)
|
||||
query = photo_auth_filter(db.query(PhotoSet)).filter(PhotoSet.lat != 0, PhotoSet.lon != 0)
|
||||
if a:
|
||||
query = query.join(TagItem).join(Tag).filter(Tag.uuid == a)
|
||||
if i:
|
||||
@ -131,44 +121,43 @@ class PhotosWeb(object):
|
||||
:param remove: target photos will have the tag specified by this uuid removed
|
||||
:param newtag: new tag name to create
|
||||
"""
|
||||
s = self.session()
|
||||
|
||||
def get_photos():
|
||||
if fromdate:
|
||||
dt = datetime.strptime(fromdate, "%Y-%m-%d")
|
||||
dt_end = dt + timedelta(days=1)
|
||||
photos = s.query(PhotoSet).filter(and_(PhotoSet.date >= dt,
|
||||
PhotoSet.date < dt_end)).order_by(PhotoSet.date)
|
||||
num_photos = s.query(func.count(PhotoSet.id)). \
|
||||
photos = db.query(PhotoSet).filter(and_(PhotoSet.date >= dt,
|
||||
PhotoSet.date < dt_end)).order_by(PhotoSet.date)
|
||||
num_photos = db.query(func.count(PhotoSet.id)). \
|
||||
filter(and_(PhotoSet.date >= dt, PhotoSet.date < dt_end)).order_by(PhotoSet.date).scalar()
|
||||
|
||||
if uuid:
|
||||
photos = s.query(PhotoSet).filter(PhotoSet.uuid == uuid)
|
||||
num_photos = s.query(func.count(PhotoSet.id)).filter(PhotoSet.uuid == uuid).scalar()
|
||||
photos = db.query(PhotoSet).filter(PhotoSet.uuid == uuid)
|
||||
num_photos = db.query(func.count(PhotoSet.id)).filter(PhotoSet.uuid == uuid).scalar()
|
||||
return photos, num_photos
|
||||
|
||||
if remove:
|
||||
rmtag = s.query(Tag).filter(Tag.uuid == remove).first()
|
||||
rmtag = db.query(Tag).filter(Tag.uuid == remove).first()
|
||||
photoq, _ = get_photos()
|
||||
for photo in photoq:
|
||||
s.query(TagItem).filter(TagItem.tag_id == rmtag.id, TagItem.set_id == photo.id).delete()
|
||||
s.commit()
|
||||
db.query(TagItem).filter(TagItem.tag_id == rmtag.id, TagItem.set_id == photo.id).delete()
|
||||
db.commit()
|
||||
|
||||
if newtag:
|
||||
s.add(Tag(title=newtag.capitalize(), name=newtag, slug=slugify(newtag)))
|
||||
s.commit()
|
||||
db.add(Tag(title=newtag.capitalize(), name=newtag, slug=slugify(newtag)))
|
||||
db.commit()
|
||||
|
||||
photos, num_photos = get_photos()
|
||||
|
||||
if tag: # Create the tag on all the photos
|
||||
tag = s.query(Tag).filter(Tag.uuid == tag).first()
|
||||
tag = db.query(Tag).filter(Tag.uuid == tag).first()
|
||||
for photo in photos.all():
|
||||
if 0 == s.query(func.count(TagItem.id)).filter(TagItem.tag_id == tag.id,
|
||||
TagItem.set_id == photo.id).scalar():
|
||||
s.add(TagItem(tag_id=tag.id, set_id=photo.id))
|
||||
s.commit()
|
||||
if 0 == db.query(func.count(TagItem.id)).filter(TagItem.tag_id == tag.id,
|
||||
TagItem.set_id == photo.id).scalar():
|
||||
db.add(TagItem(tag_id=tag.id, set_id=photo.id))
|
||||
db.commit()
|
||||
|
||||
alltags = s.query(Tag).order_by(Tag.name).all()
|
||||
alltags = db.query(Tag).order_by(Tag.name).all()
|
||||
yield self.render("create_tags.html", images=photos, alltags=alltags,
|
||||
num_photos=num_photos, fromdate=fromdate, uuid=uuid)
|
||||
|
||||
@ -207,21 +196,20 @@ class DateView(object):
|
||||
|
||||
@cherrypy.expose
|
||||
def index(self, date=None, page=0):
|
||||
s = self.master.session()
|
||||
if date:
|
||||
page = int(page)
|
||||
pgsize = 100
|
||||
dt = datetime.strptime(date, "%Y-%m-%d")
|
||||
dt_end = dt + timedelta(days=1)
|
||||
total_sets = photo_auth_filter(s.query(func.count(PhotoSet.id))). \
|
||||
total_sets = photo_auth_filter(db.query(func.count(PhotoSet.id))). \
|
||||
filter(and_(PhotoSet.date >= dt, PhotoSet.date < dt_end)).first()[0]
|
||||
images = photo_auth_filter(s.query(PhotoSet)).filter(and_(PhotoSet.date >= dt,
|
||||
PhotoSet.date < dt_end)).order_by(PhotoSet.date). \
|
||||
images = photo_auth_filter(db.query(PhotoSet)).filter(and_(PhotoSet.date >= dt,
|
||||
PhotoSet.date < dt_end)).order_by(PhotoSet.date). \
|
||||
offset(page * pgsize).limit(pgsize).all()
|
||||
yield self.master.render("date.html", page=page, pgsize=pgsize, total_sets=total_sets,
|
||||
images=[i for i in images], date=dt)
|
||||
return
|
||||
images = photo_auth_filter(s.query(PhotoSet, func.strftime('%Y-%m-%d',
|
||||
images = photo_auth_filter(db.query(PhotoSet, func.strftime('%Y-%m-%d',
|
||||
PhotoSet.date).label('gdate'),
|
||||
func.count('photos.id'),
|
||||
func.strftime('%Y', PhotoSet.date).label('year'),
|
||||
@ -242,9 +230,7 @@ class ThumbnailView(object):
|
||||
@cherrypy.expose
|
||||
def index(self, item_type, thumb_size, uuid):
|
||||
uuid = uuid.split(".")[0]
|
||||
s = self.master.session()
|
||||
|
||||
query = photo_auth_filter(s.query(Photo).join(PhotoSet))
|
||||
query = photo_auth_filter(db.query(Photo).join(PhotoSet))
|
||||
|
||||
query = query.filter(Photo.set.has(uuid=uuid)) if item_type == "set" \
|
||||
else query.filter(Photo.uuid == uuid) if item_type == "one" \
|
||||
@ -284,10 +270,8 @@ class DownloadView(object):
|
||||
@cherrypy.expose
|
||||
def index(self, item_type, uuid, preview=False):
|
||||
uuid = uuid.split(".")[0]
|
||||
s = self.master.session()
|
||||
|
||||
query = None if item_type == "set" \
|
||||
else photo_auth_filter(s.query(Photo)).filter(Photo.uuid == uuid) if item_type == "one" \
|
||||
else photo_auth_filter(db.query(Photo)).filter(Photo.uuid == uuid) if item_type == "one" \
|
||||
else None # TODO set download query
|
||||
|
||||
item = query.first()
|
||||
@ -311,8 +295,7 @@ class PhotoView(object):
|
||||
@cherrypy.expose
|
||||
def index(self, uuid):
|
||||
# uuid = uuid.split(".")[0]
|
||||
s = self.master.session()
|
||||
photo = photo_auth_filter(s.query(PhotoSet)).filter(or_(PhotoSet.uuid == uuid, PhotoSet.slug == uuid)).first()
|
||||
photo = photo_auth_filter(db.query(PhotoSet)).filter(or_(PhotoSet.uuid == uuid, PhotoSet.slug == uuid)).first()
|
||||
if not photo:
|
||||
raise cherrypy.HTTPError(404)
|
||||
yield self.master.render("photo.html", image=photo)
|
||||
@ -327,8 +310,7 @@ class PhotoView(object):
|
||||
* "Make private":
|
||||
* "Save": update the photo's title, description, and date_offset fields
|
||||
"""
|
||||
s = self.master.session()
|
||||
photo = s.query(PhotoSet).filter(PhotoSet.uuid == uuid).first()
|
||||
photo = db.query(PhotoSet).filter(PhotoSet.uuid == uuid).first()
|
||||
if op == "Make public":
|
||||
photo.status = PhotoStatus.public
|
||||
elif op == "Make private":
|
||||
@ -338,14 +320,13 @@ class PhotoView(object):
|
||||
photo.description = description
|
||||
photo.slug = slugify(title) or None
|
||||
photo.date_offset = int(offset) if offset else 0
|
||||
s.commit()
|
||||
db.commit()
|
||||
raise cherrypy.HTTPRedirect('/photo/{}'.format(photo.slug or photo.uuid), 302)
|
||||
|
||||
@cherrypy.expose
|
||||
@require_auth
|
||||
def edit(self, uuid):
|
||||
s = self.master.session()
|
||||
photo = photo_auth_filter(s.query(PhotoSet)).filter(PhotoSet.uuid == uuid).first()
|
||||
photo = photo_auth_filter(db.query(PhotoSet)).filter(PhotoSet.uuid == uuid).first()
|
||||
yield self.master.render("photo_edit.html", image=photo)
|
||||
|
||||
|
||||
@ -361,20 +342,18 @@ class TagView(object):
|
||||
def index(self, uuid, page=0):
|
||||
page = int(page)
|
||||
pgsize = 100
|
||||
s = self.master.session()
|
||||
|
||||
if uuid == "untagged":
|
||||
numphotos = photo_auth_filter(s.query(func.count(PhotoSet.id))). \
|
||||
filter(~PhotoSet.id.in_(s.query(TagItem.set_id))).scalar()
|
||||
photos = photo_auth_filter(s.query(PhotoSet)).filter(~PhotoSet.id.in_(s.query(TagItem.set_id))).\
|
||||
numphotos = photo_auth_filter(db.query(func.count(PhotoSet.id))). \
|
||||
filter(~PhotoSet.id.in_(db.query(TagItem.set_id))).scalar()
|
||||
photos = photo_auth_filter(db.query(PhotoSet)).filter(~PhotoSet.id.in_(db.query(TagItem.set_id))).\
|
||||
offset(page * pgsize). \
|
||||
limit(pgsize).all()
|
||||
yield self.master.render("untagged.html", images=photos, total_items=numphotos, pgsize=pgsize, page=page)
|
||||
else:
|
||||
tag = s.query(Tag).filter(or_(Tag.uuid == uuid, Tag.slug == uuid)).first()
|
||||
numphotos = photo_auth_filter(s.query(func.count(Tag.id)).join(TagItem).join(PhotoSet)). \
|
||||
tag = db.query(Tag).filter(or_(Tag.uuid == uuid, Tag.slug == uuid)).first()
|
||||
numphotos = photo_auth_filter(db.query(func.count(Tag.id)).join(TagItem).join(PhotoSet)). \
|
||||
filter(Tag.id == tag.id).scalar()
|
||||
photos = photo_auth_filter(s.query(PhotoSet)).join(TagItem).join(Tag). \
|
||||
photos = photo_auth_filter(db.query(PhotoSet)).join(TagItem).join(Tag). \
|
||||
filter(Tag.id == tag.id). \
|
||||
order_by(PhotoSet.date.desc()). \
|
||||
offset(page * pgsize). \
|
||||
@ -393,24 +372,23 @@ class TagView(object):
|
||||
- Make all public: mark all photos under this tag as public
|
||||
- Make all private: mark all photos under this tag as private
|
||||
"""
|
||||
s = self.master.session()
|
||||
tag = s.query(Tag).filter(or_(Tag.uuid == uuid, Tag.slug == uuid)).first()
|
||||
tag = db.query(Tag).filter(or_(Tag.uuid == uuid, Tag.slug == uuid)).first()
|
||||
if op == "Demote to tag":
|
||||
tag.is_album = 0
|
||||
elif op == "Promote to album":
|
||||
tag.is_album = 1
|
||||
elif op == "Delete tag":
|
||||
s.query(TagItem).filter(TagItem.tag_id == tag.id).delete()
|
||||
s.delete(tag)
|
||||
s.commit()
|
||||
db.query(TagItem).filter(TagItem.tag_id == tag.id).delete()
|
||||
db.delete(tag)
|
||||
db.commit()
|
||||
raise cherrypy.HTTPRedirect('/', 302)
|
||||
elif op == "Make all public":
|
||||
# TODO smarter query
|
||||
for photo in s.query(PhotoSet).join(TagItem).join(Tag).filter(Tag.id == tag.id).all():
|
||||
for photo in db.query(PhotoSet).join(TagItem).join(Tag).filter(Tag.id == tag.id).all():
|
||||
photo.status = PhotoStatus.public
|
||||
elif op == "Make all private":
|
||||
# TODO smarter query
|
||||
for photo in s.query(PhotoSet).join(TagItem).join(Tag).filter(Tag.id == tag.id).all():
|
||||
for photo in db.query(PhotoSet).join(TagItem).join(Tag).filter(Tag.id == tag.id).all():
|
||||
photo.status = PhotoStatus.private
|
||||
elif op == "Save":
|
||||
tag.title = title
|
||||
@ -418,14 +396,13 @@ class TagView(object):
|
||||
tag.slug = slugify(title)
|
||||
else:
|
||||
raise Exception("Invalid op: '{}'".format(op))
|
||||
s.commit()
|
||||
db.commit()
|
||||
raise cherrypy.HTTPRedirect('/tag/{}'.format(tag.slug or tag.uuid), 302)
|
||||
|
||||
@cherrypy.expose
|
||||
@require_auth
|
||||
def edit(self, uuid):
|
||||
s = self.master.session()
|
||||
tag = s.query(Tag).filter(Tag.uuid == uuid).first()
|
||||
tag = db.query(Tag).filter(Tag.uuid == uuid).first()
|
||||
yield self.master.render("tag_edit.html", tag=tag)
|
||||
|
||||
|
||||
@ -453,12 +430,12 @@ def main():
|
||||
web = PhotosWeb(library, tpl_dir)
|
||||
|
||||
def validate_password(realm, username, password):
|
||||
s = library.session()
|
||||
if s.query(User).filter(User.name == username, User.password == pwhash(password)).first():
|
||||
if db.query(User).filter(User.name == username, User.password == pwhash(password)).first():
|
||||
return True
|
||||
return False
|
||||
|
||||
cherrypy.tree.mount(web, '/', {'/': {'tools.trailing_slash.on': False,
|
||||
'tools.db.on': True,
|
||||
'error_page.403': web.error,
|
||||
'error_page.404': web.error},
|
||||
'/static': {"tools.staticdir.on": True,
|
||||
|
Loading…
Reference in New Issue
Block a user