ServerPortfolio  2.0
Python parsers and server
 All Classes Namespaces Files Functions Variables Properties Pages
ValidStockUpdate.py
Go to the documentation of this file.
1 ## @package serverportfolio.ValidStockUpdate
2 # @brief Define class ValidStockUpdate.
3 #
4 # Last changed $Id$
5 
6 # Split validation, check and save of a Stock after update
7 
8 from __future__ import with_statement # 2.5 only
9 
10 import logging, types, math
11 import datetime, time, threading
12 
13 from serverportfolio import Utils
14 from serverportfolio.GlobalDicts import EAction
15 from serverportfolio.StockTemplates import StTmpl
16 from serverportfolio.PortfolioException import PortfolioError #, ParserError, QueryError
17 
18 from serverportfolio.Validation import ValidationTkinter
19 
20 ## @class ValidStockUpdate
21 # @brief Extend class Stock with functions related to the validation and saving after a stock update: most of the post_process functionalities.
22 #
23 # Object is owned by a Stock object, created in Stock.post_process\n
24 #
25 # Internally dict_interactive is used to check and validate data from Stock.dict_stock\n
26 # In save_new_data it is merged back to Stock.dict_stock and write into file.
28  ## @brief Constructor.
29  # Always owned by a single parent Stock
30  # @param parent_stock stock object
31  # @param tk_manager instance of a ManagerTk, needed in interactive session
32  def __init__(self, parent_stock, tk_manager=None):
33  self._logger = logging.getLogger('SP.ValidUpdateStock')
34  self._logger.debug('Init ValidUpdateStock')
35  self._logger.debug('parent symbol: %s', parent_stock.symbol)
36  self._logger.debug('tk_manager: %s' % tk_manager)
37  ## @brief Associated to an unique stock.
38  self._parent_stock = parent_stock
39  ## @brief Dictionary of data after check_new_data.
41  ## @brief Store a ManagerTk interface in multi-thread mode.
42  self._tk_manager = tk_manager
43 
44  ## @brief Return the parent Stock.
45  def get_parent(self):
46  return self._parent_stock
47 
48  ## @name Generic functions to valid a specific action
49  ## @{
50 
51  ## @brief Assure the post_processing of a Stock after the parsing of one unique action.
52  # This function is intended to be executed during the post_processing stage of a stock, with a specific action.
53  # @param e_action action
54  # @param option_post option to the post-process stage
55  def post_process_one_action(self, e_action, option_post): #post_process in init ?
56  self._logger.debug("post_process_one_action %s", e_action.name)
57  interactive = option_post['interactive']
58  to_save = option_post['to_save']
59  # if to_save and interactive False. nothing to do !
60 
61  # reset dict_interactive for each new_action, maybe not if nothing to update ...
62  # to be in init ? case multiple call ?
63  # self._dict_interactive = {}
64  # compare data and generate a valid dict_interactive
65  try :
66  self._logger.debug("== check new data")
67  self.check_new_data( e_action, interactive)
68  except PortfolioError as ex:
69  self._logger.error("Caught PortfolioError in check_new_data")
70  self._logger.error("ex: %s" % ex.get_format_string())
71  raise
72  # user validation if interactive
73  if interactive == True:
74  self._logger.debug("Interactive True")
75  self._logger.debug("beforeGUI")
76  try:
77  self.valid_interactive() #e_action)
78  except Exception as ex:
79  self._logger.error("Caught Exception from ValidationTkinter ex: %s" % ex )
80  raise
81  self._logger.debug("out GUI dict_interactive: %s" % self._dict_interactive)
82  # save to the filesystem
83  if to_save:
84  # better to test if empty, but should work and produce the same file, good for unit-test
85  if any(self._dict_interactive):
86  # merge dict_interactive to dict_xml, then write the file
87  self.save_new_data(e_action)
88  else:
89  self._logger.info("dict_interactive is empty, nothing to update")
90  else :
91  self._logger.debug("Save flag not set")
92 
93  ## @brief Check the validity of the last updated values from Stock.dict_stock and generate dict_interactive.
94  # Executed before an automatic or interactive update of the XML data (_dict_xml)\n
95  # Updated data are stored in self.dict_interactive
96  def check_new_data(self, e_action, interactive ):
97  self._logger.debug("check_new_data e_action %s" % e_action)
98  self._logger.debug("interactive %s", interactive)
99  dict_inter_action = None
100  # valid an xml update
101  if self.get_parent().saved_as(e_action.name, 'xml'):
102  try:
103  # fill stock._dict_xml, to compare with previous values(if any)
104  # Should check it is not already in memory !!
105  # to move to read_xml ? better ?
106  if not any(self.get_parent().get_dict_xml()):
107  self.get_parent().read_xml()
108  # specific to one action
109  dict_inter_action = self._valid_new_value_xml( e_action.name, interactive)
110  except PortfolioError as ex:
111  self._logger.error("Caught PortfolioError in check_new_data")
112  raise
113  except Exception as ex:
114  self._logger.error("Caught Exception in check_new_data: %s" % ex)
115  raise
116  # case HistPrice/...
117  if self.get_parent().saved_as(e_action.name, 'csv'):
118  try:
119  # Only HistPrice is implemented, need other function ? or default one
120  dict_inter_action = self._valid_new_value_csv( e_action.name, interactive )
121  except PortfolioError as ex:
122  self._logger.error("Caught PortfolioError in check_new_data")
123  raise
124  except Exception as ex:
125  self._logger.error("Caught Exception in check_new_data: %s" % ex)
126  raise
127 
128  # update dict_interactive
129  if any(dict_inter_action):
130  self._dict_interactive[e_action.name] = dict_inter_action
131 
132  ## @brief Final merge (after user/auto-validation) and write to file.
133  # The correct format xml/csv is called.
134  def save_new_data(self, e_action ):
135  self._logger.debug("save_new_data e_action.name %s" % e_action.name)
136  # save in xml format
137  if self.get_parent().saved_as(e_action.name, 'xml'):
138  try:
139  self.save_new_xml_data()
140  except Exception as ex:
141  self._logger.info("Caught Exception ex:%s" % ex)
142  raise
143  # specific functions here
144  if self.get_parent().saved_as(e_action.name, 'csv'):
145  print "Deal with CSV"
146  if e_action == EAction.HistPrice:
147  if any(self._dict_interactive):
148  self.get_parent().save_hist_price()
149  else:
150  print "to write"
151 
152  ## @brief User validation of the new data.
153  # Use a Tk GUI Interface for the validation.\n
154  # Tkinter does not support multi-threading, a lock must be implemented.
155  # Should try to regroup 2 interfaces in one
156  def valid_interactive(self): # e_action not used
157  self._logger.debug("valid_interactive, tk_manager %s" % self._tk_manager)
158  try:
159  # use tk_manager interface, necessary if not in the main thread
160  if self._tk_manager:
161  # update of dict_interactive, send to the queue and wait answer from the queue_answer
162  self._tk_manager.write([self.get_parent().symbol, self._dict_interactive])
163  self._dict_interactive = self._tk_manager.read_answer()
164  # create directly the GUI
165  else:
166  app = ValidationTkinter(self.get_parent().symbol, self._dict_interactive)
167  app.mainloop()
168  # app use quit, not destroyed internally
169  app.destroy()
170  except Exception as ex:
171  self._logger.error("Caught exception from ValidationTkinter/ ex:")
172  raise
173  ## @}
174 
175 # ############ Specific functions to deal with XML data
176  ## @name Specific functions to deal with XML
177  ## @{
178 
179 #
180 # this function is not used,loop over action
181 
182  ## @brief Check the validity of the new retrieved data stored Stock._dict_stock.
183  # Executed before an automatic or interactive update of the XML data (_dict_xml)\n
184  # Valid data are stored in self.dict_interactive\n
185  # @param opt_interactive optional validation by user (GUI)
186  def check_new_xml_data(self, opt_interactive = False):
187  self._logger.debug("opt_interactive %s", opt_interactive)
188  dict_interactive = {}
189  # create a list of data to update, need specialization by template ??
190  # at best, check if some data are present !!
191  self._logger.debug("_dict_stock: %s", self._dict_stock)
192  # check global error ?
193  #if self.get_dict_parser()['global_error']:
194  # self._logger.debug("Global Error ", self.get_dict_parser()['global_error'] )
195  # loop over the recent action retrieved by parser
196  for key,value in self.get_action().iteritems() :
197  self._logger.debug("key/value %s %s" % (key, value))
198 
199  if self.saved_as( key, 'xml' ):
200  # should not throw exception if possible, better error for interactive mode
201  dict_action_inter = self._valid_new_value_xml( key, opt_interactive )
202 
203  if any(dict_action_inter):
204  dict_interactive[ key ] = dict_action_inter
205 
206  #else pass
207 
208  self._logger.debug("\nFinal dict_interactive:\n%s" % dict_interactive)
209  print Utils.pretty_dict( dict_interactive )
210  self.dict_interactive = dict_interactive
211 
212  ## @brief Final merge (after user/auto validation) and write XML file.
213  # This step includes the merging of the data in self.dist_interactive,\n
214  # and (re-)write the file by a call to write_xml, include TEST_MODE for unit-test
215  def save_new_xml_data(self):
216  self._logger.debug("Entry save_new_xml_data")
217  # loop over the data in dict_interactive (have been validated)
218  for (key_action, value_action) in self._dict_interactive.iteritems():
219  self._logger.debug("key_action/value_action %s / %s" % (key_action, value_action))
220  # dictionary to update
221  dict_xml = self.get_parent().get_dict_xml( key_action )
222  # for each action loop over key_data and value_data of dict_interactive
223  # check if modifed to 0 or 2
224  for (key_data, value_data) in value_action.iteritems():
225  self._logger.debug("\nkey_data / value_data %s / %s" % (key_data, value_data))
226  # certainly to save, or to write into file for feedback
227  if value_data['repl_add'] > 0:
228  self._logger.info("update xml value / key_data : %s / %s / %s " % \
229  (self.get_parent().symbol, key_data, value_data['new_value']) )
230  # value can overwite the default, use the last one ...?
231  if dict_xml['template'][key_data][-1]['attrib']['append'] == True:
232  #b_append_data = True
233  print "append data"
234  new_tmpl_value = StTmpl.get_template_one_value_xml()
235  new_tmpl_value['value'] = value_data['new_value']
236  new_tmpl_value['attrib']['date'] = value_data['date']
237  new_tmpl_value['attrib']['source'] = value_data['source']
238  new_tmpl_value['attrib']['append'] = 'True'
239  # append to the list
240  dict_xml['template'][ key_data ].append( new_tmpl_value )
241  # replace the last value, do not modify the previous entries
242  else:
243  dict_xml['template'][ key_data ][-1]['value'] = value_data['new_value']
244  # format timestamp for C++, transform on the fly ? date added automatically
245  dict_xml['template'][ key_data ][-1]['attrib']['date'] = value_data['date']
246  dict_xml['template'][ key_data ][-1]['attrib']['source'] = value_data['source']
247  # delete entry ? just pass
248  #else:
249  self._logger.debug("dict_xml before wirte_xml:\n%s " % Utils.pretty_dict(self.get_parent().get_dict_xml()) )
250  self._logger.debug("dict_xml before wirte_xml:\n%s " % (self.get_parent().get_dict_xml()) )
251  try:
252  self.get_parent().write_xml()
253  except Exception as ex:
254  self._logger.info("Caught Exception ex:%s" % ex)
255  raise
256 
257 # ####### Helper functions
258 
259  ## @brief Valid last values retrieved with parsers, try to guess values to be updated.
260  # \pre last values in parent dict_stock and dict_stock should not be None, assert
261  # @param key_action action name
262  # @param interactive
263  # @return value to be stored in dict_interactive['action']
264  def _valid_new_value_xml(self, key_action, interactive ):
265  self._logger.debug("Entry _valid_new_xml_value, key_action %s", key_action)
266  self._logger.debug("interactive %s", interactive)
267  # sure existing if here
268  self._logger.debug("new_data / _dict_stock action %s", self.get_parent().get_action( key_action ))
269  # not sure existing
270  self._logger.debug("xml_data / _dict_xml %s", self.get_parent().get_dict_xml( key_action ))
271  # shortcut action / key for new_data and xml_data
272  new_data = self.get_parent().get_action( key_action )
273  assert ( new_data != None ), "New_data should not be None at this stage"
274  # always try to update the source, better when initialising a new XML file
275  # assert to test at start of post-process
276  assert 'source' in new_data, "source is missing"
277  new_data_source = new_data['source']
278 
279  # maybe empty, maybe better to init in get_dict_xml, certainly
280  # could ask xml_dict_value / xml_dict_config
281  xml_data = self.get_parent().get_dict_xml( key_action )
282 
283  # look like a global attrib entry was present (from _templ_action), and then specific for each value
284  #if xml_data['config']['attrib']['source'] == None:
285  # xml_data['config']['attrib']['source'] = new_data_source
286 
287  # values to update, all will be finally stored dict_interactive
288  dict_inter_action = {}
289  # named data, e.g. 'BVPS' : value
290  # cannot delete entry during an iteration with iteritems(), with keys() seems ok
291  # loop over new_data, could be used with only one or 2 entries ?
292  for key_data in new_data.keys():
293  # case of other...only if interactive
294  if (key_data == 'other') | (key_data == 'error') | (key_data == 'source'): # source exception catched and continue
295  continue
296  # shortcut and allow to correct type in case of None (in XML)
297  new_value = new_data[key_data]
298  # get the last one from the list
299  # try maybe could be used with 'other', here only for test
300  try :
301  xml_value = xml_data['template'][key_data][-1]['value']
302  # empty template one_value not correct
303  except:
304  self._logger.error("key_data %s not found in xml_value" % key_data)
305  # to set a default ? '',0 ?
306  continue
307  # type_value is dependent of action
308  if ( key_action == "Fundamental"):
309  type_values = types.FloatType
310  elif ( key_action == 'Info'):
311  type_values = types.StringType
312  else:
313  self._logger.error("action not implemented in list_update_xml_value_generic")
314  type_values = types.StringType
315  # should work for most for these two types. send only simple variable, no lists
316  try:
317  status = ValidStockUpdate.check_value( new_value, xml_value, type_values )
318  # happens in case of None and None, return 2 wrong
319  except Exception as ex:
320  self._logger.info("Exception check_value: new_value %s, xml_value %s, status %d" % (new_value, xml_value, status))
321  self._logger.info("ex: %s" % ex)
322 
323  self._logger.debug("new_value %s, xml_value %s, status %d" % (new_value, xml_value, status))
324 
325  # discard data by default, delete from _dict_parser, do not insert in the dict_interactive
326  # xml unchanged
327  if ( status <= 0 ) | ( ( status==1) & (not interactive) ):
328  self._logger.debug("Discard this data")
329  # Can delete during iteration with keys() or values() only
330  # del self.get_dict_parser( key_action )[key_data]
331  continue
332  # interactive session, status == 2 force update
333  # insert element of comparison in dict_interactive
334  elif (status == 2 ) | (( status == 1) & interactive) :
335  # get a deepcopy, erase previous data
336  # validXML should not be in templ
337  #tmpl_new_value = StTmpl.get_template_stock("ValidXML")
338  tmpl_new_value = StTmpl.get_template_valid_xml_value()
339  # fill with new value
340  tmpl_new_value['new_value'] = new_value
341  # original value
342  tmpl_new_value['xml_value'] = xml_value
343  # date could come from parser, today is also ok
344  tmpl_new_value['date'] = datetime.datetime.now().strftime("%Y-%m-%d")
345  # status for the mode of update
346  tmpl_new_value['repl_add'] = status
347  # update source, needed here, to pass to final dictionary
348  tmpl_new_value['source'] = new_data_source
349  # add all terms to dict_interactive
350  dict_inter_action[ key_data ] = tmpl_new_value
351  self._logger.debug("dict_inter_action %s: %s" % (key_action,dict_inter_action))
352  return dict_inter_action
353 
354  ## @}
355 
356  ## @name Specific functions to deal with CSV data
357  ## @{
358 
359 # not used, loop over all actions
360  ## @brief Check the CSV data are not wrong.
361  #
362  # Bug with Yahoo, sometimes data download are wrong (can be similar in the web page).
363  # Bug found:
364  # - Volume all 0, or some 0
365  # - All values are identical : 4703.3, 4703.3, 4703.3, 4703.3
366  # - GSZ : 25 and 26 December, 1 january same value, volume 0 ! (can be hard coded a for a few days..)
367  #
368  # Return True if correct, and then can proceed with saving the data into file.\n
369  # Return False, some errors are found, the data should not be saved by default. But still downloadable ?
370  #
371  # Set global error to HistPrice, set local HistPrice error
372  # return True if correct
373  # to extend DivSplit, InstValue not needed now
374  def check_list_csv(self, opt_interactive = False):
375  self._logger.debug("check_list_csv interactive %s", opt_interactive)
376 
377  dict_interactive = {}
378 
379  # only list_csv or list_split, list_div to update
380  # loop over the recent action retrieved by parser, common to HistPrice...
381  for key_action,value in self.get_action().iteritems() :
382 
383  # apply only to HistPrice
384  if key_action != 'HistPrice':
385  continue
386 
387  dict_action_inter = self._valid_new_value_csv( key_action, opt_interactive )
388 
389  # return false if only key is integer(0), should not happen if here (and HistPrice/list_csv)
390  #if any(dict_action_inter):
391  if len(dict_action_inter):
392  dict_interactive[ key_action ] = dict_action_inter
393 
394  self._logger.debug("\nFinal dict_interactive:\n%s" % dict_interactive)
395 
396  # because of key integer, cannot use
397  #print Utils.pretty_dict( dict_interactive )
398  self.dict_interactive = dict_interactive
399 
400  # with GSZ HistPrice, example of output without error, to use in test
401  # new_data / _dict_parser {'source': 'YCSV', 'last_date': datetime.datetime(2015, 3, 2, 0, 0),
402  # 'first_date': datetime.datetime(1970, 1, 1, 1, 0),
403  # 'list_csv': [[1425250800, 19.8, 19.86, 19.37, 19.44, 6279800.0], [1424991600, 19.46, 19.88, 19.35, 19.88, 8207000.0], [1424905200, 19.51, 19.74, 19.39, 19.48, 6426300.0], [1424818800, 19.25, 19.45, 19.17, 19.29, 5575500.0], [1424732400, 18.86, 19.35, 18.62, 19.24, 8116600.0], [1424646000, 18.8, 18.84, 18.59, 18.81, 5173700.0], [1424386800, 18.66, 18.74, 18.44, 18.59, 6623200.0], [1424300400, 18.53, 18.69, 18.43, 18.68, 5664000.0], [1424214000, 18.91, 18.98, 18.56, 18.68, 4637300.0], [1424127600, 18.51, 18.76, 18.27, 18.74, 4519600.0], [1424041200, 18.71, 18.72, 18.54, 18.6, 3649000.0], [1423782000, 18.89, 18.92, 18.72, 18.72, 6893900.0], [1423695600, 18.75, 18.86, 18.5, 18.8, 5891100.0], [1423609200, 18.99, 19.0, 18.67, 18.77, 5178200.0], [1423522800, 18.75, 18.99, 18.5, 18.96, 4967600.0], [1423436400, 18.9, 18.94, 18.53, 18.68, 6571700.0], [1423177200, 19.19, 19.21, 19.0, 19.01, 4635800.0], [1423090800, 19.33, 19.4, 19.1, 19.15, 8390900.0], [1423004400, 19.63, 19.73, 19.46, 19.6, 5781600.0], [1422918000, 19.9, 20.05, 19.55, 19.7, 9223500.0], [1422831600, 19.72, 19.88, 19.59, 19.83, 4641200.0]],
404  # 'error': None}
405 
406 # Only HistPrice checked
407  ## @brief Fill dict_interactive in case of list of data (HistPrice action implemented)
408  # dict_inter_action stores chronological group of data, with or without errors. Same notation status 0, 1 or 2.\n
409  # Groups are continguous data considered as valid or invalid.
410  # @param key_action name of the action
411  # @param interactive boolean if interactive mode is present. Wrong data will have a status of 1 if active.
412  # @return a dict_interactive for this specific action
413  def _valid_new_value_csv(self, key_action, interactive):
414  self._logger.debug("action %s", key_action)
415  # copy code from _check_new_xml
416  self._logger.debug("Entry _valid_new_value_csv: %s", self.get_parent().symbol)
417  self._logger.debug("interactive %s", interactive)
418  # sure existing if here
419  self._logger.debug("new_data / _dict_parser %s", self.get_parent().get_action( key_action ))
420  # shortcut param action / key
421  new_data = self.get_parent().get_action( key_action )
422  assert ( new_data != None ), "New_data should not be None at this stage"
423  # values to update, all stored finally in dict_interactive
424  dict_inter_action = {}
425  # use as key for dict_interactive
426  count_group = 0
427  for key_data in new_data.keys():
428  # check dates, only for list, maybe 'list_*'
429  # only stored in dict_inter_action[HistPrice], what for Div/Split ?
430  if key_data != 'list_csv':
431  continue
432 
433  #initial value, first for sure exists ?
434  first_date = Utils.timestamp_to_string(new_data[key_data][0][0])
435  first_values = new_data[key_data][0]
436  # need a function here status, error = _error_list_csv(values)
437  first_status, first_error = ValidStockUpdate._error_list_csv(new_data[key_data][0], interactive)
438  # case only one value !
439  count_values = 0
440  #print "number of data", (len(new_data[key_data]))
441  #for values in self.get_dict_parser(key_action)['list_csv']:
442  for values in new_data[key_data][1:]: #['list_csv'] only list_csv ?
443  # can check first and last dates correspond to dates saved in templates
444  count_values += 1
445  # check specific for HistPrice with YahooCSV, list of possible bugs
446  status, error = ValidStockUpdate._error_list_csv(values, interactive)
447  if (status != first_status) | ( count_values == ( len(new_data[key_data])-1) ) :
448  #dict_inter_action[key_data][count_group] = {
449  dict_inter_action[count_group] = {
450  'repl_add' : first_status,
451  'error' : first_error,
452  'first_date': first_date,
453  'first_values' : first_values,
454  # need previous one if not at the end
455  'last_date': Utils.timestamp_to_string( new_data[key_data][count_values-1][0] ),
456  'last_values' : new_data[key_data][count_values-1]
457  }
458  # if last data in list, overwrite last and break
459  if count_values == len(new_data[key_data]) :
460  dict_inter_action[count_group]['last_date'] = Utils.timestamp_to_string(values[0])
461  dict_inter_action[count_group]['last_values'] = values
462  break
463  # still some data
464  count_group += 1
465  # reset first to actual
466  first_status = status
467  first_error = error
468  first_date = Utils.timestamp_to_string( values[0] )
469  first_values = values
470  self._logger.debug("dict_inter_action %s: %s" % (key_action,dict_inter_action))
471  return dict_inter_action
472 
473  ## @brief Check for common error in historical price data and return a status and an error code.
474  # @param values all 6 values [date,4 values,volume] in list_csv (HistPrice)
475  # @param interactive boolean
476  # @return tuple(status,error_code)
477  @staticmethod
478  def _error_list_csv( values, interactive ):
479  # default error
480  status = 0
481  # can change interactively all entries
482  if interactive:
483  status = 1
484  # default value
485  error = ''
486  # check values bug, second overwrittes
487  if values[1] == values[2] == values[3] == values[4]:
488  error = 'values equal'
489  if float(values[5]) == 0:
490  error = 'volume missing'
491  else:
492  status = 2
493  return (status, error)
494 
495 # to write, using a very similar wrapper. data already in stock
496 # previous in YahooCSV.UpdateCSV
497 # GetListCSV replaced by UpdateStocks.update_data
498 # here only to test ? or done before ?
499 # to send new_data to wrapPyStock.UpdateCSV( self.symbol, listCSV, GlobalDicts.TEST_MODE )
500 # function to write : py_to_C, (C_to_py) , generic function to transform structure list_csv and other.
501 
502 # Not used here ? only in Stock ? still in use ?
503  ## @brief Generate a sub-list of list_csv, starting from the last call until the last_date (included)
504  #
505  # First call, after initialization, start at the first data of the list\n
506  # Not found a nice way to run the first call, must use None\n
507  # or could pass the list_data as well, but confusing...
508  # @code
509  # self.gen_part_of_list_csv(None)
510  # sub_list_generator.send(None)
511  # ...
512  # sub_list = sub_list_generator.send( last_date )
513  # @endcode
514  #
515  # @param last_date, certainly always the
516  # @return a list of csv [last_call, last_date]
517  def gen_part_of_list_csv(self, last_date):
518  #self._logger.debug("entry gen_part_list_csv, last_date %s %s" % (last_date,Utils.timestamp_to_string(last_date)))
519  self._logger.debug("entry gen_part_list_csv, last_date %s " % (last_date) )
520  assert 'list_csv' in self.get_action('HistPrice'),'list_csv not loaded !'
521  # only for initialization send(None)
522  if last_date == None:
523  last_date = yield None
524  # fixed to this list, not chronologic in memory
525  p_list = self.get_action('HistPrice','list_csv')
526  i = len(p_list)-1
527 
528  part_list = []
529  while i >= 0:
530  if p_list[i][0] <= last_date:
531  part_list.append(p_list[i])
532  else:
533  # return the sub-list
534  last_date = yield part_list
535  # last_date updated and reinitialise a new sub-list
536  part_list = [ p_list[i] ]
537  i -= 1
538  # what's for the last ? never called.
539  return
540  ## @}
541 
542  # ########## Static methods
543 
544  ## @brief Generic function to check if new_value can be updated.
545  # Return:
546  # - 2 Update in all cases
547  # - 1 Propose to update if Interactive, otherwise discard. Output in log to control(maybe other file). Used also if new_value is the default one.
548  # - 0 Invalid, eliminate in all cases
549  # -1 Valid but identical, no point to update
550  #
551  # A conversion of new_value and xml_value to type_value are performed as a check\n
552  # No exception are throw if types are wrong and return 0
553  # @param new_value recent value, obtained from parsers
554  # @param xml_value last value in xml
555  # @param type_value StringType or FloatType. Integers are converted to float.
556  @staticmethod
557  def check_value(new_value, xml_value, type_value):
558  #print "new_value ", new_value
559  #print "xml_value ", xml_value
560  #print "type to convert ", type_value
561  # try to convert, and check value if convertible
562  try:
563  # throw ValueError, float('a'), eliminate all alplanumeric, case of 56B, to deal in Parser
564  new_value = type_value(new_value)
565  status = ValidStockUpdate.is_valid_value( new_value, type_value )
566  # float(None) return TypeError, do not care really, rejected at this point
567  except (ValueError,TypeError): # as ex:
568  # static method
569  #self._logger.info("Error in type_value or is_valid_value")
570  return 0
571  except Exception as ex:
572  #self._logger.info("Got exception ex: %s", ex)
573  print "Got exception ex: %s", ex
574  return 0
575  # status 0 is not valid
576  if status == 0:
577  return 0
578  # test previous xml value
579  # if no modif, empty XML gives a NoneType
580  # need to check this
581  if xml_value == None:
582  #print "new_value ", new_value
583  xml_value = type_value()
584  # for sure nothing interessant here, can happen ??
585  if (new_value == None) | (new_value == type_value()):
586  print "\n ValidStockUpdate, case xml_value is None and new_value is also the default\n"
587  return 0
588  # nothing to loose
589  return 2
590  # normal case, a valid string, at worst a '0' string, can make more try type_value() is correct
591  try:
592  xml_value = type_value(xml_value)
593  except Exception as ex:
594  # mark as error, should be None or castable
595  #self._logger.error("Got Exception in conversion of xml_value ex: %s" % ex)
596  return 1
597  # same value no update to do
598  if new_value == xml_value:
599  return -1
600  # new added, maybe more general
601  if new_value == type_value():
602  print "\n ValidStockUpdate, case new_value is default\n"
603  return 0
604  # certainly a bad new value, works only for float, no string
605  #if (new_value <= 0) & ( xml_value > 0 ):
606  # return 1
607  # else a good new_value
608  return 2
609 
610  ## @brief Guess if the input value is valid.
611  # A conversion to the type is tested efore in the code...
612  # Used only to test new value, the xml_value is considered already valid\n
613  # Return 0, 1 or 2:
614  # - 0 not valid: float('nan')
615  # - 1 valid
616  # - -1 type_value is not implemented
617  # @note A conversion to the type_value is tested before, not explicitely in the function.\n But if type_value is Float and the value not convertible, a TypeError s thrown
618  # @param value input
619  # @param type_value type of the value: string or float. Other will return -2
620  @staticmethod
621  def is_valid_value(value, type_value):
622  if type_value == types.FloatType:
623  if math.isnan( value ):
624  return 0
625  # else valid
626  return 1
627  # else string..., should not be ''
628  elif type_value == types.StringType:
629  if value == '':
630  return 0
631  # accept all other string
632  return 1
633  # not a valid entry, to print in log
634  else:
635  return -1
_dict_interactive
Dictionary of data after check_new_data.
def gen_part_of_list_csv
Generate a sub-list of list_csv, starting from the last call until the last_date (included) ...
def _valid_new_value_csv
Fill dict_interactive in case of list of data (HistPrice action implemented) dict_inter_action stores...
def check_list_csv
Check the CSV data are not wrong.
def save_new_xml_data
Final merge (after user/auto validation) and write XML file.
def valid_interactive
User validation of the new data.
def _error_list_csv
Check for common error in historical price data and return a status and an error code.
def check_new_xml_data
Check the validity of the new retrieved data stored Stock._dict_stock.
Define the global variable StockTemplates.StTmpl and dictionary templates.
def is_valid_value
Guess if the input value is valid.
For graphical user validation before saving the data.
Definition: Validation.py:1
Define custom and specific exceptions for the complete package.
Extend class Stock with functions related to the validation and saving after a stock update: most of ...
def check_new_data
Check the validity of the last updated values from Stock.dict_stock and generate dict_interactive.
def post_process_one_action
Assure the post_processing of a Stock after the parsing of one unique action.
_tk_manager
Store a ManagerTk interface in multi-thread mode.
Global variables for configuration: paths, TCP ports and generic definitions.
Definition: GlobalDicts.py:1
def save_new_data
Final merge (after user/auto-validation) and write to file.
GUI interface to validate the update and saving of data.
Definition: Validation.py:82
def check_value
Generic function to check if new_value can be updated.
def _valid_new_value_xml
Valid last values retrieved with parsers, try to guess values to be updated.