diff --git a/ImageMetaTag/db.py b/ImageMetaTag/db.py index c618fa1..c4e4857 100644 --- a/ImageMetaTag/db.py +++ b/ImageMetaTag/db.py @@ -32,6 +32,7 @@ def db_name_to_info_key(in_str): return str(in_str).replace('__', ' ') def write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False, + attempt_replace=False, timeout=DEFAULT_DB_TIMEOUT): ''' Writes image metadata to a database. @@ -48,6 +49,7 @@ def write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False, Options: * add_strict - passed into :func:`ImageMetaTag.db.write_img_to_open_db` + * attempt_replace - passed into :func:`ImageMetaTag.db.write_img_to_open_db` * timeout - default timeout to try and write to the database. This is commonly used in :func:`ImageMetaTag.savefig` @@ -61,7 +63,8 @@ def write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False, # open the database: dbcn, dbcr = open_or_create_db_file(db_file, img_info, timeout=timeout) # now write: - write_img_to_open_db(dbcr, img_filename, img_info, add_strict=add_strict) + write_img_to_open_db(dbcr, img_filename, img_info, + add_strict=add_strict, attempt_replace=attempt_replace) # now commit that databasde entry and close: dbcn.commit() dbcn.close() @@ -140,7 +143,7 @@ def read(db_file, required_tags=None, tag_strings=None, read_img_info_from_dbfile = read def merge_db_files(main_db_file, add_db_file, delete_add_db=False, - delete_added_entries=False, + delete_added_entries=False, attempt_replace=False, db_timeout=DEFAULT_DB_TIMEOUT, db_attempts=DEFAULT_DB_ATTEMPTS): ''' Merges two ImageMetaTag database files, with the contents of add_db_file added @@ -169,7 +172,8 @@ def merge_db_files(main_db_file, add_db_file, delete_add_db=False, dbcn, dbcr = open_db_file(main_db_file, timeout=db_timeout) # and add in the new contents: for add_file, add_info in add_tags.iteritems(): - write_img_to_open_db(dbcr, add_file, add_info) + write_img_to_open_db(dbcr, add_file, add_info, + attempt_replace=attempt_replace) dbcn.commit() # if we got here, then we're good! wrote_db = True @@ -332,9 +336,9 @@ def write_img_to_open_db(dbcr, filename, img_info, add_strict=False, attempt_rep except sqlite3.IntegrityError: if attempt_replace: # try an INSERT OR REPLACE - add_command.replace('INSERT ', 'INSERT OR REPLACE ') + add_repl_command = add_command.replace('INSERT ', 'INSERT OR REPLACE ') # if this fails, want it to report it's error message as is, so no 'try': - dbcr.execute(add_command, add_list) + dbcr.execute(add_repl_command, add_list) else: # this file is already in the database (as the primary key, so do nothing...) pass diff --git a/ImageMetaTag/savefig.py b/ImageMetaTag/savefig.py index 5402da2..dc4a2c1 100644 --- a/ImageMetaTag/savefig.py +++ b/ImageMetaTag/savefig.py @@ -30,7 +30,7 @@ def savefig(filename, img_format=None, img_converter=0, do_trim=False, trim_bord do_thumb=False, img_tags=None, keep_open=False, dpi=None, logo_file=None, logo_width=40, logo_padding=0, logo_pos=0, db_file=None, db_timeout=DEFAULT_DB_TIMEOUT, db_attempts=DEFAULT_DB_ATTEMPTS, - db_full_paths=False, + db_replace=False, db_full_paths=False, verbose=False, ): ''' A wrapper around matplotlib.pyplot.savefig, to include file size optimisation and @@ -54,6 +54,9 @@ def savefig(filename, img_format=None, img_converter=0, do_trim=False, trim_bord db_full_paths is True. * db_timeout - change the database timeout (in seconds). * db_attempts - change the number of attempts to write to the database. + * db_replace - if True, an image's metadata will be replaced in the database if it \ + already exists. This can be slow, and the metadata is usually the same so \ + the default is db_replace=False. * dpi - change the image resolution passed into matplotlib.savefig. * keep_open - by default, this savefig wrapper closes the figure after use, except if \ keep_open is True. @@ -151,7 +154,8 @@ def savefig(filename, img_format=None, img_converter=0, do_trim=False, trim_bord n_tries = 1 while not wrote_db and n_tries <= db_attempts: try: - db.write_img_to_dbfile(db_file, db_filename, img_tags, timeout=db_timeout) + db.write_img_to_dbfile(db_file, db_filename, img_tags, timeout=db_timeout, + attempt_replace=db_replace) wrote_db = True except sqlite3.OperationalError as OpErr: if 'database is locked' in OpErr.message: diff --git a/ImageMetaTag/webpage.py b/ImageMetaTag/webpage.py index 225b291..fef9813 100644 --- a/ImageMetaTag/webpage.py +++ b/ImageMetaTag/webpage.py @@ -102,7 +102,8 @@ def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=No * compression - default False. If True, then the json data object will be compressed \ using zlib string compression. When read into the browser, we will use \ pako to inflate it (https://github.com/nodeca/pako) - * css - CSS file used to style webpage + * css - Optional CSS file used to style webpage. By default a small amount of css is \ + written out in the page header. Returns a list of files that the the created webpage is dependent upon ''' diff --git a/docs/build/doctrees/db.doctree b/docs/build/doctrees/db.doctree index 66ecb07..d5374ff 100644 Binary files a/docs/build/doctrees/db.doctree and b/docs/build/doctrees/db.doctree differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle index 18ee086..e895f19 100644 Binary files a/docs/build/doctrees/environment.pickle and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/savefig.doctree b/docs/build/doctrees/savefig.doctree index 130a4fe..4b1c36f 100644 Binary files a/docs/build/doctrees/savefig.doctree and b/docs/build/doctrees/savefig.doctree differ diff --git a/docs/build/doctrees/webpage.doctree b/docs/build/doctrees/webpage.doctree index 4a3b97b..03ccc1f 100644 Binary files a/docs/build/doctrees/webpage.doctree and b/docs/build/doctrees/webpage.doctree differ diff --git a/docs/build/html/_modules/ImageMetaTag/db.html b/docs/build/html/_modules/ImageMetaTag/db.html index b31ea4a..cd8ed5c 100644 --- a/docs/build/html/_modules/ImageMetaTag/db.html +++ b/docs/build/html/_modules/ImageMetaTag/db.html @@ -102,6 +102,7 @@

Source code for ImageMetaTag.db

     return str(in_str).replace('__', ' ')
 
[docs]def write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False, + attempt_replace=False, timeout=DEFAULT_DB_TIMEOUT): ''' Writes image metadata to a database. @@ -118,6 +119,7 @@

Source code for ImageMetaTag.db

     Options:
 
     * add_strict - passed into :func:`ImageMetaTag.db.write_img_to_open_db`
+    * attempt_replace - passed into :func:`ImageMetaTag.db.write_img_to_open_db`
     * timeout - default timeout to try and write to the database.
 
     This is commonly used in :func:`ImageMetaTag.savefig`
@@ -131,7 +133,8 @@ 

Source code for ImageMetaTag.db

         # open the database:
         dbcn, dbcr = open_or_create_db_file(db_file, img_info, timeout=timeout)
         # now write:
-        write_img_to_open_db(dbcr, img_filename, img_info, add_strict=add_strict)
+        write_img_to_open_db(dbcr, img_filename, img_info,
+                             add_strict=add_strict, attempt_replace=attempt_replace)
         # now commit that databasde entry and close:
         dbcn.commit()
         dbcn.close()
@@ -210,7 +213,7 @@ 

Source code for ImageMetaTag.db

 read_img_info_from_dbfile = read
 
 
[docs]def merge_db_files(main_db_file, add_db_file, delete_add_db=False, - delete_added_entries=False, + delete_added_entries=False, attempt_replace=False, db_timeout=DEFAULT_DB_TIMEOUT, db_attempts=DEFAULT_DB_ATTEMPTS): ''' Merges two ImageMetaTag database files, with the contents of add_db_file added @@ -239,7 +242,8 @@

Source code for ImageMetaTag.db

                     dbcn, dbcr = open_db_file(main_db_file, timeout=db_timeout)
                     # and add in the new contents:
                     for add_file, add_info in add_tags.iteritems():
-                        write_img_to_open_db(dbcr, add_file, add_info)
+                        write_img_to_open_db(dbcr, add_file, add_info,
+                                             attempt_replace=attempt_replace)
                     dbcn.commit()
                     # if we got here, then we're good!
                     wrote_db = True
@@ -303,7 +307,7 @@ 

Source code for ImageMetaTag.db

 
def create_table_for_img_info(dbcr, img_info): 'Creates a database table, in a database cursor, to store for the input img_info' - + create_command = 'CREATE TABLE {}(fname TEXT PRIMARY KEY,'.format(SQLITE_IMG_INFO_TABLE) for key in img_info.keys(): create_command += ' "{}" TEXT,'.format(info_key_to_db_name(key)) @@ -316,7 +320,8 @@

Source code for ImageMetaTag.db

     except sqlite3.OperationalError as OpErr:
         if 'table {} already exists'.format(SQLITE_IMG_INFO_TABLE) in OpErr.message:
             # another process has just created the table, so sleep(1)
-            # This is only when a db file is created so isn't called often (and the race condition is rare!)
+            # This is only when a db file is created so isn't called often
+            # (and the race condition is rare!)
             time.sleep(1)
         else:
             # everything else needs to be reported and raised immediately:
