Work on native gui: views and database model
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Box(DbRecord):
|
||||
|
||||
table = "box"
|
||||
pk_field = "boxid"
|
||||
fields = ('boxid', 'sid', 'boxtype', 'parent', 'label', 'color', 'content',
|
||||
'weight', 'remarks')
|
||||
|
||||
def __init__(self, db, boxid=None):
|
||||
super().__init__(db, boxid)
|
||||
self.title = _("Box({})")
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Cable(DbRecord):
|
||||
|
||||
table = "cable"
|
||||
pk_field = "cableid"
|
||||
fields = ('cableid', 'vid', 'cablename', 'cabletype', 'techname', 'length',
|
||||
'conductors', 'diameter', 'xsection', 'weight', 'color', 'supplier',
|
||||
'manufacturer', 'cablecond', 'remarks')
|
||||
|
||||
def __init__(self, db, cableid=None):
|
||||
super().__init__(db, cableid)
|
||||
self.title = _("Cable({})")
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Company(DbRecord):
|
||||
|
||||
table = "company"
|
||||
pk_field = "compid"
|
||||
fields = ('compid', 'compname', 'comptype', 'comptype2', 'shortname',
|
||||
'street', 'zip', 'city', 'country', 'contact', 'phone', 'email',
|
||||
'web', 'customerno', 'contractno', 'remarks', 'flags' )
|
||||
|
||||
def __init__(self, db, compid=None):
|
||||
super().__init__(db, compid)
|
||||
self.title = _("Company({})")
|
||||
@@ -0,0 +1,137 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
"""
|
||||
No commits here: Has to be done elsewhere to support transactions!
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
class DbRecord:
|
||||
|
||||
def __init__(self, db, record_id=None):
|
||||
if not hasattr(self, "table"):
|
||||
raise TypeError("DbRecord subclass must define 'table'")
|
||||
if not hasattr(self, "pk_field"):
|
||||
raise TypeError("DbRecord subclass must define 'pk_field'")
|
||||
if not hasattr(self, "fields"):
|
||||
raise TypeError("DbRecord subclass must define 'fields'")
|
||||
|
||||
self.db = db
|
||||
self.dcur = db.dcur
|
||||
self.conn = db.conn
|
||||
self.last_update = None
|
||||
|
||||
self.title = _("DbRecord({})") # for pretty print
|
||||
|
||||
if record_id is not None:
|
||||
self.pk_value = int(record_id)
|
||||
self.load()
|
||||
else:
|
||||
self.pk_value = None
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
for field in self.fields:
|
||||
setattr(self, field, None)
|
||||
self.data = {} # for old/new checks
|
||||
|
||||
def load(self, record_id=None):
|
||||
if record_id is not None:
|
||||
self.pk_value = int(record_id)
|
||||
if self.pk_value is None:
|
||||
self.clear()
|
||||
return
|
||||
sql = ("SELECT {} FROM {} WHERE {}=%s").format(', '.join(self.fields), self.table, self.pk_field)
|
||||
try:
|
||||
self.dcur.execute(sql, (self.pk_value,))
|
||||
self.data = self.dcur.fetchone()
|
||||
except Exception as e:
|
||||
self.db.error = e
|
||||
self.clear()
|
||||
return False
|
||||
if self.data:
|
||||
for field, value in self.data.items():
|
||||
setattr(self, field, value)
|
||||
return True
|
||||
else:
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
def save(self):
|
||||
self.last_update = datetime.now()
|
||||
if self.pk_value is not None:
|
||||
return self._update()
|
||||
else:
|
||||
return self._insert()
|
||||
|
||||
def _update(self):
|
||||
sqlfield = []
|
||||
values = []
|
||||
for field in self.fields:
|
||||
newdata = getattr(self, field)
|
||||
if self.data[field] != newdata:
|
||||
sqlfield.append("{}=%s".format(field))
|
||||
values.append(newdata)
|
||||
if values:
|
||||
# only save if there are changes
|
||||
sql = (
|
||||
f"UPDATE {self.table} SET " + ",".join(sqlfield) +
|
||||
f" WHERE {self.pk_field}=%s"
|
||||
)
|
||||
values.append(self.pk_value)
|
||||
try:
|
||||
self.dcur.execute(sql, values)
|
||||
except Exception as e:
|
||||
self.db.error = e
|
||||
return False
|
||||
for field in self.fields:
|
||||
self.data[field] = getattr(self, field)
|
||||
return True
|
||||
|
||||
def _insert(self):
|
||||
sql = (
|
||||
"INSERT INTO {} ({}) "
|
||||
"VALUES ({})"
|
||||
).format(
|
||||
self.table,
|
||||
','.join(self.fields),
|
||||
','.join(["%s"] * len(self.fields))
|
||||
)
|
||||
values = []
|
||||
for field in self.fields:
|
||||
newdata = getattr(self, field)
|
||||
values.append(newdata)
|
||||
self.data[field] = newdata
|
||||
try:
|
||||
self.dcur.execute(sql, values)
|
||||
except Exception as e:
|
||||
self.db.error = e
|
||||
return False
|
||||
self.pk_value = self.dcur.lastrowid
|
||||
return True
|
||||
|
||||
def delete(self, record_id=None):
|
||||
if record_id is None:
|
||||
if self.pk_value is None:
|
||||
return False
|
||||
record_id = self.pk_value
|
||||
sql = "DELETE FROM {} WHERE {}=%s".format(self.table, self.pk_field)
|
||||
try:
|
||||
self.dcur.execute(sql, (record_id,))
|
||||
except Exception as e:
|
||||
self.db.error = e
|
||||
return False
|
||||
if self.dcur.rowcount != 1:
|
||||
return False
|
||||
if self.pk_value == record_id:
|
||||
self.pk_value = None
|
||||
self.clear()
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
out = [self.title.format(self.pk_value)]
|
||||
for field in self.fields:
|
||||
value = getattr(self, field, None)
|
||||
out.append(" {:15}: {}".format(field, value))
|
||||
return "\n".join(out)
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Document(DbRecord):
|
||||
|
||||
table = "document"
|
||||
pk_field = "docid"
|
||||
fields = ('docid', 'doctype', 'mimetype', 'filename', 'extension',
|
||||
'hash', 'docsize', 'doctime', 'docdate', 'title', 'pages', 'remarks',
|
||||
'roleaccess')
|
||||
|
||||
def __init__(self, db, dicid=None):
|
||||
super().__init__(db, docid)
|
||||
self.title = _("Document({})")
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Equipment(DbRecord):
|
||||
|
||||
table = "equipment"
|
||||
pk_field = "eid"
|
||||
fields = ('eid', 'vid' 'ename', 'shortname', 'model', 'ecat', 'serial',
|
||||
'supplier', 'manufacturer', 'purchdate', 'price', 'weight', 'remarks',
|
||||
'flags')
|
||||
|
||||
def __init__(self, db, eid=None):
|
||||
super().__init__(db, eid)
|
||||
self.title = _("Equipment({})")
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Fuse(DbRecord):
|
||||
|
||||
table = "fuse"
|
||||
pk_field = "fuseid"
|
||||
fields = ('fuseid', 'vid', 'fnumber', 'ftype', 'current', 'cableid',
|
||||
'eid', 'location', 'description', 'remarks')
|
||||
|
||||
def __init__(self, db, cableid=None):
|
||||
super().__init__(db, fuseid)
|
||||
self.title = _("Fuse({})")
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Inventory(DbRecord):
|
||||
|
||||
table = "inventory"
|
||||
pk_field = "invid"
|
||||
fields = ('invid', 'invname', 'conttype', 'sid', 'boxid', 'eid', 'number',
|
||||
'weight', 'price', 'purchdate', 'invcond', 'remarks', 'flags')
|
||||
|
||||
def __init__(self, db, invid=None):
|
||||
super().__init__(db, invid)
|
||||
self.title = _("Inventory({})")
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Maintenance(DbRecord):
|
||||
|
||||
table = "maintenance"
|
||||
pk_field = "maintid"
|
||||
fields = ('maintid', 'vid', 'eid', 'series', 'activities', 'plan_date',
|
||||
'completion_date', 'interval_hours', 'last_hours', 'remarks',
|
||||
'maintstate')
|
||||
|
||||
def __init__(self, db, maintid=None):
|
||||
super().__init__(db, maintid)
|
||||
self.title = _("Maintenance({})")
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Measurement(DbRecord):
|
||||
|
||||
table = "measurement"
|
||||
pk_field = "mid"
|
||||
fields = ('mid', 'mname', 'vid', 'eid', 'nval', 'val1', 'val2', 'val3',
|
||||
'unit', 'accuracy', 'mdate', 'note')
|
||||
|
||||
def __init__(self, db, mid=None):
|
||||
super().__init__(db, mid)
|
||||
self.title = _("Measurement({})")
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Project(DbRecord):
|
||||
|
||||
table = "project"
|
||||
pk_field = "prokid"
|
||||
fields = ('projid', 'projname', 'vid', 'responsible', 'startdate',
|
||||
'duration', 'costs_plan', 'costs_final', 'remarks', 'projstate')
|
||||
|
||||
def __init__(self, db, projid=None):
|
||||
super().__init__(db, projid)
|
||||
self.title = _("Project({})")
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Provision(DbRecord):
|
||||
|
||||
table = "provisions"
|
||||
pk_field = "provid"
|
||||
fields = ('provid', 'provname', 'provcat', 'sid', 'boxid', 'number',
|
||||
'target', 'weight', 'weight_net', 'unit', 'calories', 'storedate',
|
||||
'shelflife', 'price', 'remarks')
|
||||
|
||||
def __init__(self, db, provid=None):
|
||||
super().__init__(db, provid)
|
||||
self.title = _("Provision({})")
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Storage(DbRecord):
|
||||
|
||||
table = "storage"
|
||||
pk_field = "sid"
|
||||
fields = ('sid', 'vid', 'sname', 'stype', 'capacity', 'capaunit',
|
||||
'x', 'y', 'z', 'wx', 'wy', 'wz', 'angle', 'color', 'remarks')
|
||||
|
||||
def __init__(self, db, sid=None):
|
||||
super().__init__(db, sid)
|
||||
self.title = _("Storage({})")
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Task(DbRecord):
|
||||
|
||||
table = "task"
|
||||
pk_field = "taskid"
|
||||
fields = ('taskid', 'vid', 'projid', 'taskname', 'priority', 'taskstate',
|
||||
'state_ts', 'plandate', 'duedate', 'started', 'finished', 'responsible')
|
||||
|
||||
def __init__(self, db, taskid=None):
|
||||
super().__init__(db, taskid)
|
||||
self.title = _("Task({})")
|
||||
@@ -0,0 +1,136 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
import MySQLdb.cursors
|
||||
import bcrypt
|
||||
import random
|
||||
import string
|
||||
|
||||
class User(object):
|
||||
|
||||
@classmethod
|
||||
def genpass(cls, length=12):
|
||||
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
|
||||
|
||||
def __init__(self, db, user_id=None):
|
||||
self.conn = db.conn
|
||||
self.cur = db.conn.cursor(MySQLdb.cursors.DictCursor)
|
||||
if user_id is not None:
|
||||
self.user_id = int(user_id)
|
||||
self.load()
|
||||
else:
|
||||
self.login = None
|
||||
self.displayname = None
|
||||
self.language = 'en'
|
||||
self.role = 'undef'
|
||||
self.flags = []
|
||||
|
||||
def load(self, user_id=0):
|
||||
sql = (
|
||||
"SELECT login, displayname, language, role, flags "
|
||||
"FROM user "
|
||||
"WHERE userid=%s")
|
||||
self.cur.execute(sql, (self.user_id, ))
|
||||
row = self.cur.fetchone()
|
||||
if row:
|
||||
self.data = row
|
||||
self.login = row['login']
|
||||
self.displayname = row['displayname']
|
||||
self.language = row['language']
|
||||
self.role = row['role']
|
||||
self.flags = row['flags'].split(',') if row['flags'] else []
|
||||
else:
|
||||
self.data = None
|
||||
|
||||
def save(self):
|
||||
fields = ('login', 'displayname', 'language', 'role')
|
||||
sqlfield = []
|
||||
values = []
|
||||
for field in fields:
|
||||
newdata = getattr(self, field)
|
||||
if self.data[field] != newdata:
|
||||
sqlfield.append(f"{field}=%s")
|
||||
values.append(newdata)
|
||||
self.data[field] = newdata
|
||||
# flags set handled separately
|
||||
newflags = ','.join(self.flags) if self.flags else None
|
||||
if self.data['flags'] != newflags:
|
||||
sqlfield.append(f"flags=%s")
|
||||
values.append(newflags)
|
||||
self.data['flags'] = newflags
|
||||
if values:
|
||||
# only save if changed values exist
|
||||
sql = "UPDATE user SET " + ",".join(sqlfield) + " WHERE userid=%s"
|
||||
values.append(self.user_id)
|
||||
self.cur.execute(sql, values)
|
||||
self.conn.commit()
|
||||
|
||||
""" unused at the moment
|
||||
def mark_login(self):
|
||||
sql = "UPDATE user SET last_login=NOW() WHERE username=%s"
|
||||
self.cur.execute(sql, (self.username, ))
|
||||
self.conn.commit()
|
||||
|
||||
def mark_logout(self):
|
||||
sql = "UPDATE user SET last_logout=NOW() WHERE username=%s"
|
||||
self.cur.execute(sql, (self.username, ))
|
||||
self.conn.commit()
|
||||
"""
|
||||
|
||||
def create(self):
|
||||
"""
|
||||
Creates a new user with the first free id > 0
|
||||
The role is the lowest possible one
|
||||
"""
|
||||
sql = (
|
||||
"SELECT MIN(u1.userid+1) AS newid "
|
||||
"FROM user AS u1 LEFT JOIN user AS u2 ON u2.userid=u1.userid+1 "
|
||||
"WHERE u1.userid>=0 AND u2.userid IS NULL")
|
||||
self.cur.execute(sql)
|
||||
newid = self.cur.fetchone()['newid']
|
||||
self.user_id = newid
|
||||
# TODO check if login is unused else create other name
|
||||
# add function for that feature
|
||||
self.login = f"user{newid}"
|
||||
self.displayname = f"User #{newid}"
|
||||
self.language = 'en'
|
||||
self.role = 'sailor'
|
||||
self.flags = None
|
||||
sql = (
|
||||
"INSERT INTO user (userid, login, pass, displayname, role, flags, language) "
|
||||
"VALUES (%s,%s,%s,%s,%s,%s,%s)"
|
||||
)
|
||||
cryptpass = bcrypt.hashpw(self.genpass().encode(), bcrypt.gensalt())
|
||||
self.cur.execute(sql, (newid, self.login, cryptpass, self.displayname, self.role, self.flags, self.language))
|
||||
# create mandatory settings
|
||||
sql = "INSERT INTO settings (userid, sno, valint) VALUES (%s, 1, 0)"
|
||||
self.cur.execute(sql, (newid,))
|
||||
self.conn.commit()
|
||||
return newid
|
||||
|
||||
def delete(self):
|
||||
sql = "DELETE FROM settings WHERE userid=%s"
|
||||
self.cur.execute(sql, (self.user_id,))
|
||||
sql = "DELETE FROM user WHERE userid=%s"
|
||||
self.cur.execute(sql, (self.user_id,))
|
||||
self.conn.commit()
|
||||
|
||||
def setpass(self, newpass):
|
||||
sql = "UPDATE user SET pass=%s WHERE userid=%s"
|
||||
cryptpass = bcrypt.hashpw(newpass.encode(), bcrypt.gensalt())
|
||||
print(cryptpass, self.user_id)
|
||||
self.cur.execute(sql, (cryptpass, self.user_id))
|
||||
self.conn.commit()
|
||||
|
||||
def __repr__(self):
|
||||
# Unique description
|
||||
out = "User({})".format(self.user_id)
|
||||
return out
|
||||
|
||||
def __str__(self):
|
||||
# Prettyprint
|
||||
out = "User(%d) - %s\n" % (self.user_id, self.displayname)
|
||||
out += " F: %s\n" % self.flags
|
||||
out += " R: %s\n" % self.role
|
||||
out += " L: %s\n" % self.language
|
||||
return out
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: WTFPL
|
||||
|
||||
from .dbrecord import DbRecord
|
||||
|
||||
class Vessel(DbRecord):
|
||||
|
||||
table = "vessel"
|
||||
pk_field = "vid"
|
||||
fields = ('vid', 'vtype', 'vesselname', 'model', 'shipyard', 'loa', 'lwl',
|
||||
'beam', 'draught', 'draught_min', 'displacement', 'ballast',
|
||||
'beltpos_aft', 'beltpos_bow', 'shapefile', 'sid_default', 'remarks')
|
||||
|
||||
def __init__(self, db, vid=None):
|
||||
super().__init__(db, vid)
|
||||
self.title = _("Vessel({})")
|
||||
Reference in New Issue
Block a user