master
Martin Glueck 2024-04-23 15:29:21 +02:00
commit 2c52631787
12 changed files with 927 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/.py
/__pycache__
/test

39
Action_List.py Normal file
View File

@ -0,0 +1,39 @@
from QT import QtWidgets, QtGui, QT
from _With_Table_Widget_ import _With_Table_Widget_
from App_State import App_State
import schema
class Action_List (_With_Table_Widget_) :
settings_group = "order-list"
Columns = \
("Datum", "Benutzer", "Kind", "Text")
def __init__ (self, actions, parent = None) :
super ().__init__ (parent = parent)
self._view.setRowCount (len (actions))
for row, a in enumerate (actions) :
self._add_read_only_string (row, 0, a.date.strftime ("%H:%M:%S %d-%d-%Y"))
self._add_read_only_string (row, 1, a.user.username)
self._add_read_only_string (row, 2,
App_State.Action_Kind [a.action_kind])
self._add_read_only_string (row, 3, a.action)
header =self._view.horizontalHeader ()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
# end def __init__
# end class Action_List
class Action_Dialog (QtWidgets.QDialog) :
def __init__ (self, actions) :
super ().__init__ ()
self.setMinimumSize (640, 400)
l = QtWidgets.QHBoxLayout (self)
self.action_list = Action_List (actions)
l.addWidget (self.action_list)
self.setModal (False)
# end def __init__
# end class Action_Dialog

26
App_State.py Normal file
View File

@ -0,0 +1,26 @@
import schema
class App_State :
user_id = None
Action_Kind = \
{ 1 : "Create Order"
, 10 : "Change owner"
, 20 : "IMOS CAD started"
, 25 : "IMOS CAD closed"
}
@classmethod
def Create_Action (cls, kind, text, order = None) :
if kind not in cls. Action_Kind :
raise ValueError (f"Unknow action kind {kind}")
schema.Action \
( user = cls.user_id
, date = schema.datetime.now ()
, action_kind = kind
, action = text
, order = order
)
# end def create_action
# end class App_State

56
Login.py Normal file
View File

@ -0,0 +1,56 @@
from QT import QtWidgets, QT
import schema
class Login_Dialog (QtWidgets.QDialog) :
def __init__ (self, username = "") :
super ().__init__ ()
self.setWindowTitle ("Login")
self.layout = L = QtWidgets.QFormLayout (self)
self.user_name = QtWidgets.QLineEdit ()
self.password = QtWidgets.QLineEdit ()
self.password.setEchoMode (QtWidgets.QLineEdit.Password)
L.addRow ("Benutzername", self.user_name)
L.addRow ("Passwort", self.password)
layout = QtWidgets.QHBoxLayout ()
login = QtWidgets.QPushButton ("Login")
login.setEnabled (False)
self.error = QtWidgets.QLabel ("")
layout.addWidget (self.error)
login.clicked.connect (self._login)
layout.addStretch (10)
layout.addWidget (login)
login.setDefault (True)
L.addRow (layout)
self.login = login
self.password.editingFinished.connect (self._login)
self.user_name.editingFinished.connect(self._check_login_possible)
self.user_id = None
self.user_name.setText (username)
if username :
self.password.setFocus ()
# end def __init__
def _check_login_possible (self) :
un = self.user_name.text ()
pw = self.password.text ()
self.login.setEnabled ((len (un) >= 3) and (len (pw) >= 3))
# end def _check_login_possible
def _login (self) :
un = self.user_name.text ()
pw = self.password.text ()
with schema.orm.db_session :
user = schema.User.get (username = un)
if not user :
self.error.setText ("User nicht gefunden")
else :
if user.check_password (pw) :
self.user_id = user.id
self.accept ()
else :
self.error.setText ("Passwort falsch")
# end def _login
# end class Login_Dialog

241
Order_Display.py Normal file
View File

@ -0,0 +1,241 @@
from QT import QtWidgets, QtGui, QT
from _With_Table_Widget_ import _With_Table_Widget_
from App_State import App_State
from Action_List import Action_Dialog
import schema
import subprocess
import threading
import time
class Order_List (_With_Table_Widget_) :
settings_group = "order-list"
Header = "Aufträge"
Columns = \
("Warenkorb", "Status", "Bearbeiter", "Kommission", "Kunde", "Linie")
State_Map = \
{ 0 : "neu"
, 10 : "in Bearbeitung"
, 20 : "im CAD geöffnet"
, 90 : "Odoo"
, 95 : "Zurückgesetzt"
, 99 : "Storno"
}
def __init__ (self, parent = None) :
super ().__init__ (parent)
self._view.setContextMenuPolicy (QT.CustomContextMenu)
self._view.customContextMenuRequested.connect (self._show_context_menu)
ACT = QtGui.QAction
self._open_cad = ACT (self.tr ("Im CAD öffnen"))
self._recreate_cnc = ACT (self.tr ("CNC Programm neu erzeugen"))
self._reset_order = ACT (self.tr ("Auftrag zurücksetzen"))
self._cancel = ACT (self.tr ("Auftrag stronieren"))
self._send_to_odoo = ACT (self.tr ("Auftrag ins Odoo schicken"))
self._actions = ACT (self.tr ("Aktionsliste"))
self._open_cad.triggered.connect (self._open_order_in_cad)
self._recreate_cnc.triggered.connect (self._start_recreate_cnc)
self._reset_order.triggered.connect (self._reset_order_in_webshop)
self._cancel.triggered.connect (self._cancel_order)
self._send_to_odoo.triggered.connect (self._send_oder_to_odoo)
self._actions.triggered.connect (self._show_actions)
self._cad_processes = {}
# end def __init__
def _show_context_menu (self, pos) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
with schema.orm.db_session () :
order = schema.Order.get (basket_no = bno)
if order.user and order.user.id != App_State.user_id :
r = QtWidgets.QMessageBox.question \
( self, self.tr ("Fehler")
, self.tr
( "Dieser Auftrag wird bereits von jemand anderes "
"bearbeitet.\n"
"Wollen Sie den Auftrag als Bearbeiter übernehmen?"
)
)
if r == QtWidgets.QMessageBox.No :
return
App_State.Create_Action \
( 10
, f"User changed from {order.user.id} to {App_State.user_id}"
, order
)
order.user = schema.User [App_State.user_id]
self.update_order (order, False, row_data [0].row ())
state = order.state
if state < 20 :
cm = QtWidgets.QMenu ()
cm.addAction (self._open_cad)
cm.addAction (self._recreate_cnc)
cm.addSeparator ()
cm.addAction (self._send_to_odoo)
cm.addSeparator ()
cm.addAction (self._reset_order)
cm.addAction (self._cancel)
cm.addSeparator ()
cm.addAction (self._actions)
cm.exec (self._view.mapToGlobal (pos))
# end def _show_context_menu
def update_order (self, order, new, row = None) :
if new :
row = self._view.rowCount ()
self._view.setRowCount (row + 1)
self._add_read_only_string (row, 0, str (order.basket_no))
self._add_read_only_string (row, 1, self.State_Map [order.state])
if order.user :
self._add_read_only_string (row, 2, order.user.username)
self._add_read_only_string (row, 3, order.commission)
self._add_read_only_string (row, 4, order.imos_user)
self._add_read_only_string (row, 5, str (order.production_line))
else :
if row is None :
for row in range (self._view.rowCount ()) :
if self._view.item (row, 0).text () == order.basket_no :
break
else :
print ("Could not find order")
return
self._update_read_only_string (row, 1, self.State_Map [order.state])
self._update_read_only_string (row, 2, order.user.username)
# end def update_order
def _open_order_in_cad (self) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
with schema.orm.db_session () :
state = schema.State.get ()
order = schema.Order.get (basket_no = bno)
if order.state < 20 :
order.state = 20
self.update_order (order, False, row_data [0].row ())
self._cad_processes [bno] = subprocess.Popen \
( (r"%s\%s\%s.dwg" % (state.imos_order_dir, bno, bno), )
, shell = True
)
threading.Thread \
( target = self._wait_for_cad_close
, args = (bno, )
, daemon = True
).start ()
App_State.Create_Action \
( 20
, f"IMOS CAD started {order.basket_no}"
, order
)
# end def _open_order_in_cad
def _wait_for_cad_close (self, basket_no) :
p = self._cad_processes [basket_no]
while p.poll () is None :
time.sleep (.5)
del self._cad_processes [basket_no]
with schema.orm.db_session () :
order = schema.Order.get (basket_no = basket_no)
order.state = 10
self.update_order (order, False)
App_State.Create_Action \
( 25
, f"IMOS CAD closed {order.basket_no}"
, order
)
# end def _wait_for_cad_close
def _start_recreate_cnc (self) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
print (bno)
# end def _start_recreate_cnc
def _reset_order_in_webshop (self) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
print (bno)
# end def _reset_order_in_webshop
def _cancel_order (self) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
print (bno)
# end def _cancel_order
def _send_oder_to_odoo (self) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
print (bno)
# end def _send_oder_to_odoo
def _show_actions (self) :
row_data = self._view.selectedItems ()
if row_data :
bno = row_data [0].text ()
with schema.orm.db_session () :
order = schema.Order.get (basket_no = bno)
Action_Dialog \
(order.actions.order_by (schema.orm.desc (schema.Action.date))).exec ()
# end def _show_actions
# end class Order_List
class Order_Detail (_With_Table_Widget_) :
settings_group = "order-detail"
Header = "Auftrags Detail"
Columns = \
("Position", "Artikel", "Preis")
# end class Order_Detail
class Order_Display (QtWidgets.QSplitter) :
def __init__ (self, parent) :
super ().__init__ (parent)
self._order_list = Order_List ()
self._order_detail = Order_Detail ()
self.addWidget (self._order_list)
self.addWidget (self._order_detail)
self.orders = {}
# end def __init__
def update_orders (self) :
with schema.orm.db_session :
for o in schema.Order.select () :
db_lc = o.actions.order_by (schema.Action.date).first ().date
if o.basket_no not in self.orders :
self._order_list.update_order (o, True)
self.orders [o.basket_no] = o
o.last_changed = db_lc
else :
if db_lc > self.orders [o.basket_no].last_changed :
self._order_list.update_order (o, False)
self.orders [o.basket_no].last_changed = db_lc
# end def update_orders
def save_settings (self, settings) :
settings.beginGroup ("order-display")
settings.setValue ("geometry", self.saveGeometry ())
settings.setValue ("windowState", self.saveState ())
settings.endGroup ()
self._order_list .save_settings (settings)
self._order_detail.save_settings (settings)
# end def save_settings
def restore_settings (self, settings) :
settings.beginGroup ("order-display")
self.restoreGeometry (settings.value ("geometry" ))
self.restoreState (settings.value ("windowState"))
settings.endGroup ()
self._order_list .restore_settings (settings)
self._order_detail.restore_settings (settings)
# end def restore_settings
# end class Order_Display

97
_With_Table_Widget_.py Normal file
View File

@ -0,0 +1,97 @@
from QT import QtWidgets, QT
class _With_Table_Widget_ (QtWidgets.QWidget) :
Header = None
def __init__ (self, parent = None) :
super ().__init__ (parent)
self._box = QtWidgets.QVBoxLayout (self)
if self.Header :
header = QtWidgets.QLabel (self.Header)
self._box.addWidget (header)
self._view = QtWidgets.QTableWidget (0, len (self.Columns))
self._view.setHorizontalHeaderLabels (self.Columns)
self._box.addWidget (self._view)
self._view.setAlternatingRowColors (True)
self._view.setSelectionBehavior (QtWidgets.QAbstractItemView.SelectRows)
self._view.verticalHeader ().hide ()
self.selection_model = self._view.selectionModel ()
self.selection_model.currentChanged.connect (self._current_changed)
self._current_row = None
# end def __init__
def _add_read_only_string (self, row, col, text) :
item = QtWidgets.QTableWidgetItem (text)
flags = item.flags() & ~QT.ItemIsEditable
item.setFlags (flags)
self._view.setItem (row, col, item)
# end def _add_read_only_string
def _update_read_only_string (self, row, col, text) :
item = self._view.item (row, col)
if not item :
self._add_read_only_string (row, col, text)
else :
model = self._view.model ()
index = model.index (row, col)
model.setData (index, text)
# end def _update_read_only_string
def _current_changed (self, new_index, old_index) :
row = new_index.row ()
if row != self._current_row :
self._current_row = row
# end def _current_changed
def save_settings (self, settings) :
settings.beginGroup (self.settings_group)
settings.setValue ("column-width", self.column_width ())
if False and self.selection_model :
selection = [ self.model.item_path (i)
for i in self.selectionModel ().selectedRows ()
]
settings.setValue ("selection", selection)
settings.endGroup ()
# end def save_settings
def restore_settings (self, settings) :
settings.beginGroup (self.settings_group)
self.restore_column_width (settings.value ("column-width" ))
if False and self.selection_model :
self.restore_selection (settings.value ("selection" ))
settings.endGroup ()
# end def restore_settings
def column_width (self) :
result = []
for i in range (self._view.columnCount ()) :
result.append (self._view.columnWidth (i))
return result
# end def column_width
def restore_column_width (self, width) :
for i, w in enumerate (width or ()) :
self._view.setColumnWidth (i, int (w))
# end def restore_column_width
def restore_selection (self, selected) :
sm = self.view.selectionModel ()
sm.reset ()
model = self.view.model ()
if model and selected :
iv_index = model.invalid_index
for path_to_selected in selected :
parent_index = iv_index
try :
for pp in path_to_selected :
index = model.index (pp, 0, parent_index)
parent_index = index
sm.select (index, sm.__class__.Select | sm.__class__.Rows)
except :
pass
# end def restore_selection
# end class _With_Table_Widget_

