Move export functions to their own module.
This commit is contained in:
parent
4f03815a30
commit
821a5f3371
6 changed files with 105 additions and 89 deletions
|
@ -20,7 +20,6 @@ Documentation is still a work in progress. In general, I use:
|
||||||
- Debate whether the `UserMixin.login` method should accept usernames or I should standardize the usage of IDs only internally.
|
- Debate whether the `UserMixin.login` method should accept usernames or I should standardize the usage of IDs only internally.
|
||||||
- Ability to access user page and user photos by user's ID, not just username.
|
- Ability to access user page and user photos by user's ID, not just username.
|
||||||
- Should album size be cached on disk?
|
- Should album size be cached on disk?
|
||||||
- Organize the tag exporter functions better.
|
|
||||||
- Replace columns like area, ratio, bitrate by using expression indices or views (`width * height` etc).
|
- Replace columns like area, ratio, bitrate by using expression indices or views (`width * height` etc).
|
||||||
- Add some way to support large image albums without flooding the search results. Considering a "hidden" property so that a handful of representative images can appear in the search results, and the rest can be found on the actual Album page.
|
- Add some way to support large image albums without flooding the search results. Considering a "hidden" property so that a handful of representative images can appear in the search results, and the rest can be found on the actual Album page.
|
||||||
- Add a `Photo.merge` to combine duplicate entries.
|
- Add a `Photo.merge` to combine duplicate entries.
|
||||||
|
|
|
@ -6,3 +6,4 @@ from . import jsonify
|
||||||
from . import objects
|
from . import objects
|
||||||
from . import photodb
|
from . import photodb
|
||||||
from . import searchhelpers
|
from . import searchhelpers
|
||||||
|
from . import tag_export
|
||||||
|
|
|
@ -14,6 +14,7 @@ from . import exceptions
|
||||||
from . import helpers
|
from . import helpers
|
||||||
from . import objects
|
from . import objects
|
||||||
from . import searchhelpers
|
from . import searchhelpers
|
||||||
|
from . import tag_export
|
||||||
|
|
||||||
from voussoirkit import cacheclass
|
from voussoirkit import cacheclass
|
||||||
from voussoirkit import expressionmatch
|
from voussoirkit import expressionmatch
|
||||||
|
@ -176,76 +177,6 @@ def searchfilter_must_may_forbid(photo_tags, tag_musts, tag_mays, tag_forbids, f
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def tag_export_easybake(tags, depth=0):
|
|
||||||
lines = []
|
|
||||||
for tag in tags:
|
|
||||||
if not hasattr(tag, 'string'):
|
|
||||||
tag.string = tag.name
|
|
||||||
children = tag.children()
|
|
||||||
synonyms = tag.synonyms()
|
|
||||||
lines.append(tag.string)
|
|
||||||
|
|
||||||
for synonym in synonyms:
|
|
||||||
synonym = tag.string + '+' + synonym
|
|
||||||
lines.append(synonym)
|
|
||||||
|
|
||||||
for child in children:
|
|
||||||
child.string = tag.string + '.' + child.name
|
|
||||||
child_bake = tag_export_easybake(children, depth=depth+1)
|
|
||||||
if child_bake != '':
|
|
||||||
lines.append(child_bake)
|
|
||||||
|
|
||||||
lines = '\n'.join(lines)
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def tag_export_json(tags):
|
|
||||||
def fill(tag):
|
|
||||||
children = {child.name:fill(child) for child in tag.children()}
|
|
||||||
return children
|
|
||||||
result = {}
|
|
||||||
for tag in tags:
|
|
||||||
result[tag.name] = fill(tag)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def tag_export_qualname_map(tags):
|
|
||||||
lines = tag_export_easybake(tags)
|
|
||||||
lines = lines.split('\n')
|
|
||||||
lines = [line for line in lines if line]
|
|
||||||
qualname_map = {}
|
|
||||||
for line in lines:
|
|
||||||
key = line.split('.')[-1].split('+')[-1]
|
|
||||||
value = line.split('+')[0]
|
|
||||||
qualname_map[key] = value
|
|
||||||
return qualname_map
|
|
||||||
|
|
||||||
def tag_export_stdout(tags, depth=0):
|
|
||||||
for tag in tags:
|
|
||||||
children = tag.children()
|
|
||||||
synonyms = tag.synonyms()
|
|
||||||
|
|
||||||
pad = ' ' * depth
|
|
||||||
synpad = ' ' * (depth + 1)
|
|
||||||
print(pad + str(tag))
|
|
||||||
|
|
||||||
for synonym in synonyms:
|
|
||||||
print(synpad + synonym)
|
|
||||||
|
|
||||||
tag_export_stdout(children, depth=depth+1)
|
|
||||||
|
|
||||||
if tag.parent() is None:
|
|
||||||
print()
|
|
||||||
|
|
||||||
@decorators.time_me
|
|
||||||
def tag_export_totally_flat(tags):
|
|
||||||
result = {}
|
|
||||||
for tag in tags:
|
|
||||||
for child in tag.walk_children():
|
|
||||||
children = list(child.walk_children())
|
|
||||||
result[child] = children
|
|
||||||
for synonym in child.synonyms():
|
|
||||||
result[synonym] = children
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
@ -725,7 +656,7 @@ class PDBPhotoMixin:
|
||||||
if self._cached_frozen_children:
|
if self._cached_frozen_children:
|
||||||
frozen_children = self._cached_frozen_children
|
frozen_children = self._cached_frozen_children
|
||||||
else:
|
else:
|
||||||
frozen_children = self.export_tags(tag_export_totally_flat)
|
frozen_children = tag_export.flat_dict(self.get_tags())
|
||||||
self._cached_frozen_children = frozen_children
|
self._cached_frozen_children = frozen_children
|
||||||
else:
|
else:
|
||||||
frozen_children = None
|
frozen_children = None
|
||||||
|
@ -916,20 +847,6 @@ class PDBTagMixin:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._tag_cache = cacheclass.Cache()
|
self._tag_cache = cacheclass.Cache()
|
||||||
|
|
||||||
def export_tags(self, exporter=tag_export_stdout, specific_tag=None):
|
|
||||||
'''
|
|
||||||
Send the top-level tags to function `exporter`.
|
|
||||||
Strings 'start' and 'stop' are sent before and after the tags are sent.
|
|
||||||
Recursion is to be handled by the exporter.
|
|
||||||
'''
|
|
||||||
if specific_tag is None:
|
|
||||||
items = list(self.get_tags())
|
|
||||||
items = [item for item in items if item.parent() is None]
|
|
||||||
items.sort(key=lambda x: x.name)
|
|
||||||
else:
|
|
||||||
items = [self.get_tag(specific_tag)]
|
|
||||||
return exporter(items)
|
|
||||||
|
|
||||||
def get_tag(self, name=None, id=None):
|
def get_tag(self, name=None, id=None):
|
||||||
'''
|
'''
|
||||||
Redirect to get_tag_by_id or get_tag_by_name after xor-checking the parameters.
|
Redirect to get_tag_by_id or get_tag_by_name after xor-checking the parameters.
|
||||||
|
|
98
etiquette/tag_export.py
Normal file
98
etiquette/tag_export.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
def easybake(tags):
|
||||||
|
'''
|
||||||
|
A string where every line is the qualified name of a tag or its synonyms.
|
||||||
|
|
||||||
|
people
|
||||||
|
people.family
|
||||||
|
people.family.mother
|
||||||
|
people.family.mother+mom
|
||||||
|
'''
|
||||||
|
lines = []
|
||||||
|
tags = list(tags)
|
||||||
|
for tag in tags:
|
||||||
|
qualname = tag.qualified_name()
|
||||||
|
lines.append(qualname)
|
||||||
|
lines.extend(qualname + '+' + syn for syn in tag.synonyms())
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
def flat_dict(tags):
|
||||||
|
'''
|
||||||
|
A dictionary where every tag is its own key, and the value is a list
|
||||||
|
containing itself all of its nested children.
|
||||||
|
Synonyms not included.
|
||||||
|
|
||||||
|
{
|
||||||
|
people: [people, family, mother],
|
||||||
|
family: [family, mother],
|
||||||
|
mother: [mother],
|
||||||
|
}
|
||||||
|
|
||||||
|
The list contains itself so that you can quickly ask whether a user's
|
||||||
|
requested tag exists in that tree without having to write separate checks
|
||||||
|
for equaling the main tag versus existing in the rest of the subtree.
|
||||||
|
'''
|
||||||
|
result = {}
|
||||||
|
for tag in tags:
|
||||||
|
for child in tag.walk_children():
|
||||||
|
children = list(child.walk_children())
|
||||||
|
result[child] = children
|
||||||
|
for synonym in child.synonyms():
|
||||||
|
result[synonym] = children
|
||||||
|
return result
|
||||||
|
|
||||||
|
def nested_dict(tags):
|
||||||
|
'''
|
||||||
|
A dictionary where keys are tags, values are recursive dictionaries
|
||||||
|
of children.
|
||||||
|
Synonyms not included.
|
||||||
|
|
||||||
|
{
|
||||||
|
people: {
|
||||||
|
family: {
|
||||||
|
mother: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
result = {}
|
||||||
|
for tag in tags:
|
||||||
|
result[tag] = nested_dict(tag)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def qualified_names(tags):
|
||||||
|
'''
|
||||||
|
A dictionary where keys are string names, values are qualified names.
|
||||||
|
Synonyms included.
|
||||||
|
|
||||||
|
{
|
||||||
|
'people': 'people',
|
||||||
|
'family': 'people.family',
|
||||||
|
'mother': 'people.family.mother',
|
||||||
|
'mom': 'people.family.mother',
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
results = {}
|
||||||
|
for tag in tags:
|
||||||
|
qualname = tag.qualified_name()
|
||||||
|
results[tag.name] = qualname
|
||||||
|
for synonym in tag.synonyms():
|
||||||
|
results[synonym] = qualname
|
||||||
|
return results
|
||||||
|
|
||||||
|
def stdout(tags, depth=0):
|
||||||
|
for tag in tags:
|
||||||
|
children = tag.children()
|
||||||
|
synonyms = tag.synonyms()
|
||||||
|
|
||||||
|
pad = ' ' * depth
|
||||||
|
print(pad + tag.name)
|
||||||
|
|
||||||
|
synpad = ' ' * (depth + 1)
|
||||||
|
for synonym in synonyms:
|
||||||
|
print(synpad + '+' + synonym)
|
||||||
|
|
||||||
|
stdout(children, depth=depth+1)
|
||||||
|
|
||||||
|
if tag.parent() is None:
|
||||||
|
print()
|
|
@ -565,7 +565,7 @@ def get_search_core():
|
||||||
def get_search_html():
|
def get_search_html():
|
||||||
search_results = get_search_core()
|
search_results = get_search_core()
|
||||||
search_kwargs = search_results['search_kwargs']
|
search_kwargs = search_results['search_kwargs']
|
||||||
qualname_map = P.export_tags(exporter=etiquette.photodb.tag_export_qualname_map)
|
qualname_map = etiquette.tag_export.qualified_names(P.get_tags())
|
||||||
session = session_manager.get(request)
|
session = session_manager.get(request)
|
||||||
response = flask.render_template(
|
response = flask.render_template(
|
||||||
'search.html',
|
'search.html',
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# Use with
|
# Use with
|
||||||
# py -i etiquette_easy.py
|
# py -i etiquette_easy.py
|
||||||
|
|
||||||
import etiquette.photodb
|
import etiquette
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
P = etiquette.photodb.PhotoDB()
|
P = etiquette.photodb.PhotoDB()
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ def easytagger():
|
||||||
if i.startswith('?'):
|
if i.startswith('?'):
|
||||||
i = i.split('?')[1] or None
|
i = i.split('?')[1] or None
|
||||||
try:
|
try:
|
||||||
P.export_tags(specific_tag=i)
|
etiquette.tag_export.stdout([P.get_tag(i)])
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in a new issue