Implement search
This commit is contained in:
@@ -12,6 +12,9 @@ ColumnLayout {
|
||||
spacing: Dimensions.l
|
||||
|
||||
SearchBar {
|
||||
onSubmitted: (query) => {
|
||||
applicantModel.searchQuery = query
|
||||
}
|
||||
}
|
||||
QuickFilter {
|
||||
model: ListModel {
|
||||
|
||||
@@ -4,11 +4,23 @@ import QtQuick.Layouts
|
||||
|
||||
|
||||
TextField {
|
||||
id: field
|
||||
|
||||
signal submitted(query: string)
|
||||
|
||||
Layout.preferredWidth: 300
|
||||
placeholderText: qsTr("Suche")
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
field.submitted(field.text);
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.source: "qrc:/images/MagnifyingGlass.svg"
|
||||
isFieldButton: true
|
||||
|
||||
onClicked: {
|
||||
field.submitted(field.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
docker-compose.yml
Normal file
14
docker-compose.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
volumes:
|
||||
- mariadb:/var/lib/mysql
|
||||
ports:
|
||||
- 127.0.0.1:8000:3306
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: pyqcrm
|
||||
MARIADB_USER: pyqcrm
|
||||
MARIADB_PASSWORD: pyqcrm
|
||||
MARIADB_DATABASE: pyqcrm
|
||||
volumes:
|
||||
mariadb:
|
||||
19
lib/Config.py
Normal file
19
lib/Config.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class EncryptionConfig(TypedDict):
|
||||
ENCRYPTION_KEY_VALID: str
|
||||
ENCRYPTION_KEY: str
|
||||
|
||||
|
||||
class DatabaseConfig(TypedDict):
|
||||
DB_HOST: str
|
||||
DB_USER: str
|
||||
DB_PASS: str
|
||||
DB_NAME: str
|
||||
DB_PORT: str
|
||||
|
||||
|
||||
class Config(TypedDict):
|
||||
database: DatabaseConfig
|
||||
pyqcrm: EncryptionConfig
|
||||
@@ -2,21 +2,24 @@
|
||||
import os
|
||||
from base64 import b64encode
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import toml
|
||||
from Crypto.Random import get_random_bytes
|
||||
from PySide6.QtCore import QObject, Slot, Signal
|
||||
from peewee import OperationalError
|
||||
from platformdirs import user_config_dir
|
||||
|
||||
from .Config import Config, DatabaseConfig
|
||||
from .DB.UserManager import UserManager
|
||||
from .PyqcrmFlags import PyqcrmFlags
|
||||
from .Vermasseln import Vermasseln
|
||||
from .domain.BaseModel import database
|
||||
from .domain.BaseModel import init_database_from_config
|
||||
|
||||
|
||||
class ConfigLoader(QObject):
|
||||
__config = None
|
||||
__config: Optional[Config] = None
|
||||
__version = "0.1-alpha"
|
||||
__check_enc_key = True
|
||||
|
||||
@@ -39,10 +42,10 @@ class ConfigLoader(QObject):
|
||||
config_dir.mkdir(0o750, True, True)
|
||||
|
||||
@Slot(dict, result=bool)
|
||||
def setConfig(self, app_config):
|
||||
def setConfig(self, app_config: Config):
|
||||
if not self.__config:
|
||||
base_conf = self.__initializeConfig()
|
||||
conf = self.__checkDbConnection(app_config)
|
||||
conf = self._is_db_connectable(app_config['database'])
|
||||
app_config = toml.dumps(app_config)
|
||||
if conf:
|
||||
app_config = base_conf + app_config
|
||||
@@ -53,18 +56,18 @@ class ConfigLoader(QObject):
|
||||
self.configurationReady.emit()
|
||||
|
||||
def __initializeConfig(self):
|
||||
# print(f"In {__file__} file, __initializeConfig()")
|
||||
self.__encrypt_key = b64encode(get_random_bytes(32)).decode("utf-8")
|
||||
conf = f"[pyqcrm]\nVERSION = \"{self.__version}\"\n"
|
||||
conf = conf + f"ENCRYPTION_KEY_VALID = \"No\"\n"
|
||||
conf = conf + f"ENCRYPTION_KEY = \"{self.__encrypt_key}\"\n\n"
|
||||
return conf
|
||||
|
||||
def __checkDbConnection(self):
|
||||
if database.is_closed():
|
||||
def _is_db_connectable(self, config: DatabaseConfig):
|
||||
try:
|
||||
init_database_from_config(config)
|
||||
self.dbConnectionError.emit("Connection OK", True)
|
||||
return True
|
||||
else:
|
||||
except OperationalError as e:
|
||||
self.dbConnectionError.emit("Connection fehlgeschlagen", False)
|
||||
return False
|
||||
|
||||
@@ -190,7 +193,6 @@ class ConfigLoader(QObject):
|
||||
print(str(e))
|
||||
|
||||
def __configLoad(self):
|
||||
# print(f"In {__file__} file, __configLoad()")
|
||||
try:
|
||||
with open(self.config_dir + '/pyqcrm.toml', 'r') as f:
|
||||
config = f.read()
|
||||
@@ -200,16 +202,11 @@ class ConfigLoader(QObject):
|
||||
print("Konnte die Konfiguration nicht laden.")
|
||||
except TypeError:
|
||||
print(f"Invalid Configuration: {__file__}")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
def getConfig(self):
|
||||
# print(f"In {__file__} file, getConfig()")
|
||||
# print(self.__config)
|
||||
def get_config(self) -> Optional[Config]:
|
||||
return self.__config
|
||||
|
||||
def __setRecoveryPassword(self, key, salt=None):
|
||||
# print(f"In {__file__} file, __setRecoveryPassword()")
|
||||
key = Vermasseln.userPasswordHash(key, salt)
|
||||
return key.split("$")
|
||||
|
||||
@@ -222,7 +219,7 @@ class ConfigLoader(QObject):
|
||||
|
||||
@Slot(str, str)
|
||||
def backupConfig(self, filename, password):
|
||||
conf_file = toml.dumps(self.getConfig())
|
||||
conf_file = toml.dumps(self.get_config())
|
||||
self.__saveData(filename, password, conf_file)
|
||||
|
||||
@Slot(dict)
|
||||
@@ -233,7 +230,7 @@ class ConfigLoader(QObject):
|
||||
@Slot(result=dict)
|
||||
def getDbConf(self):
|
||||
try:
|
||||
return self.__config['database']
|
||||
return self.__config['database'] if self.__config is not None else None
|
||||
except KeyError as ex:
|
||||
print(f"Missing database configuration: {ex}")
|
||||
return None
|
||||
|
||||
@@ -28,9 +28,6 @@ class AddressModel(QAbstractListModel):
|
||||
PyqcrmDataRoles.CITY_ROLE: b"city",
|
||||
}
|
||||
|
||||
def setData(self):
|
||||
pass
|
||||
|
||||
@Slot(bool, str)
|
||||
def getAddresses(self, all, zipcode):
|
||||
data = AddressDAO().getAddressData(all, zipcode)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import uuid
|
||||
from typing import List, Callable, Any
|
||||
|
||||
from PySide6.QtCore import QModelIndex, Qt, QAbstractTableModel, Slot
|
||||
from PySide6.QtCore import QModelIndex, Qt, QAbstractTableModel, Slot, Property, Signal
|
||||
from PySide6.QtQml import QJSValue
|
||||
from peewee import Select
|
||||
|
||||
@@ -19,10 +19,12 @@ COLUMN_NAMES = ["Vorname", "Nachname", "PLZ", "Ort"]
|
||||
|
||||
class ApplicantModel(QAbstractTableModel):
|
||||
_applicants: Select
|
||||
_search_query: str = ""
|
||||
search_query_changed = Signal(str)
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._applicants = Applicant.select_table_data()
|
||||
self._query_applicants()
|
||||
|
||||
def rowCount(self, /, parent=...):
|
||||
return len(self._applicants)
|
||||
@@ -36,6 +38,15 @@ class ApplicantModel(QAbstractTableModel):
|
||||
return COLUMNS[index.column()](applicant)
|
||||
return None
|
||||
|
||||
@Property(str, notify=search_query_changed)
|
||||
def searchQuery(self):
|
||||
return self._search_query
|
||||
|
||||
@searchQuery.setter
|
||||
def searchQuery(self, value: str):
|
||||
self._search_query = value
|
||||
self._query_applicants()
|
||||
|
||||
@Slot(int, result=dict)
|
||||
def applicant(self, row) -> dict:
|
||||
applicant = Applicant.get_by_id(self._applicants[row].id)
|
||||
@@ -68,9 +79,13 @@ class ApplicantModel(QAbstractTableModel):
|
||||
applicant.email_address = values.property("emailAddress").toString() or None
|
||||
applicant.salutation = values.property("salutation").toString() or None
|
||||
applicant.save(force_insert=True)
|
||||
self._applicants = Applicant.select_table_data()
|
||||
self._query_applicants()
|
||||
|
||||
def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.ItemDataRole.DisplayRole):
|
||||
if role == Qt.ItemDataRole.DisplayRole:
|
||||
return COLUMN_NAMES[section]
|
||||
return None
|
||||
|
||||
def _query_applicants(self):
|
||||
self._applicants = Applicant.select_table_data(self._search_query)
|
||||
self.layoutChanged.emit()
|
||||
|
||||
@@ -73,7 +73,7 @@ class BusinessModel(QAbstractTableModel):
|
||||
super().__init__()
|
||||
self.__business_dao = BusinessDAO()
|
||||
self.__business_dao.newBusinessAdded.connect(self.__refreshView)
|
||||
self.__conf = ConfigLoader().getConfig()
|
||||
self.__conf = ConfigLoader().get_config()
|
||||
self.__key = self.__conf['pyqcrm']['ENCRYPTION_KEY']
|
||||
self.__getData()
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class ContactModel(QObject):
|
||||
super().__init__()
|
||||
# print(f"*** File: {__file__}, __init__()")
|
||||
#self.logger = logging.getLogger()
|
||||
self.__conf = ConfigLoader().getConfig()
|
||||
self.__conf = ConfigLoader().get_config()
|
||||
self.__key = self.__conf['pyqcrm']['ENCRYPTION_KEY']
|
||||
self.__contact_dao = ContactDAO()
|
||||
self.__contact_dao.newObjectContactAdded.connect(self.objectContactAdded)
|
||||
|
||||
@@ -19,7 +19,7 @@ class EmployeeModel(QAbstractTableModel):
|
||||
super().__init__()
|
||||
self.__employee_dao = EmployeeDAO()
|
||||
self.__employee_dao.newEmployeeAdded.connect(self.__refreshView)
|
||||
self.__conf = ConfigLoader().getConfig()
|
||||
self.__conf = ConfigLoader().get_config()
|
||||
self.__key = self.__conf['pyqcrm']['ENCRYPTION_KEY']
|
||||
self.__getData()
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class ObjectModel(QAbstractTableModel):
|
||||
super().__init__()
|
||||
self.__object_dao = ObjectDAO()
|
||||
self.__object_dao.newObjectAdded.connect(self.__refreshView)
|
||||
self.__conf = ConfigLoader().getConfig()
|
||||
self.__conf = ConfigLoader().get_config()
|
||||
self.__key = self.__conf['pyqcrm']['ENCRYPTION_KEY']
|
||||
self.__object_dao.newObjectAdded.connect(self.objectAdded)
|
||||
self.__getData()
|
||||
|
||||
@@ -22,10 +22,23 @@ class Applicant(BaseModel):
|
||||
salutation = TextField(null=False)
|
||||
|
||||
@classmethod
|
||||
def select_table_data(cls):
|
||||
def select_table_data(cls, search_query: str):
|
||||
return (
|
||||
Applicant
|
||||
.select(Applicant.id, Applicant.first_name, Applicant.last_name, ZipCode.id, ZipCode.zip_code, Town.town)
|
||||
.select(
|
||||
Applicant.id,
|
||||
Applicant.first_name,
|
||||
Applicant.last_name,
|
||||
ZipCode.id,
|
||||
ZipCode.zip_code,
|
||||
Town.town
|
||||
)
|
||||
.join(ZipCode, join_type="LEFT JOIN")
|
||||
.join(Town, join_type="LEFT JOIN")
|
||||
.where(
|
||||
(Applicant.first_name.contains(search_query)) |
|
||||
(Applicant.last_name.contains(search_query)) |
|
||||
(ZipCode.zip_code.contains(search_query)) |
|
||||
(Town.town.contains(search_query))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
from peewee import Model, MySQLDatabase
|
||||
|
||||
database = MySQLDatabase(None)
|
||||
from lib.Config import DatabaseConfig
|
||||
|
||||
database: MySQLDatabase = MySQLDatabase(None)
|
||||
|
||||
|
||||
def init_database_from_config(config: DatabaseConfig):
|
||||
database.init(
|
||||
host=config['DB_HOST'],
|
||||
user=config['DB_USER'],
|
||||
password=config['DB_PASS'],
|
||||
database=config['DB_NAME'],
|
||||
port=int(config['DB_PORT']),
|
||||
connect_timeout=5,
|
||||
)
|
||||
database.connect()
|
||||
|
||||
|
||||
class BaseModel(Model):
|
||||
|
||||
15
main.py
15
main.py
@@ -22,7 +22,7 @@ from lib.DB.EmployeeModel import EmployeeModel
|
||||
from lib.DB.ObjectModel import ObjectModel
|
||||
from lib.DB.UserManager import UserManager
|
||||
from lib.Printers import Printers
|
||||
from lib.domain.BaseModel import database
|
||||
from lib.domain.BaseModel import database, init_database_from_config
|
||||
|
||||
os.environ['QML_XHR_ALLOW_FILE_READ'] = '1'
|
||||
|
||||
@@ -42,16 +42,7 @@ user = None
|
||||
def initializeProgram():
|
||||
global address_model, applicant_model, bad_config, business_model, user, business_type, contact_model, employee_model, object_model, db_con, printers
|
||||
if not bad_config:
|
||||
dbconf = config.getConfig()['database']
|
||||
database.init(
|
||||
host=dbconf['DB_HOST'],
|
||||
user=dbconf['DB_USER'],
|
||||
password=dbconf['DB_PASS'],
|
||||
database=dbconf['DB_NAME'],
|
||||
port=int(dbconf['DB_PORT']),
|
||||
connect_timeout=5,
|
||||
)
|
||||
database.connect()
|
||||
init_database_from_config(config.get_config()['database'])
|
||||
|
||||
printers = Printers()
|
||||
if not database.is_closed():
|
||||
@@ -114,7 +105,7 @@ if __name__ == "__main__":
|
||||
|
||||
config = ConfigLoader()
|
||||
|
||||
if not config.getConfig():
|
||||
if not config.get_config():
|
||||
bad_config = True
|
||||
config.configurationReady.connect(configReady)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user