From 77185d785f081104ff7c6cdabc58786bad9ec318 Mon Sep 17 00:00:00 2001 From: jrocha Date: Fri, 25 Jan 2019 14:28:30 -0700 Subject: [PATCH 01/15] Add configparser module and config file to allow for easier user customization and configuration read/writes. Could be used to replace the programTime.txt text file that only stores a user defined ProgramTime. --- Resources/application.py | 12 ++++++++++-- Resources/tempFiles/pyforecast.cfg | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 Resources/tempFiles/pyforecast.cfg diff --git a/Resources/application.py b/Resources/application.py index eed7e2f..eaf7126 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -50,6 +50,7 @@ from scipy import stats, integrate from dateutil import parser import multiprocessing as mp +import configparser """ @@ -108,7 +109,7 @@ def purgeOldFiles(self): This function removes any old csv files from the tempFiles directory """ for file_ in os.listdir('Resources/tempFiles'): - if file_ == 'programTime.txt' or file_ == '__pycache__': + if file_ == 'programTime.txt' or file_ == '__pycache__' or 'pyforecast.cfg': continue filename = os.path.abspath('Resources/tempFiles/' + file_) os.remove(filename) @@ -119,7 +120,14 @@ def setDate(self, date): """ with open('Resources/tempFiles/programTime.txt','w') as writeFile: writeFile.write(date) - + + # This function sets the date in the software. It stores the time in a config file called 'Resources/tempFiles/pyforecast.cfg' + config = configparser.ConfigParser() + config.read('Resources/tempFiles/pyforecast.cfg') + config['DEFAULT']['ProgramTime'] = date + with open('Resources/tempFiles/pyforecast.cfg', 'w') as configfile: + config.write(configfile) + return diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg new file mode 100644 index 0000000..f804e1b --- /dev/null +++ b/Resources/tempFiles/pyforecast.cfg @@ -0,0 +1,5 @@ +[DEFAULT] +programtime = 2019-01-25 +savedirectory = Resources +savefilename = pyforecast + From 382285c54813f8c8bd0acb98ba0044859cb077fd Mon Sep 17 00:00:00 2001 From: jrocha Date: Fri, 25 Jan 2019 14:53:33 -0700 Subject: [PATCH 02/15] refactor config file read and writes. also allow setCustomDatetimeDialog to retrieve defined programtime from the cfg file. --- Resources/application.py | 22 ++++++++++++++++------ Resources/tempFiles/programTime.txt | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Resources/application.py b/Resources/application.py index eaf7126..789aa36 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -104,6 +104,20 @@ def __init__(self, customDatetime): return + def readConfig(self, configKey): + config = configparser.ConfigParser() + config.read('Resources/tempFiles/pyforecast.cfg') + return config['DEFAULT'][configKey] + + def writeConfig(self, configKey, configVal): + config = configparser.ConfigParser() + config.read('Resources/tempFiles/pyforecast.cfg') + config['DEFAULT'][configKey] = configVal + with open('Resources/tempFiles/pyforecast.cfg', 'w') as configfile: + config.write(configfile) + + return + def purgeOldFiles(self): """ This function removes any old csv files from the tempFiles directory @@ -122,11 +136,7 @@ def setDate(self, date): writeFile.write(date) # This function sets the date in the software. It stores the time in a config file called 'Resources/tempFiles/pyforecast.cfg' - config = configparser.ConfigParser() - config.read('Resources/tempFiles/pyforecast.cfg') - config['DEFAULT']['ProgramTime'] = date - with open('Resources/tempFiles/pyforecast.cfg', 'w') as configfile: - config.write(configfile) + self.writeConfig('programtime',date) return @@ -327,7 +337,7 @@ def is_date(string): return True except ValueError: return False - self.setCustomDatetimeDialogText, ok = QtWidgets.QInputDialog.getText(self, 'Set Custom Date for PyForecast', 'Set Date (YYYY-MM-DD)') + self.setCustomDatetimeDialogText, ok = QtWidgets.QInputDialog.getText(self, 'Set Custom Date for PyForecast', 'Set Date (YYYY-MM-DD)',text=self.readConfig('programtime')) if ok and is_date(self.setCustomDatetimeDialogText): self.setDate(self.setCustomDatetimeDialogText) return diff --git a/Resources/tempFiles/programTime.txt b/Resources/tempFiles/programTime.txt index 155d054..61aab22 100644 --- a/Resources/tempFiles/programTime.txt +++ b/Resources/tempFiles/programTime.txt @@ -1 +1 @@ -2019-01-24 \ No newline at end of file +2019-01-25 \ No newline at end of file From 3e807a4a5679c325b5f2aa40b1ff1660db43ee49 Mon Sep 17 00:00:00 2001 From: jrocha Date: Fri, 25 Jan 2019 15:44:44 -0700 Subject: [PATCH 03/15] update install_dependencies.bat for the config file modification and to cover users installing dependencies while within RECNET --- install_dependencies.bat | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/install_dependencies.bat b/install_dependencies.bat index 0e4a468..0178102 100644 --- a/install_dependencies.bat +++ b/install_dependencies.bat @@ -4,27 +4,29 @@ ECHO. ECHO. ECHO Installing python libraries ECHO. -pip install numpy +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org numpy ECHO O -pip install scipy +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org scipy ECHO OO -pip install requests +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org requests ECHO OOO -pip install zeep +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org zeep ECHO OOOO -pip install pandas +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org pandas ECHO OOOOO -pip install sklearn +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org sklearn ECHO OOOOOO -pip install matplotlib +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org matplotlib ECHO OOOOOOO -pip install -Iv PyQt5==5.9 +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -Iv PyQt5==5.9 ECHO OOOOOOOO -pip install datetime +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org datetime ECHO OOOOOOOOO -pip install PyQt5-tools +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org PyQt5-tools ECHO OOOOOOOOOO -pip install openpyxl +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org openpyxl +ECHO OOOOOOOOOOO +pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org configparser ECHO OOOOOOOOOOO ECHO. ECHO Press any key to exit From ae410ab5b8633cce3f2a0d6401d60a0bdc51840c Mon Sep 17 00:00:00 2001 From: jrocha Date: Mon, 28 Jan 2019 13:36:09 -0700 Subject: [PATCH 04/15] replace programTime.txt with an actual config file pyforecast.cfg. adjust software to start referring to this config file. --- Resources/Functions/miscFunctions.py | 18 +++++++++++++++--- Resources/application.py | 17 ++++++----------- Resources/tempFiles/programTime.txt | 1 - Resources/tempFiles/pyforecast.cfg | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) delete mode 100644 Resources/tempFiles/programTime.txt diff --git a/Resources/Functions/miscFunctions.py b/Resources/Functions/miscFunctions.py index 36a1623..face26a 100644 --- a/Resources/Functions/miscFunctions.py +++ b/Resources/Functions/miscFunctions.py @@ -7,6 +7,7 @@ from Resources.Functions.lists import HUCList from datetime import datetime +import configparser def isValidHUC(hucString): """ @@ -67,15 +68,26 @@ def monthLookup(month): } return monthDict[month] +def readConfig(configKey, configGroup='DEFAULT'): + config = configparser.ConfigParser() + config.read('Resources/tempFiles/pyforecast.cfg') + return config[configGroup][configKey] + +def writeConfig(configKey, configVal, configGroup='DEFAULT'): + config = configparser.ConfigParser() + config.read('Resources/tempFiles/pyforecast.cfg') + config[configGroup][configKey] = configVal + with open('Resources/tempFiles/pyforecast.cfg', 'w') as configfile: + config.write(configfile) + return + def current_date(): """ This function reads the Resources/tempFiles/programTime.txt file and returns the current date as a datetime object of that date """ + time=readConfig('programtime') - with open('Resources/tempFiles/programTime.txt','r') as readfile: - time = readfile.read() - date = datetime.strptime(time, '%Y-%m-%d') if date >= datetime.now(): date = datetime.now() diff --git a/Resources/application.py b/Resources/application.py index 789aa36..10913bc 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -104,18 +104,17 @@ def __init__(self, customDatetime): return - def readConfig(self, configKey): + def readConfig(self, configKey, configGroup='DEFAULT'): config = configparser.ConfigParser() config.read('Resources/tempFiles/pyforecast.cfg') - return config['DEFAULT'][configKey] + return config[configGroup][configKey] - def writeConfig(self, configKey, configVal): + def writeConfig(self, configKey, configVal, configGroup='DEFAULT'): config = configparser.ConfigParser() config.read('Resources/tempFiles/pyforecast.cfg') - config['DEFAULT'][configKey] = configVal + config[configGroup][configKey] = configVal with open('Resources/tempFiles/pyforecast.cfg', 'w') as configfile: config.write(configfile) - return def purgeOldFiles(self): @@ -123,19 +122,15 @@ def purgeOldFiles(self): This function removes any old csv files from the tempFiles directory """ for file_ in os.listdir('Resources/tempFiles'): - if file_ == 'programTime.txt' or file_ == '__pycache__' or 'pyforecast.cfg': + if file_ == '__pycache__' or 'pyforecast.cfg': continue filename = os.path.abspath('Resources/tempFiles/' + file_) os.remove(filename) def setDate(self, date): """ - This function sets the date in the software. It stores the time in a file called 'Resources/tempFiles/programTime.txt' + This function sets the date in the software. It stores the time in a config file called 'Resources/tempFiles/pyforecast.cfg' """ - with open('Resources/tempFiles/programTime.txt','w') as writeFile: - writeFile.write(date) - - # This function sets the date in the software. It stores the time in a config file called 'Resources/tempFiles/pyforecast.cfg' self.writeConfig('programtime',date) return diff --git a/Resources/tempFiles/programTime.txt b/Resources/tempFiles/programTime.txt deleted file mode 100644 index 61aab22..0000000 --- a/Resources/tempFiles/programTime.txt +++ /dev/null @@ -1 +0,0 @@ -2019-01-25 \ No newline at end of file diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg index f804e1b..77c1f57 100644 --- a/Resources/tempFiles/pyforecast.cfg +++ b/Resources/tempFiles/pyforecast.cfg @@ -1,5 +1,5 @@ [DEFAULT] -programtime = 2019-01-25 +programtime = 2019-01-02 savedirectory = Resources savefilename = pyforecast From 2a706cc241df55e99461926b97a1d7e37d82eac9 Mon Sep 17 00:00:00 2001 From: jrocha Date: Mon, 28 Jan 2019 14:04:14 -0700 Subject: [PATCH 05/15] Enable auto-saving when File>Save is pressed. File>SaveAs added to save forecast in a new file. Saving and opening files now writes an entry in the config file. On start-up, the config file entry is set to blank. If this entry is blank, the File>Save auto-save function defaults to the SaveAs behavior. --- Resources/GUI/PyForecast_GUI.py | 2 ++ Resources/application.py | 25 +++++++++++++++++-------- Resources/tempFiles/pyforecast.cfg | 4 ++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Resources/GUI/PyForecast_GUI.py b/Resources/GUI/PyForecast_GUI.py index 2d6ecce..3a67a63 100644 --- a/Resources/GUI/PyForecast_GUI.py +++ b/Resources/GUI/PyForecast_GUI.py @@ -1918,6 +1918,7 @@ def setupUi(self, MainWindow): self.fileMenu = self.menu.addMenu("File") self.newAction = QtWidgets.QAction('New Forecast', MainWindow) self.saveAction = QtWidgets.QAction('Save Forecast', MainWindow) + self.saveAsAction = QtWidgets.QAction('Save Forecast As...', MainWindow) self.openAction = QtWidgets.QAction('Open Forecast', MainWindow) self.addLoaderAction = QtWidgets.QAction("Edit Dataloaders",MainWindow) self.setCustomDatetimeAction = QtWidgets.QAction('Set custom datetime', MainWindow) @@ -1928,6 +1929,7 @@ def setupUi(self, MainWindow): self.exitAction = QtWidgets.QAction('Exit PyForecast', MainWindow) self.fileMenu.addAction(self.newAction) self.fileMenu.addAction(self.saveAction) + self.fileMenu.addAction(self.saveAsAction) self.fileMenu.addAction(self.openAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.addLoaderAction) diff --git a/Resources/application.py b/Resources/application.py index 10913bc..cdc5bc0 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -101,6 +101,7 @@ def __init__(self, customDatetime): self.connectEventsDensityTab() self.threadPool = QtCore.QThreadPool() + self.writeConfig('savefilename','') return @@ -187,6 +188,7 @@ def connectEventsMenuBar(self): self.docAction.triggered.connect(self.openDocs) self.versionAction.triggered.connect(self.openVersion) self.saveAction.triggered.connect(self.saveFile) + self.saveAsAction.triggered.connect(self.saveFileAs) self.openAction.triggered.connect(self.openFile) self.addLoaderAction.triggered.connect(self.addLoader) self.newAction.triggered.connect(self.newForecast) @@ -227,20 +229,26 @@ def openVersion(self): return + def saveFileAs(self): + self.saveFile(True) + return - def saveFile(self): + def saveFile(self, saveNew=False): """ Function to save the 2 main dictionaries into a pickled .fcst file. Additionally sets the file name in the window title. """ + filename = self.readConfig('savefilename') - filename = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File As', 'unititled.fcst','*.fcst')[0] - - if filename == '': - return - - if '.' not in filename: - filename = filename + '.fcst' + if filename == '' or saveNew: + filename = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File As', 'unititled.fcst','*.fcst')[0] + self.writeConfig('savefilename',filename) + + if filename == '': + return + + if '.' not in filename: + filename = filename + '.fcst' pkl = { "datasetDirectory":self.datasetDirectory, @@ -265,6 +273,7 @@ def openFile(self): try: filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File','*.fcst')[0] + self.writeConfig('savefilename',filename) if filename == '': return diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg index 77c1f57..6d8cdaf 100644 --- a/Resources/tempFiles/pyforecast.cfg +++ b/Resources/tempFiles/pyforecast.cfg @@ -1,5 +1,5 @@ [DEFAULT] -programtime = 2019-01-02 +programtime = 2019-01-28 savedirectory = Resources -savefilename = pyforecast +savefilename = From 077839a34bb987808dd283b8ee9018781ca0fd5a Mon Sep 17 00:00:00 2001 From: jrocha Date: Mon, 28 Jan 2019 15:25:40 -0700 Subject: [PATCH 06/15] Allow GUI to accept start and end year integers for POR in the Data tab. Need to modify POR entry in the data doctionary to accept a time range (2 ints) rather than just a single int. --- Resources/GUI/PyForecast_GUI.py | 41 +++++++++++++++++++++++++++--- Resources/tempFiles/pyforecast.cfg | 1 - 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Resources/GUI/PyForecast_GUI.py b/Resources/GUI/PyForecast_GUI.py index 3a67a63..01f47f1 100644 --- a/Resources/GUI/PyForecast_GUI.py +++ b/Resources/GUI/PyForecast_GUI.py @@ -1108,7 +1108,6 @@ class DataOptionsPane(QtWidgets.QWidget): # Initialize a custom QWidget def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self) self.setupUI() @@ -1133,19 +1132,41 @@ def setupUI(self): self.optionsGrid.setColumnStretch(1, 0) self.optionsGrid.setColumnStretch(2, 1) self.optionsGrid.setColumnStretch(3, 1) + self.optionsGrid.setColumnStretch(4, 1) # POR option self.porLabel = QtWidgets.QLabel("POR") self.porInfo = QtWidgets.QLabel() self.porInfo.setPixmap(QtGui.QPixmap(os.path.abspath("Resources/Fonts_Icons_Images/infoHover.png")).scaled(30,30, QtCore.Qt.KeepAspectRatio)) self.porInfo.setScaledContents(True) - self.porInfo.setToolTip('

PyForecast will attempt to download daily station data up to the POR specified here.

') + self.porInfo.setToolTip('

PyForecast will attempt to download daily station data from today to X years from today based on the POR specified here.

') + self.porYes = QtWidgets.QCheckBox("POR") + self.porYes.setChecked(True) + self.porYes.stateChanged.connect(lambda: self.porToggle()) + self.porNo = QtWidgets.QCheckBox("T1T2") + self.porNo.setChecked(False) + self.porNo.stateChanged.connect(lambda: self.porToggle()) + self.porGroup = QtWidgets.QButtonGroup(self) + self.porGroup.addButton(self.porYes) + self.porGroup.addButton(self.porNo) self.porInput = QtWidgets.QLineEdit() - self.porInput.setPlaceholderText("POR in years:") + self.porInput.setPlaceholderText("POR in years") self.porInput.setValidator(onlyInt) + self.porT1 = QtWidgets.QLineEdit() + self.porT1.setPlaceholderText("Start Year") + self.porT1.setValidator(onlyInt) + self.porT1.setVisible(False) + self.porT2 = QtWidgets.QLineEdit() + self.porT2.setPlaceholderText("End Year") + self.porT2.setValidator(onlyInt) + self.porT2.setVisible(False) self.optionsGrid.addWidget(self.porLabel, 0, 0, 1, 1) self.optionsGrid.addWidget(self.porInfo, 0, 1, 1, 1) - self.optionsGrid.addWidget(self.porInput, 0, 2, 1, 2) + self.optionsGrid.addWidget(self.porYes, 0, 2, 1, 1) + self.optionsGrid.addWidget(self.porNo, 0, 3, 1, 1) + self.optionsGrid.addWidget(self.porInput, 1, 2, 1, 2) + self.optionsGrid.addWidget(self.porT1, 1, 0, 1, 1) + self.optionsGrid.addWidget(self.porT2, 1, 2, 1, 2) # IMPUTE SWE option # self.sweImputeLabel = QtWidgets.QLabel("Impute SNOTEL") @@ -1245,6 +1266,18 @@ def setupUI(self): self.layout.addLayout(self.optionsGrid) self.setLayout(self.layout) + + def porToggle(self): + if self.porYes.isChecked(): + self.porInput.setVisible(True) + self.porT1.setVisible(False) + self.porT2.setVisible(False) + else: + self.porInput.setVisible(False) + self.porT1.setVisible(True) + self.porT2.setVisible(True) + return + """ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||| FORECAST OPTIONS TAB |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg index 6d8cdaf..d5fdf38 100644 --- a/Resources/tempFiles/pyforecast.cfg +++ b/Resources/tempFiles/pyforecast.cfg @@ -1,5 +1,4 @@ [DEFAULT] programtime = 2019-01-28 -savedirectory = Resources savefilename = From 89ede923b554d9f264043bda0231b581e12965d1 Mon Sep 17 00:00:00 2001 From: jrocha Date: Mon, 28 Jan 2019 15:32:57 -0700 Subject: [PATCH 07/15] Better year1 and year2 GUI locations --- Resources/GUI/PyForecast_GUI.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/GUI/PyForecast_GUI.py b/Resources/GUI/PyForecast_GUI.py index 01f47f1..6132856 100644 --- a/Resources/GUI/PyForecast_GUI.py +++ b/Resources/GUI/PyForecast_GUI.py @@ -1153,11 +1153,11 @@ def setupUI(self): self.porInput.setPlaceholderText("POR in years") self.porInput.setValidator(onlyInt) self.porT1 = QtWidgets.QLineEdit() - self.porT1.setPlaceholderText("Start Year") + self.porT1.setPlaceholderText("Year1") self.porT1.setValidator(onlyInt) self.porT1.setVisible(False) self.porT2 = QtWidgets.QLineEdit() - self.porT2.setPlaceholderText("End Year") + self.porT2.setPlaceholderText("Year2") self.porT2.setValidator(onlyInt) self.porT2.setVisible(False) self.optionsGrid.addWidget(self.porLabel, 0, 0, 1, 1) @@ -1165,8 +1165,8 @@ def setupUI(self): self.optionsGrid.addWidget(self.porYes, 0, 2, 1, 1) self.optionsGrid.addWidget(self.porNo, 0, 3, 1, 1) self.optionsGrid.addWidget(self.porInput, 1, 2, 1, 2) - self.optionsGrid.addWidget(self.porT1, 1, 0, 1, 1) - self.optionsGrid.addWidget(self.porT2, 1, 2, 1, 2) + self.optionsGrid.addWidget(self.porT1, 1, 2, 1, 1) + self.optionsGrid.addWidget(self.porT2, 1, 3, 1, 1) # IMPUTE SWE option # self.sweImputeLabel = QtWidgets.QLabel("Impute SNOTEL") From bf1ba98f1c111eee2bb0ff1f6adcba4f543bad75 Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 07:46:38 -0700 Subject: [PATCH 08/15] working implementation of start year selection on the Data tab. Integrates with software as is without data dictionary modifications. --- Resources/GUI/PyForecast_GUI.py | 11 ++++++----- Resources/application.py | 9 ++++++--- Resources/tempFiles/pyforecast.cfg | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Resources/GUI/PyForecast_GUI.py b/Resources/GUI/PyForecast_GUI.py index 6132856..a72a298 100644 --- a/Resources/GUI/PyForecast_GUI.py +++ b/Resources/GUI/PyForecast_GUI.py @@ -1139,26 +1139,27 @@ def setupUI(self): self.porInfo = QtWidgets.QLabel() self.porInfo.setPixmap(QtGui.QPixmap(os.path.abspath("Resources/Fonts_Icons_Images/infoHover.png")).scaled(30,30, QtCore.Qt.KeepAspectRatio)) self.porInfo.setScaledContents(True) - self.porInfo.setToolTip('

PyForecast will attempt to download daily station data from today to X years from today based on the POR specified here.

') + self.porInfo.setToolTip('

PyForecast will attempt to download daily station data based on the POR or the start year specified here.

') self.porYes = QtWidgets.QCheckBox("POR") self.porYes.setChecked(True) self.porYes.stateChanged.connect(lambda: self.porToggle()) - self.porNo = QtWidgets.QCheckBox("T1T2") + self.porNo = QtWidgets.QCheckBox("Years") self.porNo.setChecked(False) self.porNo.stateChanged.connect(lambda: self.porToggle()) self.porGroup = QtWidgets.QButtonGroup(self) self.porGroup.addButton(self.porYes) self.porGroup.addButton(self.porNo) self.porInput = QtWidgets.QLineEdit() - self.porInput.setPlaceholderText("POR in years") + self.porInput.setText("30") self.porInput.setValidator(onlyInt) self.porT1 = QtWidgets.QLineEdit() - self.porT1.setPlaceholderText("Year1") + self.porT1.setText(str(datetime.today().year - 30)) self.porT1.setValidator(onlyInt) self.porT1.setVisible(False) self.porT2 = QtWidgets.QLineEdit() - self.porT2.setPlaceholderText("Year2") + self.porT2.setText(str(datetime.today().year)) self.porT2.setValidator(onlyInt) + self.porT2.setDisabled(True) self.porT2.setVisible(False) self.optionsGrid.addWidget(self.porLabel, 0, 0, 1, 1) self.optionsGrid.addWidget(self.porInfo, 0, 1, 1, 1) diff --git a/Resources/application.py b/Resources/application.py index cdc5bc0..1a8408f 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -1200,7 +1200,7 @@ def missingDataViz(self): return dialog = MissingNoGUI.missingDialog(data) - return + return def downloadData(self, update): """ @@ -1225,7 +1225,10 @@ def downloadData(self, update): return try: - por = int(self.dataTab.dataOptions.porInput.text()) + if self.dataTab.dataOptions.porYes.isChecked(): + por = int(self.dataTab.dataOptions.porInput.text()) + else: + por = int(self.dataTab.dataOptions.porT2.text()) - int(self.dataTab.dataOptions.porT1.text()) test = math.sqrt(por) except: if update == "True": @@ -1236,7 +1239,7 @@ def downloadData(self, update): DICT = { "STATIONS": self.datasetDirectory, - "POR": self.dataTab.dataOptions.porInput.text(), + "POR": por, "UPDATE": update, "FILL": fill} diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg index d5fdf38..e64108c 100644 --- a/Resources/tempFiles/pyforecast.cfg +++ b/Resources/tempFiles/pyforecast.cfg @@ -1,4 +1,4 @@ [DEFAULT] -programtime = 2019-01-28 +programtime = 2019-01-29 savefilename = From 887d75bbbb1eb4743794d645c181d1e55c1dff34 Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 08:24:10 -0700 Subject: [PATCH 09/15] Add Visual Studio python project file for AppVeyor Continuous Integration --- PyForecast.pyproj | 144 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 PyForecast.pyproj diff --git a/PyForecast.pyproj b/PyForecast.pyproj new file mode 100644 index 0000000..faa2b01 --- /dev/null +++ b/PyForecast.pyproj @@ -0,0 +1,144 @@ + + + + Debug + 2.0 + {c605e43a-1a81-41a5-a132-cb8ab2abcc9d} + + PyForecast.pyw + + . + . + {789894c7-04a9-4a11-a6b5-3f4435165112};{1b580a1a-fdb3-4b32-83e1-6407eb2722e6};{349c5851-65df-11da-9384-00065b846f21};{888888a0-9f3d-457c-b088-3a5042f75d52} + Web launcher + + http://localhost + {StartupModule}.wsgi_app + + + + + 10.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + http://localhost + False + + + + + + + CurrentPage + True + False + False + False + + + + + + + + + False + False + + + + + \ No newline at end of file From 93559385d637e047bf7b23b9be6245ff8397f843 Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 09:42:49 -0700 Subject: [PATCH 10/15] Explore publishing of code to IIS Server --- PyForecast.pyproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PyForecast.pyproj b/PyForecast.pyproj index faa2b01..118e67a 100644 --- a/PyForecast.pyproj +++ b/PyForecast.pyproj @@ -55,6 +55,8 @@ + + From 72c295ce962ea3c04c3508db207c61fce51d19fb Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 11:45:32 -0700 Subject: [PATCH 11/15] Refactor process that adds datasets to datasetDictionary. Also validate new entries to the datasetDictionary to prevent duplicates. --- Resources/application.py | 48 ++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/Resources/application.py b/Resources/application.py index 1a8408f..46f564b 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -854,6 +854,27 @@ def addCustomWebService(self, stationDict): return + def appendDatasetDictionaryItem(self, dataID, stationType, stationNumber, stationName, stationParam, stationUnits, resamplingMethod, decodeOptions): + """ + This function adds an entry to the datasetDictionary and adds it to the Station Tab table + """ + duplicateDataset = False + #Add validation code to see if dataset already exists in datasetDirectory + + for dCounter in range(1, len(self.datasetDirectory['datasets'])): + if self.datasetDirectory['datasets'][dCounter]["TYPE"] == stationType and self.datasetDirectory['datasets'][dCounter]["ID"] == stationNumber and self.datasetDirectory['datasets'][dCounter]["Name"] == stationName and self.datasetDirectory['datasets'][dCounter]["Parameter"] == stationParam and self.datasetDirectory['datasets'][dCounter]["Units"] == stationUnits: + duplicateDataset = True + break + + if not duplicateDataset: + self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":stationUnits,"Resampling":resamplingMethod,"Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) + self.stationsTab.stationInfoPane.stationTable.addRow([dataID, stationType, stationNumber, stationName, stationParam]) + else: + button = QtWidgets.QMessageBox.question(self, 'Error','Dataset has already been selected...'.format(traceback.format_exc()), QtWidgets.QMessageBox.Ok) + if button == QtWidgets.QMessageBox.Ok: + return + + return def addToStationsList(self, stationString = ""): """ @@ -888,17 +909,16 @@ def addToStationsList(self, stationString = ""): stationNumber = instructionList[2] stationParam = instructionList[4] - if stationType == 'USGS': - + if stationType == 'USGS': decodeOptions = {"dataLoader":"USGS_NWIS"} dataID = encryptions.generateStationID(stationType, stationName, stationParam, decodeOptions['dataLoader']) - self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":"CFS","Resampling":"Mean", "Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) + units = "CFS" + resample = "Mean" + #self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":"CFS","Resampling":"Mean", "Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) elif stationType == 'SNOTEL': - decodeOptions = {"dataLoader":"NRCS_WCC"} dataID = encryptions.generateStationID(stationType, stationName, stationParam, decodeOptions['dataLoader']) - if stationParam == 'SWE': resample = 'Sample' units = 'inches' @@ -908,29 +928,29 @@ def addToStationsList(self, stationString = ""): else: resample = 'Accumulation' units = 'inches' - - self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":units,"Resampling":resample,"Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) + #self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":units,"Resampling":resample,"Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) elif stationType == 'SNOWCOURSE': - decodeOptions = {"dataLoader":"NRCS_WCC"} dataID = encryptions.generateStationID(stationType, stationName, stationParam, decodeOptions['dataLoader']) - self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":"inches","Resampling":"Sample","Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) + units = "inches" + resample = "Sample" + #self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":"inches","Resampling":"Sample","Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) elif stationType == 'USBR': - region = instructionList[5] pcode = instructionList[6] - decodeOptions = {"dataLoader":"USBR", "Region":region,"PCODE":pcode} dataID = encryptions.generateStationID(stationType, stationName, stationParam, decodeOptions['dataLoader']) - self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":"CFS","Resampling":"Mean","Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) + units = "CFS" + resample = "Mean" + #self.datasetDirectory['datasets'].append({"PYID":dataID,"TYPE":stationType,"ID":stationNumber,"Name":stationName,"Parameter":stationParam,"Units":"CFS","Resampling":"Mean","Decoding":decodeOptions, "Data":{}, "lastDateTime":None}) else: return - self.stationsTab.stationInfoPane.stationTable.addRow([dataID, stationType, stationNumber, stationName, stationParam]) - + self.appendDatasetDictionaryItem(dataID, stationType, stationNumber, stationName, stationParam, units, resample, decodeOptions) + elif instructionList[0] == 'nrcc': stationNumber = self.stationsTab.stationInfoPane.nrccInput.text() From 7f5f04be12ac19a587e28a995aebc4065d713a74 Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 13:25:55 -0700 Subject: [PATCH 12/15] Define CustomQComboBox to disable mouse-wheel events for the built-in QComboBox. Users report that mouse-scroll is intercepted by the comboboxes when the cursor hovers over them. This commit prevents that. --- Resources/GUI/PyForecast_GUI.py | 58 +++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/Resources/GUI/PyForecast_GUI.py b/Resources/GUI/PyForecast_GUI.py index a72a298..18b7a96 100644 --- a/Resources/GUI/PyForecast_GUI.py +++ b/Resources/GUI/PyForecast_GUI.py @@ -59,6 +59,20 @@ #// requiring us to extend those classes to include things like context menus, #// javascript webmaps, and matplotlib plots. +# Define a custom QComboBox that reroutes mouse wheel events +# https://stackoverflow.com/questions/3241830/qt-how-to-disable-mouse-scrolling-of-qcombobox/11866474#11866474 +class CustomQComboBox(QtWidgets.QComboBox): + def __init__(self, scrollWidget=None, *args, **kwargs): + super(CustomQComboBox, self).__init__(*args, **kwargs) + self.scrollWidget=scrollWidget + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, *args, **kwargs): + if self.hasFocus(): + return QtWidgets.QComboBox.wheelEvent(self, *args, **kwargs) + else: + return self.scrollWidget.wheelEvent(*args, **kwargs) + """ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -987,6 +1001,8 @@ def setupUI(self): # Create a layout for the widget self.layout = QtWidgets.QVBoxLayout() + self.scrollArea = QtWidgets.QScrollArea(self) + self.formScroll = QtWidgets.QFrame(self.scrollArea) # Add a header and description self.stationHeader = QtWidgets.QTextEdit() @@ -1045,7 +1061,7 @@ def setupUI(self): self.pdsiInfoButton.setPixmap(QtGui.QPixmap(os.path.abspath("Resources/Fonts_Icons_Images/infoHover.png")).scaled(30,30, QtCore.Qt.KeepAspectRatio)) self.pdsiInfoButton.setScaledContents(True) self.pdsiInfoButton.setToolTip('

