Add database upgrader script
This commit is contained in:
		
							parent
							
								
									45a8a8ccc5
								
							
						
					
					
						commit
						2d4b07c10f
					
				
					 7 changed files with 127 additions and 26 deletions
				
			
		|  | @ -1,7 +1,7 @@ | ||||||
| import string | import string | ||||||
| 
 | 
 | ||||||
| # Errors and warnings | # Errors and warnings | ||||||
| ERROR_DATABASE_OUTOFDATE = 'Database is out-of-date. {current} should be {new}' | ERROR_DATABASE_OUTOFDATE = 'Database is out-of-date. {current} should be {new}. Please use etiquette_upgrader.py' | ||||||
| ERROR_INVALID_ACTION = 'Invalid action' | ERROR_INVALID_ACTION = 'Invalid action' | ||||||
| ERROR_NO_SUCH_TAG = 'Doesn\'t exist' | ERROR_NO_SUCH_TAG = 'Doesn\'t exist' | ||||||
| ERROR_NO_TAG_GIVEN = 'No tag name supplied' | ERROR_NO_TAG_GIVEN = 'No tag name supplied' | ||||||
|  |  | ||||||
|  | @ -235,6 +235,11 @@ def get_albums_json(): | ||||||
|     return make_json_response(albums) |     return make_json_response(albums) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @site.route('/bookmarks') | ||||||
|  | def get_bookmarks(): | ||||||
|  |     return flask.render_template('bookmarks.html') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @site.route('/file/<photoid>') | @site.route('/file/<photoid>') | ||||||
| def get_file(photoid): | def get_file(photoid): | ||||||
|     requested_photoid = photoid |     requested_photoid = photoid | ||||||
|  | @ -327,7 +332,6 @@ def get_search_core(): | ||||||
|     orderby = request.args.get('orderby', None) |     orderby = request.args.get('orderby', None) | ||||||
|     if orderby: |     if orderby: | ||||||
|         orderby = orderby.replace('-', ' ') |         orderby = orderby.replace('-', ' ') | ||||||
|         orderby = orderby.replace('_', ' ') |  | ||||||
|         orderby = orderby.split(',') |         orderby = orderby.split(',') | ||||||
|     else: |     else: | ||||||
|         orderby = None |         orderby = None | ||||||
|  |  | ||||||
							
								
								
									
										60
									
								
								etiquette/etiquette_upgrader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								etiquette/etiquette_upgrader.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | import argparse | ||||||
|  | import os | ||||||
|  | import sqlite3 | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | import phototagger | ||||||
|  | 
 | ||||||
|  | def upgrade_1_to_2(sql): | ||||||
|  |     ''' | ||||||
|  |     In this version, a column `tagged_at` was added to the Photos table, to keep | ||||||
|  |     track of the last time the photo's tags were edited (added or removed). | ||||||
|  |     ''' | ||||||
|  |     cur = sql.cursor() | ||||||
|  |     cur.execute('ALTER TABLE photos ADD COLUMN tagged_at INT') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def upgrade_all(database_filename): | ||||||
|  |     ''' | ||||||
|  |     Given the filename of a phototagger database, apply all of the needed | ||||||
|  |     upgrade_x_to_y functions in order. | ||||||
|  |     ''' | ||||||
|  |     if not os.path.isfile(database_filename): | ||||||
|  |         raise FileNotFoundError(database_filename) | ||||||
|  | 
 | ||||||
|  |     sql = sqlite3.connect(database_filename) | ||||||
|  |     cur = sql.cursor() | ||||||
|  | 
 | ||||||
|  |     cur.execute('PRAGMA user_version') | ||||||
|  |     current_version = cur.fetchone()[0] | ||||||
|  |     needed_version = phototagger.DATABASE_VERSION | ||||||
|  | 
 | ||||||
|  |     if current_version == needed_version: | ||||||
|  |         print('Already up-to-date.') | ||||||
|  |         return | ||||||
|  | 
 | ||||||
|  |     for version_number in range(current_version + 1, needed_version + 1): | ||||||
|  |         print('Upgrading from %d to %d' % (current_version, version_number)) | ||||||
|  |         upgrade_function = 'upgrade_%d_to_%d' % (current_version, version_number) | ||||||
|  |         upgrade_function = eval(upgrade_function) | ||||||
|  |         upgrade_function(sql) | ||||||
|  |         sql.cursor().execute('PRAGMA user_version = 2') | ||||||
|  |         sql.commit() | ||||||
|  |         current_version = version_number | ||||||
|  |     print('Upgrades finished.') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def upgrade_all_argparse(args): | ||||||
|  |     return upgrade_all(database_filename=args.database_filename) | ||||||
|  | 
 | ||||||
|  | def main(argv): | ||||||
|  |     parser = argparse.ArgumentParser() | ||||||
|  | 
 | ||||||
|  |     parser.add_argument('database_filename') | ||||||
|  |     parser.set_defaults(func=upgrade_all_argparse) | ||||||
|  | 
 | ||||||
|  |     args = parser.parse_args(argv) | ||||||
|  |     args.func(args) | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main(sys.argv[1:]) | ||||||
|  | @ -70,6 +70,7 @@ SQL_PHOTO_COLUMNS = [ | ||||||
|     'bytes', |     'bytes', | ||||||
|     'created', |     'created', | ||||||
|     'thumbnail', |     'thumbnail', | ||||||
|  |     'tagged_at', | ||||||
| ] | ] | ||||||
| SQL_TAG_COLUMNS = [ | SQL_TAG_COLUMNS = [ | ||||||
|     'id', |     'id', | ||||||
|  | @ -101,7 +102,10 @@ SQL_SYN = {key:index for (index, key) in enumerate(SQL_SYN_COLUMNS)} | ||||||
| SQL_TAG = {key:index for (index, key) in enumerate(SQL_TAG_COLUMNS)} | SQL_TAG = {key:index for (index, key) in enumerate(SQL_TAG_COLUMNS)} | ||||||
| SQL_TAGGROUP = {key:index for (index, key) in enumerate(SQL_TAGGROUP_COLUMNS)} | SQL_TAGGROUP = {key:index for (index, key) in enumerate(SQL_TAGGROUP_COLUMNS)} | ||||||
| 
 | 
 | ||||||
| DATABASE_VERSION = 1 | # Note: Setting user_version pragma in init sequence is safe because it only | ||||||
|  | # happens after the out-of-date check occurs, so no chance of accidentally | ||||||
|  | # overwriting it. | ||||||
|  | DATABASE_VERSION = 2 | ||||||
| DB_INIT = ''' | DB_INIT = ''' | ||||||
| PRAGMA count_changes = OFF; | PRAGMA count_changes = OFF; | ||||||
| PRAGMA cache_size = 10000; | PRAGMA cache_size = 10000; | ||||||
|  | @ -124,7 +128,8 @@ CREATE TABLE IF NOT EXISTS photos( | ||||||
|     duration INT, |     duration INT, | ||||||
|     bytes INT, |     bytes INT, | ||||||
|     created INT, |     created INT, | ||||||
|     thumbnail TEXT |     thumbnail TEXT, | ||||||
|  |     tagged_at INT | ||||||
| ); | ); | ||||||
| CREATE TABLE IF NOT EXISTS tags( | CREATE TABLE IF NOT EXISTS tags( | ||||||
|     id TEXT, |     id TEXT, | ||||||
|  | @ -180,6 +185,18 @@ CREATE INDEX IF NOT EXISTS index_grouprel_parentid on tag_group_rel(parentid); | ||||||
| CREATE INDEX IF NOT EXISTS index_grouprel_memberid on tag_group_rel(memberid); | CREATE INDEX IF NOT EXISTS index_grouprel_memberid on tag_group_rel(memberid); | ||||||
| '''.format(user_version=DATABASE_VERSION) | '''.format(user_version=DATABASE_VERSION) | ||||||
| 
 | 
 | ||||||
|  | ALLOWED_ORDERBY_COLUMNS = [ | ||||||
|  |     'extension', | ||||||
|  |     'width', | ||||||
|  |     'height', | ||||||
|  |     'ratio', | ||||||
|  |     'area', | ||||||
|  |     'duration', | ||||||
|  |     'bytes', | ||||||
|  |     'created', | ||||||
|  |     'tagged_at', | ||||||
|  |     'random', | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| def _helper_extension(ext): | def _helper_extension(ext): | ||||||
|     ''' |     ''' | ||||||
|  | @ -240,18 +257,7 @@ def _helper_orderby(orderby): | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     #print(column, sorter) |     #print(column, sorter) | ||||||
|     sortable = column in [ |     if column not in ALLOWED_ORDERBY_COLUMNS: | ||||||
|         'extension', |  | ||||||
|         'width', |  | ||||||
|         'height', |  | ||||||
|         'ratio', |  | ||||||
|         'area', |  | ||||||
|         'duration', |  | ||||||
|         'bytes', |  | ||||||
|         'created', |  | ||||||
|         'random', |  | ||||||
|     ] |  | ||||||
|     if not sortable: |  | ||||||
|         warnings.warn(constants.WARNING_ORDERBY_BADCOL.format(column=column)) |         warnings.warn(constants.WARNING_ORDERBY_BADCOL.format(column=column)) | ||||||
|         return None |         return None | ||||||
|     if column == 'random': |     if column == 'random': | ||||||
|  | @ -1666,6 +1672,7 @@ class Photo(ObjectBase): | ||||||
|         self.id = row_tuple[SQL_PHOTO['id']] |         self.id = row_tuple[SQL_PHOTO['id']] | ||||||
|         self.real_filepath = row_tuple[SQL_PHOTO['filepath']] |         self.real_filepath = row_tuple[SQL_PHOTO['filepath']] | ||||||
|         self.real_filepath = normalize_filepath(self.real_filepath) |         self.real_filepath = normalize_filepath(self.real_filepath) | ||||||
|  |         self.real_path = pathclass.Path(self.real_filepath) | ||||||
|         self.filepath = row_tuple[SQL_PHOTO['override_filename']] or self.real_filepath |         self.filepath = row_tuple[SQL_PHOTO['override_filename']] or self.real_filepath | ||||||
|         self.basename = row_tuple[SQL_PHOTO['override_filename']] or os.path.basename(self.real_filepath) |         self.basename = row_tuple[SQL_PHOTO['override_filename']] or os.path.basename(self.real_filepath) | ||||||
|         self.extension = row_tuple[SQL_PHOTO['extension']] |         self.extension = row_tuple[SQL_PHOTO['extension']] | ||||||
|  | @ -1677,7 +1684,7 @@ class Photo(ObjectBase): | ||||||
|         self.duration = row_tuple[SQL_PHOTO['duration']] |         self.duration = row_tuple[SQL_PHOTO['duration']] | ||||||
|         self.created = row_tuple[SQL_PHOTO['created']] |         self.created = row_tuple[SQL_PHOTO['created']] | ||||||
|         self.thumbnail = row_tuple[SQL_PHOTO['thumbnail']] |         self.thumbnail = row_tuple[SQL_PHOTO['thumbnail']] | ||||||
|         self.real_path = pathclass.Path(self.real_filepath) |         self.tagged_at = row_tuple[SQL_PHOTO['tagged_at']] | ||||||
| 
 | 
 | ||||||
|     def __reinit__(self): |     def __reinit__(self): | ||||||
|         ''' |         ''' | ||||||
|  | @ -1709,9 +1716,10 @@ class Photo(ObjectBase): | ||||||
|                 log.debug('Preferring new {tag:s} over {par:s}'.format(tag=tag, par=parent)) |                 log.debug('Preferring new {tag:s} over {par:s}'.format(tag=tag, par=parent)) | ||||||
|                 self.remove_tag(parent) |                 self.remove_tag(parent) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         log.debug('Applying tag {tag:s} to photo {pho:s}'.format(tag=tag, pho=self)) |         log.debug('Applying tag {tag:s} to photo {pho:s}'.format(tag=tag, pho=self)) | ||||||
|  |         now = int(getnow()) | ||||||
|         self.photodb.cur.execute('INSERT INTO photo_tag_rel VALUES(?, ?)', [self.id, tag.id]) |         self.photodb.cur.execute('INSERT INTO photo_tag_rel VALUES(?, ?)', [self.id, tag.id]) | ||||||
|  |         self.photodb.cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             log.debug('Committing - add photo tag') |             log.debug('Committing - add photo tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -1913,6 +1921,8 @@ class Photo(ObjectBase): | ||||||
|                 'DELETE FROM photo_tag_rel WHERE photoid == ? AND tagid == ?', |                 'DELETE FROM photo_tag_rel WHERE photoid == ? AND tagid == ?', | ||||||
|                 [self.id, tag.id] |                 [self.id, tag.id] | ||||||
|             ) |             ) | ||||||
|  |         now = int(getnow()) | ||||||
|  |         self.photodb.cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             log.debug('Committing - remove photo tag') |             log.debug('Committing - remove photo tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  |  | ||||||
							
								
								
									
										25
									
								
								etiquette/templates/bookmarks.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								etiquette/templates/bookmarks.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | <!DOCTYPE html5> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |     {% import "header.html" as header %} | ||||||
|  |     <title>Bookmarks</title> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <link rel="stylesheet" href="/static/common.css"> | ||||||
|  |     <script src="/static/common.js"></script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | </style> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  |     {{header.make_header()}} | ||||||
|  |     <div id="content_body"> | ||||||
|  |         <a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <script type="text/javascript"> | ||||||
|  | </script> | ||||||
|  | </html> | ||||||
|  | @ -33,6 +33,7 @@ a:hover | ||||||
|     <a href="/search">Search</a> |     <a href="/search">Search</a> | ||||||
|     <a href="/tags">Browse tags</a> |     <a href="/tags">Browse tags</a> | ||||||
|     <a href="/albums">Browse albums</a> |     <a href="/albums">Browse albums</a> | ||||||
|  |     <a href="/bookmarks">Bookmarks</a> | ||||||
| </body> | </body> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -105,6 +105,7 @@ form | ||||||
|         <option value="ratio"     {%if selected_column=="ratio"%}selected{%endif%}    >Aspect Ratio</option> |         <option value="ratio"     {%if selected_column=="ratio"%}selected{%endif%}    >Aspect Ratio</option> | ||||||
|         <option value="bytes"     {%if selected_column=="bytes"%}selected{%endif%}    >File size</option> |         <option value="bytes"     {%if selected_column=="bytes"%}selected{%endif%}    >File size</option> | ||||||
|         <option value="duration"  {%if selected_column=="duration"%}selected{%endif%} >Duration</option> |         <option value="duration"  {%if selected_column=="duration"%}selected{%endif%} >Duration</option> | ||||||
|  |         <option value="tagged_at" {%if selected_column=="tagged_at"%}selected{%endif%}>Recently tagged</option> | ||||||
|         <option value="random"    {%if selected_column=="random"%}selected{%endif%}   >Random</option> |         <option value="random"    {%if selected_column=="random"%}selected{%endif%}   >Random</option> | ||||||
|     </select> |     </select> | ||||||
|     <select> |     <select> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue