From bd1c6e33a6c75e0ec6490aa4b598f2e9488ce9f7 Mon Sep 17 00:00:00 2001 From: Malcolm Brooks Date: Fri, 23 Sep 2016 14:24:38 +0100 Subject: [PATCH 1/3] Almost there, text added but layout is wrong on webpage --- lib/ImageMetaTag/img_dict.py | 46 ++++++++++++++++++++---------------- lib/ImageMetaTag/webpage.py | 42 ++++++++++++++++++++++++-------- test.py | 21 ++++++++++++---- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/lib/ImageMetaTag/img_dict.py b/lib/ImageMetaTag/img_dict.py index 0fa0d3a..7ebf3ab 100644 --- a/lib/ImageMetaTag/img_dict.py +++ b/lib/ImageMetaTag/img_dict.py @@ -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 \ @@ -38,9 +38,19 @@ 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, @@ -48,15 +58,9 @@ def __init__(self, input_dict, full_name_mapping=None, 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: @@ -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' @@ -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. @@ -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)) diff --git a/lib/ImageMetaTag/webpage.py b/lib/ImageMetaTag/webpage.py index 93b7353..ee6d82f 100644 --- a/lib/ImageMetaTag/webpage.py +++ b/lib/ImageMetaTag/webpage.py @@ -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 @@ -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 @@ -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. @@ -85,8 +87,10 @@ def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=No 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: + # (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) @@ -913,7 +917,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. @@ -924,11 +928,22 @@ 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. ''' 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(''' @@ -936,12 +951,19 @@ def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None, -
''') - +
+''') + # 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): - file_obj.write(''' + if apply_level_names: + # TODO: this is almost therem but the layout isn't quite right! + for lev in range(dict_depth): + file_obj.write(' {}
'.format(level_names[lev])) + file_obj.write('  '% (selector_prefix, lev)) + file_obj.write('
') + else: + for lev in range(dict_depth): + file_obj.write('''  ''' % (selector_prefix, lev)) # now add somewhere for the image to go: diff --git a/test.py b/test.py index 22eed08..9765799 100644 --- a/test.py +++ b/test.py @@ -338,7 +338,18 @@ def __main__(): plot_owner = 'Created by %s' % get_user_and_email() images_and_tags = {} - + + # 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', + } + # if we want to present these, have them as an ordered list, by tagorder: + tagorder_full = [tag_full_names[x] for x in tagorder] + 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): @@ -374,9 +385,10 @@ 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=tagorder_full) else: - img_dict.append(imt.ImageDict(tmp_dict)) + img_dict.append(imt.ImageDict(tmp_dict, level_names=tagorder_full)) # 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. @@ -748,7 +760,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, From 69a90ad8b8f71c55113254cd034af5259fcb35ca Mon Sep 17 00:00:00 2001 From: Malcolm Brooks Date: Mon, 26 Sep 2016 14:48:35 +0100 Subject: [PATCH 2/3] Working labelling of selectors, as long as selector_widths is set --- lib/ImageMetaTag/webpage.py | 35 +++++++++++++++++++++++++---------- test.py | 22 ++++++++++++++++------ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/lib/ImageMetaTag/webpage.py b/lib/ImageMetaTag/webpage.py index ee6d82f..9a53c06 100644 --- a/lib/ImageMetaTag/webpage.py +++ b/lib/ImageMetaTag/webpage.py @@ -50,7 +50,7 @@ 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 + * 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'. @@ -87,7 +87,7 @@ def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=No 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: - # (if show_selector_names is False, then the input level_names list is empty): + # (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', level_names=show_selector_names * img_dict.level_names) @@ -928,7 +928,8 @@ 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. + * 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: @@ -937,7 +938,7 @@ def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None, 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') + 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 @@ -956,17 +957,31 @@ def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None, # for each level of depth in the plot dictionary, add a span to hold the selector: if apply_level_names: - # TODO: this is almost therem but the layout isn't quite right! - for lev in range(dict_depth): - file_obj.write(' {}
'.format(level_names[lev])) - file_obj.write('  '% (selector_prefix, lev)) - file_obj.write('
') + # if we want labelled selectors, then write out + # a table, with pairs of label, selector, in columns: + file_obj.write(''' + + +''') + for level in range(dict_depth): + file_obj.write(''' '''.format(level_names[level])) + file_obj.write(''' + + +''') + for level in range(dict_depth): + file_obj.write(' '% (selector_prefix, level)) + file_obj.write(''' + +
{}
 
+''') else: + # simply a set of spans, in a line: for lev in range(dict_depth): file_obj.write('''  ''' % (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('''
  diff --git a/test.py b/test.py index 9765799..cbfbf6f 100644 --- a/test.py +++ b/test.py @@ -337,8 +337,6 @@ def __main__(): sort_methods = ['numeric', 'sort', 'sort', 'sort', borders_str, 'sort'] plot_owner = 'Created by %s' % get_user_and_email() - images_and_tags = {} - # what are the full names of those tags: tag_full_names = {'number of rolls': 'Number of rolls', 'plot type': 'Plot type', @@ -346,9 +344,20 @@ def __main__(): '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: - tagorder_full = [tag_full_names[x] for x in 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)): @@ -386,9 +395,10 @@ def __main__(): if not img_dict: img_dict = imt.ImageDict(tmp_dict, selector_animated=selector_animated, animation_direction=animation_direction, - level_names=tagorder_full) + level_names=sel_names_list, + selector_widths=sel_widths_list) else: - img_dict.append(imt.ImageDict(tmp_dict, level_names=tagorder_full)) + 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. From ca4be0bc6dbebdac2d9aabca7544a487a93a1c26 Mon Sep 17 00:00:00 2001 From: Malcolm Brooks Date: Mon, 26 Sep 2016 16:34:07 +0100 Subject: [PATCH 3/3] Cosmetic pylint changes --- lib/ImageMetaTag/__init__.py | 5 +++-- lib/ImageMetaTag/db.py | 6 ++++-- lib/ImageMetaTag/webpage.py | 13 +++++++------ test.py | 6 ++++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/ImageMetaTag/__init__.py b/lib/ImageMetaTag/__init__.py index 301754e..ee6a708 100644 --- a/lib/ImageMetaTag/__init__.py +++ b/lib/ImageMetaTag/__init__.py @@ -1,6 +1,7 @@ ''' ImageMetaTag is a python package built around a wrapper for -`savefig `_ in +`savefig ` +in `matplotlib `_, which adds metadata tags to supported image file formats. Once the images have been tagged, it can also be used to manage an @@ -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 diff --git a/lib/ImageMetaTag/db.py b/lib/ImageMetaTag/db.py index 79194ea..bce9b94 100644 --- a/lib/ImageMetaTag/db.py +++ b/lib/ImageMetaTag/db.py @@ -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() @@ -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: diff --git a/lib/ImageMetaTag/webpage.py b/lib/ImageMetaTag/webpage.py index 9a53c06..7cf9fa1 100644 --- a/lib/ImageMetaTag/webpage.py +++ b/lib/ImageMetaTag/webpage.py @@ -86,10 +86,11 @@ 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: @@ -183,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 \ @@ -268,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 @@ -954,7 +955,7 @@ def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None,
''') - + # for each level of depth in the plot dictionary, add a span to hold the selector: if apply_level_names: # if we want labelled selectors, then write out @@ -970,7 +971,7 @@ def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None, ''') for level in range(dict_depth): - file_obj.write('  '% (selector_prefix, level)) + file_obj.write('  ' % (selector_prefix, level)) file_obj.write(''' diff --git a/test.py b/test.py index cbfbf6f..99a9824 100644 --- a/test.py +++ b/test.py @@ -585,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, @@ -702,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,