ServerPortfolio  2.0
Python parsers and server
 All Classes Namespaces Files Functions Variables Properties Pages
Validation.py
Go to the documentation of this file.
1 ## @package serverportfolio.Validation
2 # @brief For graphical user validation before saving the data.
3 #
4 # Last changed $Id: Validation.py 21 2015-04-19 19:27:46Z michael $
5 
6 # todo extend to the update of list_csv/divsplit, extend to console ?
7 # devel version in branches/valid_stock, last v8 lost
8 
9 import logging
10 import threading, Queue
11 
12 import Tkinter as tk
13 import tkMessageBox
14 
15 # for eclipse
16 #import pydevd
17 
18 ## @brief A pure Tkinter scrollable frame that actually works!.
19 # source : http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame\n
20 # author : unknown\n
21 #
22 # Use the 'interior' attribute to place widgets inside the scrollable frame\n
23 # Construct and pack/place/grid normally\n
24 # This frame only allows vertical scrolling
25 #
26 # my modification:
27 # - WindowManager pack replaced by grid functions
28 class VerticalScrolledFrame(tk.Frame):
29 
30  def __init__(self, parent, *args, **kw):
31  tk.Frame.__init__(self, parent, *args, **kw)
32 
33  # to make resizable, horizontal and vertical
34  self.columnconfigure(0, weight=1)
35  self.rowconfigure(0, weight=1)
36  # create a canvas object and a vertical scrollbar for scrolling it
37  vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
38  vscrollbar.grid(column=1, row=0, sticky=tk.E+tk.N+tk.S)
39  # default dimension ? 265x378
40  canvas = tk.Canvas(self, bd=0, highlightthickness=0, yscrollcommand=vscrollbar.set)
41  canvas.grid(column=0, row=0, sticky=tk.W+tk.E+tk.N+tk.S)
42  vscrollbar.config(command=canvas.yview)
43  # reset the view
44  canvas.xview_moveto(0)
45  canvas.yview_moveto(0)
46  # print "canvas.cget()", canvas.cget()
47  # default ? 265x378
48  # print "canvas.config()", canvas.config()
49  # create a frame inside the canvas which will be scrolled with it
50  self.interior = interior = tk.Frame(canvas)
51  interior_id = canvas.create_window(0, 0, window=interior, anchor=tk.NW)
52 
53  # brief Track changes to the canvas and frame width and sync them.
54  # Also updating the scrollbar.
55  # Called at every update of the scrollbar/change of dimension
56  def _configure_interior(event):
57  #print "_configure_interior"
58  # update the scrollbars to match the size of the inner frame
59  size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
60  canvas.config(scrollregion="0 0 %s %s" % size)
61  if interior.winfo_reqwidth() != canvas.winfo_width():
62  # update the canvas's width to fit the inner frame
63  canvas.config(width=interior.winfo_reqwidth())
64  interior.bind('<Configure>', _configure_interior)
65 
66  # called when change dimension
67  def _configure_canvas(event):
68  #print "_configure_canvas"
69  if interior.winfo_reqwidth() != canvas.winfo_width():
70  # update the inner frame's width to fill the canvas
71  canvas.itemconfigure(interior_id, width=canvas.winfo_width())
72  canvas.bind('<Configure>', _configure_canvas)
73 
74 ## @class ValidationTkinter
75 # @brief GUI interface to validate the update and saving of data.
76 # It is used during the post-processing of the update of stock (see Stocks, ValidStockUpdate).\n
77 # The user accepts or rejects the values of the last retrieved data by the parsers.\n
78 #
79 # @note It can be used in a threaded environment with the use of ManagerTk.\n
80 #
81 # Modify Stock._dict_interactive, used as input and output
82 class ValidationTkinter(tk.Frame):
83 
84  ## @brief Constructor.
85  # @param symbol of the stock
86  # @param dict_input dict_interactive, used as input and output
87  # @param master instance of tk.Tk(), created by default
88  def __init__(self, symbol, dict_input, master=None):
89  tk.Frame.__init__(self, master)
90  self._logger_ = logging.getLogger('SP.ValidationtTk')
91  self._logger_.debug("init ValidationTkinter")
92 
93  # dict_interactive used as input/output
94  self._dict_input = dict_input
95  ## @brief store in dictionary all IntVar associated to radio buttons.
96  # Dictionary with same action/key than dict_input.
97  self._var_radiob = None
98 
99  # general setup, before adding frame
100  top=self.winfo_toplevel()
101  # can call app.destroy(), even after deleting the window(top-rigth corner)
102  top.protocol("WM_DELETE_WINDOW", self.quit_app)
103 
104  # make top and self column 0 resizable, only column needed
105  top.columnconfigure(0, weight=1)
106  top.rowconfigure(0, weight=1)
107  self.columnconfigure(0, weight=1)
108  # self.rowconfigure depends on type ?
109 
110  # default cell of the top-level windows can expand
111  self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
112 
113  # set title of the window and first label
114  self.master.title('User validation')
115  # create help menu ?
116  self.create_title( symbol )
117 
118  # create the widgets, list_csv scrollable by default
119  # self.row starts at 1, after title label
120  self.create_widgets()
121  # validation buttons
122  self.create_button_valid()
123 
124  # fix the size of the main window, give 1 1, need to call update() before
125  self.update()
126  # screen_width=app.master.winfo_screenwidth()
127  # screen_height=app.master.winfo_screenheight()
128  # print "Screen Size: ", screen_width, screen_height
129  #print "App Size ", app.winfo_width(), app.winfo_height()
130  #window_width = app.winfo_width()
131  #window_height = app.winfo_height()
132  #print "App Size ", app.winfo_reqwidth(), app.winfo_reqheight()
133  min_win_width = self.winfo_reqwidth()
134  min_win_height = self.winfo_reqheight()
135  # assign a minimum size, some bugs if the window is modified too quickly...
136  self.master.minsize(width=min_win_width, height=min_win_height)
137 
138  ## @brief Create the main window with the data from dict_input.
139  def create_widgets(self):
140  self._logger_.debug("Entry createWidgets")
141  # important, will contain the IntVars associated to 'repl_add' key and radio buttons
142  self._var_radiob = {}
143  # store the action_frame, maybe not needed
144  self.action_frame = {}
145  # action_frame counter, bug need to start at one ! after the title label
146  row_action_nb = 1
147  # loop over action and over each value
148  for key_action, value_action in self._dict_input.iteritems():
149  self._logger_.debug("key_action %s" % key_action)
150 
151  # create key, filled in create_one_input
152  self._var_radiob[key_action] = {}
153 
154  # can be included in one ScrolledFrame or in self
155  label_frame = tk.LabelFrame(self, text=key_action)
156  label_frame.grid(sticky=tk.E+tk.W+tk.N+tk.S, pady=8,padx=5)
157 
158  # guess if need scrollable or not. only for lists of csv data, key are integers
159  scrollable = None
160  if 0 in value_action:
161  scrollable = True
162  else:
163  scrollable = False
164  self._logger_.debug("scrollable %s" % scrollable)
165 
166  # case containing long list
167  if scrollable:
168  # make resizable the scrollable row
169  # share space between the Action frame at the same time ?
170  self.rowconfigure(row_action_nb, weight=1)
171  # the scrollbar stays on the right
172  label_frame.columnconfigure(0, weight=1)
173  # action frame extends vertically
174  label_frame.rowconfigure(0, weight=1)
175  #if scrollable:
176  self.action_frame[key_action] = VerticalScrolledFrame(label_frame)
177  # moved from create_one_input
178  self.action_frame[key_action].interior.columnconfigure(0, weight=1)
179  # not scrollable entry, XML data
180  else:
181  # moved from create_one_input
182  label_frame.columnconfigure(2, weight=1,minsize=100 )
183  self.action_frame[key_action] = label_frame
184 
185  # ok, make the trick ! for vertical scrollbar
186  self.action_frame[key_action].grid(sticky=tk.E+tk.W+tk.N+tk.S)
187 
188  # loop over the value, i is the row number
189  i = 0
190  for key_value, value_value in value_action.iteritems():
191  #self._logger_.debug("key_value/ value_value ", key_value, value_value)
192  # common to all, could be retrieved from value_value
193  state = value_value['repl_add']
194  # fill action_frame and self.dict
195  if scrollable:
196  self.create_one_input_csv( i, self.action_frame[key_action], key_action, value_value, state )
197  else:
198  # added key_value
199  self.create_one_input_xml(i, self.action_frame[key_action], key_action, key_value, value_value, state )
200  i += 1
201  # action frame counter
202  row_action_nb += 1
203 
204  ## @brief Check all radio buttons have been selected and update repl_add entry with _var_radiob.
205  # Method working for xml and csv data
206  def valid(self):
207  self._logger_.debug("Valid")
208  # check all values have been validated, could be common, never error in HistPrice
209  for key_action, value_action in self._var_radiob.iteritems():
210  for value in self._var_radiob[key_action].values():
211 
212  if not ((value.get() == 0) | (value.get() == 2)):
213  # make message, not all selected, continue, cancel...
214  self.popupmsg("All entries must be selected as Accepted or Rejected to continue,\nor press Quit to validate the default")
215  return
216 
217  # update repl_add entry of dict_interactive
218  for key_action in self._var_radiob.keys():
219  for name in self._var_radiob[key_action].keys():
220  # only one element in _var_radiob
221  value = self._var_radiob[key_action][name]
222  self._dict_input[key_action][name]['repl_add'] = value.get()
223 
224  # quit application after update
225  self.quit_app()
226 
227  ## @brief Fill the frame specific to xml data: Info / Fundamental.
228  # @param i row_number
229  # @param action_frame container Frame
230  # @param action EAction.name
231  # @param key_value string key of the data
232  # @param values associated
233  # @param state initial value of repl_add
234  def create_one_input_xml(self, i, action_frame, action, key_value, values, state):
235  self._logger_.debug("i value: %d, %s" % (i, values))
236  # associated to radio buttons
237  var = tk.IntVar()
238  var.set( state )
239  # key_value is unique
240  self._var_radiob[action][key_value] = var
241 
242  tk.Label( action_frame, text=key_value).grid(column=0, row=i, sticky=tk.W, padx=5, pady=2)
243  tk.Label( action_frame, text=values['xml_value']).grid(column=1, row=i, sticky=tk.W, padx=5, pady=2)
244  # option Label width=50 makes text center
245  tk.Label( action_frame, text=values['new_value']).grid(column=2, row=i, sticky=tk.W, padx=5, pady=2)
246  # radio buttons
247  tk.Radiobutton( action_frame, text='Rej.', variable=var, value=0).grid(column=3, row=i, sticky=tk.E)
248  tk.Radiobutton( action_frame, text='Acc.', variable=var, value=2).grid(column=4, row=i, sticky=tk.E)
249 
250  ## @brief Fill the frame for list_csv : HistPrice
251  # Last version, group similar entries
252  # @param i row_number
253  # @param action_frame tk.Frame
254  # @param action EAction.name
255  # @param values value of labframe
256  # @param state initial value of repl_add
257  def create_one_input_csv(self, i, action_frame, action, values, state):
258  #self._logger_.debug("values %s" % values)
259  # associated to radio buttons
260  var = tk.IntVar()
261  var.set( state )
262  # row_number i is unique
263  self._var_radiob[action][i] = var
264 
265  if state == 0:
266  color_lab = 'red'
267  else:
268  color_lab = 'black'
269 
270  # make a new Frame inside interior
271  one_entry_frame = tk.Frame( action_frame.interior )
272  # stretchable column 2
273  one_entry_frame.columnconfigure(2, weight=1, minsize=80 )
274  one_entry_frame.grid( row=i, sticky=tk.W+tk.E+tk.N+tk.S, pady=5, padx=5 )
275 
276  # should color all Label or only the error message
277  tk.Label( one_entry_frame, text='from %s' % values['first_date']) .grid() #column=2, row=i+1)
278 
279  # print first and last dates on row 0 and 1
280  f = values['first_values']
281  format_values = '%7.2f %7.2f %7.2f %7.2f %9.2e' % (f[1], f[2], f[3], f[4], f[5])
282  tk.Label( one_entry_frame, text=format_values ).grid(column=1, row=0, sticky=tk.E)
283 
284  tk.Label( one_entry_frame, text=' to %s' % values['last_date']).grid(sticky=tk.E)
285  f = values['last_values']
286  format_values = '%7.2f %7.2f %7.2f %7.2f %9.2e' % (f[1], f[2], f[3], f[4], f[5])
287  tk.Label( one_entry_frame, text=format_values ).grid(column=1, row=1, sticky=tk.E)
288 
289  # make column for error, stick on the right, as well as the next cells
290  tk.Label( one_entry_frame, text=values['error'], fg=color_lab).grid(column=2, row=0, rowspan=2, sticky=tk.W) #tk.N+tk.S+tk.E+tk.W) #tk.W)
291 
292  # radio buttons
293  tk.Radiobutton( one_entry_frame, text='Rej.', variable=var, value=0).grid(column=3, row=0, rowspan=2, sticky=tk.W)
294  tk.Radiobutton( one_entry_frame, text='Acc..', variable=var, value=2).grid(column=4, row=0, rowspan=2, sticky=tk.W)
295 
296  ## @brief Label with stock symbol
297  # @param title string
298  def create_title(self, title):
299  tk.Label(self,text=title,font=('Helvetica',12,'bold'), padx=5,pady=5).grid()
300 
301  ## @brief Create Valid and Quit buttons at the bottom of the main window.
302  # Common to all actions
304  frame_button = tk.Frame(self, pady=10)
305  # row/column values are correct by default..
306  frame_button.grid()
307  tk.Button(frame_button, text='Valid', command=self.valid).grid(column=0, row=0)
308  tk.Button(frame_button, text='Quit', command=self.quit_app).grid(column=1, row=0)
309 
310  ## @brief Message indicating that some entries have not been validated.
311  # @brief msg string to print
312  def popupmsg(self,msg):
313  tkMessageBox.showwarning("warning", msg)
314 
315  def quit_app(self):
316  # called 2 times, quit, nothing destroyed (new data added), works if caller call app.destroy()
317  self.quit()
318 
319 ## @class ManagerTk
320 # @brief Manage and serialize the access to a ValidationTkinter application, necessary when UpdateStock is run with multiple threads.
321 # @warning The object must be created and run from the main thread (tkinter multi-thread limitation)
322 #
323 # Interface to ValidationTkinter which assures serialization and thread-safety. The input dict_interactive are treated entry by entry,
324 # from an input queue and returned in an output queue. Two GUI cannot exist at the same time. The answer queue should never contain more than 1 entry.\n
325 # Data are exchanged by the use of Queue for the serialization.\n
326 #
327 # Usage, see __main__ section:
328 # @code
329 # # create the object
330 # manager = ManagerTk()
331 # # run a pool of threads aware of manager. Inside the function a stop signal ['QUIT'] will be send to the manager queue when all thread joined.
332 # thd_pool_run( list_stock, manager )
333 #
334 # # manager enters an infinite loop, and blocks at this line
335 # manager.update_me()
336 #
337 # # from the threads send and receive data
338 # manager.write( [stock_symbol], dict_interactive )
339 # new_dict_interactive = manager.read()
340 # # continue post-process...
341 # @endcode
342 #
343 # @attention The answer queue should never contain more than 1 entry.
344 #
345 # @note In this version a new tk application is recreated each time. Later one unique Tk widget could be updated.
346 class ManagerTk():
347 
348  ## @brief Constructor.
349  def __init__(self):
350  self._logger_ = logging.getLogger('SP.ManagerTk')
351  self._logger_.debug("init ManagerTk")
352  ## @brief Queue to receive and send data (dict_interactive and stock symbol).
353  ## @{
354  ## @brief new data to be processed
355  self.queue = Queue.Queue()
356  ## @brief new result data.
357  # The caller should get the result before a next entry.\n
358  # @todo to make more secure, a Pipe ?
359  self.queue_answer = Queue.Queue(1)
360  ## @}
361  ## @brief store a Tk main environment.
362  self._root_tk = tk.Tk()
363 
364  ## @name Queue functions, to be called by threads
365  ## @{
366 
367  ## @brief Write a new input to the queue.
368  # new_entry must be a list, send ['QUIT'] to stop the manager
369  # @param new_entry list with format : ['stock_symbol',dict_interactive]
370  def write(self, new_entry):
371  self.queue.put(new_entry)
372  ## @brief Return the updated dict_interactive
373  # @return modified dict interactive
374  def read_answer(self):
375  assert self.queue_answer.qsize() <= 1, "Error in queue size "
376  return self.queue_answer.get()
377 
378  # brief Clear the queue ? to check
379 # def clear(self):
380 # self.queue.put(None)
381  ## @}
382 
383  ## @brief Check regularly and indefinitely, the queue.
384  #
385  # This function is blocking.\n
386  # To stop the Manager, send the message : ['QUIT'].
387  def update_me(self):
388  self._logger_.debug("Entry update_me")
389  # try: certainly many possible errors
390  while 1:
391  # block until a new data is received
392  new_entry = self.queue.get()
393  # check termination signal
394  if new_entry[0] == "QUIT":
395  self._check_stop()
396  # return to the main_thread
397  return
398  else:
399  # original Validtkinter, certainly not necessary to recreate each time
400  dict = new_entry[1]
401  app = ValidationTkinter(new_entry[0], dict, self._root_tk)
402  # now can call and block on the usual mainloop
403  app.mainloop()
404  self._logger_.debug("return from mainloop, put result in answer queue")
405  # important to call destroy here, quit in app
406  app.destroy()
407  self.queue_answer.put( dict )
408 
409  ## @brief Check state of the object before stopping.
410  def _check_stop(self):
411  self._logger_.debug("stop the manager")
412  assert self.queue.empty() == True, "Error with queue"
413  assert self.queue_answer.empty() == True, "Error with queue"
414 
415 
416 # ########################
417 if __name__ == "__main__":
418 
419  import time, sys
420 
421  logging.basicConfig(level=logging.DEBUG)
422  logging.getLogger('SP')
423  m_logger = logging.getLogger("SP.main")
424 
425 
426  m_test = {
427  'Info': {'Sector': {'xml_value': 'Energy', 'new_value': 'Utilities', 'date': '2015-03-03', 'source': 'YQL', 'repl_add': 0},
428  'StockExchange': {'xml_value': None, 'new_value': 'Paris', 'date': '2015-03-03', 'source': 'YQL', 'repl_add': 2}
429  },
430  'Other': {'Industry': {'xml_value': None, 'new_value': 'Diversified Utilities', 'date': '2015-03-03', 'source': 'YQL', 'repl_add': 2},
431  'Name': {'xml_value': 'Toto', 'new_value': 'GDF SUEZ', 'date': '2015-03-03', 'source': 'YQL', 'repl_add': 1}
432  }
433 # 'HistPrice': {0: {'first_date': '2015-03-06 00:00:00', 'repl_add': 1, 'first_values': [1425596400, 4953.68, 4986.88, 4949.88, 4964.35, 0.0], 'last_date': '2015-03-04 00:00:00', 'error': 'volume missing', 'last_values': [1425423600, 4882.94, 4917.35, 4856.14, 4917.35, 0.0]},
434 # 1: {'first_date': '2015-03-03 00:00:00', 'repl_add': 2, 'first_values': [1425337200, 4922.43, 4936.47, 4863.92, 4869.25, 116619800.0], 'last_date': '2015-02-20 00:00:00', 'error': '', 'last_values': [1424386800, 4821.38, 4837.93, 4780.81, 4830.9, 140055500.0]},
435 # 2: {'first_date': '2015-02-19 00:00:00', 'repl_add': 0, 'first_values': [1424300400, 4788.4, 4841.69, 4770.17, 4833.28, 0.0], 'last_date': '2015-02-17 00:00:00', 'error': 'volume missing', 'last_values': [1424127600, 4720.93, 4766.67, 4683.19, 4753.99, 0.0]}, 3: {'first_date': '2015-02-16 00:00:00', 'repl_add': 2, 'first_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0], 'last_date': '2015-02-16 00:00:00', 'error': '', 'last_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0]},
436 # 4: {'first_date': '2015-02-13 00:00:00', 'repl_add': 1, 'first_values': [1423782000, 4748.04, 4779.54, 4741.2, 4759.36, 0.0], 'last_date': '2015-02-12 00:00:00', 'error': 'volume missing', 'last_values': [1423695600, 4670.08, 4746.75, 4664.03, 4726.2, 0.0]}, 5: {'first_date': '2015-02-11 00:00:00', 'repl_add': 2, 'first_values': [1423609200, 4687.87, 4694.44, 4659.46, 4679.38, 94464200.0], 'last_date': '2015-02-09 00:00:00', 'error': '', 'last_values': [1423436400, 4652.16, 4659.26, 4611.15, 4651.08, 138917900.0]},
437 # 6: {'first_date': '2015-02-06 00:00:00', 'repl_add': 1, 'first_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0], 'last_date': '2015-02-06 00:00:00', 'error': 'volume missing', 'last_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0]}, 7: {'first_date': '2015-02-05 00:00:00', 'repl_add': 2, 'first_values': [1423090800, 4654.56, 4705.1, 4647.85, 4703.3, 121316800.0], 'last_date': '2015-02-04 00:00:00', 'error': '', 'last_values': [1423004400, 4682.81, 4696.3, 4651.78, 4696.3, 130750800.0]},
438 # 8: {'first_date': '2015-02-03 00:00:00', 'repl_add': 0, 'first_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0], 'last_date': '2015-02-03 00:00:00', 'error': 'volume missing', 'last_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0]}, 9: {'first_date': '2015-02-02 00:00:00', 'repl_add': 2, 'first_values': [1422831600, 4613.31, 4646.55, 4584.37, 4627.67, 123784000.0], 'last_date': '2015-01-28 00:00:00', 'error': '', 'last_values': [1422399600, 4660.45, 4660.88, 4582.58, 4610.94, 130618600.0]},
439 # 10: {'first_date': '2015-01-27 00:00:00', 'repl_add': 1, 'first_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0], 'last_date': '2015-01-27 00:00:00', 'error': 'volume missing', 'last_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0]}, 11: {'first_date': '2015-01-26 00:00:00', 'repl_add': 2, 'first_values': [1422226800, 4620.0, 4678.83, 4616.61, 4675.13, 127771700.0], 'last_date': '2015-01-21 00:00:00', 'error': '', 'last_values': [1421794800, 4455.01, 4484.82, 4406.72, 4484.82, 160196600.0]},
440 # 12: {'first_date': '2015-01-20 00:00:00', 'repl_add': 1, 'first_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0], 'last_date': '2015-01-20 00:00:00', 'error': 'volume missing', 'last_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0]}, 13: {'first_date': '2015-01-19 00:00:00', 'repl_add': 2, 'first_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0], 'last_date': '2015-01-19 00:00:00', 'error': '', 'last_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0]}, 14: {'first_date': '2015-01-16 00:00:00', 'repl_add': 1, 'first_values': [1421362800, 4303.55, 4387.22, 4294.76, 4379.62, 0.0], 'last_date': '2015-01-15 00:00:00', 'error': 'volume missing', 'last_values': [1421276400, 4262.94, 4338.73, 4119.35, 4323.2, 0.0]},
441 # 15: {'first_date': '2015-01-14 00:00:00', 'repl_add': 2, 'first_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0], 'last_date': '2015-01-14 00:00:00', 'error': '', 'last_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0]},
442 # 16: {'first_date': '2015-01-13 00:00:00', 'repl_add': 0, 'first_values': [1421103600, 4204.77, 4307.92, 4197.81, 4290.28, 0.0], 'last_date': '2014-12-10 00:00:00', 'error': 'volume missing', 'last_values': [1418166000, 4280.34, 4306.72, 4216.18, 4227.91, 0.0]},
443 # 17: {'first_date': '2014-12-09 00:00:00', 'repl_add': 2, 'first_values': [1418079600, 4332.73, 4342.12, 4260.07, 4263.94, 149779500.0], 'last_date': '2014-11-11 00:00:00', 'error': '', 'last_values': [1415660400, 4227.12, 4252.92, 4223.68, 4244.1, 99662200.0]}
444 # }
445  }
446 
447  # large test sample, CAC40 with many missing blocks
448 # m_test = {'HistPrice': {0: {'first_date': '2015-03-06 00:00:00', 'repl_add': 1, 'first_values': [1425596400, 4953.68, 4986.88, 4949.88, 4964.35, 0.0], 'last_date': '2015-03-04 00:00:00', 'error': 'volume missing', 'last_values': [1425423600, 4882.94, 4917.35, 4856.14, 4917.35, 0.0]},
449 # 1: {'first_date': '2015-03-03 00:00:00', 'repl_add': 2, 'first_values': [1425337200, 4922.43, 4936.47, 4863.92, 4869.25, 116619800.0], 'last_date': '2015-02-20 00:00:00', 'error': '', 'last_values': [1424386800, 4821.38, 4837.93, 4780.81, 4830.9, 140055500.0]},
450 # 2: {'first_date': '2015-02-19 00:00:00', 'repl_add': 0, 'first_values': [1424300400, 4788.4, 4841.69, 4770.17, 4833.28, 0.0], 'last_date': '2015-02-17 00:00:00', 'error': 'volume missing', 'last_values': [1424127600, 4720.93, 4766.67, 4683.19, 4753.99, 0.0]}, 3: {'first_date': '2015-02-16 00:00:00', 'repl_add': 2, 'first_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0], 'last_date': '2015-02-16 00:00:00', 'error': '', 'last_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0]},
451 # 4: {'first_date': '2015-02-13 00:00:00', 'repl_add': 1, 'first_values': [1423782000, 4748.04, 4779.54, 4741.2, 4759.36, 0.0], 'last_date': '2015-02-12 00:00:00', 'error': 'volume missing', 'last_values': [1423695600, 4670.08, 4746.75, 4664.03, 4726.2, 0.0]}, 5: {'first_date': '2015-02-11 00:00:00', 'repl_add': 2, 'first_values': [1423609200, 4687.87, 4694.44, 4659.46, 4679.38, 94464200.0], 'last_date': '2015-02-09 00:00:00', 'error': '', 'last_values': [1423436400, 4652.16, 4659.26, 4611.15, 4651.08, 138917900.0]},
452 # 6: {'first_date': '2015-02-06 00:00:00', 'repl_add': 1, 'first_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0], 'last_date': '2015-02-06 00:00:00', 'error': 'volume missing', 'last_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0]}, 7: {'first_date': '2015-02-05 00:00:00', 'repl_add': 2, 'first_values': [1423090800, 4654.56, 4705.1, 4647.85, 4703.3, 121316800.0], 'last_date': '2015-02-04 00:00:00', 'error': '', 'last_values': [1423004400, 4682.81, 4696.3, 4651.78, 4696.3, 130750800.0]},
453 # 8: {'first_date': '2015-02-03 00:00:00', 'repl_add': 0, 'first_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0], 'last_date': '2015-02-03 00:00:00', 'error': 'volume missing', 'last_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0]}, 9: {'first_date': '2015-02-02 00:00:00', 'repl_add': 2, 'first_values': [1422831600, 4613.31, 4646.55, 4584.37, 4627.67, 123784000.0], 'last_date': '2015-01-28 00:00:00', 'error': '', 'last_values': [1422399600, 4660.45, 4660.88, 4582.58, 4610.94, 130618600.0]},
454 # 10: {'first_date': '2015-01-27 00:00:00', 'repl_add': 1, 'first_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0], 'last_date': '2015-01-27 00:00:00', 'error': 'volume missing', 'last_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0]}, 11: {'first_date': '2015-01-26 00:00:00', 'repl_add': 2, 'first_values': [1422226800, 4620.0, 4678.83, 4616.61, 4675.13, 127771700.0], 'last_date': '2015-01-21 00:00:00', 'error': '', 'last_values': [1421794800, 4455.01, 4484.82, 4406.72, 4484.82, 160196600.0]},
455 # 12: {'first_date': '2015-01-20 00:00:00', 'repl_add': 1, 'first_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0], 'last_date': '2015-01-20 00:00:00', 'error': 'volume missing', 'last_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0]}, 13: {'first_date': '2015-01-19 00:00:00', 'repl_add': 2, 'first_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0], 'last_date': '2015-01-19 00:00:00', 'error': '', 'last_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0]}, 14: {'first_date': '2015-01-16 00:00:00', 'repl_add': 1, 'first_values': [1421362800, 4303.55, 4387.22, 4294.76, 4379.62, 0.0], 'last_date': '2015-01-15 00:00:00', 'error': 'volume missing', 'last_values': [1421276400, 4262.94, 4338.73, 4119.35, 4323.2, 0.0]},
456 # 15: {'first_date': '2015-01-14 00:00:00', 'repl_add': 2, 'first_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0], 'last_date': '2015-01-14 00:00:00', 'error': '', 'last_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0]},
457 # 16: {'first_date': '2015-01-13 00:00:00', 'repl_add': 0, 'first_values': [1421103600, 4204.77, 4307.92, 4197.81, 4290.28, 0.0], 'last_date': '2014-12-10 00:00:00', 'error': 'volume missing', 'last_values': [1418166000, 4280.34, 4306.72, 4216.18, 4227.91, 0.0]},
458 # 17: {'first_date': '2014-12-09 00:00:00', 'repl_add': 2, 'first_values': [1418079600, 4332.73, 4342.12, 4260.07, 4263.94, 149779500.0], 'last_date': '2014-11-11 00:00:00', 'error': '', 'last_values': [1415660400, 4227.12, 4252.92, 4223.68, 4244.1, 99662200.0]}
459 # }}
460 
461  # only first one is correclty extended
462 # m_test = {'HistPrice': {0: {'first_date': '2015-03-06 00:00:00', 'repl_add': 1, 'first_values': [1425596400, 4953.68, 4986.88, 4949.88, 4964.35, 0.0], 'last_date': '2015-03-04 00:00:00', 'error': 'volume missing', 'last_values': [1425423600, 4882.94, 4917.35, 4856.14, 4917.35, 0.0]},
463 # 1: {'first_date': '2015-03-03 00:00:00', 'repl_add': 2, 'first_values': [1425337200, 4922.43, 4936.47, 4863.92, 4869.25, 116619800.0], 'last_date': '2015-02-20 00:00:00', 'error': '', 'last_values': [1424386800, 4821.38, 4837.93, 4780.81, 4830.9, 140055500.0]},
464 # 2: {'first_date': '2015-02-19 00:00:00', 'repl_add': 0, 'first_values': [1424300400, 4788.4, 4841.69, 4770.17, 4833.28, 0.0], 'last_date': '2015-02-17 00:00:00', 'error': 'volume missing', 'last_values': [1424127600, 4720.93, 4766.67, 4683.19, 4753.99, 0.0]}, 3: {'first_date': '2015-02-16 00:00:00', 'repl_add': 2, 'first_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0], 'last_date': '2015-02-16 00:00:00', 'error': '', 'last_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0]},
465 # 4: {'first_date': '2015-02-13 00:00:00', 'repl_add': 1, 'first_values': [1423782000, 4748.04, 4779.54, 4741.2, 4759.36, 0.0], 'last_date': '2015-02-12 00:00:00', 'error': 'volume missing', 'last_values': [1423695600, 4670.08, 4746.75, 4664.03, 4726.2, 0.0]}, 5: {'first_date': '2015-02-11 00:00:00', 'repl_add': 2, 'first_values': [1423609200, 4687.87, 4694.44, 4659.46, 4679.38, 94464200.0], 'last_date': '2015-02-09 00:00:00', 'error': '', 'last_values': [1423436400, 4652.16, 4659.26, 4611.15, 4651.08, 138917900.0]},
466 # 6: {'first_date': '2015-02-06 00:00:00', 'repl_add': 1, 'first_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0], 'last_date': '2015-02-06 00:00:00', 'error': 'volume missing', 'last_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0]}, 7: {'first_date': '2015-02-05 00:00:00', 'repl_add': 2, 'first_values': [1423090800, 4654.56, 4705.1, 4647.85, 4703.3, 121316800.0], 'last_date': '2015-02-04 00:00:00', 'error': '', 'last_values': [1423004400, 4682.81, 4696.3, 4651.78, 4696.3, 130750800.0]},
467 # 8: {'first_date': '2015-02-03 00:00:00', 'repl_add': 0, 'first_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0], 'last_date': '2015-02-03 00:00:00', 'error': 'volume missing', 'last_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0]}, 9: {'first_date': '2015-02-02 00:00:00', 'repl_add': 2, 'first_values': [1422831600, 4613.31, 4646.55, 4584.37, 4627.67, 123784000.0], 'last_date': '2015-01-28 00:00:00', 'error': '', 'last_values': [1422399600, 4660.45, 4660.88, 4582.58, 4610.94, 130618600.0]},
468 # 10: {'first_date': '2015-01-27 00:00:00', 'repl_add': 1, 'first_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0], 'last_date': '2015-01-27 00:00:00', 'error': 'volume missing', 'last_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0]}, 11: {'first_date': '2015-01-26 00:00:00', 'repl_add': 2, 'first_values': [1422226800, 4620.0, 4678.83, 4616.61, 4675.13, 127771700.0], 'last_date': '2015-01-21 00:00:00', 'error': '', 'last_values': [1421794800, 4455.01, 4484.82, 4406.72, 4484.82, 160196600.0]},
469 # 12: {'first_date': '2015-01-20 00:00:00', 'repl_add': 1, 'first_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0], 'last_date': '2015-01-20 00:00:00', 'error': 'volume missing', 'last_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0]}, 13: {'first_date': '2015-01-19 00:00:00', 'repl_add': 2, 'first_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0], 'last_date': '2015-01-19 00:00:00', 'error': '', 'last_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0]}, 14: {'first_date': '2015-01-16 00:00:00', 'repl_add': 1, 'first_values': [1421362800, 4303.55, 4387.22, 4294.76, 4379.62, 0.0], 'last_date': '2015-01-15 00:00:00', 'error': 'volume missing', 'last_values': [1421276400, 4262.94, 4338.73, 4119.35, 4323.2, 0.0]},
470 # 15: {'first_date': '2015-01-14 00:00:00', 'repl_add': 2, 'first_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0], 'last_date': '2015-01-14 00:00:00', 'error': '', 'last_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0]},
471 # 16: {'first_date': '2015-01-13 00:00:00', 'repl_add': 0, 'first_values': [1421103600, 4204.77, 4307.92, 4197.81, 4290.28, 0.0], 'last_date': '2014-12-10 00:00:00', 'error': 'volume missing', 'last_values': [1418166000, 4280.34, 4306.72, 4216.18, 4227.91, 0.0]},
472 # 17: {'first_date': '2014-12-09 00:00:00', 'repl_add': 2, 'first_values': [1418079600, 4332.73, 4342.12, 4260.07, 4263.94, 149779500.0], 'last_date': '2014-11-11 00:00:00', 'error': '', 'last_values': [1415660400, 4227.12, 4252.92, 4223.68, 4244.1, 99662200.0]}
473 # },
474 # 'Other': {0: {'first_date': '2015-03-06 00:00:00', 'repl_add': 1, 'first_values': [1425596400, 4953.68, 4986.88, 4949.88, 4964.35, 0.0], 'last_date': '2015-03-04 00:00:00', 'error': 'volume missing', 'last_values': [1425423600, 4882.94, 4917.35, 4856.14, 4917.35, 0.0]},
475 # 1: {'first_date': '2015-03-03 00:00:00', 'repl_add': 2, 'first_values': [1425337200, 4922.43, 4936.47, 4863.92, 4869.25, 116619800.0], 'last_date': '2015-02-20 00:00:00', 'error': '', 'last_values': [1424386800, 4821.38, 4837.93, 4780.81, 4830.9, 140055500.0]},
476 # 2: {'first_date': '2015-02-19 00:00:00', 'repl_add': 0, 'first_values': [1424300400, 4788.4, 4841.69, 4770.17, 4833.28, 0.0], 'last_date': '2015-02-17 00:00:00', 'error': 'volume missing', 'last_values': [1424127600, 4720.93, 4766.67, 4683.19, 4753.99, 0.0]}, 3: {'first_date': '2015-02-16 00:00:00', 'repl_add': 2, 'first_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0], 'last_date': '2015-02-16 00:00:00', 'error': '', 'last_values': [1424041200, 4757.52, 4766.47, 4747.22, 4751.95, 99362700.0]},
477 # 4: {'first_date': '2015-02-13 00:00:00', 'repl_add': 1, 'first_values': [1423782000, 4748.04, 4779.54, 4741.2, 4759.36, 0.0], 'last_date': '2015-02-12 00:00:00', 'error': 'volume missing', 'last_values': [1423695600, 4670.08, 4746.75, 4664.03, 4726.2, 0.0]}, 5: {'first_date': '2015-02-11 00:00:00', 'repl_add': 2, 'first_values': [1423609200, 4687.87, 4694.44, 4659.46, 4679.38, 94464200.0], 'last_date': '2015-02-09 00:00:00', 'error': '', 'last_values': [1423436400, 4652.16, 4659.26, 4611.15, 4651.08, 138917900.0]},
478 # 6: {'first_date': '2015-02-06 00:00:00', 'repl_add': 1, 'first_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0], 'last_date': '2015-02-06 00:00:00', 'error': 'volume missing', 'last_values': [1423177200, 4698.84, 4707.24, 4675.37, 4691.03, 0.0]}, 7: {'first_date': '2015-02-05 00:00:00', 'repl_add': 2, 'first_values': [1423090800, 4654.56, 4705.1, 4647.85, 4703.3, 121316800.0], 'last_date': '2015-02-04 00:00:00', 'error': '', 'last_values': [1423004400, 4682.81, 4696.3, 4651.78, 4696.3, 130750800.0]},
479 # 8: {'first_date': '2015-02-03 00:00:00', 'repl_add': 0, 'first_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0], 'last_date': '2015-02-03 00:00:00', 'error': 'volume missing', 'last_values': [1422918000, 4651.07, 4701.52, 4645.15, 4677.9, 0.0]}, 9: {'first_date': '2015-02-02 00:00:00', 'repl_add': 2, 'first_values': [1422831600, 4613.31, 4646.55, 4584.37, 4627.67, 123784000.0], 'last_date': '2015-01-28 00:00:00', 'error': '', 'last_values': [1422399600, 4660.45, 4660.88, 4582.58, 4610.94, 130618600.0]},
480 # 10: {'first_date': '2015-01-27 00:00:00', 'repl_add': 1, 'first_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0], 'last_date': '2015-01-27 00:00:00', 'error': 'volume missing', 'last_values': [1422313200, 4671.76, 4679.26, 4592.62, 4624.21, 0.0]}, 11: {'first_date': '2015-01-26 00:00:00', 'repl_add': 2, 'first_values': [1422226800, 4620.0, 4678.83, 4616.61, 4675.13, 127771700.0], 'last_date': '2015-01-21 00:00:00', 'error': '', 'last_values': [1421794800, 4455.01, 4484.82, 4406.72, 4484.82, 160196600.0]},
481 # 12: {'first_date': '2015-01-20 00:00:00', 'repl_add': 1, 'first_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0], 'last_date': '2015-01-20 00:00:00', 'error': 'volume missing', 'last_values': [1421708400, 4406.96, 4463.51, 4405.94, 4446.02, 0.0]}, 13: {'first_date': '2015-01-19 00:00:00', 'repl_add': 2, 'first_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0], 'last_date': '2015-01-19 00:00:00', 'error': '', 'last_values': [1421622000, 4388.67, 4423.25, 4367.24, 4394.93, 107393400.0]}, 14: {'first_date': '2015-01-16 00:00:00', 'repl_add': 1, 'first_values': [1421362800, 4303.55, 4387.22, 4294.76, 4379.62, 0.0], 'last_date': '2015-01-15 00:00:00', 'error': 'volume missing', 'last_values': [1421276400, 4262.94, 4338.73, 4119.35, 4323.2, 0.0]},
482 # 15: {'first_date': '2015-01-14 00:00:00', 'repl_add': 2, 'first_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0], 'last_date': '2015-01-14 00:00:00', 'error': '', 'last_values': [1421190000, 4227.46, 4302.22, 4207.37, 4223.24, 151689400.0]},
483 # 16: {'first_date': '2015-01-13 00:00:00', 'repl_add': 0, 'first_values': [1421103600, 4204.77, 4307.92, 4197.81, 4290.28, 0.0], 'last_date': '2014-12-10 00:00:00', 'error': 'volume missing', 'last_values': [1418166000, 4280.34, 4306.72, 4216.18, 4227.91, 0.0]},
484 # 17: {'first_date': '2014-12-09 00:00:00', 'repl_add': 2, 'first_values': [1418079600, 4332.73, 4342.12, 4260.07, 4263.94, 149779500.0], 'last_date': '2014-11-11 00:00:00', 'error': '', 'last_values': [1415660400, 4227.12, 4252.92, 4223.68, 4244.1, 99662200.0]}
485 # }
486 # }
487 
488 # Test only the GUI
489 
490  # one instance
491 # m_app = ValidationTkinter("toto", test)
492 # m_app.mainloop()
493 
494  # two instances one after another
495 # m_root_tk = tk.Tk()
496 # m_app = ValidationTkinter( "toto", test, root_tk)
497 # m_app.mainloop()
498 # # works here, app.destroy, with self.quit. Not working if called with delete window, see MW_DELETE
499 # m_app.destroy()
500 # m_app = ValidationTkinter( "toto", test, root_tk)
501 # m_app.mainloop()
502 # m_app.destroy()
503 #
504 # sys.exit(1)
505 
506 # Simple multi-thread test
507 
508  def m_run_thread(stock, man_tk):
509  print "Start thread name", threading.current_thread().name
510  print "tk_app ref", man_tk
511  #pydevd.settrace( 5678 )
512 
513  print "send new message to the queue"
514  man_tk.write( [ stock, m_test ])
515  print "thread waiting answer", threading.current_thread().name
516  new_dict = man_tk.read_answer()
517  print "modified dict ", new_dict
518 
519  def m_thd_pool_run(list_stock, man_tk):
520  print "Entry thd_pool_run name", threading.current_thread().name
521  #pydevd.settrace( 5678 )
522 
523  print "thd_pool creates a pool of thread"
524  thread_list = []
525  for stock in list_stock:
526  # run_thread makes pre and post processing for the single stock methods
527  t = threading.Thread(target=m_run_thread, name="thread_"+stock, args = (stock, man_tk)) #,self.e_action))
528  thread_list.append(t)
529 
530  for th in thread_list:
531  th.start()
532 
533  # master waits all thread to end
534  for th in thread_list:
535  th.join()
536 
537  print "thd_pool all joined, send quit signal"
538  man_tk.write(["QUIT"])
539  print "thd_pool will die..."
540 
541  print "\n== Starting program, master create Tk app"
542  #app = ValidationTkinter() # test, "TOTO" )
543  m_manager_tk = ManagerTk()
544  print "manager_tk ", m_manager_tk
545  m_list_stock = ["GSZ","CAC40"]
546 
547  print "\n== master creates thread pool"
548  m_thd_pool = threading.Thread(target=m_thd_pool_run, args=(m_list_stock, m_manager_tk)) #args=(task_q, result_q))
549  m_thd_pool.start()
550 
551  # let pool creates everything
552  print "\n== master after sleep, run the manager"
553  #app.mainloop()
554  # similar to a mainloop()
555  m_manager_tk.update_me()
556 
557  print "\n==master app has been killed"
558  print "thd_pool alive ?", m_thd_pool.is_alive()
559 
560  print "end of test, quit"
561  # test modified with new values of repl_add
562  #print "\n== Test Output ", m_test
def create_title
Label with stock symbol.
Definition: Validation.py:298
Manage and serialize the access to a ValidationTkinter application, necessary when UpdateStock is run...
Definition: Validation.py:346
def update_me
Check regularly and indefinitely, the queue.
Definition: Validation.py:387
def _check_stop
Check state of the object before stopping.
Definition: Validation.py:410
def create_one_input_xml
Fill the frame specific to xml data: Info / Fundamental.
Definition: Validation.py:234
A pure Tkinter scrollable frame that actually works!.
Definition: Validation.py:28
_root_tk
store a Tk main environment.
Definition: Validation.py:362
def valid
Check all radio buttons have been selected and update repl_add entry with _var_radiob.
Definition: Validation.py:206
def create_button_valid
Create Valid and Quit buttons at the bottom of the main window.
Definition: Validation.py:303
queue
Queue to receive and send data (dict_interactive and stock symbol).
Definition: Validation.py:355
def popupmsg
Message indicating that some entries have not been validated.
Definition: Validation.py:312
_var_radiob
store in dictionary all IntVar associated to radio buttons.
Definition: Validation.py:97
def create_one_input_csv
Fill the frame for list_csv : HistPrice Last version, group similar entries.
Definition: Validation.py:257
GUI interface to validate the update and saving of data.
Definition: Validation.py:82
def create_widgets
Create the main window with the data from dict_input.
Definition: Validation.py:139
def read_answer
Return the updated dict_interactive.
Definition: Validation.py:374
def write
Write a new input to the queue.
Definition: Validation.py:370