Move export functions to their own module.

This commit is contained in:
voussoir 2017-06-11 22:55:45 -07:00
parent 4f03815a30
commit 821a5f3371
6 changed files with 105 additions and 89 deletions

View file

@ -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.
- Ability to access user page and user photos by user's ID, not just username.
- 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).
- 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.

View file

@ -6,3 +6,4 @@ from . import jsonify
from . import objects
from . import photodb
from . import searchhelpers
from . import tag_export

View file

@ -14,6 +14,7 @@ from . import exceptions
from . import helpers
from . import objects
from . import searchhelpers
from . import tag_export
from voussoirkit import cacheclass
from voussoirkit import expressionmatch
@ -176,76 +177,6 @@ def searchfilter_must_may_forbid(photo_tags, tag_musts, tag_mays, tag_forbids, f
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:
frozen_children = self._cached_frozen_children
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
else:
frozen_children = None
@ -916,20 +847,6 @@ class PDBTagMixin:
super().__init__()
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):
'''
Redirect to get_tag_by_id or get_tag_by_name after xor-checking the parameters.

98
etiquette/tag_export.py Normal file
View 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()

View file

@ -565,7 +565,7 @@ def get_search_core():
def get_search_html():
search_results = get_search_core()
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)
response = flask.render_template(
'search.html',

View file

@ -1,9 +1,10 @@
# Use with
# py -i etiquette_easy.py
import etiquette.photodb
import etiquette
import os
import sys
P = etiquette.photodb.PhotoDB()
import traceback
@ -13,7 +14,7 @@ def easytagger():
if i.startswith('?'):
i = i.split('?')[1] or None
try:
P.export_tags(specific_tag=i)
etiquette.tag_export.stdout([P.get_tag(i)])
except:
traceback.print_exc()
else: