PK ! simpledms/__init__.pyPK ! (+ simpledms/__main__.pyimport os
import sys
ROOT = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, ROOT)
from main import start_gui # NOQA
if __name__ == "__main__":
start_gui()
PK ! =IcH cH simpledms/main.py"""This is the main module which serves as a starting point. It starts the UI."""
import datetime
import json
import os
import re
import sys
import webbrowser
import magic # type: ignore
import qdarkstyle # type: ignore
import rules # type: ignore
import ui.about # type: ignore
import ui.resources # type: ignore
from pdfhandler import DateExtractor
from pdfhandler import PdfHandler
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5 import uic
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtWidgets import QFileSystemModel
from utils import MyDictionaryCompleter
from utils import MyRulesWidget
from utils import MyTextEdit
CURRDIR = os.path.dirname(os.path.abspath(__file__))
class Ui(QtWidgets.QMainWindow):
"""Main Class of the simple DMS user interface.
Arguments:
nothing
Returns:
nothing
"""
def __init__(self):
"""Initialize variables and connect actions with functions."""
super(Ui, self).__init__()
uic.loadUi(os.path.join(CURRDIR, "ui", "main_simpledms.ui"), self)
self.loadpref()
self.rules = rules.Rules(self.pref["dmsroot"])
self.currentselectedrulesfolder = None
self.currentselectedsearchfolder = None
self.filemodelmonitor = QFileSystemModel()
self.rulesfoldermodel = QFileSystemModel()
self.resultfoldermodel = QFileSystemModel()
self.searchfoldermodel = QFileSystemModel()
self.textEdit_tags = MyTextEdit(self.textEdit_tags)
self.updateui_settings()
self.updateui_pdfrename()
self.show()
# Connect Widget Toolbar Actions
self.actionScan.triggered.connect(self.select_widget)
self.actionPdf.triggered.connect(self.select_widget)
self.actionSettings.triggered.connect(self.select_widget)
self.actionAbout.triggered.connect(self.show_ui_about)
self.actionExit.triggered.connect(self.select_widget)
# Connect Preferences
self.pushButton_setmonitorfolder.clicked.connect(self.browse_monitor_folder)
self.pushButton_setdmsroot.clicked.connect(self.browse_dms_root)
self.treeView_rulesfolders.clicked.connect(self.rulesfolderselected)
self.treeView_rules.doubleClicked.connect(self.ruledoubleclicked)
self.pushButton_addrule.clicked.connect(self.addruleclicked)
self.pushButton_deleterule.clicked.connect(self.deleteruleclicked)
# Connect page pdf renaming
self.listView_monitorfiles.clicked.connect(self.listView_monitorfiles_clicked)
self.treeView_output.clicked.connect(self.treeView_output_clicked)
self.pushButton_ok.clicked.connect(self.pushButton_ok_clicked)
self.listView_monitorfiles.doubleClicked.connect(
self.listView_monitorfiles_doubleclicked
)
self.lineEdit_outputfilename.textChanged.connect(self.readyforstorage)
self.pushButton_addDate.clicked.connect(self.pushButton_addDate_clicked)
# -------- Settings page -----------
def rulesfolderselected(self, signal):
"""Update ui if a folder in settings -> rules is selected."""
self.currentselectedrulesfolder = self.rulesfoldermodel.filePath(signal)
self.updateui_settings()
self.pushButton_addrule.setEnabled(True)
def ruledoubleclicked(self):
"""Open ui for rule adaption of double clicked rule."""
selectedrule = self.treeView_rules_model.itemData(
self.treeView_rules.selectedIndexes()[0]
)
rule = self.rules.returnruleofkeywords(
[selectedrule[0]], self.currentselectedrulesfolder
)
rulesdialog = MyRulesWidget(
keywords=rule[0][1],
booleanoperator=rule[0][2],
tags=rule[0][3],
doctitle=rule[0][4],
indexertags=set(self.rules.returnalltags()),
)
rulesdialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
if rulesdialog.exec_():
self.rules.replacerule(
rule[0][0],
rulesdialog.keywords,
rulesdialog.booleanoperator,
rulesdialog.tags,
rulesdialog.doctitle,
self.currentselectedrulesfolder,
)
self.updateui_settings()
def deleteruleclicked(self):
"""Delete selected rule and update ui."""
if self.treeView_rules.selectedIndexes():
selectedrule = self.treeView_rules_model.itemData(
self.treeView_rules.selectedIndexes()[0]
)
self.rules.delrule([selectedrule[0]], self.currentselectedrulesfolder)
self.updateui_settings()
def addruleclicked(self):
"""Add rule to database if it does not exist yet."""
rulesdialog = MyRulesWidget(indexertags=set(self.rules.returnalltags()))
rulesdialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
if rulesdialog.exec_():
if self.rules.returnruleofkeywords(
rulesdialog.keywords, self.currentselectedrulesfolder
):
QtWidgets.QMessageBox.information(
self, "Error", "A rule with these keywords already exists."
)
else:
self.rules.addrule(
rulesdialog.keywords,
rulesdialog.booleanoperator,
rulesdialog.tags,
rulesdialog.doctitle,
self.currentselectedrulesfolder,
)
self.updateui_settings()
def loadpref(self):
"""Load preferences: root of dms and monitorfolder."""
if os.path.isfile("pref.json"):
with open("pref.json") as f:
self.pref = json.load(f)
if not os.path.isdir(self.pref["dmsroot"]):
os.makedirs(self.pref["dmsroot"])
QtWidgets.QMessageBox.information(
self, "Attention!", "Stored path of dmsroot does not exist"
)
if not os.path.isdir(self.pref["monitorfolder"]):
os.makedirs(self.pref["monitorfolder"])
QtWidgets.QMessageBox.information(
self,
"Attention!",
"Stored path of monitorfolder does not exist.",
)
else:
# If pref.json file does not exist
if not os.path.isdir(os.path.join(os.path.expanduser("~"), "paperwork")):
os.makedirs(os.path.join(os.path.expanduser("~"), "paperwork"))
QtWidgets.QMessageBox.information(
self,
"Attention!",
"Standard path for file cabinet"
"was created. If "
"needed, please change.",
)
if not os.path.isdir(
os.path.join(os.path.expanduser("~"), "paperwork_open")
):
os.makedirs(os.path.join(os.path.expanduser("~"), "paperwork_open"))
QtWidgets.QMessageBox.information(
self,
"Attention!",
"Standard path for monitor folder"
"was created. If "
"needed, please change.",
)
self.pref = {
"dmsroot": os.path.join(os.path.expanduser("~"), "paperwork"),
"monitorfolder": os.path.join(
os.path.expanduser("~"), "paperwork_open"
),
}
self.savepref()
def savepref(self):
"""Save preferences to pref.json."""
with open("pref.json", "w") as f:
json.dump(self.pref, f)
def browse_monitor_folder(self):
"""Select monitor folder."""
# execute getExistingDirectory dialog and set the directory variable to be equal
# to the user selected directory
directory = QFileDialog.getExistingDirectory(
self, "Select a monitor folder with files to be " "processed/imported"
)
# if user didn't pick a directory don't continue
if directory:
self.pref["monitorfolder"] = directory
self.savepref()
self.updateui_settings()
self.updateui_pdfrename()
def browse_dms_root(self):
"""Select dms root folder."""
# execute getExistingDirectory dialog and set the directory variable to be equal
# to the user selected directory
directory = QFileDialog.getExistingDirectory(
self, "Select a root directory of the filing cabinet"
)
# if user didn't pick a directory don't continue
if directory:
if not len(self.rules.returnallrules()) == 0:
result = QtWidgets.QMessageBox.question(
self,
"Attention",
"If the root directory is changed, the current rules are "
"deleted! Are you sure and want to proceed?",
)
if result == QtWidgets.QMessageBox.No:
return
self.rules.resetdb(directory)
self.pref["dmsroot"] = directory
self.savepref()
# self.indexer.__init__(directory)
self.updateui_settings()
def updateui_settings(self):
"""Update ui elements of settings page."""
self.label_monitorfolder.setText(self.pref["monitorfolder"])
self.label_monitordir.setText(
".."
+ os.sep
+ os.path.basename(os.path.normpath(self.pref["monitorfolder"]))
)
self.label_dmsroot.setText(self.pref["dmsroot"])
self.rulesfoldermodel.setRootPath(self.pref["dmsroot"])
self.rulesfoldermodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs)
self.treeView_rulesfolders.setModel(self.rulesfoldermodel)
self.treeView_rulesfolders.setRootIndex(
self.rulesfoldermodel.index(self.pref["dmsroot"])
)
self.treeView_rulesfolders.hideColumn(1)
self.treeView_rulesfolders.hideColumn(2)
self.treeView_rulesfolders.hideColumn(3)
self.treeView_rules_model = QtGui.QStandardItemModel()
self.treeView_rules.setModel(self.treeView_rules_model)
self.treeView_rules_model.setHorizontalHeaderLabels(["Rules (keywords)"])
rulesoffolder = self.rules.returnrulesoffolder(self.currentselectedrulesfolder)
if rulesoffolder is not None:
for i in rulesoffolder:
rule = QtGui.QStandardItem(i[1])
self.treeView_rules_model.appendRow(rule)
# -------- Action Bar -----------
def select_widget(self):
"""Select index of stacked widget based on toolbox actions."""
sender = self.sender()
if sender.text() == "Scan":
pass
elif sender.text() == "Import":
self.stackedWidget.setCurrentIndex(0)
elif sender.text() == "Settings":
self.stackedWidget.setCurrentIndex(1)
elif sender.text() == "Exit":
QtWidgets.QApplication.instance().quit()
def show_ui_about(self):
"""Show about page."""
dialog = QDialog()
dialog.ui = ui.about.Ui_Dialog()
dialog.ui.setupUi(dialog)
dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
dialog.exec_()
# -------- pdf renaming page -----------
def listView_monitorfiles_clicked(self, index):
"""Show preview of pdf, extract date and words and propose tags and directory in dms root."""
pdffile = os.path.join(
self.pref["monitorfolder"], self.filemodelmonitor.itemData(index)[0]
)
# Check if file is really a pdf.
if "PDF" not in magic.from_file(pdffile):
QtWidgets.QMessageBox.information(
self, "Attention!", "Document is not a pdf."
)
return
self.listView_monitorfiles.setEnabled(False)
self.setCursor(QtCore.Qt.BusyCursor)
self.statusbar.showMessage("Reading pdf...")
self.pdfhandler = PdfHandler(pdffile)
self.pdfhandler.thumbheight = (
self.listView_monitorfiles.frameGeometry().height()
)
self.pdfhandler.createthumbnail()
pixmap = QtGui.QPixmap(self.pdfhandler.thumbfpath)
self.thumbnail.setPixmap(pixmap)
self.thumbnail.resize(pixmap.width(), pixmap.height())
self.analyze_text()
self.listView_monitorfiles.setEnabled(True)
self.setCursor(QtCore.Qt.ArrowCursor)
def listView_monitorfiles_doubleclicked(self, index):
"""Open doubleclicked file.
Arguments:
index: index from pyqt5 listview which element was clicked.
Returns:
nothing
"""
file2open = os.path.join(
self.pref["monitorfolder"], self.filemodelmonitor.itemData(index)[0]
)
webbrowser.open(file2open)
def listWidget_pdfthumbnails_doubleclicked(self):
"""Open file in browser."""
file2open = self.pdfhandler.filepath
if os.path.isfile(file2open):
webbrowser.open(file2open)
else:
QtWidgets.QMessageBox.information(
self, "Attention!", "File does not exist!"
)
def analyze_text(self):
"""Analyze the found text."""
text = self.pdfhandler.gettext()
dateext = DateExtractor(text)
date = dateext.getdate()
# If Date not found in text, search for date in filename
if not date:
dateext = DateExtractor(self.pdfhandler.filepath)
date = dateext.getdate()
result = self.rules.applyrule(text)
if result["doctitle"] is not None:
if date is not None:
self.lineEdit_outputfilename.setText(
date.strftime("%Y-%m-%d") + " " + result["doctitle"]
)
else:
self.lineEdit_outputfilename.setText(result["doctitle"])
else:
if date is not None:
self.lineEdit_outputfilename.setText(date.strftime("%Y-%m-%d") + " ")
else:
self.lineEdit_outputfilename.clear()
if result["tags"] is not None:
self.textEdit_tags.setText(result["tags"])
else:
self.textEdit_tags.clear()
self.destination = result["destination"]
self.treeView_output.setCurrentIndex(
self.resultfoldermodel.index(self.destination)
)
if not self.readyforstorage():
self.lineEdit_outputfilename.setFocus()
else:
self.pushButton_ok.setFocus()
self.statusbar.showMessage("Ready")
self.setCursor(QtCore.Qt.ArrowCursor)
def readyforstorage(self):
"""Check if file infos like date, text, tags and folder are set."""
if (
self.lineEdit_outputfilename.text()
and re.match(
r"(\d{4}-\d{2}-\d{2}\s\S+)",
self.lineEdit_outputfilename.text(),
re.I | re.UNICODE,
)
and self.resultfoldermodel.filePath(self.treeView_output.currentIndex())
):
self.pushButton_ok.setEnabled(True)
return True
else:
self.pushButton_ok.setEnabled(False)
return False
def pushButton_ok_clicked(self):
"""Store document with new metadata in target directory."""
self.setCursor(QtCore.Qt.BusyCursor)
self.statusbar.showMessage("Storing...")
doctitle = self.lineEdit_outputfilename.text()[11:]
date = self.lineEdit_outputfilename.text()[0:10]
tags = self.textEdit_tags.toPlainText()
tags = tags.strip(", ")
path = self.resultfoldermodel.filePath(self.treeView_output.currentIndex())
self.pdfhandler.update_and_move(path, doctitle, tags, date)
self.updateui_pdfrename()
self.setCursor(QtCore.Qt.ArrowCursor)
self.statusbar.showMessage("ready")
def pushButton_addDate_clicked(self):
"""Add current date to document name field."""
if self.lineEdit_outputfilename.text():
if re.match(r"(\d{4}-\d{2}-\d{2})", self.lineEdit_outputfilename.text()):
text = (
str(datetime.date.today())
+ self.lineEdit_outputfilename.text()[10:]
)
self.lineEdit_outputfilename.setText(text)
else:
self.lineEdit_outputfilename.setText(str(datetime.date.today()) + " ")
else:
self.lineEdit_outputfilename.setText(str(datetime.date.today()) + " ")
self.lineEdit_outputfilename.setFocus()
def treeView_output_clicked(self):
"""Check if all input is available after the target directory was selected."""
self.readyforstorage()
def updateui_pdfrename(self):
"""Update ui of page pdfrename."""
self.filemodelmonitor.setRootPath(self.pref["monitorfolder"])
self.filemodelmonitor.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.filemodelmonitor.setNameFilters(["*.pdf"])
self.filemodelmonitor.setNameFilterDisables(False)
self.listView_monitorfiles.setModel(self.filemodelmonitor)
self.listView_monitorfiles.setRootIndex(
self.filemodelmonitor.index(self.pref["monitorfolder"])
)
self.resultfoldermodel.setRootPath(self.pref["dmsroot"])
self.resultfoldermodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs)
self.treeView_output.setModel(self.resultfoldermodel)
self.treeView_output.setRootIndex(
self.resultfoldermodel.index(self.pref["dmsroot"])
)
self.treeView_output.hideColumn(1)
self.treeView_output.hideColumn(2)
self.treeView_output.hideColumn(3)
# todo: change name of header
indexertags = set(self.rules.returnalltags())
completer = MyDictionaryCompleter(myKeywords=indexertags)
self.textEdit_tags.setCompleter(completer)
def start_gui():
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
window = Ui()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
start_gui()
PK ! ڙ simpledms/pdfhandler.py"""Package for pdf handling like text extraction, and date parsing."""
import calendar
import locale
import os
import re
import subprocess # nosec
import tempfile
from typing import List
import cv2 # type: ignore
import dateparser # type: ignore
from pdfrw import PdfReader
from pdfrw import PdfWriter
from tika import parser # type: ignore
class PdfHandler:
"""Class for pdf handling."""
def __init__(self, filepath: str):
"""Initialize all variables.
Arguments:
filepath {str} -- Filepath of the pdf, e.g. /home/user/test.pdf
"""
self.filepath = filepath
self.thumbheight = 500
self.tempdirectory = tempfile.TemporaryDirectory()
self.thumbfpath = None
def gettext(self) -> str:
"""Extract text from pdf.
Returns:
str -- Extracted text.
"""
raw = parser.from_file(self.filepath)
return raw["content"]
def createthumbnail(self):
"""Create a thumbnail of first page of pdf to be shown in GUI.
Requires pdftoppm installed.
"""
args = [
"pdftoppm",
self.filepath,
os.path.join(self.tempdirectory.name, "out"),
"-l",
"1",
"-jpeg",
"-r",
"300",
]
subprocess.call(args) # nosec
im = cv2.imread(os.path.join(self.tempdirectory.name, "out-1.jpg"))
h, w, _ = im.shape
h_new = self.thumbheight
w_new = int(w * h_new / h)
im = cv2.resize(im, (w_new, h_new))
cv2.imwrite(os.path.join(self.tempdirectory.name, "out-1.jpg"), im)
self.thumbfpath = os.path.join(self.tempdirectory.name, "out-1.jpg")
def update_and_move(
self, targetdir: str, doctitle: str, tags: List[str], date: str
):
"""Update metadata of pdf and move to target directory.
Arguments:
targetdir {str} -- Target directory where pdf shall be placed.
doctitle {str} -- New document title of pdf.
tags {List[str]} -- Keywords/tags which shall be added to pdf.
date {str} -- Date which will be entered into pdf filename.
"""
pdf = PdfReader(self.filepath)
# Check for correct file ending
if doctitle[-4:] != ".pdf":
filename = date + " " + doctitle + ".pdf"
else:
filename = date + " " + doctitle
doctitle = doctitle[0:-4]
# Check for unique filename
n = 1
if os.path.isfile(os.path.join(targetdir, filename)):
filename = filename[0:-4] + "-" + str(n) + ".pdf"
while os.path.isfile(os.path.join(targetdir, filename)):
regex = re.compile(r"-\d{1,}.pdf", re.IGNORECASE)
filename = regex.sub("-" + str(n) + ".pdf", filename)
n = n + 1
pdf.Info.Keywords = tags
pdf.Info.Title = doctitle
# Write data
PdfWriter(os.path.join(targetdir, filename), trailer=pdf).write()
# try to delete file ##
try:
os.remove(self.filepath)
except OSError as e: # if failed, report it back to the user ##
print("Error: %s - %s." % (e.filename, e.strerror))
class DateExtractor:
"""Class to extract date from text."""
def __init__(self, text: str):
"""Initialize variables.
Arguments:
text {str} -- Text from which date shall be extracted.
"""
self.text = text
def getdate(self):
"""Search in text for date.
Returns:
datetime -- Found date in python datetime format.
"""
locale.setlocale(locale.LC_ALL, "")
pattern = []
prefix = r"Datum:|Date:|übermittelt| übermittelt am | übermittelt am:"
# Datum: 01.01.2018 and 01-01-2018
pattern.append(
r"((?:%s)\s*\d{1,2}[ _.-]\d{1,2}[ _.-](?:19[6789]\d|2[01][0123]\d))"
% prefix
)
# Datum: 02 Jan 2018 02. Jan 2018
pattern.append(
r"((?:Datum|Date):\s*\d{1,2}(?:[ _.-]|. )(?:%s) (?:19[6789]\d|2[01][0123]\d))"
% "|".join(calendar.month_abbr[1:])
)
# Datum: 02 Januar 2018 02. Januar 2018
pattern.append(
r"((?:Datum|Date):\s*\d{1,2}(?:[ _.-]|. )(?:%s) (?:19[6789]\d|2[01][0123]\d))"
% "|".join(calendar.month_name)
)
# 01.01.2018 and 01-01-2018
pattern.append(r"(\d{1,2}[ _.-]\d{1,2}[ _.-](?:19[6789]\d|2[01][0123]\d))")
# 02 Jan 2018 02. Jan 2018
pattern.append(
r"(\d{1,2}(?:[ _.-]|. )(?:%s) (?:19[6789]\d|2[01][0123]\d))"
% "|".join(calendar.month_abbr[1:])
)
# 02 Januar 2018 02. Januar 2018
pattern.append(
r"(\d{1,2}(?:[ _.-]|. )(?:%s) (?:19[6789]\d|2[01][0123]\d))"
% "|".join(calendar.month_name)
)
for p in pattern:
dates = re.findall(p, self.text)
if dates:
m = re.search(r"\d", dates[0])
return dateparser.parse(
dates[0][m.start() :], settings={"DATE_ORDER": "DMY"}
)
pattern = []
# 2018_02_01
pattern.append(r"((?:19[6789]\d|2[01][0123]\d)[ _.-]\d{1,2}[ _.-]\d{1,2})")
for p in pattern:
dates = re.findall(p, self.text)
if dates:
m = re.search(r"\d", dates[0])
return dateparser.parse(
dates[0][m.start() :], settings={"DATE_ORDER": "MDY"}
)
# if __name__ == "__main__":
# dir_path = os.path.dirname(os.path.realpath(__file__))
# file = os.path.join(dir_path, "../tests/Letter_de.pdf")
# pdfh = PdfHandler(file)
# text = pdfh.gettext()
# dateex = DateExtractor(text)
# print(dateex.getdate())
# source_text = "dasd übermittelt 02.12.1999 555 01.01.2018 "
# dateex = DateExtractor(source_text)
# print(dateex.getdate())
# source_text = "dasd 2018_02_01_02_03 "
# dateex = DateExtractor(source_text)
# print(dateex.getdate())
PK ! simpledms/rules.py# coding=utf-8
import os
import sqlite3
from itertools import chain
class Rules:
sqlinsert = """
INSERT INTO rules(keywords, booleanoperator, tags, doctitle, destination) VALUES
(?, ?, ?, ?, ?);
"""
sqlreplace = "" "REPLACE INTO rules VALUES " "(?,?, ?, ?, ?, ?);"
def __init__(self, storagepath):
# self.name = name # instance variable unique to each instance
if os.path.isfile(os.path.join(storagepath, ".rules.db")):
self.conn = sqlite3.connect(os.path.join(storagepath, ".rules.db"))
self.c = self.conn.cursor()
else:
self.conn = sqlite3.connect(os.path.join(storagepath, ".rules.db"))
self.c = self.conn.cursor()
self.conn.execute(
"CREATE TABLE rules "
"(ruleid INTEGER PRIMARY KEY, "
"keywords, booleanoperator, tags, doctitle, destination);"
)
# self.c.execute("CREATE UNIQUE INDEX ruleid ON rules (keywords)")
self.conn.commit()
def addrule(self, keywords, boolop, tags, doctitle, dest):
assert not isinstance(keywords, str)
assert not isinstance(tags, str)
self.c.execute(
self.sqlinsert,
(", ".join(keywords), boolop, ", ".join(tags), doctitle, dest),
)
self.conn.commit()
def replacerule(self, ruleid, keywords, boolop, tags, doctitle, dest):
assert not isinstance(keywords, str)
assert not isinstance(tags, str)
self.c.execute(
self.sqlreplace,
(ruleid, ", ".join(keywords), boolop, ", ".join(tags), doctitle, dest),
)
self.conn.commit()
def delrule(self, keywords, folder):
assert not isinstance(keywords, str)
self.c.execute(
"DELETE FROM rules WHERE keywords = ? AND destination = ?",
(", ".join(keywords), folder),
)
self.conn.commit()
def __del__(self):
self.conn.commit()
self.conn.close()
def applyrule(self, text):
self.c.execute("SELECT ruleid FROM rules")
ids = self.c.fetchall()
for idx in ids:
self.c.execute("SELECT * FROM rules WHERE ruleid = ?", idx)
rule = self.c.fetchone()
if rule[2] == "ALL":
if all(word.lower() in text.lower() for word in rule[1].split(", ")):
return {
"keywords": rule[1],
"boolop": rule[2],
"tags": rule[3],
"doctitle": rule[4],
"destination": rule[5],
}
elif rule[2] == "ANY":
if all(word in text for word in rule[1].split(", ")):
return {
"keywords": rule[1],
"boolop": rule[2],
"tags": rule[3],
"doctitle": rule[4],
"destination": rule[5],
}
return {"keywords": None, "tags": None, "doctitle": None, "destination": None}
def printallrules(self):
self.c.execute("SELECT * FROM rules")
rows = self.c.fetchall()
for row in rows:
print(row)
def returnallrules(self):
self.c.execute("SELECT * FROM rules")
rows = self.c.fetchall()
return rows
def returnalltags(self):
self.c.execute("SELECT tags FROM rules")
rows = self.c.fetchall()
tags = list(chain.from_iterable(rows))
ll = [i.split(", ") for i in tags]
tags = [item for sublist in ll for item in sublist]
return tags
def returnrulesoffolder(self, folder):
self.c.execute("SELECT * FROM rules WHERE destination = ?", (folder,))
rows = self.c.fetchall()
return rows
def returnruleofkeywords(self, keywords, folder):
self.c.execute(
"SELECT * FROM rules WHERE keywords = ? AND destination = ?",
(", ".join(keywords), folder),
)
row = self.c.fetchall()
return row
def resetdb(self, storagepath):
if os.path.isfile(os.path.join(storagepath, ".rules.db")):
os.remove(os.path.join(storagepath, ".rules.db"))
self.conn = sqlite3.connect(os.path.join(storagepath, ".rules.db"))
self.c = self.conn.cursor()
self.conn.execute(
"CREATE TABLE rules "
"(ruleid INTEGER PRIMARY KEY, "
"keywords, booleanoperator, tags, doctitle, destination);"
)
# self.c.execute("CREATE UNIQUE INDEX ruleid ON rules (keywords)")
self.conn.commit()
PK ! simpledms/ui/__init__.pyPK ! ! ! simpledms/ui/about.py# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'about.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(561, 266)
icon = QtGui.QIcon()
icon.addPixmap(
QtGui.QPixmap(":/icons/icons/outline_info_white_18dp.png"),
QtGui.QIcon.Normal,
QtGui.QIcon.Off,
)
Dialog.setWindowIcon(icon)
self.gridLayout = QtWidgets.QGridLayout(Dialog)
self.gridLayout.setObjectName("gridLayout")
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setCenterButtons(True)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 6, 3, 1, 1)
self.label_4 = QtWidgets.QLabel(Dialog)
self.label_4.setLocale(
QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.Germany)
)
self.label_4.setText("")
self.label_4.setPixmap(
QtGui.QPixmap(":/icons/icons/outline_info_white_18dp.png")
)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 0, 1, 1, 1)
self.label = QtWidgets.QLabel(Dialog)
self.label.setWordWrap(True)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 2, 2, 1, 2)
self.label_3 = QtWidgets.QLabel(Dialog)
font = QtGui.QFont()
font.setFamily("Bitstream Charter")
font.setPointSize(14)
font.setBold(True)
font.setWeight(75)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 0, 2, 1, 2)
self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 1, 2, 1, 2)
self.retranslateUi(Dialog)
self.buttonBox.clicked["QAbstractButton*"].connect(Dialog.close)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "About this Program"))
self.label.setText(
_translate(
"Dialog", "Images: https://github.com/google/material-design-icons"
)
)
self.label_3.setText(_translate("Dialog", "Simple Document Management System"))
self.label_2.setText(_translate("Dialog", "Author: Tobias Wartzek, 2019"))
PK ! A\Q
simpledms/ui/about.ui
Dialog
0
0
561
266
About this Program
:/icons/icons/outline_info_white_18dp.png:/icons/icons/outline_info_white_18dp.png
-
Qt::Horizontal
QDialogButtonBox::Ok
true
-
:/icons/icons/outline_info_white_18dp.png
-
Images: https://github.com/google/material-design-icons
true
-
Bitstream Charter
14
75
true
Simple Document Management System
-
Author: Tobias Wartzek, 2019
buttonBox
clicked(QAbstractButton*)
Dialog
close()
248
254
157
274
PK ! aI
simpledms/ui/editrules.py# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'editrules.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(321, 390)
icon = QtGui.QIcon()
icon.addPixmap(
QtGui.QPixmap(":/icons/icons/outline_description_white_18dp.png"),
QtGui.QIcon.Normal,
QtGui.QIcon.Off,
)
Dialog.setWindowIcon(icon)
Dialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.Germany))
self.gridLayout = QtWidgets.QGridLayout(Dialog)
self.gridLayout.setObjectName("gridLayout")
self.pushButton_ok = QtWidgets.QPushButton(Dialog)
self.pushButton_ok.setEnabled(False)
self.pushButton_ok.setText("")
icon1 = QtGui.QIcon()
icon1.addPixmap(
QtGui.QPixmap(":/icons/icons/outline_check_white_18dp.png"),
QtGui.QIcon.Normal,
QtGui.QIcon.Off,
)
self.pushButton_ok.setIcon(icon1)
self.pushButton_ok.setObjectName("pushButton_ok")
self.gridLayout.addWidget(self.pushButton_ok, 15, 1, 1, 1)
self.radioButton_any = QtWidgets.QRadioButton(Dialog)
self.radioButton_any.setObjectName("radioButton_any")
self.gridLayout.addWidget(self.radioButton_any, 1, 0, 1, 1)
self.label_7 = QtWidgets.QLabel(Dialog)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 4, 0, 1, 1)
self.textEdit_keywords = QtWidgets.QTextEdit(Dialog)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(
self.textEdit_keywords.sizePolicy().hasHeightForWidth()
)
self.textEdit_keywords.setSizePolicy(sizePolicy)
self.textEdit_keywords.setMinimumSize(QtCore.QSize(0, 50))
self.textEdit_keywords.setAcceptRichText(False)
self.textEdit_keywords.setObjectName("textEdit_keywords")
self.gridLayout.addWidget(self.textEdit_keywords, 5, 0, 4, 2)
self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 9, 0, 1, 1)
self.label_3 = QtWidgets.QLabel(Dialog)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 0, 0, 1, 2)
self.radioButton_all = QtWidgets.QRadioButton(Dialog)
self.radioButton_all.setChecked(True)
self.radioButton_all.setObjectName("radioButton_all")
self.gridLayout.addWidget(self.radioButton_all, 3, 0, 1, 1)
self.label_4 = QtWidgets.QLabel(Dialog)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 11, 0, 1, 1)
self.pushButton_cancel = QtWidgets.QPushButton(Dialog)
self.pushButton_cancel.setEnabled(True)
self.pushButton_cancel.setText("")
icon2 = QtGui.QIcon()
icon2.addPixmap(
QtGui.QPixmap(":/icons/icons/outline_cancel_white_18dp.png"),
QtGui.QIcon.Normal,
QtGui.QIcon.Off,
)
self.pushButton_cancel.setIcon(icon2)
self.pushButton_cancel.setObjectName("pushButton_cancel")
self.gridLayout.addWidget(self.pushButton_cancel, 15, 0, 1, 1)
self.textEdit_tags = QtWidgets.QTextEdit(Dialog)
self.textEdit_tags.setObjectName("textEdit_tags")
self.gridLayout.addWidget(self.textEdit_tags, 10, 0, 1, 2)
self.lineEdit_doctitle = QtWidgets.QLineEdit(Dialog)
self.lineEdit_doctitle.setObjectName("lineEdit_doctitle")
self.gridLayout.addWidget(self.lineEdit_doctitle, 12, 0, 1, 2)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Edit rules"))
self.pushButton_ok.setToolTip(
_translate("Dialog", "Keywords and title need to be filled.")
)
self.radioButton_any.setText(_translate("Dialog", "ANY"))
self.label_7.setText(_translate("Dialog", "of the following keywords"))
self.label_2.setText(_translate("Dialog", "Set these tags"))
self.label_3.setText(_translate("Dialog", "If the document contains"))
self.radioButton_all.setText(_translate("Dialog", "ALL"))
self.label_4.setText(_translate("Dialog", "and set this document title:"))
PK ! simpledms/ui/editrules.ui
Dialog
0
0
321
390
Edit rules
:/icons/icons/outline_description_white_18dp.png:/icons/icons/outline_description_white_18dp.png
-
false
Keywords and title need to be filled.
:/icons/icons/outline_check_white_18dp.png:/icons/icons/outline_check_white_18dp.png
-
ANY
-
of the following keywords
-
0
0
0
50
false
-
Set these tags
-
If the document contains
-
ALL
true
-
and set this document title:
-
true
:/icons/icons/outline_cancel_white_18dp.png:/icons/icons/outline_cancel_white_18dp.png
-
-
PK ! `_W <