Feature: move annotations from one tag to another
This commit is contained in:
parent
5fa5096c44
commit
4b0b5c2c16
5 changed files with 78 additions and 5 deletions
|
@ -522,6 +522,8 @@ class AnnotationIndex:
|
||||||
Drawing(fn, self.metadata_dir, self.drawing_dir) for fn in self.get_drawing_filenames()
|
Drawing(fn, self.metadata_dir, self.drawing_dir) for fn in self.get_drawing_filenames()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.root_tag = getRootTag()
|
||||||
self.shelve['_tags'] = {tag.id: [] for tag in self.root_tag.descendants}
|
self.shelve['_tags'] = {tag.id: [] for tag in self.root_tag.descendants}
|
||||||
self.shelve['_annotations'] = {}
|
self.shelve['_annotations'] = {}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from urllib.error import HTTPError
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
import tornado.web
|
import tornado.web
|
||||||
import tornado.websocket
|
import tornado.websocket
|
||||||
|
@ -12,6 +13,7 @@ import html
|
||||||
import argparse
|
import argparse
|
||||||
import coloredlogs
|
import coloredlogs
|
||||||
import glob
|
import glob
|
||||||
|
import filelock
|
||||||
import svganim.strokes
|
import svganim.strokes
|
||||||
import svganim.uimethods
|
import svganim.uimethods
|
||||||
|
|
||||||
|
@ -410,6 +412,41 @@ class AnnotationHandler(tornado.web.RequestHandler):
|
||||||
"tag": annotation.tag,
|
"tag": annotation.tag,
|
||||||
"audio": f"/annotation/{annotation.id}.mp3",
|
"audio": f"/annotation/{annotation.id}.mp3",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
def post(self, annotation_id):
|
||||||
|
"""change tag for given annotation"""
|
||||||
|
if annotation_id not in self.index.annotations:
|
||||||
|
raise tornado.web.HTTPError(404)
|
||||||
|
|
||||||
|
# might be set on file level, but let's try to avoid issues by keeping it simple
|
||||||
|
lock = filelock.FileLock("metadata_write.lock", timeout=10)
|
||||||
|
with lock:
|
||||||
|
newTagId = self.get_argument('tag_id')
|
||||||
|
if not self.index.has_tag(newTagId):
|
||||||
|
raise tornado.web.HTTPError(400)
|
||||||
|
|
||||||
|
annotation: svganim.strokes.Annotation = self.index.annotations[annotation_id]
|
||||||
|
|
||||||
|
logger.info(f"change tag from {annotation.tag} to {newTagId}")
|
||||||
|
|
||||||
|
# change metadata and reload index
|
||||||
|
metadata = annotation.drawing.get_metadata()
|
||||||
|
change = False
|
||||||
|
for idx, ann in enumerate(metadata['annotations']):
|
||||||
|
if ann['t_in'] == annotation.t_in and ann['t_out'] == annotation.t_out and annotation.tag == ann['tag']:
|
||||||
|
#found!?
|
||||||
|
metadata['annotations'][idx]['tag'] = newTagId
|
||||||
|
change = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if change == False:
|
||||||
|
raise HTTPError(409)
|
||||||
|
|
||||||
|
with open(annotation.drawing.metadata_fn, "w") as fp:
|
||||||
|
logger.info(f"save tag in {annotation.drawing.metadata_fn}")
|
||||||
|
json.dump(metadata, fp)
|
||||||
|
|
||||||
|
self.index.refresh()
|
||||||
|
|
||||||
|
|
||||||
class DrawingHandler(tornado.web.RequestHandler):
|
class DrawingHandler(tornado.web.RequestHandler):
|
||||||
|
@ -544,6 +581,9 @@ class TagsHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
with open('www/tags.json', 'w') as fp:
|
with open('www/tags.json', 'w') as fp:
|
||||||
fp.write(newTagsContent)
|
fp.write(newTagsContent)
|
||||||
|
|
||||||
|
# update as to load new tag into cache
|
||||||
|
self.index.refresh()
|
||||||
|
|
||||||
self.set_status(204)
|
self.set_status(204)
|
||||||
# print()
|
# print()
|
||||||
|
|
|
@ -301,9 +301,23 @@ class AnnotationManager {
|
||||||
this.saveTags();
|
this.saveTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
moveSelectedAnnotations(tag) {
|
async moveSelectedAnnotations(tag) {
|
||||||
// TODO: add button for this
|
// TODO: add button for this
|
||||||
alert(`This doesn't work yet! (move to tag ${tag.get_name()})`)
|
// alert(`This doesn't work yet! (move to tag ${tag.get_name()})`)
|
||||||
|
|
||||||
|
await Promise.all(this.selectedAnnotations.map(async (annotation) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('tag_id', tag.id)
|
||||||
|
return await fetch('/annotation/'+annotation.id, {
|
||||||
|
method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json'
|
||||||
|
// },
|
||||||
|
body: formData //JSON.stringify({'tag_id': tag.id})
|
||||||
|
}).catch((e) => alert('Something went wrong saving the tags'));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.loadAnnotationsForTag(this.selectedTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveTags() {
|
async saveTags() {
|
||||||
|
|
22
poetry.lock
generated
22
poetry.lock
generated
|
@ -2,7 +2,7 @@
|
||||||
name = "anytree"
|
name = "anytree"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
description = "Powerful and Lightweight Python Tree Data Structure.."
|
description = "Powerful and Lightweight Python Tree Data Structure.."
|
||||||
category = "main"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
@ -27,6 +27,18 @@ humanfriendly = ">=9.1"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
cron = ["capturer (>=2.4)"]
|
cron = ["capturer (>=2.4)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filelock"
|
||||||
|
version = "3.7.1"
|
||||||
|
description = "A platform independent file lock."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
|
||||||
|
testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humanfriendly"
|
name = "humanfriendly"
|
||||||
version = "10.0"
|
version = "10.0"
|
||||||
|
@ -58,7 +70,7 @@ python-versions = "*"
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
description = "Python 2 and 3 compatibility utilities"
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
category = "main"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
|
||||||
|
@ -81,7 +93,7 @@ python-versions = ">= 3.5"
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "1ad87cd5b37157a20ee2ec8f23d84b0275ca9f3aa3218c98eb18859d43aa5080"
|
content-hash = "11298c8670e03d4235a76555b1ca7e6cbf0740041f14c14fdf409f633e197bf5"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
anytree = [
|
anytree = [
|
||||||
|
@ -92,6 +104,10 @@ coloredlogs = [
|
||||||
{file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"},
|
{file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"},
|
||||||
{file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"},
|
{file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"},
|
||||||
]
|
]
|
||||||
|
filelock = [
|
||||||
|
{file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"},
|
||||||
|
{file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"},
|
||||||
|
]
|
||||||
humanfriendly = [
|
humanfriendly = [
|
||||||
{file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"},
|
{file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"},
|
||||||
{file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"},
|
{file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"},
|
||||||
|
|
|
@ -11,6 +11,7 @@ coloredlogs = "^15.0.1"
|
||||||
pydub = "^0.25.1"
|
pydub = "^0.25.1"
|
||||||
svgwrite = "^1.4.1"
|
svgwrite = "^1.4.1"
|
||||||
anytree = "^2.8.0"
|
anytree = "^2.8.0"
|
||||||
|
filelock = "^3.7.1"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue