Skip to content

Commit 65166e9

Browse files
author
stephanie
committed
merging
2 parents 9f01c6f + b858dda commit 65166e9

9 files changed

Lines changed: 985 additions & 21 deletions

File tree

ODMTools.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import pymysql
2323
#import psycopg2
2424

25+
2526
tool = LoggerTool()
2627
logger = tool.setupLogger('main', 'odmtools.log', 'a', logging.INFO)
2728
wx.Log.SetLogLevel(0)

doc/wxFormBuilder/frmFillGaps.fbp

Lines changed: 756 additions & 0 deletions
Large diffs are not rendered by default.

odmtools/controller/frmGapFill.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Subclass of dlgFill, which is generated by wxFormBuilder."""
2+
3+
from odmtools.view import clsGapFill
4+
5+
6+
# Implementing dlgFill
7+
class frmGapFill(clsGapFill.dlgFill):
8+
def __init__(self, parent, record_service):
9+
self.record_service = record_service
10+
clsGapFill.dlgFill.__init__(self, parent)
11+
12+
# Handlers for dlgFill events.
13+
def onOKBtn(self, event):
14+
#TODO add validation
15+
gapvalue= self.txtGap.Value
16+
gaptime = self.cbGap.Value
17+
fillvalue = self.txtFill.Value
18+
filltime= self.cbFill.Value
19+
20+
self.record_service.fill_gap(gap=[gapvalue, gaptime], fill=[fillvalue, filltime])
21+
self.Close()
22+
23+
def OnCancelBtn(self, event):
24+
25+
self.Close()
26+
self.Destroy()

odmtools/controller/logicEditTools.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,26 @@ def filter_date(self, endDate, startDate):
5050
else:
5151
return "Cannot filter: %s" % (self._edit_error)
5252

53+
54+
def fill_gap(self, gap, fill):
55+
self._edit_service.fill_gap(gap , fill)
56+
57+
self.refresh_edit()
58+
if self._record:
59+
self._script("edit_service.fill_gap(gap = %s, fill= %s)\n" % (gap, fill), 'black')
60+
Publisher.sendMessage("scroll")
61+
62+
63+
64+
65+
5366
def data_gaps(self, value, time_period):
5467
self._edit_service.data_gaps(value, time_period)
5568
self.refresh_selection()
5669
if self._record:
5770
self._script("edit_service.data_gaps(%s, '%s')\n" % (value, time_period), 'black')
5871
Publisher.sendMessage("scroll")
5972

60-
6173
def value_change_threshold(self, value, operator):
6274
self._edit_service.change_value_threshold(value, operator)
6375
self.refresh_selection()

odmtools/gui/mnuRibbon.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from frmFlagValues import frmFlagValues
1515
from odmtools.controller.frmLinearDrift import frmLinearDrift
1616
from odmtools.controller.frmAbout import frmAbout
17+
from odmtools.controller.frmGapFill import frmGapFill
1718
import wizSave
1819
from odmtools.common.icons import *
1920
import pandas as pd
@@ -36,9 +37,9 @@
3637
wxID_RIBBONEDITSCRIPTSAVE, wxID_RIBBONVIEWPLOT, wxID_RIBBONVIEWTABLE, wxID_RIBBONVIEWSERIES, wxID_RIBBONVIEWCONSOLE,
3738
wxID_RIBBONVIEWSCRIPT, wxID_RIBBONPLOTBLANKBTN, wxID_FileMenu, wxID_STARTDPDATE, wxID_ENDDPDATE, wxID_FRAME1SPINCTRL1,
3839
wxID_RIBBONEDITFILTER, wxID_RIBBONEDITRECORD, wxID_RIBBONEDITLINFILTER, wxID_RIBBONPLOTDATEAPPLY,
39-
wxID_RIBBONEDITRESETFILTER, wxID_RIBBONRECORDNEW, wxID_RIBBONRECORDOPEN, wxID_RIBBONRECORDSAVE] = [wx.NewId() for
40+
wxID_RIBBONEDITRESETFILTER, wxID_RIBBONRECORDNEW, wxID_RIBBONRECORDOPEN, wxID_RIBBONRECORDSAVE, wxID_GAPFILL] = [wx.NewId() for
4041
_init_ctrls in
41-
range(46)]
42+
range(47)]
4243

4344
## #################################
4445
## Build Menu and Toolbar
@@ -155,6 +156,7 @@ def _init_ctrls(self, prnt):
155156
self.edit_bar.AddSimpleButton(wxID_RIBBONEDITFLAG, "Flag", flag.GetBitmap(), "")
156157
self.edit_bar.AddSimpleButton(wxID_RIBBONEDITADDPOINT, "Add Point", add.GetBitmap(), "")
157158
self.edit_bar.AddSimpleButton(wxID_RIBBONEDITDELPOINT, "Delete Point", delete.GetBitmap(), "")
159+
self.edit_bar.AddSimpleButton(wxID_GAPFILL, "Fill Gap", add.GetBitmap(), "")
158160
#self.edit_bar.AddSimpleButton(wxID_RIBBONEDITRECORD, "Record", bitmap=record.GetBitmap(), help_string="",kind=0x4)
159161

160162
self.edit_bar.EnableButton(wxID_RIBBONEDITFILTER, False)
@@ -250,6 +252,7 @@ def bindEvents(self):
250252
self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onEditFlag, id=wxID_RIBBONEDITFLAG)
251253
self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onEditAddPoint, id=wxID_RIBBONEDITADDPOINT)
252254
self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onEditDelPoint, id=wxID_RIBBONEDITDELPOINT)
255+
self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onGapFill, id = wxID_GAPFILL)
253256

254257
self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onRecordNew, id=wxID_RIBBONRECORDNEW)
255258
self.Bind(RB.EVT_RIBBONBUTTONBAR_CLICKED, self.onRecordOpen, id=wxID_RIBBONRECORDOPEN)
@@ -466,7 +469,23 @@ def onEditAddPoint(self, event):
466469
addPoint.ShowModal()
467470

468471
event.Skip()
472+
# ###################################
473+
# Gap fill
474+
# ###################################
475+
def onGapFill(self, event):
476+
477+
gap_fill = frmGapFill(self.parent, self.parent.getRecordService())
478+
479+
if gap_fill.Show() == wx.OK:
480+
gap_fill.Destroy()
481+
482+
469483

484+
485+
486+
487+
488+
event.Skip()
470489
# ###################################
471490
# Delete Point
472491
# ###################################

odmtools/gui/plotProbability.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ def updatePlot(self):
136136
#self.prob.append(
137137
#prop = oneSeries.Probability.plot(column="DataValue", ax=self.plots)
138138
#todo FutureWarning: order is deprecated, use sort_values(...)
139+
#xValues = oneSeries.Probability.xAxis.order().values
139140
xValues = oneSeries.Probability.xAxis.order().values
141+
# yValues = oneSeries.Probability.yAxis.order().values
140142
yValues = oneSeries.Probability.yAxis.order().values
141143

142144
ax = self.plots.plot(xValues, yValues, 'bs', color=oneSeries.color,

odmtools/odmdata/memory_database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def initEditValues(self, seriesID):
201201
"""
202202
if not self.editLoaded:
203203
logger.debug("Load series from db")
204-
204+
self.series = self.series_service.get_series_by_id(seriesID)
205205
self.df = self.series_service.get_values_by_series(seriesID)
206206
self.editLoaded = True
207207