Palmer Drought Severity Index by Climate Division

') - self.pdsiInput = QtWidgets.QComboBox() + self.pdsiInput = CustomQComboBox(self.formScroll) self.pdsiInput.addItems(list(CLIMATE_DIVISIONS.divisions.keys())) self.pdsiButton = QtWidgets.QPushButton('Add') self.otherDataLayout.addWidget(self.pdsiLabel, 3, 0, 1, 1) @@ -1060,7 +1076,7 @@ def setupUI(self): self.ensoInfoButton = QtWidgets.QLabel() self.ensoInfoButton.setPixmap(QtGui.QPixmap(os.path.abspath("Resources/Fonts_Icons_Images/infoHover.png")).scaled(30,30, QtCore.Qt.KeepAspectRatio)) self.ensoInfoButton.setToolTip('

Various climate indices.

See www.cpc.ncep.noaa.gov for more information.

') - self.ensoInput = QtWidgets.QComboBox() + self.ensoInput = CustomQComboBox(self.formScroll) self.ensoInput.addItem('Nino3.4 SST') self.ensoInput.addItem('Nino3.4 SST Anomaly') self.ensoInput.addItem('PNA Teleconnection') @@ -1284,7 +1300,7 @@ def porToggle(self): ||||||||||||||||||||||||||||||||||||||||| FORECAST OPTIONS TAB |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| """ - + # Define a custom widget to display options for the data tab class FcstOptionsPane(QtWidgets.QWidget): @@ -1312,6 +1328,8 @@ def setupUI(self): # Build the left side of the options pane self.gridLayout1 = QtWidgets.QGridLayout() + self.scrollArea = QtWidgets.QScrollArea(self) + self.formScroll = QtWidgets.QFrame(self.scrollArea) self.periodLabel = QtWidgets.QLabel("Forecast Period") self.periodInfo = QtWidgets.QLabel() self.periodInfo.setPixmap(QtGui.QPixmap(os.path.abspath("Resources/Fonts_Icons_Images/infoHover.png")).scaled(30,30, QtCore.Qt.KeepAspectRatio)) @@ -1321,11 +1339,11 @@ def setupUI(self): hlayout.addWidget(self.periodLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.gridLayout1.addLayout(hlayout, 0, 0, 1, 4) - self.periodStartInput = QtWidgets.QComboBox() - self.periodStartInput.addItems(['January','February','March','April','May','June','July','August','September','October','November','December']) + self.periodStartInput = CustomQComboBox(self.formScroll) + self.periodStartInput.addItems(['TEST','January','February','March','April','May','June','July','August','September','October','November','December']) self.periodStartInput.setCurrentIndex(3) self.gridLayout1.addWidget(self.periodStartInput, 1, 0, 1, 2) - self.periodEndInput = QtWidgets.QComboBox() + self.periodEndInput = CustomQComboBox(self.formScroll) self.periodEndInput.addItems(['January','February','March','April','May','June','July','August','September','October','November','December']) self.periodEndInput.setCurrentIndex(6) self.gridLayout1.addWidget(self.periodEndInput, 1, 2, 1, 2) @@ -1338,7 +1356,7 @@ def setupUI(self): hlayout.addWidget(self.freqLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.gridLayout1.addLayout(hlayout, 2, 0, 1, 4) - self.freqInput = QtWidgets.QComboBox() + self.freqInput = CustomQComboBox(self.formScroll) self.freqInput.addItems(["Monthly", "Bi-Monthly"]) self.gridLayout1.addWidget(self.freqInput, 3, 0, 1, 4) self.eqStartLabel = QtWidgets.QLabel("Forecasts start on:") @@ -1350,14 +1368,14 @@ def setupUI(self): hlayout.addWidget(self.eqStartLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.gridLayout1.addLayout(hlayout, 4, 0, 1, 4) - self.eqStartInput = QtWidgets.QComboBox() + self.eqStartInput = CustomQComboBox(self.formScroll) self.eqStartInput.addItems(['January','February','March','April','May','June','July','August','September','October','November','December']) self.gridLayout1.addWidget(self.eqStartInput, 5, 0, 1, 4) self.wateryearStartInfo = QtWidgets.QLabel() self.wateryearStartInfo.setPixmap(QtGui.QPixmap(os.path.abspath("Resources/Fonts_Icons_Images/infoHover.png")).scaled(30,30, QtCore.Qt.KeepAspectRatio)) self.wateryearStartInfo.setToolTip('

