From 709fa88c628cffabcfaedf9a57927dc424cd6adf Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Mon, 20 Feb 2017 19:52:00 +0100 Subject: [PATCH 1/3] Navbar reduced to icons on smaller screens Feedback updater improved (#81) --- cps.py | 3 +- cps/helper.py | 254 ++++++++------- cps/static/css/style.css | 4 + cps/static/js/main.js | 68 +++- cps/templates/admin.html | 43 ++- cps/templates/layout.html | 2 +- cps/translations/de/LC_MESSAGES/messages.mo | Bin 297131 -> 297758 bytes cps/translations/de/LC_MESSAGES/messages.po | 300 +++++++++-------- cps/translations/es/LC_MESSAGES/messages.mo | Bin 297331 -> 297894 bytes cps/translations/es/LC_MESSAGES/messages.po | 304 +++++++++-------- cps/translations/fr/LC_MESSAGES/messages.mo | Bin 298502 -> 299049 bytes cps/translations/fr/LC_MESSAGES/messages.po | 306 ++++++++++-------- cps/translations/pl/LC_MESSAGES/messages.mo | Bin 297016 -> 297558 bytes cps/translations/pl/LC_MESSAGES/messages.po | 302 +++++++++-------- .../zh_Hans_CN/LC_MESSAGES/messages.mo | Bin 299192 -> 299763 bytes .../zh_Hans_CN/LC_MESSAGES/messages.po | 302 +++++++++-------- cps/web.py | 60 ++-- messages.pot | 304 +++++++++-------- 18 files changed, 1304 insertions(+), 948 deletions(-) diff --git a/cps.py b/cps.py index 4c8bfbe2..00d0f356 100755 --- a/cps.py +++ b/cps.py @@ -3,7 +3,6 @@ import os import sys -import time base_path = os.path.dirname(os.path.abspath(__file__)) # Insert local directories into path @@ -22,7 +21,7 @@ if __name__ == '__main__': http_server.listen(web.ub.config.config_port) IOLoop.instance().start() - if web.global_task == 0: + if web.helper.global_task == 0: print("Performing restart of Calibre-web") os.execl(sys.executable, sys.executable, *sys.argv) else: diff --git a/cps/helper.py b/cps/helper.py index beee00c7..20d9402a 100755 --- a/cps/helper.py +++ b/cps/helper.py @@ -6,7 +6,7 @@ import ub from flask import current_app as app import logging import smtplib -import tempfile +from tempfile import gettempdir import socket import sys import os @@ -21,13 +21,22 @@ from email.MIMEText import MIMEText from email.generator import Generator from flask_babel import gettext as _ import subprocess +import threading import shutil +import requests +import zipfile +from tornado.ioloop import IOLoop + try: import unidecode use_unidecode=True except: use_unidecode=False +# Global variables +global_task = None +updater_thread = None + def update_download(book_id, user_id): check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id == book_id).first() @@ -267,117 +276,146 @@ def update_dir_stucture(book_id, calibrepath): book.path = new_authordir + '/' + book.path.split('/')[1] db.session.commit() +class Updater(threading.Thread): -def file_to_list(file): - return [x.strip() for x in open(file, 'r') if not x.startswith('#EXT')] + def __init__(self): + threading.Thread.__init__(self) + self.status=0 -def one_minus_two(one, two): - return [x for x in one if x not in set(two)] + def run(self): + global global_task + self.status=1 + r = requests.get('https://api.github.com/repos/janeczku/calibre-web/zipball/master', stream=True) + fname = re.findall("filename=(.+)", r.headers['content-disposition'])[0] + self.status=2 + z = zipfile.ZipFile(StringIO(r.content)) + self.status=3 + tmp_dir = gettempdir() + z.extractall(tmp_dir) + self.status=4 + self.update_source(os.path.join(tmp_dir,os.path.splitext(fname)[0]),ub.config.get_main_dir) + self.status=5 + global_task = 0 + db.session.close() + db.engine.dispose() + ub.session.close() + ub.engine.dispose() + self.status=6 + # stop tornado server + server = IOLoop.instance() + server.add_callback(server.stop) + self.status=7 -def reduce_dirs(delete_files, new_list): - new_delete = [] - for file in delete_files: - parts = file.split(os.sep) - sub = '' - for i in range(len(parts)): - sub = os.path.join(sub, parts[i]) - if sub == '': - sub = os.sep - count = 0 - for song in new_list: - if song.startswith(sub): - count += 1 + def get_update_status(self): + return self.status + + def file_to_list(self, file): + return [x.strip() for x in open(file, 'r') if not x.startswith('#EXT')] + + def one_minus_two(self, one, two): + return [x for x in one if x not in set(two)] + + def reduce_dirs(self, delete_files, new_list): + new_delete = [] + for file in delete_files: + parts = file.split(os.sep) + sub = '' + for i in range(len(parts)): + sub = os.path.join(sub, parts[i]) + if sub == '': + sub = os.sep + count = 0 + for song in new_list: + if song.startswith(sub): + count += 1 + break + if count == 0: + if sub != '\\': + new_delete.append(sub) break - if count == 0: - if sub != '\\': - new_delete.append(sub) - break - return list(set(new_delete)) + return list(set(new_delete)) -def reduce_files(remove_items, exclude_items): - rf = [] - for item in remove_items: - if not item in exclude_items: - rf.append(item) - return rf + def reduce_files(self, remove_items, exclude_items): + rf = [] + for item in remove_items: + if not item in exclude_items: + rf.append(item) + return rf -def moveallfiles(root_src_dir, root_dst_dir): - change_permissions = True - if sys.platform == "win32" or sys.platform == "darwin": - change_permissions=False - else: - app.logger.debug('Update on OS-System : '+sys.platform ) - #print('OS-System: '+sys.platform ) - new_permissions=os.stat(root_dst_dir) - #print new_permissions - for src_dir, dirs, files in os.walk(root_src_dir): - dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1) - if not os.path.exists(dst_dir): - os.makedirs(dst_dir) - #print('Create-Dir: '+dst_dir) - if change_permissions: - #print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid)) - os.chown(dst_dir,new_permissions.st_uid,new_permissions.st_gid) - for file_ in files: - src_file = os.path.join(src_dir, file_) - dst_file = os.path.join(dst_dir, file_) - if os.path.exists(dst_file): - if change_permissions: - permission=os.stat(dst_file) - #print('Remove file before copy: '+dst_file) - os.remove(dst_file) - else: - if change_permissions: - permission=new_permissions - shutil.move(src_file, dst_dir) - #print('Move File '+src_file+' to '+dst_dir) - if change_permissions: - try: - os.chown(dst_file, permission.st_uid, permission.st_uid) - #print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid)) - except: - e = sys.exc_info() - #print('Fail '+str(dst_file)+' error: '+str(e)) - return - - -def update_source(source,destination): - # destination files - old_list=list() - exclude = (['vendor' + os.sep + 'kindlegen.exe','vendor' + os.sep + 'kindlegen','/app.db','vendor','/update.py']) - for root, dirs, files in os.walk(destination, topdown=True): - for name in files: - old_list.append(os.path.join(root, name).replace(destination, '')) - for name in dirs: - old_list.append(os.path.join(root, name).replace(destination, '')) - # source files - new_list = list() - for root, dirs, files in os.walk(source, topdown=True): - for name in files: - new_list.append(os.path.join(root, name).replace(source, '')) - for name in dirs: - new_list.append(os.path.join(root, name).replace(source, '')) - - delete_files = one_minus_two(old_list, new_list) - #print('raw delete list', delete_files) - - rf= reduce_files(delete_files, exclude) - #print('reduced delete list', rf) - - remove_items = reduce_dirs(rf, new_list) - #print('delete files', remove_items) - - moveallfiles(source, destination) - - for item in remove_items: - item_path = os.path.join(destination, item[1:]) - if os.path.isdir(item_path): - print("Delete dir "+ item_path) - shutil.rmtree(item_path) + def moveallfiles(self, root_src_dir, root_dst_dir): + change_permissions = True + if sys.platform == "win32" or sys.platform == "darwin": + change_permissions = False else: - try: - print("Delete file "+ item_path) - os.remove(item_path) - except: - print("Could not remove:"+item_path) - shutil.rmtree(source, ignore_errors=True) + app.logger.debug('Update on OS-System : ' + sys.platform) + # print('OS-System: '+sys.platform ) + new_permissions = os.stat(root_dst_dir) + # print new_permissions + for src_dir, dirs, files in os.walk(root_src_dir): + dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1) + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + # print('Create-Dir: '+dst_dir) + if change_permissions: + # print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid)) + os.chown(dst_dir, new_permissions.st_uid, new_permissions.st_gid) + for file_ in files: + src_file = os.path.join(src_dir, file_) + dst_file = os.path.join(dst_dir, file_) + if os.path.exists(dst_file): + if change_permissions: + permission = os.stat(dst_file) + # print('Remove file before copy: '+dst_file) + os.remove(dst_file) + else: + if change_permissions: + permission = new_permissions + shutil.move(src_file, dst_dir) + # print('Move File '+src_file+' to '+dst_dir) + if change_permissions: + try: + os.chown(dst_file, permission.st_uid, permission.st_uid) + # print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid)) + except: + e = sys.exc_info() + # print('Fail '+str(dst_file)+' error: '+str(e)) + return + + def update_source(self, source, destination): + # destination files + old_list = list() + exclude = ( + ['vendor' + os.sep + 'kindlegen.exe', 'vendor' + os.sep + 'kindlegen', '/app.db', 'vendor', '/update.py']) + for root, dirs, files in os.walk(destination, topdown=True): + for name in files: + old_list.append(os.path.join(root, name).replace(destination, '')) + for name in dirs: + old_list.append(os.path.join(root, name).replace(destination, '')) + # source files + new_list = list() + for root, dirs, files in os.walk(source, topdown=True): + for name in files: + new_list.append(os.path.join(root, name).replace(source, '')) + for name in dirs: + new_list.append(os.path.join(root, name).replace(source, '')) + + delete_files = self.one_minus_two(old_list, new_list) + + rf = self.reduce_files(delete_files, exclude) + + remove_items = self.reduce_dirs(rf, new_list) + + self.moveallfiles(source, destination) + + for item in remove_items: + item_path = os.path.join(destination, item[1:]) + if os.path.isdir(item_path): + app.logger.debug("Delete dir " + item_path) + shutil.rmtree(item_path) + else: + try: + app.logger.debug("Delete file " + item_path) + os.remove(item_path) + except: + app.logger.debug("Could not remove:" + item_path) + shutil.rmtree(source, ignore_errors=True) diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 5e6ea314..921a35ff 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -47,3 +47,7 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary{ background-color: #1C5484; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #89B9E2; } .btn-toolbar>.btn+.btn, .btn-toolbar>.btn-group+.btn, .btn-toolbar>.btn+.btn-group, .btn-toolbar>.btn-group+.btn-group { margin-left:0px; } + +.spinner {margin:0 41%;} +.spinner2 {margin:0 41%;} + diff --git a/cps/static/js/main.js b/cps/static/js/main.js index a2d56c9e..74e1a1d8 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -1,3 +1,6 @@ +var displaytext; +var updateTimerID; +var updateText; $(function() { $('.discover .row').isotope({ @@ -31,7 +34,9 @@ $(function() { url: window.location.pathname+"/../../shutdown", data: {"parameter":0}, success: function(data) { - return alert(data.text);} + $('#spinner').show(); + displaytext=data.text; + window.setTimeout(restartTimer, 3000);} }); }); $("#shutdown").click(function() { @@ -50,17 +55,66 @@ $(function() { dataType: 'json', url: window.location.pathname+"/../../get_update_status", success: function(data) { - if (data.status == true) { - $("#check_for_update").addClass('hidden'); - $("#perform_update").removeClass('hidden'); - }else{ $("#check_for_update").html(button_text); - };} + if (data.status == true) { + $("#check_for_update").addClass('hidden'); + $("#perform_update").removeClass('hidden'); + $("#update_info").removeClass('hidden'); + $("#update_info").find('span').html(data.commit); + } + } + }); + }); + $("#perform_update").click(function() { + $('#spinner2').show(); + $.ajax({ + type: "POST", + dataType: 'json', + data: { start: "True"}, + url: window.location.pathname+"/../../get_updater_status", + success: function(data) { + updateText=data.text + $("#UpdateprogressDialog #Updatecontent").html(updateText[data.status]); + console.log(data.status); + updateTimerID=setInterval(updateTimer, 2000);} }); }); - }); + +function restartTimer() { + $('#spinner').hide(); + $('#RestartDialog').modal('hide'); +} + +function updateTimer() { + $.ajax({ + dataType: 'json', + url: window.location.pathname+"/../../get_updater_status", + success: function(data) { + console.log(data.status); + $("#UpdateprogressDialog #Updatecontent").html(updateText[data.status]); + if (data.status >6){ + clearInterval(updateTimerID); + $('#spinner2').hide(); + $('#UpdateprogressDialog #updateFinished').removeClass('hidden'); + $("#check_for_update").removeClass('hidden'); + $("#perform_update").addClass('hidden'); + } + }, + error: function() { + console.log('Done'); + clearInterval(updateTimerID); + $('#spinner2').hide(); + $("#UpdateprogressDialog #Updatecontent").html(updateText[7]); + $('#UpdateprogressDialog #updateFinished').removeClass('hidden'); + $("#check_for_update").removeClass('hidden'); + $("#perform_update").addClass('hidden'); + } + }); +} + + $(window).resize(function(event) { $('.discover .row').isotope('reLayout'); }); diff --git a/cps/templates/admin.html b/cps/templates/admin.html index 31c167ee..b386e4ff 100644 --- a/cps/templates/admin.html +++ b/cps/templates/admin.html @@ -2,6 +2,7 @@ {% block body %}

{{_('User list')}}

+
@@ -76,11 +77,13 @@

{{_('Administration')}}

{% if not development %} -

{{_('Current commit timestamp')}}: {{commit}}

-
{{_('Restart Calibre-web')}}
-
{{_('Stop Calibre-web')}}
-
{{_('Check for update')}}
- +
{{_('Current commit timestamp')}}: {{commit}}
+ +

+
{{_('Restart Calibre-web')}}
+
{{_('Stop Calibre-web')}}
+
{{_('Check for update')}}
+ {% endif %} @@ -88,15 +91,17 @@ - + {% endblock %} diff --git a/cps/templates/layout.html b/cps/templates/layout.html index 71bd7941..76553ace 100644 --- a/cps/templates/layout.html +++ b/cps/templates/layout.html @@ -65,7 +65,7 @@ -
+ + +
+ +
{% for tag in tags %} {% endfor %}
- +
{% for tag in tags %} {% endfor %}
- +
{% for serie in series %} {% endfor %}
- +
{% for serie in series %} {% endfor %}
{% if languages %} - +
{% for language in languages %} {% endfor %}
- +
{% for language in languages %} {% endfor %}
diff --git a/cps/web.py b/cps/web.py index bfe42444..a33748e0 100755 --- a/cps/web.py +++ b/cps/web.py @@ -473,8 +473,10 @@ def feed_search(term): filter = True if term: entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")), - db.Books.authors.any(db.Authors.name.like("%" + term + "%")), - db.Books.title.like("%" + term + "%"))).filter(filter).all() + db.Books.series.any(db.Series.name.like("%" + term + "%")), + db.Books.authors.any(db.Authors.name.like("%" + term + "%")), + db.Books.publishers.any(db.Publishers.name.like("%" + term + "%")), + db.Books.title.like("%" + term + "%"))).filter(filter).all() entriescount = len(entries) if len(entries) > 0 else 1 pagination = Pagination(1, entriescount, entriescount) xml = render_title_template('feed.xml', searchterm=term, entries=entries, pagination=pagination) @@ -1087,9 +1089,10 @@ def search(): else: filter = True entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")), - db.Books.series.any(db.Series.name.like("%" + term + "%")), - db.Books.authors.any(db.Authors.name.like("%" + term + "%")), - db.Books.title.like("%" + term + "%"))).filter(filter).all() + db.Books.series.any(db.Series.name.like("%" + term + "%")), + db.Books.authors.any(db.Authors.name.like("%" + term + "%")), + db.Books.publishers.any(db.Publishers.name.like("%" + term + "%")), + db.Books.title.like("%" + term + "%"))).filter(filter).all() return render_title_template('search.html', searchterm=term, entries=entries) else: return render_title_template('search.html', searchterm="") @@ -1109,12 +1112,14 @@ def advanced_search(): author_name = request.args.get("author_name") book_title = request.args.get("book_title") + publisher = request.args.get("publisher") if author_name: author_name = author_name.strip() if book_title: book_title = book_title.strip() + if publisher: publisher = publisher.strip() if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \ - include_languages_inputs or exclude_languages_inputs or author_name or book_title: + include_languages_inputs or exclude_languages_inputs or author_name or book_title or publisher: searchterm = [] - searchterm.extend((author_name, book_title)) + searchterm.extend((author_name, book_title, publisher)) tag_names = db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all() searchterm.extend(tag.name for tag in tag_names) # searchterm = " + ".join(filter(None, searchterm)) @@ -1130,7 +1135,8 @@ def advanced_search(): searchterm.extend(language.name for language in language_names) searchterm = " + ".join(filter(None, searchterm)) q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_name + "%")), - db.Books.title.like("%" + book_title + "%")) + db.Books.title.like("%" + book_title + "%"), + db.Books.publishers.any(db.Publishers.name.like("%" + publisher + "%"))) for tag in include_tag_inputs: q = q.filter(db.Books.tags.any(db.Tags.id == tag)) for tag in exclude_tag_inputs:
{{_('Nickname')}}