58
create_new_database.py Normal file
View File

@ -0,0 +1,58 @@
### create new initial database
from schema import User, State, Production_Line, Order,Action, db, orm
import datetime
def get_state () :
state = State.get ()
if not state :
state = State \
( imos_export_dir = "?"
, pgiq_import_dir = "?"
, last_directory_scan = datetime.datetime.fromtimestamp (0)
)
return state
# end def get_state
if __name__ == "__main__" :
import argparse
parser = argparse.ArgumentParser ()
parser.add_argument ("database", type = str)
parser.add_argument ("-u", "--user", type = str)
parser.add_argument ("-p", "--password", type = str)
parser.add_argument ("--check", action = "store_true")
parser.add_argument ("-i", "--imos_dir", type = str)
parser.add_argument ("-P", "--pgiq_dir", type = str)
parser.add_argument ("-l", "--line", type = str)
cmd = parser.parse_args ()
db.bind (provider = "sqlite", filename = cmd.database, create_db = True)
db.generate_mapping (create_tables = True)
with orm.db_session :
breakpoint ()
if cmd.user :
u = User.get (username = cmd.user)
if not u :
u = User (username = cmd.user, password = cmd.password)
print (f"Add new user {cmd.user} to the database")
else :
if cmd.check :
print (f"Check password for user {cmd.user}: "
f"{u.check_password (cmd.password)}"
)
else:
print (f"Set new password for user {cmd.user}")
u.change_password (cmd.password)
state = get_state ()
if cmd.imos_dir :
state.imos_export_dir = cmd.imos_dir
if cmd.pgiq_dir :
state.pgiq_import_dir = cmd.pgiq_dir
print (state)
if cmd.line :
short, name = cmd.line.split (":")
line = Production_Line.get (short = short)
if not line :
line = Production_Line (short = short, name = name)
else :
line.name = name
print (line)

150
designbox_orders.py Normal file
View File

@ -0,0 +1,150 @@
import os
from QT.Main_Window import Main_Window, QtWidgets
from Order_Display import Order_Display
from Login import Login_Dialog
from App_State import App_State
import designbox_orders_rc
import schema
import pathlib
from lxml import etree
class Order_Processing (Main_Window) :
APP_NAME = "Designbox Orders"
VERSION = "0.5"
APP_ORGANISATION = ("designbox-orders", "glaser-co.at")
APP_DIR = os.path.dirname (__file__)
@classmethod
def _load_app_settings (cls, settings) :
settings.beginGroup ("TempSettings")
result = {"database" : settings.value ("database")}
result = {"username" : settings.value ("username")}
settings.endGroup ()
return result
# end def _load_app_settings
def _setupActions (self, app) :
super ()._setupActions (app)
self._add_action ("test", "Scan Dir", self.scan_orders_directory)
# end def _setupActions
def _setupGui (self) :
self.main_menu = QtWidgets.QMenuBar (self)
self.setMenuBar (self.main_menu)
file = QtWidgets.QMenu ("& File")
self.main_menu.addMenu (file)
self.tool_bar_file = self.addToolBar ("File")
self.tool_bar_file.setObjectName ("tb_file")
for a, l in ( ( self.actions.exit
, (file, self.tool_bar_file)
)
, ( (self.actions.test), (self.tool_bar_file, ))
) :
for x in l :
x.addAction (a)
self.setCentralWidget (Order_Display (self))
settings = self.settings
settings.beginGroup ("TempSettings")
user_id = settings.value ("user_id")
settings.endGroup ()
with schema.orm.db_session :
App_State.user_id = schema.User [user_id].id
self.scan_orders_directory ()
# end def _setupGui
def _find_orders (self, root_path : pathlib.Path) :
result = set ()
for path, dirs, files in root_path.walk () :
for f in files :
if f.endswith (".txt") :
xml_name = (path / f).with_suffix (".xml")
if xml_name.exists () :
result.add (xml_name)
for d in dirs :
result.intersection_update (self._find_orders (root_path / d))
return result
# end def _find_orders
def scan_orders_directory (self) :
with schema.orm.db_session :
state = schema.State.get ()
orders = self._find_orders (pathlib.Path (state.imos_export_dir))
with schema.orm.db_session :
for o in orders :
self._update_order_in_database (o)
self.centralWidget ().update_orders ()
# end def scan_orders_directory
def _update_order_in_database (self, xml_file) :
basket_no = xml_file.stem
if True :
order = schema.Order.get (basket_no = basket_no)
if not order :
xml = etree.parse (xml_file).getroot ()
com = xml.xpath ("//TEXT_SHORT") [0].text
user,line = xml.xpath ("//RETAILER") [0].text.split ("/")
order = schema.Order \
( basket_no = basket_no
, state = 0
, commission = com
, imos_user = user
, production_line = schema.Production_Line.get (short = line)
, active = True
)
schema.Action \
( user = schema.User [App_State.user_id]
, date = schema.datetime.now ()
, action_kind = 1
, action = "Auftrag importiert"
, order = order
)
# end def _update_order_in_database
def _restore_settings (self) :
super ()._restore_settings ()
self.centralWidget ().restore_settings (self.settings)
# end def _restore_settings
def save_settings (self) :
settings = self.settings
super ().save_settings ()
self.centralWidget ().save_settings (settings)
settings.beginGroup ("TempSettings")
settings.setValue ("user_id", App_State.user_id)
settings.endGroup ()
# end def save_settings
@classmethod
def pre_main_window_action (cls, settings) :
settings.beginGroup ("TempSettings")
db_file = settings.value ("database")
user_id = settings.value ("user_id")
settings.endGroup ()
schema.db.bind (provider = "sqlite", filename = db_file)
schema.db.generate_mapping (create_tables = False)
username = ""
if user_id :
with schema.orm.db_session :
username = schema.User [user_id].username
if False :
login = Login_Dialog (username)
if login.exec () == 1 :
settings.beginGroup ("TempSettings")
settings.setValue ("user_id", login.user_id)
settings.endGroup ()
return True
else :
print ("### Login skipped")
return True
# end def pre_main_window_action
# end class Order_Processing
if __name__ == "__main__" :
import argparse
parser = argparse.ArgumentParser ()
parser.add_argument ("-s","--settings-file", type = str)
parser.add_argument ("-d", "--database", type = str)
cmd = parser.parse_args ()
Order_Processing.run (Order_Processing.load_settings (cmd))

5
designbox_orders.qrc Normal file
View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="icons">
<file alias="exit.png">icons/person-from-portal.png</file>
</qresource>
</RCC>

130
designbox_orders_rc.py Normal file
View File