@@ -401,9 +406,9 @@ 

Source code for ImageMetaTag.db

     except sqlite3.IntegrityError:
         if attempt_replace:
             # try an INSERT OR REPLACE
-            add_command.replace('INSERT ', 'INSERT OR REPLACE ')
+            add_repl_command = add_command.replace('INSERT ', 'INSERT OR REPLACE ')
             # if this fails, want it to report it's error message as is, so no 'try':
-            dbcr.execute(add_command, add_list)
+            dbcr.execute(add_repl_command, add_list)
         else:
             # this file is already in the database (as the primary key, so do nothing...)
             pass
@@ -576,8 +581,8 @@ 

Source code for ImageMetaTag.db

             pass
         else:
             if allow_retries:
-                # split the list of filenames up into appropciately sized chunks, so that concurrent
-                # delete commands each have a chance to complete:
+                # split the list of filenames up into appropciately sized chunks, so that
+                # concurrent delete commands each have a chance to complete:
                 # 200 is arbriatily chosen, but seems to work
                 chunk_size = 200
                 chunks = __gen_chunk_of_list(fn_list, chunk_size)
@@ -591,10 +596,12 @@ 

Source code for ImageMetaTag.db

                             dbcn, dbcr = open_db_file(db_file, timeout=db_timeout)
                             # go through the file chunk, one by one, and delete:
                             for fname in chunk_o_filenames:
+                                del_cmd = "DELETE FROM {} WHERE fname=?"
                                 try:
-                                    dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
+                                    dbcr.execute(del_cmd.format(SQLITE_IMG_INFO_TABLE), (fname,))
                                 except sqlite3.OperationalError as OpErr_file:
