tagging on import
This commit is contained in:
parent
6765425be5
commit
4f10129c5a
|
@ -72,7 +72,7 @@ class PhotosApiV1(object):
|
||||||
ext = get_extension(file.filename)
|
ext = get_extension(file.filename)
|
||||||
assert ext in known_extensions
|
assert ext in known_extensions
|
||||||
photo_path = generate_storage_path(photo_date, photo_meta['hash'][0:8], ext)
|
photo_path = generate_storage_path(photo_date, photo_meta['hash'][0:8], ext)
|
||||||
|
# shouldn't we just trust the database?
|
||||||
if self.library.storage.exists(photo_path):
|
if self.library.storage.exists(photo_path):
|
||||||
return abort_upload(f"file already in library: {photo_path}")
|
return abort_upload(f"file already in library: {photo_path}")
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class PhotosApiV1(object):
|
||||||
|
|
||||||
# misc input validation
|
# misc input validation
|
||||||
# also if sha doesn't match uploaded metadata, abort
|
# also if sha doesn't match uploaded metadata, abort
|
||||||
# todo don't use asserts
|
# TODO don't use asserts
|
||||||
try:
|
try:
|
||||||
assert shasum == photo_meta["hash"], "uploaded file didn't match provided sha"
|
assert shasum == photo_meta["hash"], "uploaded file didn't match provided sha"
|
||||||
|
|
||||||
|
@ -130,8 +130,14 @@ class PhotosApiV1(object):
|
||||||
lat=Decimal(meta["lat"]) if meta["lat"] else None,
|
lat=Decimal(meta["lat"]) if meta["lat"] else None,
|
||||||
lon=Decimal(meta["lon"]) if meta["lon"] else None,
|
lon=Decimal(meta["lon"]) if meta["lon"] else None,
|
||||||
files=photo_objs) # TODO support title field et
|
files=photo_objs) # TODO support title field et
|
||||||
|
|
||||||
db.add(ps)
|
db.add(ps)
|
||||||
|
|
||||||
|
for tag_name in meta["tags"]:
|
||||||
|
tag = db.query(Tag).filter(Tag.name == tag_name).first()
|
||||||
|
assert tag, "unknown tag name"
|
||||||
|
db.add(TagItem(tag_id=tag.id, set=ps))
|
||||||
|
|
||||||
ps_json = ps.to_json() # we do this now to avoid a sqlalchemy bug where the object disappears after the commit
|
ps_json = ps.to_json() # we do this now to avoid a sqlalchemy bug where the object disappears after the commit
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -211,7 +217,7 @@ class PhotosApiV1(object):
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@cherrypy.popargs("uuid")
|
@cherrypy.popargs("uuid")
|
||||||
def tags(self, uuid=None, page=0, pagesize=50):
|
def tags(self, uuid=None, name=None, page=0, pagesize=50):
|
||||||
if cherrypy.request.method == "POST": # creating tag
|
if cherrypy.request.method == "POST": # creating tag
|
||||||
tagdata = json.loads(cherrypy.request.body.read())
|
tagdata = json.loads(cherrypy.request.body.read())
|
||||||
tagname = tagdata.get("name")
|
tagname = tagdata.get("name")
|
||||||
|
@ -228,12 +234,17 @@ class PhotosApiV1(object):
|
||||||
db.delete(tag)
|
db.delete(tag)
|
||||||
db.commit()
|
db.commit()
|
||||||
return {}
|
return {}
|
||||||
elif uuid: # getting tag
|
elif uuid or name: # getting tag
|
||||||
t = db.query(Tag).filter(Tag.uuid == uuid).first()
|
q = db.query(Tag)
|
||||||
|
if uuid:
|
||||||
|
q = q.filter(Tag.uuid == uuid)
|
||||||
|
if name:
|
||||||
|
q = q.filter(Tag.name == name)
|
||||||
|
t = q.first()
|
||||||
if not t:
|
if not t:
|
||||||
cherrypy.response.status = 404
|
cherrypy.response.status = 404
|
||||||
return {"error": "not found"}
|
return {"error": "not found"}
|
||||||
return t.to_json()
|
return [t.to_json()]
|
||||||
else: # getting all tags
|
else: # getting all tags
|
||||||
page, pagesize = int(page), int(pagesize)
|
page, pagesize = int(page), int(pagesize)
|
||||||
return [t.to_json() for t in
|
return [t.to_json() for t in
|
||||||
|
|
|
@ -4,7 +4,7 @@ import argparse
|
||||||
import requests
|
import requests
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
from photoapp.utils import get_extension
|
from photoapp.utils import get_extension
|
||||||
from photoapp.types import known_extensions, PhotoStatus
|
from photoapp.types import known_extensions, PhotoStatus, Tag
|
||||||
from photoapp.common import pwhash
|
from photoapp.common import pwhash
|
||||||
from photoapp.ingest import get_photosets
|
from photoapp.ingest import get_photosets
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
@ -93,8 +93,11 @@ class PhotoApiClient(object):
|
||||||
def create_tag(self, name, title, description):
|
def create_tag(self, name, title, description):
|
||||||
return self.post("tags", json={"name": name, "title": title, "description": description})
|
return self.post("tags", json={"name": name, "title": title, "description": description})
|
||||||
|
|
||||||
def list_tags(self):
|
def list_tags(self, name):
|
||||||
return self.get("tags")
|
params = {}
|
||||||
|
if name:
|
||||||
|
params["name"] = name
|
||||||
|
return self.get("tags", params=params)
|
||||||
|
|
||||||
# def delete_user(self, username):
|
# def delete_user(self, username):
|
||||||
# return self.delete("user", params={"username": username})
|
# return self.delete("user", params={"username": username})
|
||||||
|
@ -143,6 +146,7 @@ def get_args():
|
||||||
p_ingest = sp_action.add_parser("ingest", help="import images into the library")
|
p_ingest = sp_action.add_parser("ingest", help="import images into the library")
|
||||||
p_ingest.add_argument("-c", "--copy-of", help="existing uuid the imported images will be placed under")
|
p_ingest.add_argument("-c", "--copy-of", help="existing uuid the imported images will be placed under")
|
||||||
p_ingest.add_argument("-w", "--workers", default=1, type=int, help="number of parallel uploads")
|
p_ingest.add_argument("-w", "--workers", default=1, type=int, help="number of parallel uploads")
|
||||||
|
p_ingest.add_argument("-t", "--tag", help="tag name to apply to the import")
|
||||||
p_ingest.add_argument("files", nargs="+", help="files to import")
|
p_ingest.add_argument("files", nargs="+", help="files to import")
|
||||||
|
|
||||||
sp_action.add_parser("stats", help="show library statistics")
|
sp_action.add_parser("stats", help="show library statistics")
|
||||||
|
@ -161,7 +165,8 @@ def get_args():
|
||||||
p_create.add_argument("-t", "--title")
|
p_create.add_argument("-t", "--title")
|
||||||
p_create.add_argument("-d", "--description")
|
p_create.add_argument("-d", "--description")
|
||||||
|
|
||||||
p_tagaction.add_parser('list', help='list tags')
|
p_tag_list = p_tagaction.add_parser('list', help='list tags')
|
||||||
|
p_tag_list.add_argument("-n", "--name", help="tag name to lookup")
|
||||||
|
|
||||||
p_delete = p_tagaction.add_parser('delete', help='delete tags')
|
p_delete = p_tagaction.add_parser('delete', help='delete tags')
|
||||||
p_delete.add_argument("-n", "--name", required=True)
|
p_delete.add_argument("-n", "--name", required=True)
|
||||||
|
@ -228,6 +233,16 @@ def main():
|
||||||
raise
|
raise
|
||||||
|
|
||||||
elif args.action == "ingest":
|
elif args.action == "ingest":
|
||||||
|
tag = None
|
||||||
|
if args.tag:
|
||||||
|
tag = args.tag.lower()
|
||||||
|
try:
|
||||||
|
client.list_tags(name=tag).json()
|
||||||
|
except requests.exceptions.HTTPError as he:
|
||||||
|
if he.response.status_code != 404:
|
||||||
|
raise
|
||||||
|
client.create_tag(tag, tag, "")
|
||||||
|
|
||||||
sets, skipped = get_photosets(args.files)
|
sets, skipped = get_photosets(args.files)
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
|
@ -255,6 +270,13 @@ def main():
|
||||||
|
|
||||||
def upload_set(set_):
|
def upload_set(set_):
|
||||||
payload = set_.to_json()
|
payload = set_.to_json()
|
||||||
|
|
||||||
|
# add tags to set
|
||||||
|
if args.tag:
|
||||||
|
# it's kinda cheap to do this in json instead of building out the PhotoSet data structure
|
||||||
|
# but trust me it's worth it
|
||||||
|
payload["tags"] = [tag]
|
||||||
|
|
||||||
payload["files"] = {os.path.basename(photo.path): photo.to_json() for photo in set_.files}
|
payload["files"] = {os.path.basename(photo.path): photo.to_json() for photo in set_.files}
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
|
@ -334,7 +356,7 @@ def main():
|
||||||
client.create_tag(args.name, args.title, args.description)
|
client.create_tag(args.name, args.title, args.description)
|
||||||
elif args.action_tag == "list":
|
elif args.action_tag == "list":
|
||||||
rows = []
|
rows = []
|
||||||
for tag in client.list_tags().json():
|
for tag in client.list_tags(name=args.name).json():
|
||||||
rows.append([tag["name"], tag["title"], maybetruncate(tag["description"], 24), tag["uuid"]])
|
rows.append([tag["name"], tag["title"], maybetruncate(tag["description"], 24), tag["uuid"]])
|
||||||
print(tabulate(rows, headers=["name", "title", "description", "uuid"]))
|
print(tabulate(rows, headers=["name", "title", "description", "uuid"]))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue