IDManager: added import/export plus little cosmetic enhancements.
--- a/PSKManagement.py Tue Nov 20 11:32:42 2018 +0100
+++ b/PSKManagement.py Wed Nov 21 14:10:51 2018 +0100
@@ -7,7 +7,10 @@
import os
import time
import json
+from zipfile import ZipFile
+# PSK Management Data model :
+# [[ID,Desc, LastKnownURI, LastConnect]]
COL_ID,COL_URI,COL_DESC,COL_LAST = range(4)
def _pskpath(project_path):
@@ -16,51 +19,52 @@
def _mgtpath(project_path):
return os.path.join(_pskpath(project_path), 'management.json')
-def _default():
- return ['', # default description
+def _default(ID):
+ return [ID,
+ '', # default description
None, # last known URI
None] # last connection date
+def _dataByID(data):
+ return {row[COL_ID]:row for row in data}
+
def _LoadData(project_path):
+ """ load known keys metadata """
if os.path.isdir(_pskpath(project_path)):
_path = _mgtpath(project_path)
- # load known keys metadata
- # {ID:(Desc, LastKnownURI, LastConnect)}
- return json.loads(open(_path).read()) \
- if os.path.exists(_path) else {}
- return {}
+ if os.path.exists(_path):
+ return json.loads(open(_path).read())
+ return []
-def GetData(project_path):
- # [(ID, Desc, LastKnownURI, LastConnect)
- data = []
- loaded_data = _LoadData(project_path)
+def _filterData(psk_files, data_input):
+ input_by_ID = _dataByID(data_input)
+ output = []
# go through all secret files available an build data
# out of data recoverd from json and list of secret.
# this implicitly filters IDs out of metadata who's
# secret is missing
- psk_files = os.listdir(_pskpath(project_path))
for filename in psk_files:
if filename.endswith('.secret'):
ID = filename[:-7] # strip filename extension
- meta = loaded_data.get(ID,_default())
- data.append([ID]+meta)
- return data
+ output.append(input_by_ID.get(ID,_default(ID)))
+ return output
+def GetData(project_path):
+ loaded_data = _LoadData(project_path)
+ psk_files = os.listdir(_pskpath(project_path))
+ return _filterData(psk_files, loaded_data)
def DeleteID(project_path, ID):
secret_path = os.path.join(_pskpath(project_path), ID+'.secret')
os.remove(secret_path)
-def _StoreData(project_path, data):
+def SaveData(project_path, data):
pskpath = _pskpath(project_path)
if not os.path.isdir(pskpath):
os.mkdir(pskpath)
with open(_mgtpath(project_path), 'w') as f:
f.write(json.dumps(data))
-def SaveData(project_path, data):
- to_store = {row[0]:row[1:] for row in data}
- _StoreData(project_path, to_store)
def UpdateID(project_path, ID, secret, URI):
pskpath = _pskpath(project_path)
@@ -71,11 +75,49 @@
with open(secpath, 'w') as f:
f.write(ID+":"+secret)
+ # here we directly use _LoadData, avoiding filtering that could be long
data = _LoadData(project_path)
- dataForID = [ID] + (data.get(ID, _default()) if data else _default())
+ idata = _dataByID(data)
+ dataForID = idata.get(ID, _default(ID)) if data else _default(ID)
dataForID[COL_URI] = URI
# FIXME : could store time instead os a string and use DVC model's cmp
# then date display could be smarter, etc - sortable sting hack for now
dataForID[COL_LAST] = time.strftime('%y/%M/%d-%H:%M:%S')
- data[ID] = dataForID[1:]
- _StoreData(project_path, data)
+ SaveData(project_path, data)
+
+def ExportIDs(project_path, export_zip):
+ with ZipFile(export_zip, 'w') as zf:
+ path = _pskpath(project_path)
+ for nm in os.listdir(path):
+ if nm.endswith('.secret') or nm == 'management.json':
+ zf.write(os.path.join(path, nm), nm)
+
+def ImportIDs(project_path, import_zip, should_I_replace_callback):
+ data = GetData(project_path)
+
+ zip_loaded_data = json.loads(zf.open('management.json').read())
+ name_list = zf.namelist()
+ zip_filtered_data = _filterData(name_list, loaded_data)
+
+ idata = _dataByID(data)
+
+ for imported_row in zip_filtered_data:
+ ID = imported_row[COL_ID]
+ existing_row = idata.get(ID, None)
+ if existing_row is None:
+ data.append(imported_row)
+ else:
+ # callback returns the selected list for merge or none if canceled
+ result = should_I_replace_callback(existing_row, imported_row)
+ if result is None:
+ break
+
+ if result:
+ # replace with imported
+ existing_row[:] = imported_row
+ # copy the key of selected
+ self.extract(ID+".secret", _pskpath(project_path))
+
+ SaveData(project_path, data)
+
+
--- a/controls/IDBrowser.py Tue Nov 20 11:32:42 2018 +0100
+++ b/controls/IDBrowser.py Wed Nov 21 14:10:51 2018 +0100
@@ -11,14 +11,14 @@
from PSKManagement import COL_ID,COL_URI,COL_DESC,COL_LAST
class IDBrowserModel(dv.PyDataViewIndexListModel):
- def __init__(self, psk_path, columncount):
- self.psk_path = psk_path
+ def __init__(self, project_path, columncount):
+ self.project_path = project_path
self.columncount = columncount
- self.data = PSK.GetData(psk_path)
+ self.data = PSK.GetData(project_path)
dv.PyDataViewIndexListModel.__init__(self, len(self.data))
def _saveData(self):
- PSK.SaveData(self.psk_path, self.data)
+ PSK.SaveData(self.project_path, self.data)
def GetColumnType(self, col):
return "string"
@@ -59,7 +59,7 @@
rows.sort(reverse=True)
for row in rows:
- PSK.DeleteID(self.psk_path, self.data[row][COL_ID])
+ PSK.DeleteID(self.project_path, self.data[row][COL_ID])
del self.data[row]
self.RowDeleted(row)
self._saveData()
@@ -69,13 +69,20 @@
self.RowAppended()
self._saveData()
+ def Import(self, filepath, sircb):
+ PSK.ImportIDs(self.project_path, filepath, sircb)
+
+ def Export(self, filepath):
+ PSK.ExportIDs(self.project_path, filepath)
+
colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE
class IDBrowser(wx.Panel):
def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs):
- wx.Panel.__init__(self, parent, -1, size=(400,200))
+ big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None
+ wx.Panel.__init__(self, parent, -1, size=(800 if big else 400,
+ 600 if big else 200))
- self.isManager = SelectURICallBack is None and SelectIDCallBack is None
self.SelectURICallBack = SelectURICallBack
self.SelectIDCallBack = SelectIDCallBack
@@ -89,12 +96,12 @@
ColumnsDesc = [
args(_("ID"), COL_ID, width = 100),
- args(_("Last URI"), COL_URI, width = 80),
+ args(_("Last URI"), COL_URI, width = 160 if big else 80),
args(_("Description"), COL_DESC, width = 200,
mode = dv.DATAVIEW_CELL_EDITABLE
if self.isManager
else dv.DATAVIEW_CELL_INERT),
- args(_("Last connection"), COL_LAST, width = 100),
+ args(_("Last connection"), COL_LAST, width = 120),
]
self.model = IDBrowserModel(ctr.ProjectPath, len(ColumnsDesc))
@@ -171,14 +178,22 @@
self.SelectURICallBack(URI)
def OnExportButton(self, evt):
- # TODO
- wx.MessageBox(_('?'),
- _('Mhe'),
- wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
+ dialog = wx.FileDialog(self, _("Choose a file"),
+ wildcard = _("PSK ZIP files (*.zip)|*.zip"),
+ style = wx.SAVE | wx.OVERWRITE_PROMPT)
+ if dialog.ShowModal() == wx.ID_OK:
+ self.model.Export(dialog.GetPath())
+
+ def ShouldIReplaceCallback(self,some,stuff):
+ # TODO
+ wx.MessageBox("TODO : ShouldIReplaceCallback")
+ return True
def OnImportButton(self, evt):
- # TODO
- wx.MessageBox(_('?'),
- _('Mhe'),
- wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT)
+ dialog = wx.FileDialog(self, _("Choose a file"),
+ wildcard = _("PSK ZIP files (*.zip)|*.zip"),
+ style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
+ if dialog.ShowModal() == wx.ID_OK:
+ self.model.Import(dialog.GetPath(),
+ self.ShouldIReplaceCallback)