-                                    if OpErr_file.message == 'no such table: {}'.format(SQLITE_IMG_INFO_TABLE):
+                                    err_check = 'no such table: {}'.format(SQLITE_IMG_INFO_TABLE)
+                                    if OpErr_file.message == err_check:
                                         # the db file exists, but it doesn't have anything in it:
                                         if not skip_warning:
                                             msg = ('WARNING: Unable to delete file entry "{}" from'
diff --git a/docs/build/html/_modules/ImageMetaTag/webpage.html b/docs/build/html/_modules/ImageMetaTag/webpage.html
index 6e5af98..5b0efb7 100644
--- a/docs/build/html/_modules/ImageMetaTag/webpage.html
+++ b/docs/build/html/_modules/ImageMetaTag/webpage.html
@@ -132,7 +132,7 @@ 

Source code for ImageMetaTag.webpage

                     show_singleton_selectors=True, optgroups=None,
                     url_type='int', only_show_rel_url=False, verbose=False,
                     style='horiz dropdowns', write_intmed_tmpfile=False,
-                    description=None, keywords=None):
+                    description=None, keywords=None, css=None):
     '''
     Writes out an :class:`ImageMetaTag.ImageDict` as a webpage, to a given file location.
     The files are created as temporary files and when complete they replace any files that
@@ -172,6 +172,8 @@ 

Source code for ImageMetaTag.webpage

     * compression - default False. If True, then the json data object will be compressed \
                     using zlib string compression. When read into the browser, we will use \
                     pako to inflate it (https://github.com/nodeca/pako)
+    * css - Optional CSS file used to style webpage. By default a small amount of css is \
+            written out in the page header.
 
     Returns a list of files that the the created webpage is dependent upon
     '''
@@ -236,14 +238,26 @@ 

Source code for ImageMetaTag.webpage

         ind = _indent_up_one(ind)
         out_file.write(ind + '<head>\n')
         ind = _indent_up_one(ind)
-        if not title is None:
+        if title is not None:
             out_file.write('{}<title>{}</title>\n'.format(ind, title))
         out_str = ind+'<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\n'
         out_file.write(out_str)
 
