From aae131a8214e5b2f4f73da53da1f1c1ddb79893d Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:11:43 -0400 Subject: [PATCH 1/9] Updated version and messages to 2.4.2 --- 1_ElectronicFieldNotes.py | 2 +- message.txt | 4 ++-- msg.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/1_ElectronicFieldNotes.py b/1_ElectronicFieldNotes.py index d472755..eeeaa1c 100644 --- a/1_ElectronicFieldNotes.py +++ b/1_ElectronicFieldNotes.py @@ -94,7 +94,7 @@ ##mode = "DEBUG" mode = "PRODUCTION" -EHSN_VERSION = "v2.4.1" +EHSN_VERSION = "v2.4.2" eHSN_WINDOW_SIZE = (1100, 730) # import wx.lib.inspection diff --git a/message.txt b/message.txt index 038c229..ed2703f 100644 --- a/message.txt +++ b/message.txt @@ -1,8 +1,8 @@ -V2.4.1 +V2.4.2 **************************************************************************************************** -eHSN 2.4.1 has many new features and bug fixes, which are detailed in the Release Notes on Confluence: https://watersurveyofcanada.atlassian.net/wiki/spaces/WSCan/pages/4226187318/eHSN+2.4.1+Release+Notes +eHSN 2.4.2 has many new features and bug fixes, which are detailed in the Release Notes on Confluence: https://watersurveyofcanada.atlassian.net/wiki/spaces/WSCan/pages/4290445319/eHSN+2.4.2+Mandatory+Release **************************************************************************************************** diff --git a/msg.txt b/msg.txt index 038c229..ed2703f 100644 --- a/msg.txt +++ b/msg.txt @@ -1,8 +1,8 @@ -V2.4.1 +V2.4.2 **************************************************************************************************** -eHSN 2.4.1 has many new features and bug fixes, which are detailed in the Release Notes on Confluence: https://watersurveyofcanada.atlassian.net/wiki/spaces/WSCan/pages/4226187318/eHSN+2.4.1+Release+Notes +eHSN 2.4.2 has many new features and bug fixes, which are detailed in the Release Notes on Confluence: https://watersurveyofcanada.atlassian.net/wiki/spaces/WSCan/pages/4290445319/eHSN+2.4.2+Mandatory+Release **************************************************************************************************** From d2ab4c88c9b3922b38f10eef73226e398a44aa8b Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:13:39 -0400 Subject: [PATCH 2/9] Bug fix for QRevMS missing values --- IngestQRevIntMSManager.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/IngestQRevIntMSManager.py b/IngestQRevIntMSManager.py index dd7e984..8e4746a 100644 --- a/IngestQRevIntMSManager.py +++ b/IngestQRevIntMSManager.py @@ -82,13 +82,19 @@ def GetFrequencyUnit(filePath): return GetRoot(filePath).find('Instrument').find('Frequency').attrib['unitsCode'] def GetADCPDepth(filePath): - return GetRoot(filePath).find('VerticalDetails/Vertical/Processing/Depth/ADCPDepth').text + try: + return GetRoot(filePath).find('VerticalDetails/Vertical/Processing/Depth/ADCPDepth').text + except: + return "" def GetDiagTest(filePath): return GetRoot(filePath).find('QA').find('DiagnosticTestResult').text def GetMagDeclination(filePath): - return GetRoot(filePath).find('VerticalDetails/Vertical/Processing/Navigation/MagneticVariation').text + try: + return GetRoot(filePath).find('VerticalDetails/Vertical/Processing/Navigation/MagneticVariation').text + except: + return "" #Calculate the mean time From 4fd6344c4f3668fab1a3ea509ba373519d42997d Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:14:38 -0400 Subject: [PATCH 3/9] Bug fix for QRev 4.41.x versioning --- IngestQRevManager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IngestQRevManager.py b/IngestQRevManager.py index 7f1bc9d..b2cb7f1 100644 --- a/IngestQRevManager.py +++ b/IngestQRevManager.py @@ -31,7 +31,9 @@ def GetDate(filePath): def GetQRevVersion(filePath): QRevVer = GetRoot(filePath).attrib['QRevVersion'] - return float(QRevVer[(len(QRevVer) - 5):]) + versionFull = QRevVer.split()[1] + values = versionFull.split('.') + return float(values[0] + '.' + values[1]) #Summary def GetStartTime(filePath): From 58e3f3de07ea333ca2b392570f7bbf456e283bde Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:15:28 -0400 Subject: [PATCH 4/9] Added option to mid section coefficient dropdown --- InstrumentDeploymentInfoPanel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InstrumentDeploymentInfoPanel.py b/InstrumentDeploymentInfoPanel.py index a7c9a45..1981187 100644 --- a/InstrumentDeploymentInfoPanel.py +++ b/InstrumentDeploymentInfoPanel.py @@ -83,7 +83,7 @@ def __init__(self, mode, *args, **kwargs): self.flowAngleLbl = "Flow Angle" self.flowAngleList = ["", "Perpendicular", "Varied"] self.coEffLbl = "Coefficient" - self.coefficientList = ["","-1", "1", "0.88"] + self.coefficientList = ["","-1", "1", "0.88", "0.88+1.00"] self.methodLbl = "Method" self.methodList = ["", "0.6", "0.2/0.8", "0.6+0.2/0.8", "0.6+0.5", "0.5", "0.5+0.2/0.8", "Surface", "0.2/0.6/0.8", "0.2/0.5/0.8", "ADCP"] self.locatedLbl = "Located" @@ -505,7 +505,7 @@ def InitUI(self): self.coEffCtrl = MyComboBox(self.midsectionMethodInfoPanel, choices=self.coefficientList, style=wx.CB_DROPDOWN, size=(180, -1)) # self.coEffCtrl = wx.TextCtrl(self.midsectionMethodInfoPanel, style=wx.TE_PROCESS_ENTER, size=(180, -1)) # self.coEffCtrl.Bind(wx.EVT_TEXT, NumberControl.FloatNumberControl) - self.coEffCtrl.Bind(wx.EVT_KILL_FOCUS, NumberControl.Round2) + # self.coEffCtrl.Bind(wx.EVT_KILL_FOCUS, NumberControl.Round2) self.coEffCtrl.Bind(wx.EVT_TEXT, self.OnChangeResetBGColour) mmiHorizontalSizer1.Add(self.numOfPanelsTxt, 0, wx.EXPAND|wx.TOP|wx.RIGHT, 5) mmiHorizontalSizer1.Add(self.numOfPanelsScroll, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) From 6608e55007affbfdc1a71ba781d55de72d2d72cc Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:17:04 -0400 Subject: [PATCH 5/9] Updated AttachBox for Inventory Management files --- AttachBox.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/AttachBox.py b/AttachBox.py index c681dab..cf931e7 100644 --- a/AttachBox.py +++ b/AttachBox.py @@ -83,6 +83,14 @@ def add(self): def add_remove(self, evt): if evt.GetEventObject().GetLabel() == "+": + + # Only allow for one submission of an inventory management file + if self.name == "InventoryManagement" and len(self.buttonList) > 0: + info = wx.MessageDialog(self, 'Only one Inventory Management file may be included.', 'Unable to add additonal file', + wx.OK | wx.ICON_ERROR) + info.ShowModal() + return + self.columnList.append(wx.BoxSizer(wx.HORIZONTAL)) self.buttonList.append(wx.Button(self, label="-", size=self.buttonSize)) self.addrList.append(wx.TextCtrl(self, size=self.barSize)) @@ -149,6 +157,9 @@ def updateLabels(self): elif self.name == "DischargeSummary" and self.addrList[id].GetValue() != "": self.count += 1 self.labelList[id].ChangeValue(stnNum + "_" + dateVal + "_M" + str(self.count)) + # There can only ever be one submission for inventory management + elif self.name == "InventoryManagement" and self.addrList[id].GetValue() != "": + self.labelList[id].ChangeValue(stnNum + "_" + dateVal + "_Inventory") def Browse(self, evt): id = self.browseList.index(evt.GetEventObject()) @@ -171,6 +182,12 @@ def Browse(self, evt): wx.OK | wx.ICON_ERROR) info.ShowModal() return + # If the inventory management file is not a pdf, then display a warning to the user + if extension != '.md' and self.name == "InventoryManagement": + info = wx.MessageDialog(self, 'The Inventory Management File type must be Markdown.', 'Incorect file type', + wx.OK | wx.ICON_ERROR) + info.ShowModal() + return else: # Set the root path of the parent to be the new root path From b1db29525b6e43148323973003df515e0a79d44d Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:19:14 -0400 Subject: [PATCH 6/9] Added Site Visit Optimization selection box --- SiteVisitOptimizationBox.py | 556 ++++++++++++++++++++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 SiteVisitOptimizationBox.py diff --git a/SiteVisitOptimizationBox.py b/SiteVisitOptimizationBox.py new file mode 100644 index 0000000..00bc546 --- /dev/null +++ b/SiteVisitOptimizationBox.py @@ -0,0 +1,556 @@ +# All works in this code have been curated by ECCC and licensed under the GNU General Public License v3.0. +# Read more: https://www.gnu.org/licenses/gpl-3.0.en.html + +import wx +import re + +class SVOPanel(wx.Panel): + def __init__(self, parent, func, *args, **kwargs): + super(SVOPanel, self).__init__(*args, **kwargs) + self.parent = parent + # Set func to * + self.func = "*" + self.manager = None + # Get the passed in name type + self.name = func + + # --------------------------------------------------------- + + # Type headers + self.table1_header1 = 'Type of Visit:' + self.table1_header2 = 'Assumptions:' + + # Type checkbox titles + self.table1_title1 = 'Planned' + self.table1_title2 = 'Unplanned' + + # Type comments + self.table1_comment1 = ' Visit is intentionally planned based on known or anticipated data quality needs, hydraulic conditions,\n or measurement opportunities' + self.table1_comment2 = ' Visit is triggered by a high/low flow event requiring discharge measurement, equipment failure, gauge destroyed, etc.' + + # Sizes + #self.table1_title_width = 105 + self.table1_title_width = 170 + self.table1_comment_width = 955 - 24 - self.table1_title_width + + # --------------------------------------------------------- + + # Outcome headers + self.table2_header1 = 'Visit Reason:' + self.table2_header2 = 'Assumptions:' + + # Outcome checkbox titles + self.table2_title1 = 'Data Maintenance' + self.table2_title2 = 'Equipment/Instrumentation' + self.table2_title3 = 'Infrastructure' + + # Outcome comments + self.table2_comment1 = ' Includes rating and stage maintenance' + self.table2_comment2 = ' Equipment serviced during visit (power, telemetry, logger, sensor)' + self.table2_comment3 = ' Infrastructure fixed/maintenance during visit (gauge, grounds, access, infrastructure)' + + # Sizes + self.table2_title_width = 170 + self.table2_comment_width = 955 - 24 - self.table2_title_width + + # --------------------------------------------------------- + + # Type headers + self.table3_header1 = 'Special Service Request:' + self.table3_header2 = 'Assumptions:' + + # Type checkbox titles + self.table3_title1 = 'Partner Request' + self.table3_title2 = 'Management' + + # Type comments + self.table3_comment1 = ' Funding partner has requested a visit for data quality purposes' + self.table3_comment2 = ' Management (any level) has requested a visit outside the anticipated planned visits for operation/maintenance of the hydrometric\n station and data quality needs: Head of Operations, District Manager has requested a visit for operational purposes' + + # Sizes + #self.table3_title_width = 105 + self.table3_title_width = 170 + self.table3_comment_width = 955 - 24 - self.table3_title_width + + # --------------------------------------------------------- + + # Text box labels + self.commentLbl = "Comments:" + self.summaryLbl = "Summary changes (Station Health Remarks):" + + # Fonts + self.fontTitle = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD) + #self.fontItem = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL) + + #Font stuff + #f = self.GetFont() + #dc = wx.WindowDC(self) + #dc.SetFont(f) + #self.width, self.TextLabelRowHeight = dc.GetTextExtent("Ag") + #self.TextLabelRowHeight *= 1.34 + + self.InitUI() + + + def InitUI(self): + self.layoutSizer = wx.BoxSizer(wx.VERTICAL) + self.header1_colSizer = wx.BoxSizer(wx.HORIZONTAL) + self.table1_colSizer = wx.BoxSizer(wx.HORIZONTAL) + self.header2_colSizer = wx.BoxSizer(wx.HORIZONTAL) + self.table2_colSizer = wx.BoxSizer(wx.HORIZONTAL) + self.header3_colSizer = wx.BoxSizer(wx.HORIZONTAL) + self.table3_colSizer = wx.BoxSizer(wx.HORIZONTAL) + self.textbox_sizer = wx.BoxSizer(wx.VERTICAL) + + # -------------------------------------------------------------------- + + ######################## + ####### HEADER 1 ####### + ######################## + + # Checkboxes (empty) + self.header1_cb_sizer = wx.BoxSizer(wx.VERTICAL) + self.header1_cb_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(24, -1)) + self.header1_cb_panel.SetSizer(self.header1_cb_sizer) + self.header1_colSizer.Add(self.header1_cb_panel, 0, wx.EXPAND) + + # Text column 1 + self.header1_col1_sizer = wx.BoxSizer(wx.VERTICAL) + self.header1_col1_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.header1_col1_panel.SetSizer(self.header1_col1_sizer) + self.header1_colSizer.Add(self.header1_col1_panel, 0, wx.EXPAND) + + # Text column 2 + self.header1_col2_sizer = wx.BoxSizer(wx.VERTICAL) + self.header1_col2_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.header1_col2_panel.SetSizer(self.header1_col2_sizer) + self.header1_colSizer.Add(self.header1_col2_panel, 1, wx.EXPAND) + + # -------------------- + # Adding header values + # -------------------- + + # Header column 1 + header1_head_col1 = wx.StaticText(self.header1_col1_panel, label=self.table1_header1, size=(self.table1_title_width, 20), style=wx.ALIGN_CENTER_HORIZONTAL) + header1_head_col1.SetFont(self.fontTitle) + self.header1_col1_sizer.Add(header1_head_col1, 1) + + # Header column 2 + header1_head_col2 = wx.StaticText(self.header1_col2_panel, label=self.table1_header2, size=(self.table1_comment_width, 20), style=wx.ALIGN_CENTER_HORIZONTAL) + header1_head_col2.SetFont(self.fontTitle) + self.header1_col2_sizer.Add(header1_head_col2, 1) + + + ####################### + ####### TABLE 1 ####### + ####################### + + # Checkboxes + self.table1_cb_sizer = wx.BoxSizer(wx.VERTICAL) + self.table1_cb_panel = wx.Panel(self, style=wx.SIMPLE_BORDER, size=(24, -1)) + self.table1_cb_panel.SetSizer(self.table1_cb_sizer) + self.table1_colSizer.Add(self.table1_cb_panel, 0, wx.EXPAND) + + # Text column 1 + self.table1_col1_sizer = wx.BoxSizer(wx.VERTICAL) + self.table1_col1_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.table1_col1_panel.SetSizer(self.table1_col1_sizer) + self.table1_colSizer.Add(self.table1_col1_panel, 0, wx.EXPAND) + + # Text column 2 + self.table1_col2_sizer = wx.BoxSizer(wx.VERTICAL) + self.table1_col2_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.table1_col2_panel.SetSizer(self.table1_col2_sizer) + self.table1_colSizer.Add(self.table1_col2_panel, 1, wx.EXPAND) + + # ------------------- + # Adding table values + # ------------------- + + # Checkbox row 1 + self.table1_cb1 = wx.CheckBox(self.table1_cb_panel, size=(24, 34)) + self.table1_cb1.Bind(wx.EVT_CHECKBOX, self.OnTypeChange) + self.table1_cb1.SetValue(False) + self.table1_cb_sizer.Add(self.table1_cb1, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 1 column 1 + table1_row1_col1 = wx.StaticText(self.table1_col1_panel, label=self.table1_title1, size=(self.table1_title_width, 34), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table1_row1_col1.SetFont(self.fontItem) + self.table1_col1_sizer.Add(table1_row1_col1, 1) + + # Comment row 1 column 2 + table1_row1_col2 = wx.StaticText(self.table1_col2_panel, label=self.table1_comment1, size=(self.table1_comment_width, 34), style=wx.SIMPLE_BORDER) + #table1_row1_col2.SetFont(self.fontItem) + self.table1_col2_sizer.Add(table1_row1_col2, 1) + + # ----------------------- + + # Checkbox row 2 + self.table1_cb2 = wx.CheckBox(self.table1_cb_panel, size=(24, 20)) + self.table1_cb2.Bind(wx.EVT_CHECKBOX, self.OnTypeChange) + self.table1_cb2.SetValue(False) + self.table1_cb_sizer.Add(self.table1_cb2, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 2 column 1 + table1_row2_col1 = wx.StaticText(self.table1_col1_panel, label=self.table1_title2, size=(self.table1_title_width, 20), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table1_row2_col1.SetFont(self.fontItem) + self.table1_col1_sizer.Add(table1_row2_col1, 0) # This is set to 0 to allow the row to be its own size + + # Comment row 2 column 2 + table1_row2_col2 = wx.StaticText(self.table1_col2_panel, label=self.table1_comment2, size=(self.table1_comment_width, 20), style=wx.SIMPLE_BORDER) + #table1_row2_col2.SetFont(self.fontItem) + self.table1_col2_sizer.Add(table1_row2_col2, 0) # This is set to 0 to allow the row to be its own size + + # -------------------------------------------------------------------- + + ######################## + ####### HEADER 2 ####### + ######################## + + # Checkboxes (empty) + self.header2_cb_sizer = wx.BoxSizer(wx.VERTICAL) + self.header2_cb_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(24, -1)) + self.header2_cb_panel.SetSizer(self.header2_cb_sizer) + self.header2_colSizer.Add(self.header2_cb_panel, 0, wx.EXPAND) + + # Text column 1 + self.header2_col1_sizer = wx.BoxSizer(wx.VERTICAL) + self.header2_col1_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.header2_col1_panel.SetSizer(self.header2_col1_sizer) + self.header2_colSizer.Add(self.header2_col1_panel, 0, wx.EXPAND) + + # Text column 2 + self.header2_col2_sizer = wx.BoxSizer(wx.VERTICAL) + self.header2_col2_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.header2_col2_panel.SetSizer(self.header2_col2_sizer) + self.header2_colSizer.Add(self.header2_col2_panel, 1, wx.EXPAND) + + # -------------------- + # Adding header values + # -------------------- + + # Header column 1 + header2_head_col1 = wx.StaticText(self.header2_col1_panel, label=self.table2_header1, size=(self.table2_title_width, 20), style=wx.ALIGN_CENTER_HORIZONTAL) + header2_head_col1.SetFont(self.fontTitle) + self.header2_col1_sizer.Add(header2_head_col1, 1) + + # Header column 2 + header2_head_col2 = wx.StaticText(self.header2_col2_panel, label=self.table2_header2, size=(self.table2_comment_width, 20), style=wx.ALIGN_CENTER_HORIZONTAL) + header2_head_col2.SetFont(self.fontTitle) + self.header2_col2_sizer.Add(header2_head_col2, 1) + + + ####################### + ####### TABLE 2 ####### + ####################### + + # Checkboxes + self.table2_cb_sizer = wx.BoxSizer(wx.VERTICAL) + self.table2_cb_panel = wx.Panel(self, style=wx.SIMPLE_BORDER, size=(24, -1)) + self.table2_cb_panel.SetSizer(self.table2_cb_sizer) + self.table2_colSizer.Add(self.table2_cb_panel, 0, wx.EXPAND) + + # Text column 1 + self.table2_col1_sizer = wx.BoxSizer(wx.VERTICAL) + self.table2_col1_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.table2_col1_panel.SetSizer(self.table2_col1_sizer) + self.table2_colSizer.Add(self.table2_col1_panel, 0, wx.EXPAND) + + # Text column 2 + self.table2_col2_sizer = wx.BoxSizer(wx.VERTICAL) + self.table2_col2_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.table2_col2_panel.SetSizer(self.table2_col2_sizer) + self.table2_colSizer.Add(self.table2_col2_panel, 1, wx.EXPAND) + + # ------------------- + # Adding table values + # ------------------- + + # Checkbox row 1 + self.table2_cb1 = wx.CheckBox(self.table2_cb_panel, size=(24, 20)) + self.table2_cb1.Bind(wx.EVT_CHECKBOX, self.OnReasonChange) + self.table2_cb1.SetValue(False) + self.table2_cb_sizer.Add(self.table2_cb1, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 1 column 1 + table2_row1_col1 = wx.StaticText(self.table2_col1_panel, label=self.table2_title1, size=(self.table2_title_width, 20), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table2_row1_col1.SetFont(self.fontItem) + self.table2_col1_sizer.Add(table2_row1_col1, 1) + + # Comment row 1 column 2 + table2_row1_col2 = wx.StaticText(self.table2_col2_panel, label=self.table2_comment1, size=(self.table2_comment_width, 20), style=wx.SIMPLE_BORDER) + #table2_row1_col2.SetFont(self.fontItem) + self.table2_col2_sizer.Add(table2_row1_col2, 1) + + # ----------------------- + + # Checkbox row 2 + self.table2_cb2 = wx.CheckBox(self.table2_cb_panel, size=(24, 20)) + self.table2_cb2.Bind(wx.EVT_CHECKBOX, self.OnReasonChange) + self.table2_cb2.SetValue(False) + self.table2_cb_sizer.Add(self.table2_cb2, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 2 column 1 + table2_row2_col1 = wx.StaticText(self.table2_col1_panel, label=self.table2_title2, size=(self.table2_title_width, 20), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table2_row2_col1.SetFont(self.fontItem) + self.table2_col1_sizer.Add(table2_row2_col1, 1) + + # Comment row 2 column 2 + table2_row2_col2 = wx.StaticText(self.table2_col2_panel, label=self.table2_comment2, size=(self.table2_comment_width, 20), style=wx.SIMPLE_BORDER) + #table2_row2_col2.SetFont(self.fontItem) + self.table2_col2_sizer.Add(table2_row2_col2, 1) + + # ----------------------- + + # Checkbox row 3 + self.table2_cb3 = wx.CheckBox(self.table2_cb_panel, size=(24, 20)) + self.table2_cb3.Bind(wx.EVT_CHECKBOX, self.OnReasonChange) + self.table2_cb3.SetValue(False) + self.table2_cb_sizer.Add(self.table2_cb3, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 3 column 1 + table2_row3_col1 = wx.StaticText(self.table2_col1_panel, label=self.table2_title3, size=(self.table2_title_width, 20), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table2_row3_col1.SetFont(self.fontItem) + self.table2_col1_sizer.Add(table2_row3_col1, 1) + + # Comment row 3 column 2 + table2_row3_col2 = wx.StaticText(self.table2_col2_panel, label=self.table2_comment3, size=(self.table2_comment_width, 20), style=wx.SIMPLE_BORDER) + #table2_row3_col2.SetFont(self.fontItem) + self.table2_col2_sizer.Add(table2_row3_col2, 1) + + # -------------------------------------------------------------------- + + ######################## + ####### HEADER 3 ####### + ######################## + + # Checkboxes (empty) + self.header3_cb_sizer = wx.BoxSizer(wx.VERTICAL) + self.header3_cb_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(24, -1)) + self.header3_cb_panel.SetSizer(self.header3_cb_sizer) + self.header3_colSizer.Add(self.header3_cb_panel, 0, wx.EXPAND) + + # Text column 1 + self.header3_col1_sizer = wx.BoxSizer(wx.VERTICAL) + self.header3_col1_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.header3_col1_panel.SetSizer(self.header3_col1_sizer) + self.header3_colSizer.Add(self.header3_col1_panel, 0, wx.EXPAND) + + # Text column 2 + self.header3_col2_sizer = wx.BoxSizer(wx.VERTICAL) + self.header3_col2_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.header3_col2_panel.SetSizer(self.header3_col2_sizer) + self.header3_colSizer.Add(self.header3_col2_panel, 1, wx.EXPAND) + + # -------------------- + # Adding header values + # -------------------- + + # Header column 1 + header3_head_col1 = wx.StaticText(self.header3_col1_panel, label=self.table3_header1, size=(self.table3_title_width, 20), style=wx.ALIGN_CENTER_HORIZONTAL) + header3_head_col1.SetFont(self.fontTitle) + self.header3_col1_sizer.Add(header3_head_col1, 1) + + # Header column 2 + header3_head_col2 = wx.StaticText(self.header3_col2_panel, label=self.table3_header2, size=(self.table3_comment_width, 20), style=wx.ALIGN_CENTER_HORIZONTAL) + header3_head_col2.SetFont(self.fontTitle) + self.header3_col2_sizer.Add(header3_head_col2, 1) + + + ####################### + ####### TABLE 3 ####### + ####################### + + # Checkboxes + self.table3_cb_sizer = wx.BoxSizer(wx.VERTICAL) + self.table3_cb_panel = wx.Panel(self, style=wx.SIMPLE_BORDER, size=(24, -1)) + self.table3_cb_panel.SetSizer(self.table3_cb_sizer) + self.table3_colSizer.Add(self.table3_cb_panel, 0, wx.EXPAND) + + # Text column 1 + self.table3_col1_sizer = wx.BoxSizer(wx.VERTICAL) + self.table3_col1_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.table3_col1_panel.SetSizer(self.table3_col1_sizer) + self.table3_colSizer.Add(self.table3_col1_panel, 0, wx.EXPAND) + + # Text column 2 + self.table3_col2_sizer = wx.BoxSizer(wx.VERTICAL) + self.table3_col2_panel = wx.Panel(self, style=wx.BORDER_NONE, size=(-1, -1)) + self.table3_col2_panel.SetSizer(self.table3_col2_sizer) + self.table3_colSizer.Add(self.table3_col2_panel, 1, wx.EXPAND) + + # ------------------- + # Adding table values + # ------------------- + + # Checkbox row 1 + self.table3_cb1 = wx.CheckBox(self.table3_cb_panel, size=(24, 20)) + self.table3_cb1.Bind(wx.EVT_CHECKBOX, self.OnSSRChange) + self.table3_cb1.SetValue(False) + self.table3_cb_sizer.Add(self.table3_cb1, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 1 column 1 + table3_row1_col1 = wx.StaticText(self.table3_col1_panel, label=self.table3_title1, size=(self.table3_title_width, 20), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table3_row1_col1.SetFont(self.fontItem) + self.table3_col1_sizer.Add(table3_row1_col1, 0) # This is set to 0 to allow the row to be its own size + + # Comment row 1 column 2 + table3_row1_col2 = wx.StaticText(self.table3_col2_panel, label=self.table3_comment1, size=(self.table3_comment_width, 20), style=wx.SIMPLE_BORDER) + #table3_row1_col2.SetFont(self.fontItem) + self.table3_col2_sizer.Add(table3_row1_col2, 0) # This is set to 0 to allow the row to be its own size + + # ----------------------- + + # Checkbox row 2 + self.table3_cb2 = wx.CheckBox(self.table3_cb_panel, size=(24, 34)) + self.table3_cb2.Bind(wx.EVT_CHECKBOX, self.OnSSRChange) + self.table3_cb2.SetValue(False) + self.table3_cb_sizer.Add(self.table3_cb2, 0, wx.EXPAND|wx.LEFT, 4) + + # Comment row 2 column 1 + table3_row2_col1 = wx.StaticText(self.table3_col1_panel, label=self.table3_title2, size=(self.table3_title_width, 34), style=wx.SIMPLE_BORDER|wx.ALIGN_CENTER_HORIZONTAL) + #table3_row2_col1.SetFont(self.fontItem) + self.table3_col1_sizer.Add(table3_row2_col1, 1) + + # Comment row 2 column 2 + table3_row2_col2 = wx.StaticText(self.table3_col2_panel, label=self.table3_comment2, size=(self.table3_comment_width, 34), style=wx.SIMPLE_BORDER) + #table3_row2_col2.SetFont(self.fontItem) + self.table3_col2_sizer.Add(table3_row2_col2, 1) + + # -------------------------------------------------------------------- + + ################################### + ####### COMMENT AND SUMMARY ####### + ################################### + + comment_txt = wx.StaticText(self, label=self.commentLbl) + comment_txt.SetFont(self.fontTitle) + self.comment_box = wx.TextCtrl(self, size=(-1, 20), style=wx.TE_MULTILINE|wx.TE_BESTWRAP) + self.comment_box.Bind(wx.EVT_TEXT, self.OnCommentChange) + self.textbox_sizer.Add(comment_txt, 0, wx.EXPAND|wx.LEFT|wx.TOP, 5) + self.textbox_sizer.Add(self.comment_box, 1, wx.EXPAND|wx.TOP, 3) + + summary_txt = wx.StaticText(self, label=self.summaryLbl) + summary_txt.SetFont(self.fontTitle) + self.summary_box = wx.TextCtrl(self, size=(-1, 20), style=wx.TE_MULTILINE|wx.TE_BESTWRAP|wx.TE_READONLY) + self.summary_box.SetBackgroundColour((225, 225, 225)) + self.textbox_sizer.Add(summary_txt, 0, wx.EXPAND|wx.LEFT|wx.TOP, 5) + self.textbox_sizer.Add(self.summary_box, 1, wx.EXPAND|wx.TOP, 3) + + # ----------------------- + + self.layoutSizer.Add((5, 5), 0, wx.EXPAND) + self.layoutSizer.Add(self.header1_colSizer, 0, wx.EXPAND) + self.layoutSizer.Add(self.table1_colSizer, 0, wx.EXPAND) + self.layoutSizer.Add((20, 20), 0, wx.EXPAND) + self.layoutSizer.Add(self.header2_colSizer, 0, wx.EXPAND) + self.layoutSizer.Add(self.table2_colSizer, 0, wx.EXPAND) + self.layoutSizer.Add((20, 20), 0, wx.EXPAND) + self.layoutSizer.Add(self.header3_colSizer, 0, wx.EXPAND) + self.layoutSizer.Add(self.table3_colSizer, 0, wx.EXPAND) + self.layoutSizer.Add((20, 20), 0, wx.EXPAND) + self.layoutSizer.Add(self.textbox_sizer, 1, wx.EXPAND) + + self.SetSizer(self.layoutSizer) + + + + def OnTypeChange(self, evt): + + # Have it not be multiple choice + cb_id = evt.GetId() + if cb_id != self.table1_cb1.GetId(): + self.table1_cb1.SetValue(False) + if cb_id != self.table1_cb2.GetId(): + self.table1_cb2.SetValue(False) + + self.addTextSummary() + + def OnReasonChange(self, evt): + self.addTextSummary() + + def OnSSRChange(self, evt): + + # Have it not be multiple choice + cb_id = evt.GetId() + if cb_id != self.table3_cb1.GetId(): + self.table3_cb1.SetValue(False) + if cb_id != self.table3_cb2.GetId(): + self.table3_cb2.SetValue(False) + + self.addTextSummary() + + def OnCommentChange(self, evt): + self.addTextSummary() + + + def addTextSummary(self): + added_text = [] + + # Clear all the text entered prior + current_text = self.parent.manager.manager.envCondManager.stationHealthRemarksCtrl + cleared_text = re.sub(r"\@#.*?\#@", "", current_text) + full_text = cleared_text.rstrip("\n") + + # Add newline if not there + if (not full_text.endswith('\n')) and (len(full_text) > 0): + added_text.append('\n') + + # Add the type line (only one is chosen) + if self.table1_cb1.IsChecked(): + added_text.append('@# Type of Visit: ' + self.table1_title1 + ' #@\n') + elif self.table1_cb2.IsChecked(): + added_text.append('@# Type of Visit: ' + self.table1_title2 + ' #@\n') + + # Build and add reason line + reason_list = [] + if self.table2_cb1.IsChecked(): + reason_list.append(self.table2_title1) + if self.table2_cb2.IsChecked(): + reason_list.append(self.table2_title2) + if self.table2_cb3.IsChecked(): + reason_list.append(self.table2_title3) + if len(reason_list) > 0: + reason_txt = '@# Visit Reason: ' + for i, txt in enumerate(reason_list): + if i < len(reason_list)-1 and len(reason_list) > 1: + reason_txt = reason_txt + txt + ', ' + else: + reason_txt = reason_txt + txt + reason_txt = reason_txt + ' #@\n' + added_text.append(reason_txt) + + # Add the special service request line (only one is chosen) + if self.table3_cb1.IsChecked(): + added_text.append('@# Special Service Request: ' + self.table3_title1 + ' #@\n') + elif self.table3_cb2.IsChecked(): + added_text.append('@# Special Service Request: ' + self.table3_title2 + ' #@\n') + + # Add the comment + if self.comment_box.GetValue() != '': + comment_txt = self.comment_box.GetValue() + cleaned_txt = comment_txt.replace("\n", " ") + final_comment = '@# Comment: ' + cleaned_txt + ' #@\n' + added_text.append(final_comment) + + # Add all lines to full text + for txt in added_text: + full_text = full_text + txt + + # Set the text + self.summary_box.SetValue(full_text) + self.parent.manager.manager.envCondManager.stationHealthRemarksCtrl = full_text + + + +def main(): + app = wx.App() + + frame = wx.Frame(None, size=(850, 600)) + SVOPanel(wx.frame, "DEBUG", frame) + + frame.Show() + app.MainLoop() + +if __name__ == "__main__": + main() \ No newline at end of file From e560ba82706d603a44c2eeccd98f4785f672aa23 Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:20:24 -0400 Subject: [PATCH 7/9] Added SVO box and Inventory Management box --- AttachmentPanel.py | 96 +++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/AttachmentPanel.py b/AttachmentPanel.py index e279db5..0055a43 100644 --- a/AttachmentPanel.py +++ b/AttachmentPanel.py @@ -14,6 +14,7 @@ from AttachFolderBox import * from AttachPhotoBox import * from AttachOtherBox import * +from SiteVisitOptimizationBox import * import ntpath import tempfile from os import chdir @@ -68,11 +69,11 @@ def InitUI(self): self.SetSizer(self.layout) sizerList = [] - for i in range(11): + for i in range(15): sizerList.append(wx.BoxSizer(wx.HORIZONTAL)) spaceList = [] - for x in range(11): + for x in range(15): spaceList.append(wx.StaticText(self, label="")) note = wx.StaticText(self, label="The Field Visit Package (ZIP file) will include the eHSN xml and pdf, as well as the below attachments.") @@ -101,6 +102,17 @@ def InitUI(self): Txt8 = AttachTag("Other", "", self, size=self.tagSize) self.attachBox8 = AttachOtherBox(self, "*", self, size=(955, 100), style=wx.SIMPLE_BORDER) + Txt9 = AttachTag("Inventory Management", "Markdown Files (.md)", self, size = self.tagSize) + self.attachBox9 = AttachBox(self, "InventoryManagement", self, size=(955, 100), style=wx.SIMPLE_BORDER) + + self.largeSpacer = wx.StaticText(self, size=(955, 20), label="") + self.SVOHeader = wx.StaticText(self, label="Site Visit Optimization:", style=wx.ALIGN_CENTRE_VERTICAL) + font = wx.Font(12, wx.ROMAN, wx.FONTSTYLE_NORMAL, wx.BOLD, False) + self.SVOHeader.SetFont(font) + + Txt10 = AttachTag("", "", self, size=self.tagSize) + self.attachBox10 = SVOPanel(self, "*", self, size=(955, 530), style=wx.SIMPLE_BORDER) + self.zipAddr = wx.TextCtrl(self, size=self.barSize) self.zipAddr.SetValue(self.rootPath) # Hide the text field @@ -139,21 +151,35 @@ def InitUI(self): sizerList[6].Add(self.attachBox6) sizerList[7].Add(self.indent) - sizerList[7].Add(Txt7, 1, wx.EXPAND | wx.ALL, 5) - sizerList[7].Add(self.attachBox7) + sizerList[7].Add(Txt9, 1, wx.EXPAND | wx.ALL, 5) + sizerList[7].Add(self.attachBox9) sizerList[8].Add(self.indent) - sizerList[8].Add(Txt8, 1, wx.EXPAND | wx.ALL, 5) - sizerList[8].Add(self.attachBox8) + sizerList[8].Add(Txt7, 1, wx.EXPAND | wx.ALL, 5) + sizerList[8].Add(self.attachBox7) + + sizerList[9].Add(self.indent) + sizerList[9].Add(Txt8, 1, wx.EXPAND | wx.ALL, 5) + sizerList[9].Add(self.attachBox8) + + sizerList[10].Add(self.noteIndent) + sizerList[10].Add(self.largeSpacer) - sizerList[9].Add(self.zipSpace) - sizerList[9].Add(self.zipper) + sizerList[11].Add(self.noteIndent) + sizerList[11].Add(self.SVOHeader) + + sizerList[12].Add(self.indent) + sizerList[12].Add(Txt10, 1, wx.EXPAND | wx.ALL, 5) + sizerList[12].Add(self.attachBox10) + + sizerList[13].Add(self.zipSpace) + sizerList[13].Add(self.zipper) # Hidden text field - sizerList[10].Add(self.zipAddr) + sizerList[14].Add(self.zipAddr) self.layout.Add(TitlePanel, 0, wx.EXPAND | wx.ALL, 3) - for i in range(11): + for i in range(15): self.layout.Add(spaceList[i]) self.layout.Add(sizerList[i]) @@ -218,7 +244,7 @@ def Zip(self, evt, openSaveDialog=True): Tag = stnNum + "_" + date + "_FV" filePath = "c:\\temp\\eHSN\\" - boxList = [self.attachBox1, self.attachBox2, self.attachBox3, self.attachBox4, self.attachBox5, self.attachBox6, self.attachBox7, self.attachBox8] + boxList = [self.attachBox1, self.attachBox2, self.attachBox3, self.attachBox4, self.attachBox5, self.attachBox6, self.attachBox9, self.attachBox7, self.attachBox8] pathList = [] for box in boxList: pathList.append(box.returnPath()) @@ -241,6 +267,7 @@ def Zip(self, evt, openSaveDialog=True): mmtPdf = self.attachBox6.returnPath() mmtFile = self.attachBox5.returnFilePath() mmtFolder = self.attachBox5.returnFolderPath() + inventoryFiles = self.attachBox9.returnPath() SIT = self.attachBox7.returnSIT() STR = self.attachBox7.returnSTR() @@ -304,6 +331,14 @@ def Zip(self, evt, openSaveDialog=True): rename = stnNum + "_" + date + "_M" + str(count) + extension shutil.copy(mmtFile[i], dir_temp + "\\" + rename) mmtFile[i] = dir_temp + "\\" + rename + # Inventory management markdown files + for i in range(len(inventoryFiles)): + if valid(inventoryFiles[i]): + name, extension = os.path.splitext(inventoryFiles[i]) + # Do not include the count in the file name + rename = stnNum + "_" + date + "_Inventory" + extension + shutil.copy(inventoryFiles[i], dir_temp + "\\" + rename) + inventoryFiles[i] = dir_temp + "\\" + rename # Photos and drawings count = 0 for i in range(len(SIT)): @@ -424,6 +459,9 @@ def Zip(self, evt, openSaveDialog=True): for path in mmtFile: if valid(path): zipfile.write(path, Tag + "\\" + ntpath.basename(path)) + for path in inventoryFiles: + if valid(path): + zipfile.write(path, Tag + "\\" + ntpath.basename(path)) # Discharge measurement folders count = 0 @@ -570,22 +608,26 @@ def Zip(self, evt, openSaveDialog=True): if valid(path): zipfile.write(path, Tag + "\\" + ntpath.basename(path)) - # Adding the details from the Inventory Management tab - # This generates a markdown file and returns the filepath - # Running this three times deliberately as some changes are not caught in initial call - inventory_text_filepath = self.parent.inventoryManagement.printChangesOutput() - inventory_text_filepath = self.parent.inventoryManagement.printChangesOutput() - inventory_text_filepath = self.parent.inventoryManagement.printChangesOutput() - - if inventory_text_filepath != "": - - # Write the file to the zip - if valid(inventory_text_filepath): - zipfile.write(inventory_text_filepath, Tag + "\\" + stnNum + "_" + date + "_Inventory.md") - - # Delete the file once it is in the zip - if os.path.exists(inventory_text_filepath): - os.remove(inventory_text_filepath) + # Only add the inventory management file if the user has not added their own + inventory_pathname = Tag + "/" + stnNum + "_" + date + "_Inventory.md" + if not inventory_pathname in zipfile.namelist(): + + # Adding the details from the Inventory Management tab + # This generates a markdown file and returns the filepath + # Running this three times deliberately as some changes are not caught in initial call + inventory_text_filepath = self.parent.inventoryManagement.printChangesOutput() + inventory_text_filepath = self.parent.inventoryManagement.printChangesOutput() + inventory_text_filepath = self.parent.inventoryManagement.printChangesOutput() + + if inventory_text_filepath != "": + + # Write the file to the zip + if valid(inventory_text_filepath): + zipfile.write(inventory_text_filepath, Tag + "\\" + stnNum + "_" + date + "_Inventory.md") + + # Delete the file once it is in the zip + if os.path.exists(inventory_text_filepath): + os.remove(inventory_text_filepath) zipfile.close() if openSaveDialog: From 006f9ea78fdcfc33d578e0b7aebaaa151c7f12ad Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:22:26 -0400 Subject: [PATCH 8/9] Added help text and export button to Inventory tab --- InventoryManagementPanel.py | 111 +++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 9 deletions(-) diff --git a/InventoryManagementPanel.py b/InventoryManagementPanel.py index c4cc356..af51c16 100644 --- a/InventoryManagementPanel.py +++ b/InventoryManagementPanel.py @@ -465,11 +465,21 @@ def __init__(self, parent, title): Lbl5 = wx.StaticText(self, label='Device listed here not at station: ') Itm5 = wx.StaticText(self, label='Transfer to known warehouse; if unknown, set inactive with remark "location unknown".') + Lbl6 = wx.StaticText(self, label='Communication device types: ') + # Text field needed to allow for copy/paste + Itm6 = wx.TextCtrl(self, size=(-1, 82), value='CAMERA - IP OR PDT\n\ +EXTERNAL MODEM\n\ +IMAGE VELOCIMETRY CAMERA SYSTEM\n\ +NETWORK - CELL IP\n\ +SATELLITE - GOES - HDR', style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_NO_VSCROLL) + Itm6.SetBackgroundColour((225, 225, 225)) + Lbl1.SetFont(fontTitle) Lbl2.SetFont(fontTitle) Lbl3.SetFont(fontTitle) Lbl4.SetFont(fontTitle) Lbl5.SetFont(fontTitle) + Lbl6.SetFont(fontTitle) # Wrapping text Itm1.Wrap(240) @@ -494,6 +504,8 @@ def __init__(self, parent, title): popupSizer.Add(Itm4, 0, wx.ALL | wx.EXPAND, 5) popupSizer.Add(Lbl5, 0, wx.ALL | wx.EXPAND, 5) popupSizer.Add(Itm5, 0, wx.ALL | wx.EXPAND, 5) + popupSizer.Add(Lbl6, 0, wx.ALL | wx.EXPAND, 5) + popupSizer.Add(Itm6, 0, wx.ALL | wx.EXPAND, 5) buttonsSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) popupSizer.Add(buttonsSizer, 0, wx.ALL | wx.EXPAND, 5) @@ -676,6 +688,7 @@ def __init__(self, mode, dir, *args, **kwargs): self.inventoryManageLbl = "Inventory Management" self.helpBtnLbl = "Help" self.resetBtnLbl = "Reset Tables" + self.exportBtnLbl = "Export Changes" # Labels for table self.stationIDLbl = "Station ID" @@ -788,10 +801,14 @@ def InitUI(self): #self.resetTabBtn.Bind(wx.EVT_BUTTON, self.printChangesOutputTesting) self.resetTabBtn.Bind(wx.EVT_BUTTON, self.OnReset) + self.exportBtn = wx.Button(self.titlePanel, label=self.exportBtnLbl) + self.exportBtn.Bind(wx.EVT_BUTTON, self.OnExport) + titleSizer = wx.BoxSizer(wx.HORIZONTAL) titleSizer.Add(self.inventoryManageTxt, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 130) titleSizer.Add(self.helpBtn, 0, wx.EXPAND|wx.ALL|wx.RIGHT, 5) titleSizer.Add(self.resetTabBtn, 0, wx.EXPAND|wx.ALL|wx.RIGHT, 5) + titleSizer.Add(self.exportBtn, 0, wx.EXPAND|wx.ALL|wx.RIGHT, 5) self.titlePanel.SetSizer(titleSizer) @@ -1663,17 +1680,23 @@ def InitUI(self): def OnAddPressTop(self, e): if self.mode == "DEBUG": print("add") + + if self.top_table_dataframe.empty: + dlg = wx.MessageDialog(self, "No Station selected above, please choose a station on the front page", 'Error', wx.OK) + res = dlg.ShowModal() + if res == wx.ID_OK: + dlg.Destroy() + return self.AddEntryTop(True) # Add an empty blank row to the dataframe for the top table # Set the station ID and set the new index - if not self.top_table_dataframe.empty: - self.top_table_dataframe.loc[self.top_table_dataframe.shape[0]] = [""] * self.top_table_dataframe.shape[1] - self.top_table_dataframe.at[self.top_table_dataframe.shape[0]-1, 'Station ID'] = self.topStation - self.top_table_dataframe.at[self.top_table_dataframe.shape[0]-1, 'dataset_index_marker'] = str(self.top_table_dataframe.shape[0]-1) + '_new' - if self.mode == "DEBUG": - print(self.top_table_dataframe) + self.top_table_dataframe.loc[self.top_table_dataframe.shape[0]] = [""] * self.top_table_dataframe.shape[1] + self.top_table_dataframe.at[self.top_table_dataframe.shape[0]-1, 'Station ID'] = self.topStation + self.top_table_dataframe.at[self.top_table_dataframe.shape[0]-1, 'dataset_index_marker'] = str(self.top_table_dataframe.shape[0]-1) + '_new' + if self.mode == "DEBUG": + print(self.top_table_dataframe) # Update and refresh self.invenManTopSizerV.Layout() @@ -2739,7 +2762,15 @@ def OnTextType(self, event): def getCurrentTime(self): timestamp = dt.now() return timestamp.strftime('%Y-%m-%d %H:%M:%S') - + + def getFormattedTime(self): + timestamp = dt.now() + return timestamp.strftime('%Y%m%d') + + def valid(self, path): + if path != None and path != "" and not path.isspace(): + return True + return False def textTooltip(self, event): textCtr=event.GetEventObject() @@ -2859,8 +2890,15 @@ def CategorySetPopups(self, evt): for index, ckbox in enumerate(self.selectTopSizer.GetChildren()): if ckbox.GetWindow().IsChecked(): - # Get the saved index and the cateogry type + # Get the saved index dataset_index = self.top_table_dataframe.at[index, 'dataset_index_marker'] + + # If the user is setting communication details for a new row (_new) + # then update the row in the dataframe based on the current values in the table + if 'new' in str(dataset_index): + self.updateDataframeRow(index) + + # Get the saved cateogry type device_type = self.top_table_dataframe.at[index, 'Category'] # If a device has been transferred to a warehouse, then it shouldn't be modifiable @@ -2881,7 +2919,7 @@ def CategorySetPopups(self, evt): return elif device_type not in self.commTypes.keys(): - dlg = wx.MessageDialog(self, "Device is not a communication device", 'Error', wx.OK) + dlg = wx.MessageDialog(self, "Device is not a communication device, see Help for communication device types", 'Error', wx.OK) res = dlg.ShowModal() if res == wx.ID_OK: dlg.Destroy() @@ -3022,6 +3060,12 @@ def OnReset(self, evt): + # Export the saved state + def OnExport(self, evt): + self.exportChangesOutput("") + + + # Set the values of status change and deployment status when checkbox is clicked # For the bottom table def OnDataCheckBottom(self, event): @@ -4176,6 +4220,55 @@ def printChangesOutput(self): else: return "" + + + + def exportChangesOutput(self, savedFilePath): + + # This generates a markdown file and returns the filepath + # Running this three times deliberately as some changes are not caught in initial call + inventory_text_filepath = self.printChangesOutput() + inventory_text_filepath = self.printChangesOutput() + inventory_text_filepath = self.printChangesOutput() + + # If the filepath is not empty (nothing to save) and is a valid filepath + if inventory_text_filepath != "": + if self.valid(inventory_text_filepath): + + default_filename = self.topStation + "_" + self.getFormattedTime() + "_Inventory.md" + + if savedFilePath == "": + # Save file dialog box + FileSaveDialog = wx.FileDialog(self, "Inventory Management File save location", os.path.dirname(os.path.realpath(sys.argv[0])), default_filename, 'Markdown File (*.md)|*.md', + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT | wx.FD_CHANGE_DIR) + + # If the user cancels the choice, destroy the dialog and remove the saved file + if FileSaveDialog.ShowModal() == wx.ID_CANCEL: + FileSaveDialog.Destroy() + if os.path.exists(inventory_text_filepath): + os.remove(inventory_text_filepath) + return + + # Get the saved filename and filepath + savedFilePath = FileSaveDialog.GetPath() + + FileSaveDialog.Destroy() + + else: + savedFilePath = os.path.join(savedFilePath, default_filename) + + # If a filename of the same name exists at the location, then delete this first + if os.path.exists(savedFilePath): + os.remove(savedFilePath) + + # Transfer the file from the prior location to the new location + os.rename(inventory_text_filepath, savedFilePath) + + # Remove the old file + if os.path.exists(inventory_text_filepath): + os.remove(inventory_text_filepath) + + return ######################################################################## From 5c4038d8725e2cc146c034b17dfdab156d98bcf7 Mon Sep 17 00:00:00 2001 From: Adam Mohiuddin Date: Fri, 26 Jun 2026 17:25:10 -0400 Subject: [PATCH 9/9] Including Inventory markdowns in xml save options --- ElectronicFieldNotesGUI.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ElectronicFieldNotesGUI.py b/ElectronicFieldNotesGUI.py index dbef7da..955bb4c 100644 --- a/ElectronicFieldNotesGUI.py +++ b/ElectronicFieldNotesGUI.py @@ -1008,6 +1008,11 @@ def OnFileSave(self, e): self.manager.ExportAsXML(self.fullname, None) print("File saved") + + # Export the markdown file from inventory management + savedFilePath = os.path.dirname(os.path.abspath(self.fullname)) + self.inventoryManagement.exportChangesOutput(savedFilePath) + return True return False @@ -1050,6 +1055,11 @@ def OnFileSaveAs(self, evt): self.fullname = path # self.ResetSaveAsIni(path) print("XML file saved") + + # Export the markdown file from inventory management + savedFilePath = os.path.dirname(os.path.abspath(self.fullname)) + self.inventoryManagement.exportChangesOutput(savedFilePath) + return True