diff --git a/ODMTools.py b/ODMTools.py index ecbea16..9f5382a 100755 --- a/ODMTools.py +++ b/ODMTools.py @@ -20,7 +20,8 @@ import pyodbc import pymysql -import psycopg2 +#import psycopg2 + tool = LoggerTool() logger = tool.setupLogger('main', 'odmtools.log', 'a', logging.INFO) diff --git a/README.md b/README.md index e4d4af9..4b02394 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ If you want to try ODM Tools, we have created a couple of [testing databases](ht ##ODM Compatibility -ODM Tools Python is currenlty fully compatible with ODM Version 1.1.1. We are working on ODM2 compatibility, and should have it worked out this summer. +ODM Tools Python is currenlty fully compatible with ODM Version 1.1.1. We are working on ODM2 compatibility, and should have it worked out this winter. ##Releases @@ -19,10 +19,10 @@ To make running ODMTools easier we have included installers. Please select the o ####Windows Recommended Release: -+ [Window v1.2.5-beta](https://github.com/ODM2/ODMToolsPython/releases/download/v1.2.5_beta/ODMTools_1.2.5_Beta_Win_Installer.exe) ++ [Window v1.2.8-beta](https://github.com/ODM2/ODMToolsPython/releases/download/v1.2.8_beta/ODMTools_1.2.8_Beta_Win_Installer.exe) ####Mac -+ [Mac v1.2.5-beta](https://github.com/ODM2/ODMToolsPython/releases/download/v1.2.5_beta/ODMTools_v1.2.5-beta_Mac_installer.pkg.zip) ++ [Mac v1.2.8-beta](https://github.com/ODM2/ODMToolsPython/releases/download/v1.2.8_beta/ODMTools_v1.2.8-beta_Mac_installer.pkg.zip) ####Linux + *Please run from source* @@ -30,15 +30,17 @@ Recommended Release: Running From Source ------------------- -+ [Matplotlib-1.4.3](https://github.com/matplotlib/matplotlib/releases/tag/v1.4.3) -+ [Pandas-0.16.0](https://github.com/pydata/pandas/releases) ++ [Matplotlib](https://github.com/matplotlib/matplotlib/releases/) ++ [Pandas](https://github.com/pydata/pandas/releases) + [Pip](http://docs.python-guide.org/en/latest/starting/install/win.html) + [PyMySQL](https://github.com/petehunt/PyMySQL/) -+ [Pyodbc-3.0.7](https://code.google.com/p/pyodbc/downloads/detail?name=pyodbc-3.0.7.win-amd64-py2.7.exe) ++ [Pyodbc](https://code.google.com/p/pyodbc/downloads/) + [Python-2.7.9 x64/x32](http://www.python.org/download/releases/2.7.9/) (Python 3 version isn't available) -+ [Psycopg2-2.4.6](http://initd.org/psycopg/docs/install.html) -+ [Sqlalchemy-1.0.0] (http://pypi.python.org/pypi/SQLAlchemy/1.0.0) -+ [wxpython-3.0.0](http://www.wxpython.org/download.php) ++ [Psycopg2](http://initd.org/psycopg/docs/install.html) ++ [Sqlalchemy] (http://pypi.python.org/pypi/SQLAlchemy/) ++ [wxpython](http://www.wxpython.org/download.php) ++ [Numpy](http://www.scipy.org/scipylib/download.html) ++ [Scipy](https://www.scipy.org/scipylib/download.html) Install the following software/libraries. It is recommended to create an environment using 'Conda'. @@ -46,7 +48,7 @@ It is recommended to create an environment using 'Conda'. Example environment creation: -+ conda create -n odmtools python=2.7.8 wxpython matplotlib pandas pyodbc sqlalchemy pip psycopg2 ++ conda create -n odmtools python=2.7.8 wxpython matplotlib pandas numpy scipy pyodbc sqlalchemy pip psycopg2 + pip install pymysql diff --git a/doc/developer_docs/source/conf.py b/doc/developer_docs/source/conf.py index 99709e5..33205a8 100644 --- a/doc/developer_docs/source/conf.py +++ b/doc/developer_docs/source/conf.py @@ -55,9 +55,9 @@ # built documents. # # The short X.Y version. -version = 'beta-v1.2.0' +version = '1.2.8' # The full version, including alpha/beta/rc tags. -release = 'beta-v1.2.0' +release = 'beta-v1.2.8' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/wxFormBuilder/frmFillGaps.fbp b/doc/wxFormBuilder/frmFillGaps.fbp new file mode 100644 index 0000000..ddb8126 --- /dev/null +++ b/doc/wxFormBuilder/frmFillGaps.fbp @@ -0,0 +1,756 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + clsGapFill + 1000 + none + 0 + frmGapFill + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + decl + + + + 0 + wxID_ANY + 315,217 + 315,217 + dlgFill + + 315,217 + wxDEFAULT_DIALOG_STYLE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bsForm + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + This function fills any gaps less than the gap duration with a no-data value at the fill frequency + + 0 + + + 0 + + 1 + lblInstructions + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + 300 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 3 + wxBOTH + + + 0 + + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Gap Duration: + + 0 + + + 0 + + 1 + lblGap + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + txtGap + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "second" "minute" "hour" "days" "week" "month" "day" "year" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + cbGap + 1 + + + protected + 1 + + Resizable + 1 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Fill Frequency: + + 0 + + + 0 + + 1 + lblFill + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + txtFill + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "second" "minute" "hour" "day" "week" "month" "year" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + cbFill + 1 + + + protected + 1 + + Resizable + 1 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer1 + protected + + OnCancelBtn + + + + onOKBtn + + + + + + + + diff --git a/odmtools/common/icons/icons.py b/odmtools/common/icons/icons.py index a464088..fcae478 100644 --- a/odmtools/common/icons/icons.py +++ b/odmtools/common/icons/icons.py @@ -3,6 +3,7 @@ # from wx.lib.embeddedimage import PyEmbeddedImage + _3d_graph = PyEmbeddedImage( "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGw0lEQVR42sXXe1BU1xkA8O8+" "dveyPHZl7/JYApEQBDEaJQ9nSBEEY1JHbR5a09ExaceQ1kg61aZD9A8ztpnptJPQaGKIcaJt" @@ -1719,3 +1720,154 @@ "hNvlcPw3vJ+CK10wus5FVgFzXZAG+AOAKNxEU+FoqQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAx" "MC0wMi0xMVQxNTowMTo0MC0wNjowMD+ZiIwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDQtMDEt" "MThUMTQ6MjU6MTQtMDY6MDDHhSvaAAAAAElFTkSuQmCC") + +magicwand =PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAGXRFWHRTb2Z0d2FyZQBBZG9i" + "ZSBJbWFnZVJlYWR5ccllPAAADZBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tl" + "dCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1l" + "dGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQu" + "Mi4yLWMwNjMgNTMuMzUyNjI0LCAyMDA4LzA3LzMwLTE4OjA1OjQxICAgICAgICAiPgogPHJk" + "ZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50" + "YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9" + "Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6eG1wUmlnaHRz" + "PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIKICAgIHhtbG5zOnBob3Rv" + "c2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOklw" + "dGM0eG1wQ29yZT0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcENvcmUvMS4wL3htbG5z" + "LyIKICAgeG1wUmlnaHRzOldlYlN0YXRlbWVudD0iaHR0cDovL2Jsb2cuYWRkaWN0ZWR0b2Nv" + "ZmZlZS5kZSIKICAgcGhvdG9zaG9wOkF1dGhvcnNQb3NpdGlvbj0iIj4KICAgPGRjOnJpZ2h0" + "cz4KICAgIDxyZGY6QWx0PgogICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+wqkg" + "ICAgICAgICAgICYjeEE7IDIwMDkgYnkgT2xpdmVyIFR3YXJkb3dza2k8L3JkZjpsaT4KICAg" + "IDwvcmRmOkFsdD4KICAgPC9kYzpyaWdodHM+CiAgIDxkYzpjcmVhdG9yPgogICAgPHJkZjpT" + "ZXE+CiAgICAgPHJkZjpsaT5PbGl2ZXIgVHdhcmRvd3NraTwvcmRmOmxpPgogICAgPC9yZGY6" + "U2VxPgogICA8L2RjOmNyZWF0b3I+CiAgIDxkYzp0aXRsZT4KICAgIDxyZGY6QWx0PgogICAg" + "IDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCIvPgogICAgPC9yZGY6QWx0PgogICA8L2Rj" + "OnRpdGxlPgogICA8eG1wUmlnaHRzOlVzYWdlVGVybXM+CiAgICA8cmRmOkFsdD4KICAgICA8" + "cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiLz4KICAgIDwvcmRmOkFsdD4KICAgPC94bXBS" + "aWdodHM6VXNhZ2VUZXJtcz4KICAgPElwdGM0eG1wQ29yZTpDcmVhdG9yQ29udGFjdEluZm8K" + "ICAgIElwdGM0eG1wQ29yZTpDaUFkckV4dGFkcj0iIgogICAgSXB0YzR4bXBDb3JlOkNpQWRy" + "Q2l0eT0iIgogICAgSXB0YzR4bXBDb3JlOkNpQWRyUmVnaW9uPSIiCiAgICBJcHRjNHhtcENv" + "cmU6Q2lBZHJQY29kZT0iIgogICAgSXB0YzR4bXBDb3JlOkNpQWRyQ3RyeT0iIgogICAgSXB0" + "YzR4bXBDb3JlOkNpVGVsV29yaz0iIgogICAgSXB0YzR4bXBDb3JlOkNpRW1haWxXb3JrPSIi" + "CiAgICBJcHRjNHhtcENvcmU6Q2lVcmxXb3JrPSIiLz4KICA8L3JkZjpEZXNjcmlwdGlvbj4K" + "IDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAK" + "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + "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" + "AOxm99VU2O0FAAAAAElFTkSuQmCC") + diff --git a/odmtools/controller/frmGapFill.py b/odmtools/controller/frmGapFill.py new file mode 100644 index 0000000..ce34a3b --- /dev/null +++ b/odmtools/controller/frmGapFill.py @@ -0,0 +1,26 @@ +"""Subclass of dlgFill, which is generated by wxFormBuilder.""" + +from odmtools.view import clsGapFill + + +# Implementing dlgFill +class frmGapFill(clsGapFill.dlgFill): + def __init__(self, parent, record_service): + self.record_service = record_service + clsGapFill.dlgFill.__init__(self, parent) + + # Handlers for dlgFill events. + def onOKBtn(self, event): + #TODO add validation + gapvalue= self.txtGap.Value + gaptime = self.cbGap.Value + fillvalue = self.txtFill.Value + filltime= self.cbFill.Value + + self.record_service.fill_gap(gap=[gapvalue, gaptime], fill=[fillvalue, filltime]) + self.Close() + + def OnCancelBtn(self, event): + + self.Close() + self.Destroy() diff --git a/odmtools/controller/frmLinearDrift.py b/odmtools/controller/frmLinearDrift.py index d10a967..86cb131 100644 --- a/odmtools/controller/frmLinearDrift.py +++ b/odmtools/controller/frmLinearDrift.py @@ -13,7 +13,10 @@ def OnBtnOKButton(self, event): Perform Drift Correction based on given input. Catch errors if the user enters something invalid """ try: - self._record_service.drift_correction(float(self.txtFinalGapValue.GetValue())) + result = self._record_service.drift_correction(float(self.txtFinalGapValue.GetValue())) + if not result: + dial = wx.MessageDialog( None, "Linear drift can only be performed on one continuous data selection. \nPlease modify your selection and try again.", "Bad Input", wx.OK) + dial.ShowModal() except Exception as e: dial = wx.MessageDialog(None, "Unable to convert value to float %s" % e, "Bad Input", wx.OK | wx.ICON_ERROR) diff --git a/odmtools/controller/frmSeriesSelector.py b/odmtools/controller/frmSeriesSelector.py index 4563190..bc090b3 100755 --- a/odmtools/controller/frmSeriesSelector.py +++ b/odmtools/controller/frmSeriesSelector.py @@ -459,13 +459,13 @@ def setFilter(self, site_code='', var_code='', advfilter=''): :return: """ if site_code and var_code: - self.siteFilter = TextSearch(self.tblSeries, columns=self.tblSeries.columns[3:4],text=site_code) - self.variableFilter = TextSearch(self.tblSeries, columns=self.tblSeries.columns[6:7],text=var_code) + self.siteFilter = TextSearch(self.tblSeries, columns=self.tblSeries.columns[2:6],text=site_code) + self.variableFilter = TextSearch(self.tblSeries, columns=self.tblSeries.columns[3:8],text=var_code) self.tblSeries.SetFilter(Chain(self.siteFilter, self.variableFilter)) elif site_code: - self.tblSeries.SetFilter(TextSearch(self.tblSeries, columns=self.tblSeries.columns[3:4], text=site_code)) + self.tblSeries.SetFilter(TextSearch(self.tblSeries, columns=self.tblSeries.columns[2:6], text=site_code)) elif var_code: - self.tblSeries.SetFilter(TextSearch(self.tblSeries, columns=self.tblSeries.columns[6:7], text=var_code)) + self.tblSeries.SetFilter(TextSearch(self.tblSeries, columns=self.tblSeries.columns[3:8], text=var_code)) elif advfilter: self.tblSeries.SetFilter(advfilter) else: @@ -490,6 +490,14 @@ def onReadyToPlot(self, event): except Exception as e : object = self.tblSeries.GetSelectedObject() + self.drawPlot(object) + logger.debug("refreshing...") + self.Refresh() + + logger.debug("Finish Plotting") + + def drawPlot(self, object): + if not self.tblSeries.IsChecked(object): Publisher.sendMessage("removePlot", seriesID=object.id) Publisher.sendMessage("updateCursor", deselectedObject=object) @@ -499,12 +507,6 @@ def onReadyToPlot(self, event): self.pnlPlot.addPlot(self.memDB, object.id) Publisher.sendMessage("updateCursor", selectedObject=object) - logger.debug("refreshing...") - self.Refresh() - - logger.debug("Finish Plotting") - - #from meliae import scanner #scanner.dump_all_objects("plot_plotting.dat") @@ -527,6 +529,17 @@ def getSelectedObject(self, event): #print "Updating Cursor", editingObject.id Publisher.sendMessage("updateCursor", selectedObject=editingObject) + def onBtnClear(self, event): + logger.debug("Clearing all selected objects from Series Catalog") + chcklist = self.tblSeries.GetCheckedObjects() + + self.tblSeries.RefreshObject(self.tblSeries.editingObject) + for c in chcklist: + self.tblSeries.SetCheckState(c, False) + Publisher.sendMessage("removeMultPlot", seriesIDs= chcklist) + Publisher.sendMessage("updateCursor", deselectedObject=object) + #self.drawPlot(c) + self.Refresh() def onReadyToEdit(self): """Choose a series to edit from the series selector diff --git a/odmtools/controller/logicEditTools.py b/odmtools/controller/logicEditTools.py index 25b7ae2..f83e14c 100644 --- a/odmtools/controller/logicEditTools.py +++ b/odmtools/controller/logicEditTools.py @@ -50,6 +50,19 @@ def filter_date(self, endDate, startDate): else: return "Cannot filter: %s" % (self._edit_error) + + def fill_gap(self, gap, fill): + self._edit_service.fill_gap(gap , fill) + + self.refresh_edit() + if self._record: + self._script("edit_service.fill_gap(gap = %s, fill= %s)\n" % (gap, fill), 'black') + Publisher.sendMessage("scroll") + + + + + def data_gaps(self, value, time_period): self._edit_service.data_gaps(value, time_period) self.refresh_selection() @@ -57,7 +70,6 @@ def data_gaps(self, value, time_period): self._script("edit_service.data_gaps(%s, '%s')\n" % (value, time_period), 'black') Publisher.sendMessage("scroll") - def value_change_threshold(self, value, operator): self._edit_service.change_value_threshold(value, operator) self.refresh_selection() @@ -184,10 +196,11 @@ def interpolate(self): def drift_correction(self, gap_width): ret = self._edit_service.drift_correction(gap_width) - self.refresh_edit() - if self._record: - self._script("edit_service.drift_correction(%s)\n" % (gap_width), 'black') - Publisher.sendMessage("scroll") + if ret: + self.refresh_edit() + if self._record: + self._script("edit_service.drift_correction(%s)\n" % (gap_width), 'black') + Publisher.sendMessage("scroll") return ret def reset_filter(self): diff --git a/odmtools/gui/mnuRibbon.py b/odmtools/gui/mnuRibbon.py index 360f6cc..0c872c8 100755 --- a/odmtools/gui/mnuRibbon.py +++ b/odmtools/gui/mnuRibbon.py @@ -14,18 +14,20 @@ from frmFlagValues import frmFlagValues from odmtools.controller.frmLinearDrift import frmLinearDrift from odmtools.controller.frmAbout import frmAbout +from odmtools.controller.frmGapFill import frmGapFill import wizSave +from odmtools.common.icons import magicwand from odmtools.common.icons import * import pandas as pd - # # Enable logging import logging + # from odmtools.common.logger import LoggerTool # # tool = LoggerTool() # logger = tool.setupLogger(__name__, __name__ + '.log', 'w', logging.DEBUG) -logger =logging.getLogger('main') +logger = logging.getLogger('main') [wxID_PANEL1, wxID_RIBBONPLOTTIMESERIES, wxID_RIBBONPLOTPROB, wxID_RIBBONPLOTHIST, wxID_RIBBONPLOTBOX, wxID_RIBBONPLOTSUMMARY, wxID_RIBBONPLOTTSTYPE, wxID_RIBBONPLOTTSCOLOR, wxID_RIBBONPLOTTSLEGEND, wxID_RIBBONPLOTBOXTYPE, @@ -36,9 +38,11 @@ wxID_RIBBONEDITSCRIPTSAVE, wxID_RIBBONVIEWPLOT, wxID_RIBBONVIEWTABLE, wxID_RIBBONVIEWSERIES, wxID_RIBBONVIEWCONSOLE, wxID_RIBBONVIEWSCRIPT, wxID_RIBBONPLOTBLANKBTN, wxID_FileMenu, wxID_STARTDPDATE, wxID_ENDDPDATE, wxID_FRAME1SPINCTRL1, wxID_RIBBONEDITFILTER, wxID_RIBBONEDITRECORD, wxID_RIBBONEDITLINFILTER, wxID_RIBBONPLOTDATEAPPLY, - wxID_RIBBONEDITRESETFILTER, wxID_RIBBONRECORDNEW, wxID_RIBBONRECORDOPEN, wxID_RIBBONRECORDSAVE] = [wx.NewId() for - _init_ctrls in - range(46)] + wxID_RIBBONEDITRESETFILTER, wxID_RIBBONRECORDNEW, wxID_RIBBONRECORDOPEN, wxID_RIBBONRECORDSAVE, wxID_GAPFILL] = [ + wx.NewId() for + _init_ctrls in + range(47)] + ## ################################# ## Build Menu and Toolbar @@ -47,17 +51,16 @@ class mnuRibbon(RB.RibbonBar): def _init_ctrls(self, prnt): RB.RibbonBar.__init__(self, name='ribbon', parent=prnt, id=wxID_PANEL1) - #self.SetArtProvider(RB.RibbonMSWArtProvider()) + # self.SetArtProvider(RB.RibbonMSWArtProvider()) self.SetArtProvider(RB.RibbonAUIArtProvider()) self.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) fileMenu = RB.RibbonPage(self, wxID_FileMenu, "File") - #----PlotMenu------------- + # ----PlotMenu------------- home = RB.RibbonPage(self, wx.ID_ANY, "Plot") - - #------Plot Type --------------------------------------------------------------------------- + # ------Plot Type --------------------------------------------------------------------------- plot_panel = RB.RibbonPanel(home, wx.ID_ANY, "Plots", wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, RB.RIBBON_PANEL_NO_AUTO_MINIMISE) @@ -68,8 +71,7 @@ def _init_ctrls(self, prnt): self.plots_bar.AddSimpleButton(wxID_RIBBONPLOTBOX, "Box/Whisker", box_whisker.GetBitmap(), "") self.plots_bar.AddSimpleButton(wxID_RIBBONPLOTSUMMARY, "Summary", summary.GetBitmap(), "") - - #-- PLOT OPTIONS----------------------------------------------------------------------------- + # -- PLOT OPTIONS----------------------------------------------------------------------------- PlotOptions_panel = RB.RibbonPanel(home, wx.ID_ANY, "Plot Options", wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, RB.RIBBON_PANEL_NO_AUTO_MINIMISE) self.PlotsOptions_bar = RB.RibbonButtonBar(PlotOptions_panel, wx.ID_ANY) @@ -84,7 +86,7 @@ def _init_ctrls(self, prnt): self.PlotsOptions_bar.EnableButton(wxID_RIBBONPLOTBLANKBTN, False) self.spnBins = wx.SpinCtrl(id=wxID_FRAME1SPINCTRL1, initial=50, max=100, min=1, name='spnBins', - parent=self.PlotsOptions_bar, pos=wx.Point(85, 7), #without color button + parent=self.PlotsOptions_bar, pos=wx.Point(85, 7), # without color button size=wx.Size(44, 25), style=wx.SP_ARROW_KEYS) self.spnBins.Enabled = False @@ -107,14 +109,11 @@ def _init_ctrls(self, prnt): self.dpStartDate = wx.DatePickerCtrl(id=wxID_STARTDPDATE, name=u'dpStartDate', parent=self.dateTime_buttonbar, pos=wx.Point(5, 8), size=wx.Size(120, 24), style=wx.DP_DROPDOWN) - #self.dpStartDate.SetValue(wx.DateTimeFromDMY(16, 1, 2008, 0, 0, 0)) - #self.dpStartDate.SetLabel(repr(wx.DateTimeFromDMY(16, 1, 2008, 0, 0, 0))) + self.dpStartDate.SetToolTipString(u'Start Date') self.dpEndDate = wx.DatePickerCtrl(id=wxID_ENDDPDATE, name=u'dpEndDate', parent=self.dateTime_buttonbar, pos=wx.Point(5, 40), size=wx.Size(120, 24), style=wx.DP_DROPDOWN) - #self.dpEndDate.SetValue(wx.DateTimeFromDMY(01, 04, 2008, 0, 0, 0)) - #self.dpEndDate.SetLabel(repr(wx.DateTimeFromDMY(1, 04, 2008, 0, 0, 0))) self.dpEndDate.SetToolTipString(u'End Date') self.dateTime_buttonbar.AddSimpleButton(wxID_RIBBONPLOTDATEAPPLY, "Apply", date_setting.GetBitmap(), "") @@ -127,7 +126,7 @@ def _init_ctrls(self, prnt): self.dpEndDate.Enabled = False self.dpStartDate.Enabled = False - #------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- editPage = RB.RibbonPage(self, wx.ID_ANY, "Edit") main_panel = RB.RibbonPanel(editPage, wx.ID_ANY, "Main", wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, @@ -135,7 +134,7 @@ def _init_ctrls(self, prnt): self.main_bar = RB.RibbonButtonBar(main_panel) self.editbutton = self.main_bar.AddSimpleButton(wxID_RIBBONEDITSERIES, "Edit Series", bitmap=edit.GetBitmap(), help_string="", - kind=0x4) #kind sets the button to be a True or False + kind=0x4) # kind sets the button to be a True or False self.main_bar.AddSimpleButton(wxID_RIBBONEDITRESTORE, "Restore Series", restore.GetBitmap(), "") self.main_bar.AddSimpleButton(wxID_RIBBONEDITSAVE, "Save", save_data.GetBitmap(), "") @@ -143,7 +142,7 @@ def _init_ctrls(self, prnt): self.main_bar.EnableButton(wxID_RIBBONEDITRESTORE, False) self.main_bar.EnableButton(wxID_RIBBONEDITSAVE, False) - #------------------------------------------------------------------------------ + # ------------------------------------------------------------------------------ edit_panel = RB.RibbonPanel(editPage, wx.ID_ANY, "Edit Functions", wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, RB.RIBBON_PANEL_NO_AUTO_MINIMISE) self.edit_bar = RB.RibbonButtonBar(edit_panel) @@ -155,16 +154,17 @@ def _init_ctrls(self, prnt): self.edit_bar.AddSimpleButton(wxID_RIBBONEDITFLAG, "Flag", flag.GetBitmap(), "") self.edit_bar.AddSimpleButton(wxID_RIBBONEDITADDPOINT, "Add Point", add.GetBitmap(), "") self.edit_bar.AddSimpleButton(wxID_RIBBONEDITDELPOINT, "Delete Point", delete.GetBitmap(), "") - #self.edit_bar.AddSimpleButton(wxID_RIBBONEDITRECORD, "Record", bitmap=record.GetBitmap(), help_string="",kind=0x4) + self.edit_bar.AddSimpleButton(wxID_GAPFILL, "Fill Gap", magicwand.GetBitmap(), "") self.edit_bar.EnableButton(wxID_RIBBONEDITFILTER, False) self.edit_bar.EnableButton(wxID_RIBBONEDITCHGVALUE, False) + self.edit_bar.EnableButton(wxID_RIBBONEDITRESETFILTER, False) self.edit_bar.EnableButton(wxID_RIBBONEDITINTEROPOLATE, False) self.edit_bar.EnableButton(wxID_RIBBONEDITLINFILTER, False) self.edit_bar.EnableButton(wxID_RIBBONEDITFLAG, False) self.edit_bar.EnableButton(wxID_RIBBONEDITADDPOINT, False) self.edit_bar.EnableButton(wxID_RIBBONEDITDELPOINT, False) - #self.edit_bar.EnableButton(wxID_RIBBONEDITRECORD, False) + self.edit_bar.EnableButton(wxID_GAPFILL, False) self.record_panel = RB.RibbonPanel(editPage, wx.ID_ANY, "Recording Options", wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, RB.RIBBON_PANEL_NO_AUTO_MINIMISE) @@ -173,14 +173,12 @@ def _init_ctrls(self, prnt): self.record_bar.AddSimpleButton(wxID_RIBBONRECORDNEW, "New Script", newscript.GetBitmap(), "") self.record_bar.AddSimpleButton(wxID_RIBBONRECORDOPEN, "Open Script", openscript.GetBitmap(), "") self.record_bar.AddSimpleButton(wxID_RIBBONRECORDSAVE, "Save Script", savescript.GetBitmap(), "") - #self.record_panel.Hide() self.record_bar.EnableButton(wxID_RIBBONRECORDNEW, False) self.record_bar.EnableButton(wxID_RIBBONRECORDOPEN, False) self.record_bar.EnableButton(wxID_RIBBONRECORDSAVE, False) - - #------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- viewPage = RB.RibbonPage(self, wx.ID_ANY, "View") @@ -194,7 +192,7 @@ def _init_ctrls(self, prnt): view_bar.AddSimpleButton(wxID_RIBBONVIEWSCRIPT, "PythonScript", script.GetBitmap(), "") self.scriptPanel = RB.RibbonPanel(viewPage, wx.ID_ANY, "Script Options", wx.NullBitmap, wx.DefaultPosition, - wx.DefaultSize, RB.RIBBON_PANEL_NO_AUTO_MINIMISE) + wx.DefaultSize, RB.RIBBON_PANEL_NO_AUTO_MINIMISE) self.scriptBar = RB.RibbonButtonBar(self.scriptPanel) self.scriptBar.AddSimpleButton(wxID_RIBBONRECORDNEW, "New Script", newscript.GetBitmap(), "") @@ -250,6 +248,7 @@ def bindEvents(self): self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onEditFlag, id=wxID_RIBBONEDITFLAG) self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onEditAddPoint, id=wxID_RIBBONEDITADDPOINT) self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onEditDelPoint, id=wxID_RIBBONEDITDELPOINT) + self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onGapFill, id=wxID_GAPFILL) self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onRecordNew, id=wxID_RIBBONRECORDNEW) self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onRecordOpen, id=wxID_RIBBONRECORDOPEN) @@ -268,14 +267,14 @@ def initPubSub(self): Publisher.subscribe(self.resetDateRange, "resetdate") Publisher.subscribe(self.onToggleEdit, "toggleEdit") Publisher.subscribe(self.setEdit, "setEdit") - #Publisher.subscribe(self.updateSeriesCurrentDateTime, "updateSeriesCurrentDateTime") + # Publisher.subscribe(self.updateSeriesCurrentDateTime, "updateSeriesCurrentDateTime") def onFileMenu(self, event): if not self.GetActivePage() == 0: self.CurrPage = self.GetActivePage() if self.GetActivePage() == 0: - #reset activepage to original + # reset activepage to original self.SetActivePageByIndex(self.CurrPage) menu = wx.Menu() self.Bind(wx.EVT_MENU, self.onChangeDBConfig, menu.Append(wx.ID_ANY, "Change DB Configuration")) @@ -308,8 +307,6 @@ def onDateChanged(self, event): event.Skip() def resetDateRange(self, startDate, endDate, currStart=None, currEnd=None): - #maxStart = wx.DateTimeFromDMY(startDate.day, startDate.month - 1, startDate.year) - #maxEnd = wx.DateTimeFromDMY(endDate.day, endDate.month - 1, endDate.year) maxStart = _pydate2wxdate(startDate) maxEnd = _pydate2wxdate(endDate) @@ -329,8 +326,6 @@ def onResetFilter(self, event): recordService = self.parent.getRecordService() recordService.reset_filter() - - def isEmptySelection(self, dataframe): if isinstance(dataframe, pd.DataFrame): if dataframe.empty: @@ -350,7 +345,7 @@ def onLineDrift(self, event): except EmptySelection: val = wx.MessageBox("You have no points selected, " "Please select points before applying a Linear Drift Correction", - 'Linear Drift Correction', wx.OK | wx.ICON_WARNING) + 'Linear Drift Correction', wx.OK | wx.ICON_WARNING) return lin_drift = frmLinearDrift(self.parent, self.parent.getRecordService()) @@ -379,7 +374,7 @@ def onRecordOpen(self, event): def onRecordSave(self, event): script = self.parent.txtPythonScript script.OnSaveAs(event) - #pass + # pass # ################################### # Save Wizard @@ -421,21 +416,22 @@ def onEditChangeValue(self, event): # ################################### def onEditInterpolate(self, event): - dataframe = self.parent.getRecordService().get_filtered_points() try: self.isEmptySelection(dataframe) except EmptySelection: val = wx.MessageBox("You have no points selected, Please select points before interpolating.", - 'Interpolation', wx.OK | wx.ICON_EXCLAMATION, parent=self.parent) + 'Interpolation', wx.OK | wx.ICON_EXCLAMATION, parent=self.parent) return - val = wx.MessageBox("You have chosen to interpolate the %s selected points.\nDo you want to continue?" % len(dataframe), - 'Interpolation', wx.YES_NO | wx.ICON_QUESTION | wx.CENTRE, parent=self.parent) - if val == 2: #wx.ID_YES: + val = wx.MessageBox( + "You have chosen to interpolate the %s selected points.\nDo you want to continue?" % len(dataframe), + 'Interpolation', wx.YES_NO | wx.ICON_QUESTION | wx.CENTRE, parent=self.parent) + if val == 2: # wx.ID_YES: self.parent.getRecordService().interpolate() event.Skip() + # ################################### # Flag # ################################### @@ -467,6 +463,18 @@ def onEditAddPoint(self, event): event.Skip() + # ################################### + # Gap fill + # ################################### + def onGapFill(self, event): + + gap_fill = frmGapFill(self.parent, self.parent.getRecordService()) + + if gap_fill.Show() == wx.OK: + gap_fill.Destroy() + + event.Skip() + # ################################### # Delete Point # ################################### @@ -476,12 +484,13 @@ def onEditDelPoint(self, event): self.isEmptySelection(dataframe) except EmptySelection: wx.MessageBox("There are no points to delete", - 'Delete Points', wx.OK | wx.ICON_WARNING, parent=self.parent) + 'Delete Points', wx.OK | wx.ICON_WARNING, parent=self.parent) return - val = wx.MessageBox("You have chosen to delete the %s selected points.\nDo you want to continue?" % len(dataframe), - 'Deleting Points', wx.YES_NO | wx.ICON_QUESTION, parent=self.parent) - if val == 2: #wx.ID_YES: + val = wx.MessageBox( + "You have chosen to delete the %s selected points.\nDo you want to continue?" % len(dataframe), + 'Deleting Points', wx.YES_NO | wx.ICON_QUESTION, parent=self.parent) + if val == 2: # wx.ID_YES: self.parent.getRecordService().delete_points() event.Skip() @@ -509,16 +518,9 @@ def onEditSeries(self, event=None): if not self.isEdit: self.onToggleEdit(False) - #self.parent.addEdit() else: Publisher.sendMessage(("stopEdit"), event=event) - #self.parent.stopEdit() - - # # Start editing right when a series is being edited - #self.parent.getRecordService().toggle_record() - #record_service.toggle_record() - #logger.debug("Recording? %s" % record_service._record) event.Skip() @@ -674,7 +676,7 @@ def enableButtons(self, plot, isActive): self.spnBins.Enabled = False self.enableDateSelection(True) - #Summary + # Summary elif plot == 4: self.PlotsOptions_bar.EnableButton(wxID_RIBBONPLOTTSTYPE, False) self.PlotsOptions_bar.EnableButton(wxID_RIBBONPLOTTSLEGEND, False) @@ -682,7 +684,6 @@ def enableButtons(self, plot, isActive): self.spnBins.Enabled = False self.enableDateSelection(True) - # TODO change states when points are selected rather than all at once def toggleEditButtons(self, state): @@ -691,7 +692,6 @@ def toggleEditButtons(self, state): self.main_bar.EnableButton(wxID_RIBBONEDITSAVE, state) self.edit_bar.EnableButton(wxID_RIBBONEDITFILTER, state) - # when points are selected self.edit_bar.EnableButton(wxID_RIBBONEDITLINFILTER, state) self.edit_bar.EnableButton(wxID_RIBBONEDITRESETFILTER, state) @@ -700,18 +700,18 @@ def toggleEditButtons(self, state): self.edit_bar.EnableButton(wxID_RIBBONEDITFLAG, state) self.edit_bar.EnableButton(wxID_RIBBONEDITADDPOINT, state) self.edit_bar.EnableButton(wxID_RIBBONEDITDELPOINT, state) - #self.edit_bar.EnableButton(wxID_RIBBONEDITRECORD, state) + self.edit_bar.EnableButton(wxID_GAPFILL, state) self.record_bar.EnableButton(wxID_RIBBONRECORDOPEN, state) self.record_bar.EnableButton(wxID_RIBBONRECORDNEW, state) self.record_bar.EnableButton(wxID_RIBBONRECORDSAVE, state) - self.enableDateSelection(not state) + def _pydate2wxdate(date): - import datetime - assert isinstance(date, (datetime.datetime, datetime.date)) - tt = date.timetuple() - dmy = (tt[2], tt[1]-1, tt[0]) - return wx.DateTimeFromDMY(*dmy) + import datetime + assert isinstance(date, (datetime.datetime, datetime.date)) + tt = date.timetuple() + dmy = (tt[2], tt[1] - 1, tt[0]) + return wx.DateTimeFromDMY(*dmy) diff --git a/odmtools/gui/plotProbability.py b/odmtools/gui/plotProbability.py index 6e23761..7cf1551 100644 --- a/odmtools/gui/plotProbability.py +++ b/odmtools/gui/plotProbability.py @@ -136,7 +136,9 @@ def updatePlot(self): #self.prob.append( #prop = oneSeries.Probability.plot(column="DataValue", ax=self.plots) #todo FutureWarning: order is deprecated, use sort_values(...) + #xValues = oneSeries.Probability.xAxis.order().values xValues = oneSeries.Probability.xAxis.order().values + # yValues = oneSeries.Probability.yAxis.order().values yValues = oneSeries.Probability.yAxis.order().values ax = self.plots.plot(xValues, yValues, 'bs', color=oneSeries.color, diff --git a/odmtools/gui/plotTimeSeries.py b/odmtools/gui/plotTimeSeries.py index d5026b1..9591598 100755 --- a/odmtools/gui/plotTimeSeries.py +++ b/odmtools/gui/plotTimeSeries.py @@ -495,7 +495,7 @@ def updateCursor(self, selectedObject=None, deselectedObject=None): elif len(self.axislist) < 1: currentAxis = None self.configureCursor(currentAxis=currentAxis) - elif deselectedObject: + else:#elif deselectedObject: """ Deactivate Cursor. This happens when the plot is deselected """ diff --git a/odmtools/gui/pnlPlot.py b/odmtools/gui/pnlPlot.py index 16d10af..40b7163 100644 --- a/odmtools/gui/pnlPlot.py +++ b/odmtools/gui/pnlPlot.py @@ -81,6 +81,7 @@ def initPubSub(self): Publisher.subscribe(self.onShowLegend, "onShowLegend") Publisher.subscribe(self.onNumBins, "onNumBins") Publisher.subscribe(self.onRemovePlot, "removePlot") + Publisher.subscribe(self.onRemovePlots, "removeMultPlot") Publisher.subscribe(self.onChangeSelection, "changePlotSelection") Publisher.subscribe(self.onUpdateValues, "updateValues") Publisher.subscribe(self.clear, "clearPlot") @@ -154,6 +155,11 @@ def onRemovePlot(self, seriesID): self._seriesPlotInfo.update(seriesID, False) self.redrawPlots() + def onRemovePlots(self, seriesIDs): + for series in seriesIDs: + self._seriesPlotInfo.update(series.id, False) + self.redrawPlots() + def redrawPlots(self): logger.debug("Plot Summary") diff --git a/odmtools/meta/data.py b/odmtools/meta/data.py index 8ef5943..8a317c7 100644 --- a/odmtools/meta/data.py +++ b/odmtools/meta/data.py @@ -1,5 +1,5 @@ app_name = "ODMTools" -version = "1.2.5_Beta" +version = "1.2.8_Beta" copyright = "Copyright (c) 2013 - 2015, Utah State University. All rights reserved." description = "ODMTools is a python application for managing observational data using the Observations Data Model. " \ "ODMTools allows you to query, visualize, and edit data stored in an Observations Data Model (ODM) database." \ diff --git a/odmtools/odmdata/memory_database.py b/odmtools/odmdata/memory_database.py index 2ae1763..34e5f09 100644 --- a/odmtools/odmdata/memory_database.py +++ b/odmtools/odmdata/memory_database.py @@ -21,6 +21,7 @@ def __init__(self, taskserver=None): self.df = None # Series_Service handles remote database self.series_service = None + # Memory_service handles in memory database self.mem_service = SeriesService("sqlite:///:memory:") # TODO clean up closing of program @@ -201,7 +202,7 @@ def initEditValues(self, seriesID): """ if not self.editLoaded: logger.debug("Load series from db") - + self.series = self.series_service.get_series_by_id(seriesID) self.df = self.series_service.get_values_by_series(seriesID) self.editLoaded = True diff --git a/odmtools/odmservices/edit_service.py b/odmtools/odmservices/edit_service.py index d3448fd..b142432 100644 --- a/odmtools/odmservices/edit_service.py +++ b/odmtools/odmservices/edit_service.py @@ -17,6 +17,21 @@ # logger = tool.setupLogger(__name__, __name__ + '.log', 'w', logging.DEBUG) logger =logging.getLogger('main') +class time(object): + time_units = { + 'second': 's', + 'minute': 'm', + 'hour': 'h', + 'day': 'D', + 'week': 'W', + 'month': 'M', + 'year': 'Y' + } + + def __init__(self, value, time_period): + self.value = value + self.time_period = time_period + class EditService(): # Mutual exclusion: cursor, or connection_string def __init__(self, series_id, connection=None, connection_string="", debug=False): @@ -86,6 +101,7 @@ def _test_filter_previous(self): return df + def datetime2dataframe(self, datetime_list): """ Converts datetime_list to a pandas Dataframe @@ -141,19 +157,51 @@ def filter_date(self, before, after): if before and after: self.filtered_dataframe = df[(df.index < before) & (df.index > after)] + def fill_gap(self, gap, fill): + + df = self.memDB.getDataValuesDF() + gaps= self.find_gaps(df, gap[0], gap[1]) + points = [] + series= self.memDB.series + timegap = np.timedelta64(fill[0], self.time_units[fill[1]]) + + #if gaps is not of type dataframe- put it in a dataframe + #if not isinstance(gaps, pd.DataFrame + for g in gaps.iterrows(): + row = g[1] + e = row.datetime + s = row.dateprev + + #prime the loop + s = s + timegap + # for each gap time period in the larger gap ( until datetime = prev value) + while s < e: + utc_offset = (series.begin_date_time-series.begin_date_time_utc).total_seconds()/3600 + points.append((-9999, None, s, utc_offset, s, None, None, u'nc', None, None, series.site_id, series.variable_id, series.method_id, series.source_id, series.quality_control_level_id)) + #('-9999', None, DATE, series.begin_date_time_utc, UTCDATE, None, None, u'nc', None, None, + # series.site_id, series.variable_id, series.method_id, series.source_id, + # series.quality_control_level_id + + s = s + timegap + #print points + self.add_points(points) + + time_units = { + 'second': 's', + 'minute': 'm', + 'hour': 'h', + 'day': 'D', + 'week': 'W', + 'month': 'M', + 'year': 'Y' + } + # Data Gaps - def data_gaps(self, value, time_period): - df = self._test_filter_previous() - time_units = { - 'second': 's', - 'minute': 'm', - 'hour': 'h', - 'day': 'D', - 'week': 'W', - 'month': 'M', - 'year': 'Y' - } + + def find_gaps(self, df, value, time_period): + + # make a copy of the dataframe in order to modify it to be in the form we need to determine data gaps copy_df = df @@ -165,19 +213,29 @@ def data_gaps(self, value, time_period): value = int(value) # create a bool column indicating which rows meet condition - filtered_results = copy_df['datetime'].diff() >= np.timedelta64(value, time_units[time_period]) + filtered_results = copy_df['datetime'].diff() > np.timedelta64(value, self.time_units[time_period]) # filter on rows that passed previous condition - copy_df = copy_df[filtered_results] + return copy_df[filtered_results] + + + + def data_gaps(self, value, time_period): + df = self._test_filter_previous() + copy_df = self.find_gaps(df, value, time_period) + print (copy_df) # merge values and remove duplicates. this hack allows for both values to be marked when selecting data gaps newdf = pd.concat([copy_df['datetime'], copy_df['dateprev']], join='inner') - self.filtered_dataframe = df[df.index.isin(newdf.drop_duplicates().dropna())] # clean up del copy_df - del filtered_results - del newdf + + + self.filtered_dataframe= df[df.index.isin(newdf.drop_duplicates().dropna())] + + + def change_value_threshold(self, value, operator): @@ -310,7 +368,6 @@ def change_value(self, value, operator): def add_points(self, points): # todo: add the ability to send in multiple datetimes to a single 'point' - self.memDB.addPoints(points) self._populate_series() @@ -347,19 +404,22 @@ def interpolate(self): self.memDB.update(update_list) - self._populate_series() self.filtered_dataframe = self._series_points_df[self._series_points_df.index.isin(ids)] + def drift_correction(self, gap_width): + + if self.isOneGroup(): tmp_filter_list =self.get_filtered_points() startdate =tmp_filter_list.index[0] x_l = (tmp_filter_list.index[-1]-startdate).total_seconds() - + #nodv= self.memDB.series_service.get_variable_by_id(self.memDB.df["VariableID"][0]) + nodv = self.memDB.series.variable.no_data_value # y_n = y_0 + G(x_i / x_l) - f = lambda row : row["DataValue"]+(gap_width * ((row.name-startdate).total_seconds() / x_l)) + f = lambda row : row["DataValue"]+(gap_width * ((row.name-startdate).total_seconds() / x_l)) if row["DataValue"] != nodv else row["DataValue"] tmp_filter_list["DataValue"]=tmp_filter_list.apply(f, axis = 1) update_list = [{"value": row["DataValue"], "id":index} for index, row in tmp_filter_list.iterrows()] diff --git a/odmtools/view/clsExisting.py b/odmtools/view/clsExisting.py index 2be9204..477f29b 100644 --- a/odmtools/view/clsExisting.py +++ b/odmtools/view/clsExisting.py @@ -7,9 +7,6 @@ # self.myOlv = FastObjectListView(self, -1, style=wx.LC_REPORT) ''' - - - # -*- coding: utf-8 -*- ########################################################################### @@ -48,7 +45,8 @@ def __init__(self, parent): wx.DefaultSize, 0) bSizer2.Add(self.rbOverwrite, 0, wx.ALL, 5) - self.rbAppend = wx.RadioButton(self.m_panel1, wx.ID_ANY, u"Append To Series", wx.DefaultPosition, + self.rbAppend = wx.RadioButton(self.m_panel1, wx.ID_ANY, + u"Append to Series (adds values to the end of a series)", wx.DefaultPosition, wx.DefaultSize, 0) bSizer2.Add(self.rbAppend, 0, wx.ALL, 5) @@ -60,14 +58,15 @@ def __init__(self, parent): bSizer3 = wx.BoxSizer(wx.VERTICAL) self.lblOverlap = wx.StaticText(self.m_panel1, wx.ID_ANY, u"If Data Overlaps:", wx.DefaultPosition, - wx.DefaultSize, 0) + wx.DefaultSize, 0) self.lblOverlap.Wrap(-1) self.lblOverlap.Enable(False) bSizer3.Add(self.lblOverlap, 0, wx.ALL, 5) - #Group 2 - self.rbNew = wx.RadioButton(self.m_panel1, wx.ID_ANY, u"Keep New", wx.DefaultPosition, wx.DefaultSize, style=wx.RB_GROUP) + # Group 2 + self.rbNew = wx.RadioButton(self.m_panel1, wx.ID_ANY, u"Keep New", wx.DefaultPosition, wx.DefaultSize, + style=wx.RB_GROUP) self.rbNew.Enable(False) bSizer3.Add(self.rbNew, 0, wx.ALL, 5) @@ -99,17 +98,12 @@ def __init__(self, parent): def __del__(self): pass - # Virtual event handlers, overide them in your derived class def OnOLVItemSelected(self, event): event.Skip() - def onOverwrite(self, event): event.Skip() - def onAppend(self, event): event.Skip() - - diff --git a/odmtools/view/clsGapFill.py b/odmtools/view/clsGapFill.py new file mode 100644 index 0000000..95c1208 --- /dev/null +++ b/odmtools/view/clsGapFill.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +########################################################################### +## Python code generated with wxFormBuilder (version Jun 17 2015) +## http://www.wxformbuilder.org/ +## +## PLEASE DO "NOT" EDIT THIS FILE! +########################################################################### + +import wx +import wx.xrc + + +########################################################################### +## Class dlgFill +########################################################################### + +class dlgFill(wx.Dialog): + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition, + size=wx.Size(315, 217), style=wx.DEFAULT_DIALOG_STYLE) + + self.SetSizeHintsSz(wx.Size(315, 217), wx.Size(315, 217)) + + bsForm = wx.BoxSizer(wx.VERTICAL) + + self.lblInstructions = wx.StaticText(self, wx.ID_ANY, + u"This function fills any gaps greater than the gap duration with a no-data value at the fill frequency.", + wx.DefaultPosition, wx.DefaultSize, 0) + self.lblInstructions.Wrap(300) + bsForm.Add(self.lblInstructions, 0, wx.ALL, 5) + + fgSizer1 = wx.FlexGridSizer(0, 3, 0, 0) + fgSizer1.SetFlexibleDirection(wx.BOTH) + fgSizer1.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) + + self.lblGap = wx.StaticText(self, wx.ID_ANY, u"Gap Duration:", wx.DefaultPosition, wx.DefaultSize, 0) + self.lblGap.Wrap(-1) + fgSizer1.Add(self.lblGap, 0, wx.ALL, 5) + + self.txtGap = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + fgSizer1.Add(self.txtGap, 0, wx.ALL, 5) + + cbGapChoices = [u"second", u"minute", u"hour", u"days", u"week", u"month", u"day", u"year"] + self.cbGap = wx.ComboBox(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, cbGapChoices, 0) + self.cbGap.SetSelection(1) + fgSizer1.Add(self.cbGap, 1, wx.ALL, 5) + + self.lblFill = wx.StaticText(self, wx.ID_ANY, u"Fill Frequency:", wx.DefaultPosition, wx.DefaultSize, 0) + self.lblFill.Wrap(-1) + fgSizer1.Add(self.lblFill, 0, wx.ALL, 5) + + self.txtFill = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + fgSizer1.Add(self.txtFill, 0, wx.ALL, 5) + + cbFillChoices = [u"second", u"minute", u"hour", u"day", u"week", u"month", u"year"] + self.cbFill = wx.ComboBox(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, cbFillChoices, 0) + self.cbFill.SetSelection(1) + fgSizer1.Add(self.cbFill, 1, wx.ALL, 5) + + bsForm.Add(fgSizer1, 1, wx.EXPAND, 5) + + m_sdbSizer1 = wx.StdDialogButtonSizer() + self.m_sdbSizer1OK = wx.Button(self, wx.ID_OK) + m_sdbSizer1.AddButton(self.m_sdbSizer1OK) + self.m_sdbSizer1Cancel = wx.Button(self, wx.ID_CANCEL) + m_sdbSizer1.AddButton(self.m_sdbSizer1Cancel) + m_sdbSizer1.Realize(); + + bsForm.Add(m_sdbSizer1, 1, wx.EXPAND, 5) + + self.SetSizer(bsForm) + self.Layout() + + self.Centre(wx.BOTH) + + # Connect Events + self.m_sdbSizer1Cancel.Bind(wx.EVT_BUTTON, self.OnCancelBtn) + self.m_sdbSizer1OK.Bind(wx.EVT_BUTTON, self.onOKBtn) + + def __del__(self): + pass + + # Virtual event handlers, overide them in your derived class + def OnCancelBtn(self, event): + event.Skip() + + def onOKBtn(self, event): + event.Skip() + + diff --git a/odmtools/view/clsSeriesSelector.py b/odmtools/view/clsSeriesSelector.py index fa4a00f..d47f689 100755 --- a/odmtools/view/clsSeriesSelector.py +++ b/odmtools/view/clsSeriesSelector.py @@ -9,8 +9,8 @@ wxID_PNLSERIESSELECTORCHECKSITE, wxID_PNLSERIESSELECTORCHECKVARIABLE, wxID_PNLSERIESSELECTORLBLSITE, wxID_PNLSERIESSELECTORLBLVARIABLE, wxID_PNLSERIESSELECTORtableSeries, wxID_PNLSERIESSELECTORPANEL1, wxID_PNLSERIESSELECTORPANEL2, wxID_PNLSIMPLE, wxID_PNLRADIO, wxID_FRAME1RBADVANCED, wxID_FRAME1RBALL, - wxID_FRAME1RBSIMPLE, wxID_FRAME1SPLITTER, wxID_PNLSPLITTER, wxID_PNLSERIESSELECTORtableSeriesTest, ] = [ - wx.NewId() for _init_ctrls in range(18)] + wxID_FRAME1RBSIMPLE, wxID_FRAME1SPLITTER, wxID_PNLSPLITTER, wxID_PNLSERIESSELECTORtableSeriesTest,wxID_CLEAR, ] = [ + wx.NewId() for _init_ctrls in range(19)] class ClsSeriesSelector(wx.Panel): @@ -29,13 +29,20 @@ def __init__(self, parent, dbservice): self.export_service = self.service_manager.get_export_service() self.selectedIndex = 0 self.isEditing = False + + ## Radio Sizer - ## def _init_coll_boxSizer5_Items(self, parent): - ## # generated method, don't edit - ## - ## parent.AddWindow(self.rbAll, 0, border=1, flag=wx.ALL) - ## parent.AddWindow(self.rbSimple, 0, border=1, flag=wx.ALL) - ## parent.AddWindow(self.rbAdvanced, 0, border=1, flag=wx.ALL) + def _init_coll_boxSizer5_Items(self, parent): + # generated method, don't edit + + pnlSpacer= wx.Panel(id=0, name='pnlSpacer', parent=self.pnlRadio, pos=wx.Point(3, 3), + size=wx.Size(0, 25), style=wx.TAB_TRAVERSAL) + parent.AddWindow(self.rbAll, 0, border=1, flag=wx.ALL) + parent.AddWindow(self.rbSimple, 0, border=1, flag=wx.ALL) + parent.AddWindow(self.rbAdvanced, 0, border=1, flag=wx.ALL) + parent.AddWindow(pnlSpacer, 100, border=1, flag=wx.EXPAND) + + parent.AddWindow(self.btnClear, 0, border=1, flag=wx.ALL) ## Splitter Sizer @@ -74,27 +81,26 @@ def _init_coll_boxSizer6_Items(self, parent): def _init_sizers(self): # generated method, don't edit - self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL) - self.boxSizer2 = wx.BoxSizer(orient=wx.HORIZONTAL) - self.boxSizer3 = wx.BoxSizer(orient=wx.VERTICAL) - self.boxSizer4 = wx.BoxSizer(orient=wx.HORIZONTAL) - ## self.boxSizer5 = wx.BoxSizer(orient=wx.HORIZONTAL) - self.boxSizer6 = wx.BoxSizer(orient=wx.VERTICAL) - - self._init_coll_boxSizer1_Items(self.boxSizer1) - self._init_coll_boxSizer2_Items(self.boxSizer2) - self._init_coll_boxSizer3_Items(self.boxSizer3) - self._init_coll_boxSizer4_Items(self.boxSizer4) - ## self._init_coll_boxSizer5_Items(self.boxSizer5) - self._init_coll_boxSizer6_Items(self.boxSizer6) - - self.SetSizer(self.boxSizer1) - ## self.pnlRadio.SetSizer(self.boxSizer5) - ## self.pnlSite.SetSizer(self.boxSizer4) - ## self.pnlVar.SetSizer(self.boxSizer2) - self.cpnlSimple.SetSizer(self.boxSizer6) - self.pnlData.SetSizer(self.boxSizer3) - # self.pnlRadio.SetSizer(self.boxSizer5) + boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL) + boxSizer2 = wx.BoxSizer(orient=wx.HORIZONTAL) + boxSizer3 = wx.BoxSizer(orient=wx.VERTICAL) + boxSizer4 = wx.BoxSizer(orient=wx.HORIZONTAL) + boxSizer5 = wx.BoxSizer(orient=wx.HORIZONTAL) + boxSizer6 = wx.BoxSizer(orient=wx.VERTICAL) + + self._init_coll_boxSizer1_Items(boxSizer1) + self._init_coll_boxSizer2_Items(boxSizer2) + self._init_coll_boxSizer3_Items(boxSizer3) + self._init_coll_boxSizer4_Items(boxSizer4) + self._init_coll_boxSizer5_Items(boxSizer5) + self._init_coll_boxSizer6_Items(boxSizer6) + + self.SetSizer(boxSizer1) + self.pnlRadio.SetSizer(boxSizer5) + self.pnlSite.SetSizer(boxSizer4) + self.pnlVar.SetSizer(boxSizer2) + self.cpnlSimple.SetSizer(boxSizer6) + self.pnlData.SetSizer(boxSizer3) def _init_ctrls(self): # generated method, don't edit @@ -105,16 +111,19 @@ def _init_ctrls(self): ## Radio panel self.pnlRadio = wx.Panel(id=wxID_PNLRADIO, name='pnlRadio', parent=self, pos=wx.Point(3, 3), - size=wx.Size(919, 20), style=wx.TAB_TRAVERSAL) + size=wx.Size(100000000, 25), style=wx.TAB_TRAVERSAL) self.rbAll = wx.RadioButton(id=wxID_FRAME1RBALL, label=u'All', name=u'rbAll', parent=self.pnlRadio, pos=wx.Point(0, 0), size=wx.Size(81, 20), style=0) self.rbSimple = wx.RadioButton(id=wxID_FRAME1RBSIMPLE, label=u'Simple Filter', name=u'rbSimple', - parent=self.pnlRadio, pos=wx.Point(81, 0), size=wx.Size(112, 20), style=0) + parent=self.pnlRadio, pos=wx.Point(0, 0), size=wx.Size(112, 20), style=0) self.rbAdvanced = wx.RadioButton(id=wxID_FRAME1RBADVANCED, label=u'Advanced Filter', name=u'rbAdvanced', - parent=self.pnlRadio, pos=wx.Point(193, 0), size=wx.Size(104, 20), style=0) + parent=self.pnlRadio, pos=wx.Point(0, 0), size=wx.Size(104, 20), style=0) + + self.btnClear = wx.Button(id=wxID_CLEAR, label="Clear Plot", name=u'btnClear', parent=self.pnlRadio, + pos=wx.Point(0, 0), size=wx.Size(80, 20), style=0) self.rbAll.SetValue(True) @@ -138,7 +147,7 @@ def _init_ctrls(self): self.lblSite = wx.StaticText(id=wxID_PNLSERIESSELECTORLBLSITE, label=u'Site', name=u'lblSite', parent=self.pnlSite, pos=wx.Point(30, 0), size=wx.Size(60, 21), style=0) - self.lblSite.SetToolTipString(u'staticText1') + self.lblSite.SetToolTipString(u'') self.cbSites.SetLabel(u'') #self.checkSite.SetValue(False) @@ -155,7 +164,7 @@ def _init_ctrls(self): self.cbVariables = wx.ComboBox(choices=[], id=wxID_PNLSERIESSELECTORCBVARIABLES, name=u'cbVariables', parent=self.pnlVar, pos=wx.Point(100, 0), size=wx.Size(700, 25), style=wx.CB_READONLY, - value='comboBox4') + value='') self.cbVariables.SetLabel(u'') self.cbVariables.Enable(False) @@ -163,6 +172,7 @@ def _init_ctrls(self): self.rbAll.Bind(wx.EVT_RADIOBUTTON, self.onRbAllRadiobutton, id=wxID_FRAME1RBALL) self.rbSimple.Bind(wx.EVT_RADIOBUTTON, self.onRbSimpleRadiobutton, id=wxID_FRAME1RBSIMPLE) self.rbAdvanced.Bind(wx.EVT_RADIOBUTTON, self.onRbAdvancedRadiobutton, id=wxID_FRAME1RBADVANCED) + self.btnClear.Bind(wx.EVT_BUTTON, self.onBtnClear, id=wxID_CLEAR) self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.onPaneChanged, self.cpnlSimple) self.checkSite.Bind(wx.EVT_CHECKBOX, self.onCheck, id=wxID_PNLSERIESSELECTORCHECKSITE) @@ -193,10 +203,13 @@ def _init_ctrls(self): ## Virtual Event Handlers def onReadyToPlot(self, event): - event.Skip() + pass def onReadyToEdit(self, event): - event.Skip() + pass + + def onBtnClear(self, event): + pass def stopEdit(self): pass diff --git a/setup/Mac/ODMTools.packproj b/setup/Mac/ODMTools.packproj index 4093601..462f175 100644 --- a/setup/Mac/ODMTools.packproj +++ b/setup/Mac/ODMTools.packproj @@ -567,9 +567,9 @@ IFPkgDescriptionTitle - ODMTools_v1.2.5 + ODMTools_v1.2.8 IFPkgDescriptionVersion - 1.2.5 Beta + 1.2.8 Beta @@ -586,7 +586,7 @@ CFBundleName ODMTools CFBundleShortVersionString - 1.2.5 + 1.2.8 Options @@ -621,7 +621,7 @@ IFPkgFlagPackageSelection 0 Name - ODMTools_v1.2.5-beta_Mac_installer + ODMTools_v1.2.8-beta_Mac_installer Status 1 Type diff --git a/setup/Windows/odmtools_console.iss b/setup/Windows/odmtools_console.iss index 702606a..659ac95 100644 --- a/setup/Windows/odmtools_console.iss +++ b/setup/Windows/odmtools_console.iss @@ -3,8 +3,8 @@ #define MyAppName "ODMTools" -#define MyAppVersion "1.2.5_Beta" -#define MyAppExeLongName "ODMTools_1.2.5_Beta_win32_x86_64_console.exe" +#define MyAppVersion "1.2.8_Beta" +#define MyAppExeLongName "ODMTools_1.2.8_Beta_win32_x86_64_console.exe" #define MyAppPublisher "ODM2" #define MyAppURL "https://github.com/ODM2/ODMToolsPython" #define MyAppExeName "ODMTools.exe" diff --git a/setup/Windows/odmtools_no_console.iss b/setup/Windows/odmtools_no_console.iss index b068ddf..d429c56 100644 --- a/setup/Windows/odmtools_no_console.iss +++ b/setup/Windows/odmtools_no_console.iss @@ -4,8 +4,8 @@ #define MyAppName "ODMTools" -#define MyAppExeLongName "ODMTools_1.2.5_Beta_win32_x86_64.exe" -#define MyAppVersion "1.2.5_Beta" +#define MyAppExeLongName "ODMTools_1.2.8_Beta_win32_x86_64.exe" +#define MyAppVersion "1.2.8_Beta" #define MyAppPublisher "ODM2" #define MyAppURL "https://github.com/ODM2/ODMToolsPython" diff --git a/setup/Windows/odmtools_setup.iss b/setup/Windows/odmtools_setup.iss index baa273b..05bf730 100644 --- a/setup/Windows/odmtools_setup.iss +++ b/setup/Windows/odmtools_setup.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "ODMTools" -#define MyAppVersion "1.2.5_Beta" +#define MyAppVersion "1.2.8_Beta" #define MyAppPublisher "ODM2" #define MyAppURL "https://github.com/ODM2/ODMToolsPython" #define MyAppExeName "ODMTools.exe" diff --git a/setup/Windows/odmtools_setup_build.iss b/setup/Windows/odmtools_setup_build.iss new file mode 100644 index 0000000..cd72382 --- /dev/null +++ b/setup/Windows/odmtools_setup_build.iss @@ -0,0 +1,46 @@ +; Script generated by the Inno Setup Script Wizard. +; http://cyrille.rossant.net/create-a-standalone-windows-installer-for-your-python-application/ +; http://www.jrsoftware.org/isinfo.php +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +#define MyAppName "ODMTools" +#define OrgName "UCHIC" +#define MyAppVersion "v1.2.8-beta" +#define MyAppURL "https://github.com/ODM2/ODMToolsPython" + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. +; Do not use the same AppId value in installers for other applications. +; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) +AppId={{B0DD13A0-C200-4562-B94B-2BEEB1782E3E} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +;AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher = iUtah +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={pf}\{#MyAppName} +DefaultGroupName={#MyAppName} +OutputBaseFilename={#MyAppName}_{#MyAppVersion}_Installer +SetupIconFile={#MyAppDir}\odmtools\common\icons\ODMTools.ico +Compression=lzma +SolidCompression=yes + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: "D:\DEV\Releases\ODMTools\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{group}\{#MyAppName}"; Filename: "{app}\run.bat"; WorkingDir: "{app}"; +Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" +Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Dirs] +Name: "{localappdata}\{#OrgName}\{#MyAppName}" diff --git a/setup/Windows/test.iss b/setup/Windows/test.iss index b5b669d..bd60868 100644 --- a/setup/Windows/test.iss +++ b/setup/Windows/test.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "ODMTools" -#define MyAppVersion "1.2.0" +#define MyAppVersion "1.2.8" #define MyAppPublisher "My Company, Inc." #define MyAppURL "http://www.example.com/" #define MyAppExeName "ODMTools_console.exe" @@ -33,8 +33,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] -Source: "C:\Users\jmeline_\Documents\GitHub\ODMToolsPython\setup\Dist\ODMTools_1.2.0_Beta_win32_x86_64_console\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "C:\Users\jmeline_\Documents\GitHub\ODMToolsPython\setup\Dist\ODMTools_1.2.0_Beta_win32_x86_64_console\ODMTools_1.2.0_Beta_win32_x86_64_console.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "D:\DEV\ODMToolsPython\setup\Dist\ODMTools_1.2.8_Beta_win32_x86_64_console\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "D:\DEV\ODMToolsPython\setup\Dist\ODMTools_1.2.8_Beta_win32_x86_64_console\ODMTools_1.2.8_Beta_win32_x86_64_console.exe"; DestDir: "{app}"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] diff --git a/setup/version.txt b/setup/version.txt index 2ded985..3f61d78 100644 --- a/setup/version.txt +++ b/setup/version.txt @@ -31,10 +31,10 @@ VSVersionInfo( u'040904b0', [StringStruct(u'CompanyName', u'Utah Water Research Laboratory'), StringStruct(u'ProductName', u'ODMTools'), - StringStruct(u'ProductVersion', u'1.2.5 beta'), + StringStruct(u'ProductVersion', u'1.2.8 beta'), StringStruct(u'InternalName', u'ODMTools'), StringStruct(u'OriginalFilename', u'ODMTools.exe'), - StringStruct(u'FileVersion', u'1.2.5 beta'), + StringStruct(u'FileVersion', u'1.2.8 beta'), StringStruct(u'FileDescription', u'ODMTools is a python application for managing observational data using the Observations Data Model. ODMTools allows you to query, visualize, and edit data stored in an Observations Data Model (ODM) database.ODMTools was originally developed as part of the CUAHSI Hydrologic Information System.'), StringStruct(u'LegalCopyright', u'Copyright (c) 2013, Utah State University. All rights reserved.'), StringStruct(u'LegalTrademarks', u'N/A'),])