odmtools/odmservices/edit_service.py

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@
1717
# logger = tool.setupLogger(__name__, __name__ + '.log', 'w', logging.DEBUG)
1818
logger =logging.getLogger('main')
1919

20+
class time(object):
21+
time_units = {
22+
'second': 's',
23+
'minute': 'm',
24+
'hour': 'h',
25+
'day': 'D',
26+
'week': 'W',
27+
'month': 'M',
28+
'year': 'Y'
29+
}
30+
31+
def __init__(self, value, time_period):
32+
self.value = value
33+
self.time_period = time_period
34+
2035
class EditService():
2136
# Mutual exclusion: cursor, or connection_string
2237
def __init__(self, series_id, connection=None, connection_string="", debug=False):
@@ -86,6 +101,7 @@ def _test_filter_previous(self):
86101

87102
return df
88103

104+
89105
def datetime2dataframe(self, datetime_list):
90106
""" Converts datetime_list to a pandas Dataframe
91107
@@ -141,19 +157,51 @@ def filter_date(self, before, after):
141157
if before and after:
142158
self.filtered_dataframe = df[(df.index < before) & (df.index > after)]
143159

160+
def fill_gap(self, gap, fill):
161+
162+
df = self.memDB.getDataValuesDF()
163+
gaps= self.find_gaps(df, gap[0], gap[1])
164+
points = []
165+
series= self.memDB.series
166+
timegap = np.timedelta64(fill[0], self.time_units[fill[1]])
167+
168+
#if gaps is not of type dataframe- put it in a dataframe
169+
#if not isinstance(gaps, pd.DataFrame
170+
for g in gaps.iterrows():
171+
row = g[1]
172+
e = row.datetime
173+
s = row.dateprev
174+
175+
#prime the loop
176+
s = s + timegap
177+
# for each gap time period in the larger gap ( until datetime = prev value)
178+
while s < e:
179+
utc_offset = (series.begin_date_time-series.begin_date_time_utc).total_seconds()/3600
180+
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))
181+
#('-9999', None, DATE, series.begin_date_time_utc, UTCDATE, None, None, u'nc', None, None,
182+
# series.site_id, series.variable_id, series.method_id, series.source_id,
183+
# series.quality_control_level_id
184+
185+
s = s + timegap
186+
#print points
187+
self.add_points(points)
188+
189+
time_units = {
190+
'second': 's',
191+
'minute': 'm',
192+
'hour': 'h',
193+
'day': 'D',
194+
'week': 'W',
195+
'month': 'M',
196+
'year': 'Y'
197+
}
198+
144199
# Data Gaps
145-
def data_gaps(self, value, time_period):
146-
df = self._test_filter_previous()
147200

148-
time_units = {
149-
'second': 's',
150-
'minute': 'm',
151-
'hour': 'h',
152-
'day': 'D',
153-
'week': 'W',
154-
'month': 'M',
155-
'year': 'Y'
156-
}
201+
202+
def find_gaps(self, df, value, time_period):
203+
204+
157205

158206
# make a copy of the dataframe in order to modify it to be in the form we need to determine data gaps
159207
copy_df = df
@@ -165,19 +213,29 @@ def data_gaps(self, value, time_period):
165213
value = int(value)
166214

167215
# create a bool column indicating which rows meet condition
168-
filtered_results = copy_df['datetime'].diff() >= np.timedelta64(value, time_units[time_period])
216+
filtered_results = copy_df['datetime'].diff() > np.timedelta64(value, self.time_units[time_period])
169217

170218
# filter on rows that passed previous condition
171-
copy_df = copy_df[filtered_results]
219+
return copy_df[filtered_results]
220+
221+
172222

223+
224+
def data_gaps(self, value, time_period):
225+
df = self._test_filter_previous()
226+
copy_df = self.find_gaps(df, value, time_period)
227+
print (copy_df)
173228
# merge values and remove duplicates. this hack allows for both values to be marked when selecting data gaps
174229
newdf = pd.concat([copy_df['datetime'], copy_df['dateprev']], join='inner')
175-
self.filtered_dataframe = df[df.index.isin(newdf.drop_duplicates().dropna())]
176230

177231
# clean up
178232
del copy_df
179-
del filtered_results
180-
del newdf
233+
234+
235+
self.filtered_dataframe= df[df.index.isin(newdf.drop_duplicates().dropna())]
236+
237+
238+
181239

182240
def change_value_threshold(self, value, operator):
183241

@@ -310,7 +368,6 @@ def change_value(self, value, operator):
310368

311369
def add_points(self, points):
312370
# todo: add the ability to send in multiple datetimes to a single 'point'
313-
314371
self.memDB.addPoints(points)
315372

316373
self._populate_series()

odmtools/view/clsGapFill.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# -*- coding: utf-8 -*-
2+
3+
###########################################################################
4+
## Python code generated with wxFormBuilder (version Jun 17 2015)
5+
## http://www.wxformbuilder.org/
6+
##
7+
## PLEASE DO "NOT" EDIT THIS FILE!
8+
###########################################################################
9+
10+
import wx
11+
import wx.xrc
12+
13+
14+
###########################################################################
15+
## Class dlgFill
16+
###########################################################################
17+
18+
class dlgFill(wx.Dialog):
19+
def __init__(self, parent):
20+
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition,
21+
size=wx.Size(315, 217), style=wx.DEFAULT_DIALOG_STYLE)
22+
23+
self.SetSizeHintsSz(wx.Size(315, 217), wx.Size(315, 217))
24+
25+
bsForm = wx.BoxSizer(wx.VERTICAL)
26+
27+
self.lblInstructions = wx.StaticText(self, wx.ID_ANY,
28+
u"This function fills any gaps greater than the gap duration with a no-data value at the fill frequency.",
29+
wx.DefaultPosition, wx.DefaultSize, 0)
30+
self.lblInstructions.Wrap(300)
31+
bsForm.Add(self.lblInstructions, 0, wx.ALL, 5)
32+
33+
fgSizer1 = wx.FlexGridSizer(0, 3, 0, 0)
34+
fgSizer1.SetFlexibleDirection(wx.BOTH)
35+
fgSizer1.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
36+
37+
self.lblGap = wx.StaticText(self, wx.ID_ANY, u"Gap Duration:", wx.DefaultPosition, wx.DefaultSize, 0)
38+
self.lblGap.Wrap(-1)
39+
fgSizer1.Add(self.lblGap, 0, wx.ALL, 5)
40+
41+
self.txtGap = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
42+
fgSizer1.Add(self.txtGap, 0, wx.ALL, 5)
43+
44+
cbGapChoices = [u"second", u"minute", u"hour", u"days", u"week", u"month", u"day", u"year"]
45+
self.cbGap = wx.ComboBox(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, cbGapChoices, 0)
46+
self.cbGap.SetSelection(1)
47+
fgSizer1.Add(self.cbGap, 1, wx.ALL, 5)
48+
49+
self.lblFill = wx.StaticText(self, wx.ID_ANY, u"Fill Frequency:", wx.DefaultPosition, wx.DefaultSize, 0)
50+
self.lblFill.Wrap(-1)
51+
fgSizer1.Add(self.lblFill, 0, wx.ALL, 5)
52+
53+
self.txtFill = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
54+
fgSizer1.Add(self.txtFill, 0, wx.ALL, 5)
55+
56+
cbFillChoices = [u"second", u"minute", u"hour", u"day", u"week", u"month", u"year"]
57+
self.cbFill = wx.ComboBox(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, cbFillChoices, 0)
58+
self.cbFill.SetSelection(1)
59+
fgSizer1.Add(self.cbFill, 1, wx.ALL, 5)
60+
61+
bsForm.Add(fgSizer1, 1, wx.EXPAND, 5)
62+
63+
m_sdbSizer1 = wx.StdDialogButtonSizer()
64+
self.m_sdbSizer1OK = wx.Button(self, wx.ID_OK)
65+
m_sdbSizer1.AddButton(self.m_sdbSizer1OK)
66+
self.m_sdbSizer1Cancel = wx.Button(self, wx.ID_CANCEL)
67+
m_sdbSizer1.AddButton(self.m_sdbSizer1Cancel)
68+
m_sdbSizer1.Realize();
69+
70+
bsForm.Add(m_sdbSizer1, 1, wx.EXPAND, 5)
71+
72+
self.SetSizer(bsForm)
73+
self.Layout()
74+
75+
self.Centre(wx.BOTH)
76+
77+
# Connect Events
78+
self.m_sdbSizer1Cancel.Bind(wx.EVT_BUTTON, self.OnCancelBtn)
79+
self.m_sdbSizer1OK.Bind(wx.EVT_BUTTON, self.onOKBtn)
80+
81+
def __del__(self):
82+
pass
83+
84+
# Virtual event handlers, overide them in your derived class
85+
def OnCancelBtn(self, event):
86+
event.Skip()
87+
88+
def onOKBtn(self, event):
89+
event.Skip()
90+
91+

0 commit comments

Comments
 (0)