Counts for tags in index
This commit is contained in:
parent
4b0b5c2c16
commit
adce8d067c
4 changed files with 51 additions and 31 deletions
|
@ -14,7 +14,7 @@ import tempfile
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
from anytree import NodeMixin, RenderTree, iterators
|
from anytree import NodeMixin, RenderTree, iterators
|
||||||
from anytree.exporter import JsonExporter
|
from anytree.exporter import JsonExporter, DictExporter
|
||||||
from anytree.importer import JsonImporter, DictImporter
|
from anytree.importer import JsonImporter, DictImporter
|
||||||
|
|
||||||
logger = logging.getLogger('svganim.strokes')
|
logger = logging.getLogger('svganim.strokes')
|
||||||
|
@ -538,10 +538,15 @@ class AnnotationIndex:
|
||||||
self.shelve['_annotations'][annotation.id] = annotation
|
self.shelve['_annotations'][annotation.id] = annotation
|
||||||
if annotation.tag not in self.shelve['_tags']:
|
if annotation.tag not in self.shelve['_tags']:
|
||||||
self.shelve['_tags'][annotation.tag] = [annotation]
|
self.shelve['_tags'][annotation.tag] = [annotation]
|
||||||
|
logger.error(f"Use of non-existing tag {annotation.tag}")
|
||||||
else:
|
else:
|
||||||
self.shelve['_tags'][annotation.tag].append(
|
self.shelve['_tags'][annotation.tag].append(
|
||||||
annotation
|
annotation
|
||||||
)
|
)
|
||||||
|
tag = self.root_tag.find_by_id(annotation.tag)
|
||||||
|
if tag is not None:
|
||||||
|
tag.annotation_count += 1
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def drawings(self) -> dict[str, Drawing]:
|
def drawings(self) -> dict[str, Drawing]:
|
||||||
|
@ -715,7 +720,7 @@ def strokes2D(strokes):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
class Tag(NodeMixin):
|
class Tag(NodeMixin):
|
||||||
def __init__(self, id, name = None, description = "", color = None, parent=None, children=None):
|
def __init__(self, id, name = None, description = "", color = None, parent=None, children=None, annotation_count=None):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = self.id if name is None else name
|
self.name = self.id if name is None else name
|
||||||
self.color = color
|
self.color = color
|
||||||
|
@ -724,6 +729,8 @@ class Tag(NodeMixin):
|
||||||
if children:
|
if children:
|
||||||
self.children = children
|
self.children = children
|
||||||
|
|
||||||
|
self.annotation_count = 0 #always zero!
|
||||||
|
|
||||||
if self.id == 'root' and not self.is_root:
|
if self.id == 'root' and not self.is_root:
|
||||||
logger.error("Root node shouldn't have a parent assigned")
|
logger.error("Root node shouldn't have a parent assigned")
|
||||||
|
|
||||||
|
@ -747,8 +754,11 @@ class Tag(NodeMixin):
|
||||||
return t
|
return t
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def toJson(self) -> str:
|
def toJson(self, with_counts=False) -> str:
|
||||||
return JsonExporter(indent=2).export(self)
|
ignore_counts=lambda attrs: [(k, v) for k, v in attrs if k != "annotation_count"]
|
||||||
|
attrFilter = None if with_counts else ignore_counts
|
||||||
|
|
||||||
|
return JsonExporter(DictExporter(attriter=attrFilter), indent=2).export(self)
|
||||||
|
|
||||||
def loadTagFromJson(string) -> Tag:
|
def loadTagFromJson(string) -> Tag:
|
||||||
tree: Tag = JsonImporter(DictImporter(Tag)).import_(string)
|
tree: Tag = JsonImporter(DictImporter(Tag)).import_(string)
|
||||||
|
|
|
@ -44,15 +44,21 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
;
|
;
|
||||||
} */
|
} */
|
||||||
#tags li.selected > ul>li.add-tag {
|
#tags li.selected>ul>li.add-tag {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tags > li > ul >li.add-tag {
|
#tags>li>ul>li.add-tag {
|
||||||
display: block;
|
display: block;
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#annotations .annotations-actions {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: darkgray;
|
||||||
|
font-size: 80%;
|
||||||
|
border-bottom: solid 3px #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,26 +79,30 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
#tags li:hover > div > input.rm-tag{
|
|
||||||
|
#tags li:hover>div>input.rm-tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
#tags li div{
|
|
||||||
position:relative
|
#tags li div {
|
||||||
|
position: relative
|
||||||
}
|
}
|
||||||
#tags input.rm-tag:hover{
|
|
||||||
|
#tags input.rm-tag:hover {
|
||||||
color: red;
|
color: red;
|
||||||
transform: rotate(20deg);
|
transform: rotate(20deg);
|
||||||
}
|
}
|
||||||
#tags input.rm-tag{
|
|
||||||
|
#tags input.rm-tag {
|
||||||
/* display: none; */
|
/* display: none; */
|
||||||
position:absolute;
|
position: absolute;
|
||||||
right:0;
|
right: 0;
|
||||||
top:0;
|
top: 0;
|
||||||
padding:0;
|
padding: 0;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tags .selected>div {
|
#tags .selected>div {
|
||||||
|
@ -143,9 +153,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#annotations li {
|
#annotations li {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#annotations img {
|
#annotations img {
|
||||||
/* width: 400px; */
|
/* width: 400px; */
|
||||||
|
@ -179,7 +187,6 @@
|
||||||
#annotations .svganim_player:hover .controls {
|
#annotations .svganim_player:hover .controls {
|
||||||
visibility: visible !important;
|
visibility: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script src="assets/nouislider-15.5.0.js"></script>
|
<script src="assets/nouislider-15.5.0.js"></script>
|
||||||
<script src="assets/wNumb-1.2.0.min.js"></script>
|
<script src="assets/wNumb-1.2.0.min.js"></script>
|
||||||
|
|
|
@ -558,9 +558,10 @@ class TagsHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
self.set_header("Content-Type", "application/json")
|
self.set_header("Content-Type", "application/json")
|
||||||
with open('www/tags.json', 'r') as fp:
|
self.write(self.index.root_tag.toJson(with_counts=True))
|
||||||
# TODO: enrich with counts
|
# with open('www/tags.json', 'r') as fp:
|
||||||
self.write(fp.read())
|
# # TODO: enrich with counts
|
||||||
|
# self.write(fp.read())
|
||||||
|
|
||||||
def put(self):
|
def put(self):
|
||||||
# data = json.loads(self.request.body)
|
# data = json.loads(self.request.body)
|
||||||
|
|
|
@ -8,12 +8,13 @@ class AnnotationManager {
|
||||||
|
|
||||||
this.selectedAnnotations = [];
|
this.selectedAnnotations = [];
|
||||||
this.selectedTag = null;
|
this.selectedTag = null;
|
||||||
this.loadTags(tagsUrl);
|
this.tagsUrl = tagsUrl;
|
||||||
|
this.loadTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTags(tagFile) {
|
loadTags() {
|
||||||
// tags config
|
// tags config
|
||||||
const request = new Request(tagFile);
|
const request = new Request(this.tagsUrl);
|
||||||
return fetch(request)
|
return fetch(request)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(rootTag => {
|
.then(rootTag => {
|
||||||
|
@ -31,7 +32,7 @@ class AnnotationManager {
|
||||||
const tagSubEl = document.createElement('ul');
|
const tagSubEl = document.createElement('ul');
|
||||||
const tagEl = document.createElement('div');
|
const tagEl = document.createElement('div');
|
||||||
|
|
||||||
tagEl.innerText = tag.get_name();
|
tagEl.innerText = `${tag.get_name()} (${tag.annotation_count ?? 0})`;
|
||||||
tagEl.addEventListener('click', (ev) => {
|
tagEl.addEventListener('click', (ev) => {
|
||||||
this.selectTag(tag);
|
this.selectTag(tag);
|
||||||
})
|
})
|
||||||
|
@ -318,6 +319,7 @@ class AnnotationManager {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.loadAnnotationsForTag(this.selectedTag)
|
this.loadAnnotationsForTag(this.selectedTag)
|
||||||
|
this.loadTags() //updates the counts
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveTags() {
|
async saveTags() {
|
||||||
|
|
Loading…
Reference in a new issue