-        if style == 'horiz dropdowns':
-            # write out a little css at the top:
-            css = '''{0}<style>
+        if css:
+            shutil.copy(css, file_dir)
+            base_css = os.path.basename(css)
+            page_dependencies.append(base_css)
+            out_str = ind+'<link rel="stylesheet" type="text/css" href="{0}">\n'
+            out_file.write(out_str.format(base_css))
+
+        else:
+            if style == 'horiz dropdowns':
+                # write out a little css at the top:
+                css = '''{0}<style>
+{0}  body {{
+{0}    background-color: #ffffff;
+{0}    color: #000000;
+{0}  }}
 {0}  body, div, dl, dt, dd, li, h1, h2 {{
 {0}    margin: 0;
 {0}    padding: 0;
@@ -306,7 +320,7 @@ 

Source code for ImageMetaTag.webpage

 {0}  }}
 {0}</style>
 '''
-            out_file.write(css.format(ind))
+                out_file.write(css.format(ind))
 
         # now write out the specific stuff to the html header:
         if img_dict is None:
@@ -334,14 +348,11 @@ 

Source code for ImageMetaTag.webpage

         out_file.write(ind + '</head>\n')
 
         # now start the body:
-        margins = 'leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"'
-        bgcolor = 'bcolor="#FFFFFF"'
-        text_color = 'text="#000000"'
-        out_file.write('{}<body {} {} {}>\n'.format(ind, bgcolor, text_color, margins))
+        out_file.write('{}<body>\n'.format(ind))
 
         # the preamble is the first thing to go in the body:
-        if not preamble is None:
-            out_file.write(preamble)
+        if preamble is not None:
+            out_file.write(preamble + '\n')
         # now the img_dict content:
         if img_dict is None:
             out_file.write('<p><h1>No images are available for this page.</h1></p>')
@@ -362,14 +373,14 @@ 

Source code for ImageMetaTag.webpage

                                   show_singleton_selectors=show_singleton_selectors,
                                   animated_level=anim_level)
         # the body is done, so the postamble comes in:
-        if not postamble is None:
+        if postamble is not None:
             out_file.write(postamble + '\n')
         # finish the body, and html:
         out_file.write(ind + '</body>\n')
         out_file.write('\n</html>')
 
 
-        if  write_intmed_tmpfile:
+        if write_intmed_tmpfile:
             tmp_files_to_mv = json_files + [(tmp_html_filepath, filepath)]
         else:
             tmp_files_to_mv = json_files
@@ -435,12 +446,12 @@ 

Source code for ImageMetaTag.webpage

     if ind is None:
         ind = ''
 
-    if not description is None:
+    if description is not None:
         file_obj.write('{}<meta name="description" content="{}">\n'.format(ind, description))
-    if not keywords is None:
+    if keywords is not None:
         file_obj.write('{}<meta name="keywords" content="{}">\n'.format(ind, keywords))
 
-    if not img_dict is None:
+    if img_dict is not None:
         ## add a reference to the data structure:
         #out_str = '{}<script type="text/javascript" src="{}"></script>\n'.format(ind, json_files)
         #file_obj.write(out_str)
@@ -450,7 +461,7 @@ 

Source code for ImageMetaTag.webpage

             out_str = '{}<script type="text/javascript" src="{}"></script>\n'.format(ind, js_file)
             file_obj.write(out_str)
 
-        # now write out the javascript cnfiguration variables:
+        # now write out the javascript configuration variables:
         file_obj.write(ind + '<script type="text/javascript">\n')
         ind = _indent_up_one(ind)
         # define, read in and parse the json file:
@@ -495,7 +506,7 @@ 

Source code for ImageMetaTag.webpage

         if initial_selectors is None:
             # if it's not set, then set it to something invalid, and the validator
             # in the javascript will sort it out. It MUST be the right length though:
-            file_obj.write('{}var selected_id = {}\n;'.format(ind, str([-1]*dict_depth)))
+            file_obj.write('{}var selected_id = {};\n'.format(ind, str([-1]*dict_depth)))
         else:
             if not isinstance(initial_selectors, list):
                 msg = 'Input initial_selectors must be a list, of length the depth of the ImageDict'
@@ -590,7 +601,8 @@ 

Source code for ImageMetaTag.webpage

 
         # now some top level things:
         if style == 'horiz dropdowns':
-            file_obj.write('''{0}// other top level derived variables
+            file_obj.write('''
+{0}// other top level derived variables
 {0}// the depth of the ImageMetaTag ImageDict (number of selectors):
 {0}var n_deep = selected_id.length;
 {0}// a list of the options available to the animator buttons, with the current selection
diff --git a/docs/build/html/db.html b/docs/build/html/db.html
index fa7f040..ee896ef 100644
--- a/docs/build/html/db.html
+++ b/docs/build/html/db.html
@@ -109,7 +109,7 @@ 

Commonly used functionsFor most use cases, the following functions provide the required functionality to use the database:

-ImageMetaTag.db.write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False, timeout=6)[source]
+ImageMetaTag.db.write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False, attempt_replace=False, timeout=6)[source]

Writes image metadata to a database.

Arguments:

Returns a list of files that the the created webpage is dependent upon

diff --git a/release_process b/release_process index ddc3119..546eff0 100644 --- a/release_process +++ b/release_process @@ -15,7 +15,7 @@ convention (http://semver.org/) time to make sure it works, as the code at this stage is what will be released. -2) Update the documentation, from the docs directory: +3) Update the documentation, from the docs directory: cd docs * Clean the documentation build: make html clean @@ -23,18 +23,20 @@ convention (http://semver.org/) new build: PYTHONPATH=../ make html -3) Commit the version number and documentation changes: +4) Commit the version number and documentation changes: git commit -a git push origin {{ branch_name }} -4) Start a pull request for your branch on your github fork and follow the +5) Start a pull request for your branch on your github fork and follow the process to merge it to the master branch. +6) Test the package works at the commit which you intend to release by installing from the specific commit to a temporary location. Install it using the setup.py and run the test.py script. + From this point onwards, admin rights are required to both image-meta-tag and image-meta-tag-feedstock repositories. -5) Pulbish a release in the image-meta-tag repository for the new release +7) Pulbish a release in the image-meta-tag repository for the new release number at: https://github.com/SciTools-incubator/image-meta-tag/releases * Tag version is just the version number e.g. '0.6.7' in the code. * The convention used for the release title adds 'vn' before the number, e.g. @@ -42,7 +44,7 @@ number at: https://github.com/SciTools-incubator/image-meta-tag/releases * The release description can be used to give a very brief outline of the changes. -6) Update the recipe on the conda-forge-feedstock: +8) Update the recipe on the conda-forge-feedstock: * The main repository is: https://github.com/conda-forge/image-meta-tag-feedstock * The location of admin branches is: