From 4f10129c5aec33c48b023ffb15a5ec39e31a5710 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 14 Oct 2019 19:25:53 -0700 Subject: [PATCH] tagging on import --- photoapp/api.py | 23 +++++++++++++++++------ photoapp/cli.py | 32 +++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/photoapp/api.py b/photoapp/api.py index 87d4455..1a5a552 100644 --- a/photoapp/api.py +++ b/photoapp/api.py @@ -72,7 +72,7 @@ class PhotosApiV1(object): ext = get_extension(file.filename) assert ext in known_extensions 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): return abort_upload(f"file already in library: {photo_path}") @@ -84,7 +84,7 @@ class PhotosApiV1(object): # misc input validation # also if sha doesn't match uploaded metadata, abort - # todo don't use asserts + # TODO don't use asserts try: 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, lon=Decimal(meta["lon"]) if meta["lon"] else None, files=photo_objs) # TODO support title field et + 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 try: @@ -211,7 +217,7 @@ class PhotosApiV1(object): @cherrypy.expose @cherrypy.tools.json_out() @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 tagdata = json.loads(cherrypy.request.body.read()) tagname = tagdata.get("name") @@ -228,12 +234,17 @@ class PhotosApiV1(object): db.delete(tag) db.commit() return {} - elif uuid: # getting tag - t = db.query(Tag).filter(Tag.uuid == uuid).first() + elif uuid or name: # getting tag + 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: cherrypy.response.status = 404 return {"error": "not found"} - return t.to_json() + return [t.to_json()] else: # getting all tags page, pagesize = int(page), int(pagesize) return [t.to_json() for t in diff --git a/photoapp/cli.py b/photoapp/cli.py index 4df05e4..56f80e8 100644 --- a/photoapp/cli.py +++ b/photoapp/cli.py @@ -4,7 +4,7 @@ import argparse import requests from requests.exceptions import HTTPError 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.ingest import get_photosets from urllib.parse import urlparse @@ -93,8 +93,11 @@ class PhotoApiClient(object): def create_tag(self, name, title, description): return self.post("tags", json={"name": name, "title": title, "description": description}) - def list_tags(self): - return self.get("tags") + def list_tags(self, name): + params = {} + if name: + params["name"] = name + return self.get("tags", params=params) # def delete_user(self, 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.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("-t", "--tag", help="tag name to apply to the import") p_ingest.add_argument("files", nargs="+", help="files to import") 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("-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.add_argument("-n", "--name", required=True) @@ -228,6 +233,16 @@ def main(): raise 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) rows = [] @@ -255,6 +270,13 @@ def main(): def upload_set(set_): 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} files = [] @@ -334,7 +356,7 @@ def main(): client.create_tag(args.name, args.title, args.description) elif args.action_tag == "list": 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"]]) print(tabulate(rows, headers=["name", "title", "description", "uuid"]))