Implement logging
This commit is contained in:
@@ -23,8 +23,6 @@ import logging
|
|||||||
import hashlib
|
import hashlib
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
|
||||||
|
|
||||||
# This allows for the program to be launched from the
|
# This allows for the program to be launched from the
|
||||||
# dir where it's been copied instead of needing to be
|
# dir where it's been copied instead of needing to be
|
||||||
# installed
|
# installed
|
||||||
@@ -32,20 +30,7 @@ install_dir = os.path.realpath(__file__)
|
|||||||
install_dir = pathlib.Path(install_dir).parents[1]
|
install_dir = pathlib.Path(install_dir).parents[1]
|
||||||
sys.path.append(str(install_dir))
|
sys.path.append(str(install_dir))
|
||||||
|
|
||||||
# Initialize logging
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
# This is outside the UI declaration to allow sharing
|
|
||||||
# the object across modules without explicit redeclaration
|
|
||||||
logger_filename = os.path.join(
|
|
||||||
QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation),
|
|
||||||
'Lector',
|
|
||||||
'Lector.log')
|
|
||||||
logging.basicConfig(
|
|
||||||
filename=logger_filename,
|
|
||||||
filemode='a',
|
|
||||||
format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
|
|
||||||
datefmt='%H:%M:%S',
|
|
||||||
level=logging.ERROR)
|
|
||||||
logger = logging.getLogger('lector.main')
|
|
||||||
|
|
||||||
from lector import database
|
from lector import database
|
||||||
from lector import sorter
|
from lector import sorter
|
||||||
@@ -100,6 +85,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
Settings(self).read_settings() # This should populate all variables that need
|
Settings(self).read_settings() # This should populate all variables that need
|
||||||
# to be remembered across sessions
|
# to be remembered across sessions
|
||||||
|
|
||||||
|
# Initialize logging
|
||||||
|
self.logger = init_logging(self.settings['log_level'])
|
||||||
|
self.logger.log(60, 'Application started')
|
||||||
|
|
||||||
# Initialize icon factory
|
# Initialize icon factory
|
||||||
self.QImageFactory = QImageFactory(self)
|
self.QImageFactory = QImageFactory(self)
|
||||||
|
|
||||||
@@ -256,7 +245,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
|
|
||||||
# Get list of available parsers
|
# Get list of available parsers
|
||||||
self.available_parsers = '*.' + ' *.'.join(sorter.available_parsers)
|
self.available_parsers = '*.' + ' *.'.join(sorter.available_parsers)
|
||||||
logger.info('Available parsers: ' + self.available_parsers)
|
self.logger.info('Available parsers: ' + self.available_parsers)
|
||||||
|
|
||||||
# The Library tab gets no button
|
# The Library tab gets no button
|
||||||
self.tabWidget.tabBar().setTabButton(
|
self.tabWidget.tabBar().setTabButton(
|
||||||
@@ -423,7 +412,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
if not file_paths:
|
if not file_paths:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(
|
self.logger.info(
|
||||||
'Attempting to open: ' + ', '.join(file_paths))
|
'Attempting to open: ' + ', '.join(file_paths))
|
||||||
|
|
||||||
contents = sorter.BookSorter(
|
contents = sorter.BookSorter(
|
||||||
@@ -437,7 +426,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
# Notification feedback in case all books return nothing
|
# Notification feedback in case all books return nothing
|
||||||
|
|
||||||
if not contents:
|
if not contents:
|
||||||
logger.error('No parseable files found')
|
self.logger.error('No parseable files found')
|
||||||
return
|
return
|
||||||
|
|
||||||
successfully_opened = []
|
successfully_opened = []
|
||||||
@@ -447,7 +436,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
file_data = contents[i]
|
file_data = contents[i]
|
||||||
Tab(file_data, self)
|
Tab(file_data, self)
|
||||||
successfully_opened.append(file_data['path'])
|
successfully_opened.append(file_data['path'])
|
||||||
logger.info(
|
self.logger.info(
|
||||||
'Successfully opened: ' + ', '.join(file_paths))
|
'Successfully opened: ' + ', '.join(file_paths))
|
||||||
|
|
||||||
if self.settings['last_open_tab'] == 'library':
|
if self.settings['last_open_tab'] == 'library':
|
||||||
@@ -1049,6 +1038,21 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
QtWidgets.qApp.exit()
|
QtWidgets.qApp.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def init_logging(log_level):
|
||||||
|
location_prefix = QtCore.QStandardPaths.writableLocation(
|
||||||
|
QtCore.QStandardPaths.AppDataLocation)
|
||||||
|
os.makedirs(location_prefix, exist_ok=True)
|
||||||
|
logger_filename = os.path.join(location_prefix, 'Lector.log')
|
||||||
|
logging.basicConfig(
|
||||||
|
filename=logger_filename,
|
||||||
|
filemode='a',
|
||||||
|
format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
|
||||||
|
datefmt='%Y/%M/%d %H:%M:%S',
|
||||||
|
level=log_level)
|
||||||
|
logging.addLevelName(60, 'HammerTime') ## Messages that MUST be logged
|
||||||
|
return logging.getLogger('lector.main')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
app.setApplicationName('Lector') # This is needed for QStandardPaths
|
app.setApplicationName('Lector') # This is needed for QStandardPaths
|
||||||
@@ -1063,8 +1067,7 @@ def main():
|
|||||||
translations_out_string = '(Translations found)'
|
translations_out_string = '(Translations found)'
|
||||||
if not translations_found:
|
if not translations_found:
|
||||||
translations_out_string = '(No translations found)'
|
translations_out_string = '(No translations found)'
|
||||||
log_string = f'Locale: {QtCore.QLocale.system().name()}' + translations_out_string
|
print(f'Locale: {QtCore.QLocale.system().name()}' + translations_out_string)
|
||||||
logger.info(log_string)
|
|
||||||
|
|
||||||
form = MainUI()
|
form = MainUI()
|
||||||
form.show()
|
form.show()
|
||||||
@@ -1073,5 +1076,4 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logger.info('Application start')
|
|
||||||
main()
|
main()
|
||||||
|
@@ -26,7 +26,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class DatabaseInit:
|
class DatabaseInit:
|
||||||
def __init__(self, location_prefix):
|
def __init__(self, location_prefix):
|
||||||
os.makedirs(location_prefix, exist_ok=True)
|
|
||||||
self.database_path = os.path.join(location_prefix, 'Lector.db')
|
self.database_path = os.path.join(location_prefix, 'Lector.db')
|
||||||
|
|
||||||
self.books_table_columns = {
|
self.books_table_columns = {
|
||||||
|
@@ -50,7 +50,7 @@ class Library:
|
|||||||
'LIKE')
|
'LIKE')
|
||||||
|
|
||||||
if not books:
|
if not books:
|
||||||
logger.error('Database returned nothing')
|
logger.warning('Database returned nothing')
|
||||||
return
|
return
|
||||||
|
|
||||||
elif mode == 'addition':
|
elif mode == 'addition':
|
||||||
|
@@ -102,7 +102,8 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
|
|||||||
try:
|
try:
|
||||||
return self.header_data[column]
|
return self.header_data[column]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print('Table proxy model: Can\'t find header for column', column)
|
logger.error(
|
||||||
|
'Table proxy model: Can\'t find header for column' + str(column))
|
||||||
# The column will be called IndexError. Not a typo.
|
# The column will be called IndexError. Not a typo.
|
||||||
return 'IndexError'
|
return 'IndexError'
|
||||||
|
|
||||||
@@ -347,31 +348,3 @@ class MostExcellentFileSystemModel(QtWidgets.QFileSystemModel):
|
|||||||
for i in deletable:
|
for i in deletable:
|
||||||
del self.tag_data[i]
|
del self.tag_data[i]
|
||||||
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
# Unbork this
|
|
||||||
class FileSystemProxyModel(QtCore.QSortFilterProxyModel):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super(FileSystemProxyModel, self).__init__(parent)
|
|
||||||
|
|
||||||
def filterAcceptsRow(self, row_num, parent):
|
|
||||||
model = self.sourceModel()
|
|
||||||
filter_out = [
|
|
||||||
'boot', 'dev', 'etc', 'lost+found', 'opt', 'pdb',
|
|
||||||
'proc', 'root', 'run', 'srv', 'sys', 'tmp', 'twonky',
|
|
||||||
'usr', 'var', 'bin', 'kdeinit5__0', 'lib', 'lib64', 'sbin']
|
|
||||||
|
|
||||||
name_index = model.index(row_num, 0)
|
|
||||||
valid_data = model.data(name_index)
|
|
||||||
|
|
||||||
print(valid_data)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
if valid_data in filter_out:
|
|
||||||
return False
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
@@ -53,7 +53,7 @@ class EPUB:
|
|||||||
self.zip_file = zipfile.ZipFile(
|
self.zip_file = zipfile.ZipFile(
|
||||||
self.filename, mode='r', allowZip64=True)
|
self.filename, mode='r', allowZip64=True)
|
||||||
except (KeyError, AttributeError, zipfile.BadZipFile):
|
except (KeyError, AttributeError, zipfile.BadZipFile):
|
||||||
print('Cannot parse ' + self.filename)
|
logger.error('Malformed zip file ' + self.filename)
|
||||||
return
|
return
|
||||||
|
|
||||||
def parse_xml(self, filename, parser):
|
def parse_xml(self, filename, parser):
|
||||||
@@ -61,7 +61,8 @@ class EPUB:
|
|||||||
this_xml = self.zip_file.read(filename).decode()
|
this_xml = self.zip_file.read(filename).decode()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
short_filename = os.path.basename(self.filename)
|
short_filename = os.path.basename(self.filename)
|
||||||
print(f'{str(filename)} not found in {short_filename}')
|
warning_string = f'{str(filename)} not found in {short_filename}'
|
||||||
|
logger.warning(warning_string)
|
||||||
return
|
return
|
||||||
|
|
||||||
root = BeautifulSoup(this_xml, parser)
|
root = BeautifulSoup(this_xml, parser)
|
||||||
@@ -82,7 +83,8 @@ class EPUB:
|
|||||||
try:
|
try:
|
||||||
return root_item.get('full-path')
|
return root_item.get('full-path')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print(f'ePub module: {self.filename} has a malformed container.xml')
|
error_string = f'ePub module: {self.filename} has a malformed container.xml'
|
||||||
|
logger.error(error_string)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
possible_filenames = ('content.opf', 'package.opf')
|
possible_filenames = ('content.opf', 'package.opf')
|
||||||
@@ -107,7 +109,7 @@ class EPUB:
|
|||||||
if file_path_actual:
|
if file_path_actual:
|
||||||
return self.zip_file.read(file_path_actual)
|
return self.zip_file.read(file_path_actual)
|
||||||
else:
|
else:
|
||||||
print('ePub module can\'t find ' + filename)
|
logger.error('ePub module can\'t find ' + filename)
|
||||||
|
|
||||||
#______________________________________________________
|
#______________________________________________________
|
||||||
|
|
||||||
|
@@ -88,6 +88,11 @@ class Settings:
|
|||||||
'currentProfileIndex', 0))
|
'currentProfileIndex', 0))
|
||||||
self.parent.comic_profile = self.settings.value(
|
self.parent.comic_profile = self.settings.value(
|
||||||
'comicProfile', self.default_comic_profile)
|
'comicProfile', self.default_comic_profile)
|
||||||
|
try:
|
||||||
|
log_level = int(self.settings.value('logLevel', logging.WARNING))
|
||||||
|
except ValueError:
|
||||||
|
log_level = 30
|
||||||
|
self.parent.settings['log_level'] = log_level
|
||||||
self.settings.endGroup()
|
self.settings.endGroup()
|
||||||
|
|
||||||
self.settings.beginGroup('lastOpen')
|
self.settings.beginGroup('lastOpen')
|
||||||
@@ -149,7 +154,6 @@ class Settings:
|
|||||||
self.settings.endGroup()
|
self.settings.endGroup()
|
||||||
|
|
||||||
def save_settings(self):
|
def save_settings(self):
|
||||||
print('Saving settings...')
|
|
||||||
current_settings = self.parent.settings
|
current_settings = self.parent.settings
|
||||||
|
|
||||||
self.settings.beginGroup('mainWindow')
|
self.settings.beginGroup('mainWindow')
|
||||||
@@ -183,6 +187,7 @@ class Settings:
|
|||||||
current_profile3])
|
current_profile3])
|
||||||
self.settings.setValue('currentProfileIndex', current_profile_index)
|
self.settings.setValue('currentProfileIndex', current_profile_index)
|
||||||
self.settings.setValue('comicProfile', self.parent.comic_profile)
|
self.settings.setValue('comicProfile', self.parent.comic_profile)
|
||||||
|
self.settings.setValue('logLevel', self.parent.settings['log_level'])
|
||||||
self.settings.endGroup()
|
self.settings.endGroup()
|
||||||
|
|
||||||
current_tab_index = self.parent.tabWidget.currentIndex()
|
current_tab_index = self.parent.tabWidget.currentIndex()
|
||||||
@@ -228,3 +233,5 @@ class Settings:
|
|||||||
self.settings.beginGroup('annotations')
|
self.settings.beginGroup('annotations')
|
||||||
self.settings.setValue('annotationList', current_settings['annotations'])
|
self.settings.setValue('annotationList', current_settings['annotations'])
|
||||||
self.settings.endGroup()
|
self.settings.endGroup()
|
||||||
|
|
||||||
|
logger.info('Settings saved')
|
||||||
|
@@ -192,7 +192,7 @@ class SettingsUI(QtWidgets.QDialog, settingswindow.Ui_Dialog):
|
|||||||
self.main_window.generate_library_filter_menu(paths)
|
self.main_window.generate_library_filter_menu(paths)
|
||||||
directory_data = {}
|
directory_data = {}
|
||||||
if not paths:
|
if not paths:
|
||||||
logger.error('Database: No paths for settings...')
|
logger.warning('No paths saved for books')
|
||||||
else:
|
else:
|
||||||
# Convert to the dictionary format that is
|
# Convert to the dictionary format that is
|
||||||
# to be fed into the QFileSystemModel
|
# to be fed into the QFileSystemModel
|
||||||
|
@@ -177,7 +177,8 @@ class BookSorter:
|
|||||||
or os.path.exists(self.hashes_and_paths[file_md5])):
|
or os.path.exists(self.hashes_and_paths[file_md5])):
|
||||||
|
|
||||||
if not self.hashes_and_paths[file_md5] == filename:
|
if not self.hashes_and_paths[file_md5] == filename:
|
||||||
print(f'{os.path.basename(filename)} is already in database')
|
warning_string = f'{os.path.basename(filename)} is already in database'
|
||||||
|
logger.warning(warning_string)
|
||||||
return
|
return
|
||||||
|
|
||||||
# This allows for eliminating issues with filenames that have
|
# This allows for eliminating issues with filenames that have
|
||||||
@@ -190,7 +191,7 @@ class BookSorter:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not valid_extension:
|
if not valid_extension:
|
||||||
print(filename + ' has an unsupported extension')
|
logger.error(filename + ' has an unsupported extension')
|
||||||
return
|
return
|
||||||
|
|
||||||
book_ref = sorter[file_extension](filename, self.temp_dir, file_md5)
|
book_ref = sorter[file_extension](filename, self.temp_dir, file_md5)
|
||||||
@@ -317,7 +318,7 @@ class BookSorter:
|
|||||||
|
|
||||||
del self.processed_books
|
del self.processed_books
|
||||||
processing_time = str(time.time() - start_time)
|
processing_time = str(time.time() - start_time)
|
||||||
logger.info('Finished processing in:' + processing_time)
|
logger.info('Finished processing in ' + processing_time)
|
||||||
return return_books
|
return return_books
|
||||||
|
|
||||||
|
|
||||||
|
@@ -124,9 +124,10 @@ class BackGroundBookSearch(QtCore.QThread):
|
|||||||
|
|
||||||
if self.valid_directories:
|
if self.valid_directories:
|
||||||
initiate_threads()
|
initiate_threads()
|
||||||
print(len(self.valid_files), 'books found')
|
info_string = str(len(self.valid_files)) + ' books found'
|
||||||
|
logger.info(info_string)
|
||||||
else:
|
else:
|
||||||
print('No valid directories')
|
logger.error('No valid directories')
|
||||||
|
|
||||||
|
|
||||||
class BackGroundCacheRefill(QtCore.QThread):
|
class BackGroundCacheRefill(QtCore.QThread):
|
||||||
|
@@ -319,7 +319,7 @@ class Tab(QtWidgets.QWidget):
|
|||||||
try:
|
try:
|
||||||
required_position = self.metadata['position']['cursor_position']
|
required_position = self.metadata['position']['cursor_position']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print('Database: Cursor position error. Recommend retry.')
|
logging.error('Database: Cursor position error. Recommend retry.')
|
||||||
return
|
return
|
||||||
|
|
||||||
if cursor_position:
|
if cursor_position:
|
||||||
|
Reference in New Issue
Block a user