@ -0,0 +1,130 @@
# Resource object code (Python 3)
# Created by: object code
# Created by: The Resource Compiler for Qt version 6.6.2
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
qt_resource_data = b"\
\x00\x00\x05\xaf\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\
\x00\x00\x00\x09pHYs\x00\x00\x01b\x00\x00\x01b\
\x01_'\xd0S\x00\x00\x00\x19tEXtSof\
tware\x00www.inksca\
pe.org\x9b\xee<\x1a\x00\x00\x05<ID\
ATh\x81\xb5\x9a]\xa8UE\x14\xc7\x7f\xff\xedQ\
K\x8bLK\xafD\x18\x91EH\x10!\xe5\x17\x09\xa1\
\x99\x9aVRQ\x81\x95=\x18A\x11Q\x06\x11fd\
T\x12H\x94\x0fE\x81\x1f\x94=\x18J\x96\xa4\xdd>\
(\xd3|\x88\xa8\x88\xec\x9a\xa4\xf6\xa0i\x8a\x96f\x8a\
\xe6\xeaa\xf6\xbe\x9c\xb3\xcf\xde{f\xf6\xb9\xfd\xe1p\
\xce\x9dY\x9fw\xd6^\xb3f\xcd\x96\x99\x91\x87\xa4\xa1\
\xc0$`\x18\xb0\x1f\xd8bf\x7f\xb6\x11v\x08IC\
\x80\x85\xc0\x8d\xc0\xa5\xc0\xaf\xc0\xc7\xc0b3;\x12$\
#\xef@*\xf4V`@\xd3\xf0I`\x9d\x99\xfd\xd5\
\x07vgz\x86\x01[\x80+\x0a\xa6{\x80\x89fv\
\xc8'')\x18\x9bL\xab\xf1\x00\x03\x81)\x92\x14k\
h\x05\x9e\xa1\xd8x\xd2\xf1\x85!BZ\x1c\x904\x1a\
\x18QB{\x010:\xd4\xba\x00\xdc\xd6\xe1<\xd0\xbe\
\x02\xd7x\xe8\xaf\x0e\x11\x1a\x88\xb2\x7fT\x86\xae\x10!\
\xbd\x0eH\xea\x02\xce\xf3\xd0\x0f\x9142Dp\x00\xf6\
{\xe6\x7f\x0f\x11\xd2\xbc\x02\x97\x07*\xee\xab0Z\xd7\
\xe1<\x90:\x90>\x9c\x97\x04*\x1e\xd5G\x0f\xf3s\
\xb8lS\x84\x1e`q\x88\x90l\x05\x86\x02g\x05*\
>\x1b\xb80\x90\xb6\x14i\x8a\x1c\x07,\x05~\x04\x8e\
\xa7\xdfK\x81q!)\x14\xa0\x91~_\x14\xa9\xbf\x0b\
8\x90\x1f\x94\xd4\x0f\xb7)\xcd\x06&\xa6t\xe7\x03\x87\
\x80\x9f\x81/\x81\x0f\x81o\xcc\xecL\xbaY=\x1e\xa9\
\xbbU\xa7\x99!i\x1a0*\x82o\x97\x99u\xe7\x8c\
\x9f\x05\xbc\x08\x8c\x09\xe0\xdf\x07|\x00\xbc\x0f|jf\
'#t\xb7 \x0b\xa1a\x91|\xc3\xb3\x1f\x92\x1a\x92\
^\x01\xd6\x13f<\xc0H`>\xb0\x01\xd8-\xe9Y\
I\xbe\xb4Z\x88DR\x7f\xe0\x9cH\xbe\xc1\x92\x06J\
\x1a\x08\xac\x05\x1e\xad\xa3<E\x17\xb0\x08\xe8\x91tw\
,s\x82\x8b\xd1:\x18\x0e\xbc\x07\xcc\xaa\xc9\x9f\xc7y\
\xc0jI\xaf\xc7d\xb9N\x1cX\x06\xdc\x5c\x93\xb7\x0a\
\x0f\x02KB\x89\x13\xe2\xc3\x07\x5c]4\xbbb\xbe\x07\
\x98\x8b\x0b\x8f;k\xc8_ \xe9\xde\x10\xc2\x04\x97\xd7\
cQ\xb5\x0ft\x03c\xcd\xecm3\xdb\x0f\xdcQC\
>\xc0\x12I\xe7\xfa\x88\x12`P\x0d\xe1\x7f\x94\x8cw\
\x03\xb7\x98\xd91\x00I\x83\x80\x195\xe4\x83[\xbd'\
|Du\x1d8\x88\xcb\xe1\xcdX\x8f3\xfe\x9f\xa6\xb1\
\x99\xc0\xe0\x1a\xf23<,\xa92B\x12\xc2K\x88<\
\xde\x05n\x02\x16\x00\xd3\xcd,o<\xd4\x0f\x9f\x0cC\
\x81\x87\xaa\x08\x1a@\xbf\x9a\xc2\xfb\x99\xd9&`S\xd1\
dd\xf8\x18\xf0=\xc5\xe7\x8d\x97$%\xc0J3k\
\x0b\xdd\x84\xfa\x0e4<\xf31\xe1\xf3\x15\xf0T\xc9\x5c\
\x7f\xe0e\xe0\x80$K?{%-\x914\xa0\x11`\
H\x19|\x8e\xdf\x1e!k\xa5\x99m\x94\xb4\x15\x98\x10\
@?\x12x\x12\xb0NV\xa0\x94/\x0d\x9f\x99\x81r\
\x8e\x03k\xd2\xdf\xf3\x80\xa3\x116\xcc-\xeaJ\xf4\x05\
&\x10\x1e>k\xb3v\x8d\x99\xed\xc09\x1e\xdc\x83J\
\x80\x7f\xa3\xcds\xa8\xe2\x8b)\xcdW4\xffaf\x9b\
\x81kq\xcf\x85\x0f\xab\x12\xe0t\x84\xb2fT9p\
,P\xc6\x1e\xe0\xf3\xfc`\xba\x12\xd7\x03Sq\x0e\xee\
\xc4\x85Z\x86\xbd\xb8ziQ\xc3cH\x15\xaa\xf8\xb6\
\xe1R\xa3\xaf\xaa\x5cefg\x8a&\xcc\xb5\x0c?I\
?\xa5\xf8_B\xc8\xcc\xf6\xe0N\x5c>\xcc\x914\xb1\
\xa6~\xc09p\xa2&o\xcb\xae\x9b\x9e\xcc\xeej\x1a\
z\x04\x7fog\x0c\xb0Y\xd2\x1b\x92j\x95\xf5\x09\xad\
\xb1\x15\x83|\xd90\x07xU\xd2p\x003\xfb\x0d\xd7\
g\xfd\xc1#G\xb8\xe3\xe5vI\xf7\xc4\x1a\xd1\x89\x03\
y\xbe\xc7pe\xf6\xaa\xf4\x98\x9a=\x8cc\x81\xa7i\
w8\x8f\x11\xc0;\x926H\xf2u\x08{\xd1'+\
i\x02\xae\xc7\x030\x0dX\x93\xd5\xf2fv\xca\xcc\
^\x00\xae\xc2\xf5\xfe}\x98\x01t\xa7-\x1a/\x84k\
eO\x0e\xa0\x1b\x9f\x1a\x17\xd4t\xc5\xa5\xbeyf\xd6\
\x92\xcf\xd30Y\x8a\xbf\xb9\xfb\x80\x99-\xf7)I\x80\
\xc3\x01\xc6\x8c\x07\xee#\xdcx\x80\xcb\x80\xeb\xf2\x83f\
\xb6\x1a\xb8\x12x\x13\x97j\xcb05DI\xa8\x03\xd3\
B\x84\xe5p\x14x\xabh\xc2\xcc\x0e\x9b\xd9|\xdcf\
\xf5K\x09\x7f\xd09%1\xb3S\x84\xef\x9c1x\xcd\
w\xaf\x96\x86\xd7\xf6\x92\xe92\xc7Z\x90\x15s\xbeF\
\xea\xc6\x10aM\xd8\x0a<\xef#J\x1bc7\x94L\
w\x97\x8c\xb7 s`\x9f\x87n\x1b\xae&\xa9\xa2;\
\x0d\xec\xc0\xdd}M)8^\x16a\x12\xc5m\x9dc\
\xc0\xe6\x00\xfe\xde\xc3\xcc^\x0f\x9d\x01_\xa7\x1fp%\
\xf0\xc1\x10\x05\x1e\x94\x1d9?\x0bm\xf86\x87Ph\
I\xf1w\x1f\x19\x0f0\xbdd\xfc\xa3P\x01\x09\xf4V\
~\xbb\x03yv\x85\x0a\xaf\x82\xa4\x8bq\xe9\xb4\x08\xc1\
\xcf\x5c\xf3\x89\xac\xec\xba'\x8f\xdd\xa1\xc2=(;r\
\xfedf\xc1:z\x1dH\xdb\x80\xbe\xeb\xfd#f\xe6\
{^B\xd1q\xf8@\xfb=\xf1\xb7\x1e\xfa\xefb\x84\
{\xd0\xb6K\xa7\x88J\xd9-\x0e\x98\xd9N\xca\xefo\
\x0f\x12\xb8\xb9\x04\xa2\xe8\xb4v\x94\xc0\xf4\x99\xa1\xa8+\
\xf1\x05\xee\xe5\x8ef\x9c\xc0\xddeU\xd5.\xb1XQ\
0\xb6,\xf6\xbe\xac\xedm\x15\x80\xf4t4\x09W\xdf\
g\xaf\xdb\x04\xbd\xfe\x12\xacXj\xe0\xee\x82\xef\xc7\xad\
\xc6r`\xa1\x99E5\x19\xfe\x036\xda\x8b\xa9m\xbd\
\x82T\x00\x00\x00\x00IEND\xaeB`\x82\
"
qt_resource_name = b"\
\x00\x05\
\x00o\xa6S\
\x00i\
\x00c\x00o\x00n\x00s\
\x00\x08\
\x0f\x07Z\xc7\
\x00e\
\x00x\x00i\x00t\x00.\x00p\x00n\x00g\
"
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x8e\xf0\xcd\xec\xfe\
"
def qInitResources():
QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

122
schema.py Normal file
View File

@ -0,0 +1,122 @@
from pony import orm
import datetime
from hashlib import sha256
from datetime import datetime
db = orm.Database ()
key = "b9281aea-4a9e-4721-b932-5b5ffbc66cfe"
class User (db.Entity) :
username = orm.Required (str, unique = True)
password = orm.Required (str)
active = orm.Optional (bool)
last_logedin = orm.Optional (datetime)
created = orm.Optional (datetime)
actions = orm.Set ("Action")
orders = orm.Set ("Order")
def __init__ (self, username, password, active = True) :
pwd = f"{key}:{password}"
password = sha256 (pwd.encode ("utf-8")).hexdigest ()
super ().__init__ \
( username = username
, password = password
, active = active
, created = datetime.now ()
)
# end def __init__
def check_password (self, password) :
pwd = f"{key}:{password}"
password = sha256 (pwd.encode ("utf-8")).hexdigest ()
return self.password == password
# end def check_password
def change_password (self, password) :
pwd = f"{key}:{password}"
self.password = sha256 (pwd.encode ("utf-8")).hexdigest ()
# end def change_password
# end class User
class Production_Line (db.Entity) :
short = orm.Required (str)
name = orm.Required (str)
orders = orm.Set ("Order")
def __str__ (self) :
return f"{self.name} ({self.short})"
# end def __str__
# end class Production_Line
class Order (db.Entity) :
basket_no = orm.Required (str)
state = orm.Required (int)
commission = orm.Optional (str)
imos_user = orm.Required (str)
production_line = orm.Required (Production_Line)
user = orm.Optional (User)
active = orm.Optional (bool)
actions = orm.Set ("Action")
order_lines = orm.Set ("Order_Line")
# end class Order
class Action (db.Entity) :
user = orm.Required (User)
date = orm.Required (datetime)
action_kind = orm.Required (int)
action = orm.Optional (str)
order = orm.Optional (Order)
# end class Action
class Odoo_Article (db.Entity) :
name = orm.Required (str)
default_text = orm.Optional (str)
default_price = orm.Optional (float)
order_lines = orm.Set ("Order_Line")
# end class Odoo_Article
class Order_Line (db.Entity) :
order = orm.Required (Order)
text = orm.Required (str)
price = orm.Required (float)
odoo_article = orm.Required (Odoo_Article)
# end class Order_Line
class State (db.Entity) :
imos_export_dir = orm.Required (str)
imos_order_dir = orm.Required (str)
sim_archive = orm.Required (str)
pgiq_import_dir = orm.Required (str)
last_directory_scan = orm.Required (datetime)
def __str__ (self) :
return f"""
IMOS dir {self.imos_export_dir}
PGIQ dir {self.pgiq_import_dir}
Last scan {self.last_directory_scan}
""".strip ()
# end def __str__
# end class State
if __name__ == "__main__" :
import argparse
parser = argparse.ArgumentParser ()
parser.add_argument ("database", type = str)
parser.add_argument ("-d", "--sql-debug", action = "store_true")
cmd = parser.parse_args ()
orm.set_sql_debug (cmd.sql_debug)
db.bind (provider = "sqlite", filename = cmd.database, create_db = True)
db.generate_mapping (create_tables = True)
if False :
with orm.db_session :
u = User (username = "glueckm", password = "123")
if True :
with orm.db_session :
u = User.get (username = "glueckm")
print (u, u.check_password ("123"))