Skip to content

Commit

Permalink
Merge pull request #16 from SciTools-incubator/AddSelectorNames
Browse files Browse the repository at this point in the history
Add selector names to web pages
  • Loading branch information
Malcolm Brooks authored Sep 27, 2016
2 parents a3123ec + ca4be0b commit a8d9dcb
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 43 deletions.
5 changes: 3 additions & 2 deletions lib/ImageMetaTag/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'''
ImageMetaTag is a python package built around a wrapper for
`savefig <http://matplotlib.org/api/pyplot_api.html?highlight=savefig#matplotlib.pyplot.savefig>`_ in
`savefig <http://matplotlib.org/api/pyplot_api.html?highlight=savefig#matplotlib.pyplot.savefig>`
in
`matplotlib <http://matplotlib.org/>`_, which adds metadata tags to supported image file formats.
Once the images have been tagged, it can also be used to manage an
Expand Down Expand Up @@ -29,4 +30,4 @@
from ImageMetaTag.img_dict import ImageDict, readmeta_from_image, dict_heirachy_from_list, \
dict_split, simple_dict_filter, check_for_required_keys
# we want all of the functions in webpage and db, as a separate level
import webpage, db
import ImageMetaTag.webpage, ImageMetaTag.db
6 changes: 4 additions & 2 deletions lib/ImageMetaTag/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ def del_plots_from_dbfile(db_file, filenames, do_vacuum=True, allow_retries=True
dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
except:
if not skip_warning:
# if this fails, print a warning... need to figure out why this happens
# if this fails, print a warning...
# need to figure out why this happens
print 'WARNING: unable to delete file entry: "%s", type "%s" from database' \
% (fname, type(fname))
dbcn.commit()
Expand All @@ -458,7 +459,8 @@ def del_plots_from_dbfile(db_file, filenames, do_vacuum=True, allow_retries=True
dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
except:
if not skip_warning:
# if this fails, print a warning... need to figure out why this happens
# if this fails, print a warning...
# need to figure out why this happens
print 'WARNING: unable to delete file entry: "%s", type "%s" from database' \
% (fname, type(fname))
# commit every 100 to give other processes a chance:
Expand Down
46 changes: 25 additions & 21 deletions lib/ImageMetaTag/img_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class ImageDict():
of metadata items, use :func:`ImageMetaTag.dict_heirachy_from_list`
Options:
* full_name_mapping - a dictionary mapping each tagname to a full name/description of what \
the metadata item means.
* level_names - a list of the tagnames, in full, giving a name/description of what \
the metadata item means. Ordered by level of the input dict.
* selector_widths - a list of html strings giving the widths of each selector in the \
output webpage.
* selector_animated - an integer indicating which selector on the output webpage is \
Expand All @@ -38,25 +38,29 @@ class ImageDict():
moves backwards.
'''
def __init__(self, input_dict, full_name_mapping=None,
def __init__(self, input_dict, level_names=None,
selector_widths=None, selector_animated=None,
animation_direction=None):

if level_names is None:
self.level_names = None
else:
if not isinstance(level_names, list):
msg = 'A mapping of key names to full names has been supplied, but it is not a list'
raise ValueError(msg)
else:
self.level_names = level_names

# set the dictionary:
self.dict = input_dict
# now list the keys, at each level, as lists. These can be reorderd by the calling routine,
# so when the dictionary is written out, they can be in the desired order:
self.list_keys_by_depth()

dict_depth = self.dict_depth()

if full_name_mapping is None:
self.full_name_mapping = None
else:
if not isinstance(full_name_mapping, dict):
msg = 'A mapping of key names to full names has been supplied, but it is not a dict'
raise ValueError(msg)
else:
self.full_name_mapping = full_name_mapping
if self.level_names is not None:
if dict_depth != len(self.level_names):
raise ValueError('Mismatch between depth of dictionary and level_names')

# this controls the width of the selector, on the resultant webpage:
if selector_widths is None:
Expand Down Expand Up @@ -119,8 +123,13 @@ def append(self, new_dict, devmode=False, skip_key_relist=False):
if not skip_key_relist:
self.list_keys_by_depth(devmode=devmode)

# TODO: if there is a full_name_mapping, check that the
# new item hasn't added something to it
# if there is a level_names, check that the
# new dict is consistent:
if isinstance(new_dict, ImageDict) and self.level_names is not None:
if new_dict.level_names is not None:
if self.level_names != new_dict.level_names:
raise ValueError('Attempting to append two ImageDict objects with'
' different level_names')

def dict_union(self, in_dict, new_dict):
'produces the union of a dictionary of dictionaries'
Expand Down Expand Up @@ -178,9 +187,6 @@ def remove(self, rm_dict, skip_key_relist=False):
if not skip_key_relist:
self.list_keys_by_depth()

# TODO: if there is a full_name_mapping, check that the
# new item hasn't removed something to it

def dict_remove(self, in_dict, rm_dict):
'''
removes a dictionary of dictionaries from another, larger, one.
Expand Down Expand Up @@ -275,10 +281,8 @@ def list_keys_by_depth(self, devmode=False):

out_keys = {}
for level in keys.keys():
# convert to a list:
out_keys[level] = list(keys[level])
# and sort HERE:
out_keys[level] = sorted(out_keys[level])
# convert to a sorted list:
out_keys[level] = sorted(list(keys[level]))

self.keys = out_keys
self.subdirs = sorted(list(subdirs))
Expand Down
62 changes: 50 additions & 12 deletions lib/ImageMetaTag/webpage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'''
Sub-module containing functions to write out an ImageDict to a webpage.
Sub-module containing functions to write out an :class:`ImageMetaTag.ImageDict` to a webpage.
This can either be done using write_full_page, to produce a page with just a set of
selectors to browse the ImageDict, or the different components can be added to a
Expand Down Expand Up @@ -31,10 +31,10 @@

def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=None,
preamble=None, postamble=None, internal=False,
initial_selectors=None,
initial_selectors=None, show_selector_names=False,
url_type='int', only_show_rel_url=False, verbose=False):
'''
Writes out an ImageDict as a webpage, to a given file location.
Writes out an :class:`ImageMetaTag.ImageDict` as a webpage, to a given file location.
The file is overwritten.
Currently only able to write out a page with horizontal dropdown menus, but other
Expand All @@ -50,6 +50,8 @@ def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=No
* internal - If True, internal copies of the dojo Javascript API and css files will be used.
* initial_selectors - A list of initial values for the selectors, to be passed into \
:func:`ImageMetaTag.webpage.write_js_setup`.
* show_selector_names - switches on diplsaying the selector full names defined by the \
:class:`ImageMetaTag.ImageDict`.full_name_mapping
* url_type - determines the type of URL at the bottom of the ImageMetaTag section. Can be \
'int' or 'str'.
* only_show_rel_url - If True, the wepage will only show relative urls in is link section.
Expand Down Expand Up @@ -84,9 +86,12 @@ def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=No
file_list_name=file_list_name,
initial_selectors=initial_selectors, pagename=page_filename,
url_separator='|', url_type=url_type)
# now write out the end, which includes the placeholders for the actual stuff that appears on the page:
# now write out the end, which includes the placeholders for the actual
# stuff that appears on the page:
# (if show_selector_names is False, then the input level_names list is empty):
write_js_placeholders(file_obj=out_file, dict_depth=img_dict.dict_depth(),
style='horiz dropdowns')
style='horiz dropdowns',
level_names=show_selector_names * img_dict.level_names)

if not postamble is None:
out_file.write(postamble)
Expand Down Expand Up @@ -179,7 +184,7 @@ def write_js(img_dict, file_obj=None, selector_prefix=None, list_prefix=None, fi
only_show_rel_url=False, devmode=False):
'''
Writes an ImageDict to a file object, as a set of javascript variables.
* selector_prefix - prefix for the variable names of the selectors (these are visible to \
those people viewing the webpage!)
* list_prefix - prefix to the javascript variable names to hold the lists indices that map \
Expand Down Expand Up @@ -264,7 +269,7 @@ def write_js(img_dict, file_obj=None, selector_prefix=None, list_prefix=None, fi
# write out the file names in two stages, first the subdirectories, then use those
# in the filenames..
file_obj.write('var sd = %s;\n' % img_dict.subdirs)

file_obj.write('var file_list = [\n')
for (item, ind_of_item) in enumerate(key_ind):
tmp_dict = img_dict.dict
Expand Down Expand Up @@ -913,7 +918,7 @@ def write_js_setup(img_dict, file_obj=None, pagename=None, tab_s_name=None,
''')

def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None,
style='horiz dropdowns'):
style='horiz dropdowns', level_names=False):
'''
Write the final details (which is the stuff that actually gets read!) at the end of a tab
containing stdout stuff to a file object.
Expand All @@ -924,27 +929,60 @@ def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None,
those people viewing the webpage!)
* style - In future, it would be great to write out different types of webpages. For now \
they are always horizontal dropdown menus: 'horiz dropdowns'.
* level_names - a list of full names, for the selectors, of length dict_depth. This does \
not work well if :class:`ImageMetaTag.ImageDict`.selector_widths is not set.
'''

if selector_prefix is None:
selector_prefix, _junk1, _junk2 = write_js_setup_defaults()

apply_level_names = False
if level_names:
if not isinstance(level_names, list):
raise ValueError('level_names needs to be a list of length dict_depth')
if len(level_names) != dict_depth:
raise ValueError('level_names needs to be a list, of length dict_depth')
apply_level_names = True
else:
apply_level_names = False

if style == 'horiz dropdowns':
file_obj.write('''
<!-- Now f"or some placeholders for the scripts to put content -->
<table border=0 cellspacing=0 cellpadding=0 width=99% align=center>
<tr>
<td>
<font size=3>
<br>''')
<br>
''')

# for each level of depth in the plot dictionary, add a span to hold the selector:
# TODO: include the selector full_name_mapping
for lev in range(dict_depth):
if apply_level_names:
# if we want labelled selectors, then write out
# a table, with pairs of label, selector, in columns:
file_obj.write('''
<table border=0 cellspacing=3 cellpadding=0>
<tr>
''')
for level in range(dict_depth):
file_obj.write(''' <td>{}</td>'''.format(level_names[level]))
file_obj.write('''
</tr>
<tr>
''')
for level in range(dict_depth):
file_obj.write(' <td><span id="%s%s">&nbsp;</span></td>' % (selector_prefix, level))
file_obj.write('''
</tr>
</table>
''')
else:
# simply a set of spans, in a line:
for lev in range(dict_depth):
file_obj.write('''
<span id="%s%s">&nbsp;</span>''' % (selector_prefix, lev))

# now add somewhere for the image to go:
# now add somewhere for the animator buttons and the image(s):
file_obj.write('''
<br>
<span id="animator1">&nbsp;</span>
Expand Down
37 changes: 31 additions & 6 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,28 @@ def __main__():
sort_methods = ['numeric', 'sort', 'sort', 'sort', borders_str, 'sort']
plot_owner = 'Created by %s' % get_user_and_email()

# what are the full names of those tags:
tag_full_names = {'number of rolls': 'Number of rolls',
'plot type': 'Plot type',
'plot color': 'Plot color',
'image trim': 'Image trimmed?',
'border': 'Image border',
'image compression': 'Image compression',
}
sel_widths = {'number of rolls': '180px',
'plot type': '120px',
'plot color': '200px',
'image trim': '150px',
'border': '100px',
'image compression': '200px',
}
# if we want to present these, have them as an ordered list, by tagorder:
sel_names_list = [tag_full_names[x] for x in tagorder]
sel_widths_list = [sel_widths[x] for x in tagorder]

# this will become a large dict of images and their metadata:
images_and_tags = {}

metadata_pickle = '%s/meta.p' % img_savedir
if not (args.skip_plotting and os.path.isfile(metadata_pickle) and os.path.isfile(imt_db)):
if os.path.isfile(imt_db):
Expand Down Expand Up @@ -374,9 +394,11 @@ def __main__():
tmp_dict = imt.dict_heirachy_from_list(img_info, img_file, tagorder)
if not img_dict:
img_dict = imt.ImageDict(tmp_dict, selector_animated=selector_animated,
animation_direction=animation_direction)
animation_direction=animation_direction,
level_names=sel_names_list,
selector_widths=sel_widths_list)
else:
img_dict.append(imt.ImageDict(tmp_dict))
img_dict.append(imt.ImageDict(tmp_dict, level_names=sel_names_list))

# this test the same thing, but created in parallel. This isn't needed for a small
# set of plots like this example, but the code appears to scale well.
Expand Down Expand Up @@ -563,7 +585,8 @@ def __main__():
tmp_img_info = {}
for tag_name in tagorder:
tmp_img_info[tag_name] = img_info[tag_name]
# with the exception of the one at multi_depth, which is the group_name:
# with the exception of the one at multi_depth,
# which is the group_name:
tmp_img_info[tagorder[multi_depth]] = group_name

tmp_dict = imt.dict_heirachy_from_list(tmp_img_info,
Expand Down Expand Up @@ -680,7 +703,8 @@ def __main__():
tmp_img_info = {}
for tag_name in tagorder:
tmp_img_info[tag_name] = img_info[tag_name]
# with the exception of the one at multi_depth, which is the group_name:
# with the exception of the one at multi_depth,
# which is the group_name:
tmp_img_info[tagorder[multi_depth]] = group_name

tmp_dict = imt.dict_heirachy_from_list(tmp_img_info,
Expand Down Expand Up @@ -748,7 +772,8 @@ def __main__():
'Test ImageDict webpage',
preamble=webpage_preamble, postamble=webpage_postamble,
initial_selectors=initial_selectors,
verbose=True, internal=False, only_show_rel_url=True)
verbose=True, internal=False, only_show_rel_url=True,
show_selector_names=True)
imt.webpage.write_full_page(img_dict, out_page_para,
'Test ImageDict webpage (Parallel version)',
preamble=webpage_preamble, postamble=webpage_postamble,
Expand Down

0 comments on commit a8d9dcb

Please sign in to comment.