Specify the first month of your water year

') self.wateryearStartLabel = QtWidgets.QLabel("Wateryear starts on:") - self.wateryearStartInput = QtWidgets.QComboBox() + self.wateryearStartInput = CustomQComboBox(self.formScroll) self.wateryearStartInput.addItems(['January','February','March','April','May','June','July','August','September','October','November','December']) self.wateryearStartInput.setCurrentIndex(9) hlayout = QtWidgets.QHBoxLayout() @@ -1375,7 +1393,7 @@ def setupUI(self): hlayout.addWidget(self.targetLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.gridLayout1.addLayout(hlayout, 8, 0, 1, 4) - self.targetInput = QtWidgets.QComboBox() + self.targetInput = CustomQComboBox(self.formScroll) self.gridLayout1.addWidget(self.targetInput, 9, 0, 1, 4) self.precipLabel = QtWidgets.QLabel("Accumulate Precipitation") self.precipInfo = QtWidgets.QLabel() @@ -1396,7 +1414,7 @@ def setupUI(self): hlayout.addWidget(self.precipInputNo) self.gridLayout1.addLayout(hlayout, 10, 0, 1, 4) self.accumLabel = QtWidgets.QLabel("Accumulate From:") - self.accumStart = QtWidgets.QComboBox() + self.accumStart = CustomQComboBox(self.formScroll) self.accumStart.addItems(['October','November','December','January','February','March','April','May','June','July','August','September']) hlayout = QtWidgets.QHBoxLayout() hlayout.addWidget(self.accumLabel) @@ -1600,6 +1618,8 @@ def setupUI(self,model='none'): # Set the tab layout self.layout = QtWidgets.QVBoxLayout() self.layout2 = QtWidgets.QVBoxLayout() + self.scrollArea = QtWidgets.QScrollArea(self) + self.formScroll = QtWidgets.QFrame(self.scrollArea) # Add widgets to the layout self.eqSelectLabel = QtWidgets.QLabel("Select Equation") @@ -1611,7 +1631,7 @@ def setupUI(self,model='none'): hlayout.addWidget(self.eqSelectLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.layout.addLayout(hlayout) - self.eqSelect = QtWidgets.QComboBox() + self.eqSelect = CustomQComboBox(self.formScroll) self.eqSelect.addItems([]) self.layout.addWidget(self.eqSelect) self.featSelMethodLabel = QtWidgets.QLabel("Feature Selection Method") @@ -1623,7 +1643,7 @@ def setupUI(self,model='none'): hlayout.addWidget(self.featSelMethodLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.layout.addLayout(hlayout) - self.featSelInput = QtWidgets.QComboBox() + self.featSelInput = CustomQComboBox(self.formScroll) self.featSelInput.addItems(["Sequential Floating Forward Selection", "Sequential Floating Backwards Selection"])#["Forward Selection","Backward Selection",["Sequential Floating Forward Selection"],"Floating Backward"]) self.layout.addWidget(self.featSelInput) self.numModelsLabel = QtWidgets.QLabel("Number of Models") @@ -1648,7 +1668,7 @@ def setupUI(self,model='none'): hlayout.addWidget(self.crossValLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.layout.addLayout(hlayout) - self.crossValInput = QtWidgets.QComboBox() + self.crossValInput = CustomQComboBox(self.formScroll) self.crossValInput.addItems(["Leave One Out","K-Fold (5 folds)","K-Fold (10 folds)"]) self.layout.addWidget(self.crossValInput) self.scoreLabel = QtWidgets.QLabel("Model Scoring Method") @@ -1660,7 +1680,7 @@ def setupUI(self,model='none'): hlayout.addWidget(self.scoreLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.layout.addLayout(hlayout) - self.scoreInput = QtWidgets.QComboBox() + self.scoreInput = CustomQComboBox(self.formScroll) self.scoreInput.addItems(["Cross Validated Adjusted R2","Root Mean Squared Prediction Error","Cross Validated Nash-Sutcliffe"]) self.layout.addWidget(self.scoreInput) @@ -1673,7 +1693,7 @@ def setupUI(self,model='none'): hlayout.addWidget(self.distLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.layout.addLayout(hlayout) - self.distInput = QtWidgets.QComboBox() + self.distInput = CustomQComboBox(self.formScroll) self.distInput.addItems(["Normal"])#, "Lognormal"]) self.layout.addWidget(self.distInput) @@ -1724,6 +1744,8 @@ def setupUI(self,model='none'): # Set the tab layout self.layout = QtWidgets.QVBoxLayout() + self.scrollArea = QtWidgets.QScrollArea(self) + self.formScroll = QtWidgets.QFrame(self.scrollArea) # Add the options self.bigLabel = QtWidgets.QLabel("COMING SOON........") @@ -1737,7 +1759,7 @@ def setupUI(self,model='none'): hlayout.addWidget(self.structureLabel) hlayout.addSpacerItem(QtWidgets.QSpacerItem(400,40,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.layout.addLayout(hlayout) - self.structureInput = QtWidgets.QComboBox() + self.structureInput = CustomQComboBox(self.formScroll) self.structureInput.addItems(["1 Hidden Layer","2 Hidden Layers","3 Hidden Layers"]) self.layout.addWidget(self.structureInput) @@ -1791,6 +1813,8 @@ def setupUI(self,model='none'): self.bigLabel = QtWidgets.QLabel('COMING SOON...') self.layout.addWidget(self.bigLabel) self.setLayout(self.layout) + self.scrollArea = QtWidgets.QScrollArea(self) + self.formScroll = QtWidgets.QFrame(self.scrollArea) # Set up the regression selection pane class RegressionSelectionPane(QtWidgets.QWidget): From cc6bead5faa63f85969e250fcd746bbb370a43db Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 13:53:42 -0700 Subject: [PATCH 13/15] don't know why i thought python arrays start at 1... --- Resources/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/application.py b/Resources/application.py index 46f564b..959010e 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -861,7 +861,7 @@ def appendDatasetDictionaryItem(self, dataID, stationType, stationNumber, statio duplicateDataset = False #Add validation code to see if dataset already exists in datasetDirectory - for dCounter in range(1, len(self.datasetDirectory['datasets'])): + for dCounter in range(0, len(self.datasetDirectory['datasets'])): if self.datasetDirectory['datasets'][dCounter]["TYPE"] == stationType and self.datasetDirectory['datasets'][dCounter]["ID"] == stationNumber and self.datasetDirectory['datasets'][dCounter]["Name"] == stationName and self.datasetDirectory['datasets'][dCounter]["Parameter"] == stationParam and self.datasetDirectory['datasets'][dCounter]["Units"] == stationUnits: duplicateDataset = True break From bcafaf312d9359b73a44d375335ba9c0ebe9bc20 Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 14:21:02 -0700 Subject: [PATCH 14/15] changes based on reviews for pull request #1 reviews --- Resources/application.py | 21 ++++----------------- Resources/tempFiles/pyforecast.cfg | 2 +- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Resources/application.py b/Resources/application.py index 959010e..8738f3c 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -34,7 +34,7 @@ # Import other scripts from Resources.GIS import CLIMATE_DIVISIONS -from Resources.Functions.miscFunctions import isValidHUC, monthLookup, current_date +from Resources.Functions.miscFunctions import isValidHUC, monthLookup, current_date, readConfig, writeConfig from Resources.Functions import DataDownloadV4, ProcessDataV2, RestServiceV2, encryptions, importSpread, FeatureSelectionV3, kernelDensity # Import additional libraries @@ -101,23 +101,10 @@ def __init__(self, customDatetime): self.connectEventsDensityTab() self.threadPool = QtCore.QThreadPool() - self.writeConfig('savefilename','') + writeConfig('savefilename','') return - def readConfig(self, configKey, configGroup='DEFAULT'): - config = configparser.ConfigParser() - config.read('Resources/tempFiles/pyforecast.cfg') - return config[configGroup][configKey] - - def writeConfig(self, configKey, configVal, configGroup='DEFAULT'): - config = configparser.ConfigParser() - config.read('Resources/tempFiles/pyforecast.cfg') - config[configGroup][configKey] = configVal - with open('Resources/tempFiles/pyforecast.cfg', 'w') as configfile: - config.write(configfile) - return - def purgeOldFiles(self): """ This function removes any old csv files from the tempFiles directory @@ -132,7 +119,7 @@ def setDate(self, date): """ This function sets the date in the software. It stores the time in a config file called 'Resources/tempFiles/pyforecast.cfg' """ - self.writeConfig('programtime',date) + writeConfig('programtime',date) return @@ -341,7 +328,7 @@ def is_date(string): return True except ValueError: return False - self.setCustomDatetimeDialogText, ok = QtWidgets.QInputDialog.getText(self, 'Set Custom Date for PyForecast', 'Set Date (YYYY-MM-DD)',text=self.readConfig('programtime')) + self.setCustomDatetimeDialogText, ok = QtWidgets.QInputDialog.getText(self, 'Set Custom Date for PyForecast', 'Set Date (YYYY-MM-DD)',text=readConfig('programtime')) if ok and is_date(self.setCustomDatetimeDialogText): self.setDate(self.setCustomDatetimeDialogText) return diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg index e64108c..bcc90e7 100644 --- a/Resources/tempFiles/pyforecast.cfg +++ b/Resources/tempFiles/pyforecast.cfg @@ -1,4 +1,4 @@ [DEFAULT] -programtime = 2019-01-29 +programtime = 2019-01-01 savefilename = From 4fd693ce12f20b08e6c405d1531204e9afb2454f Mon Sep 17 00:00:00 2001 From: jrocha Date: Tue, 29 Jan 2019 15:23:16 -0700 Subject: [PATCH 15/15] Add additional validation on the porT1 input --- Resources/application.py | 2 ++ Resources/tempFiles/pyforecast.cfg | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Resources/application.py b/Resources/application.py index 8738f3c..e5d8769 100644 --- a/Resources/application.py +++ b/Resources/application.py @@ -1235,6 +1235,8 @@ def downloadData(self, update): if self.dataTab.dataOptions.porYes.isChecked(): por = int(self.dataTab.dataOptions.porInput.text()) else: + if int(self.dataTab.dataOptions.porT1.text()) <= 1901: + test = math.sqrt("a") por = int(self.dataTab.dataOptions.porT2.text()) - int(self.dataTab.dataOptions.porT1.text()) test = math.sqrt(por) except: diff --git a/Resources/tempFiles/pyforecast.cfg b/Resources/tempFiles/pyforecast.cfg index bcc90e7..e64108c 100644 --- a/Resources/tempFiles/pyforecast.cfg +++ b/Resources/tempFiles/pyforecast.cfg @@ -1,4 +1,4 @@ [DEFAULT] -programtime = 2019-01-01 +programtime = 2019-01-29 savefilename =