ServerPortfolio  2.0
Python parsers and server
 All Classes Namespaces Files Functions Variables Properties Pages
UpdateStocks.py
Go to the documentation of this file.
1 ## @package serverportfolio.UpdateStocks
2 # @brief Define class UpdateStocks and ParserFactory.
3 #
4 # Regroup main functions to update stocks (call parsers to query web page and post-processing: save_to_file/save_to_xml/interactive mode)\n
5 # Module made apart from DictionaryStocks and Stock to avoid circular/recursive import\n
6 #
7 # Last Changed $Id: UpdateStocks.py 29 2015-05-05 11:09:54Z michael $
8 
9 # Object and associated parser can be re-used if they use identical parser (YahooCSV can do many actions on historic data)
10 # To develop methods to change parser if required
11 
12 import types, logging
13 import threading
14 
15 from serverportfolio.DictionaryStocks import DictionaryStocks
16 # Stock not needed ? always work with symbol, of through DictionaryStocks
17 # can use obj_stock.state, imported indirectly by DictionaryStock ?
18 # from serverportfolio.Stock import Stock, InvalidStock
19 
20 
21 from serverportfolio import Utils
22 from serverportfolio.GlobalDicts import EAction
23 from serverportfolio.PortfolioException import PortfolioError, ParserError, QueryError
24 
25 from serverportfolio.Parsers.Parser_Bourso import Parser_Bourso
26 from serverportfolio.Parsers.YahooCSV import YahooCSV
27 from serverportfolio.Parsers.YahooYQL import YahooYQL
28 
29 # only for new added static methods
30 from serverportfolio.Validation import ManagerTk
31 
32 ## @class UpdateStocks
33 # @brief Update data of a list of Stock's (only one action by call is possible).
34 #
35 # This module allows to choose:
36 # - multi-thread mode : if possible with the action
37 # - option for the post-processing: save to file, interactive session
38 #
39 # Create and store a appropriate parser object to query a web page and make the post-processing: validation/interactive mode, save_to_file/save_to_xml.\n
40 # Re-use of the internal parser object needed only for Server, to improve.\n
41 #
42 # Used mainly by the RunParser tool.\n
43 # Used by C++, with simple arguments.\n
44 # If optional argument easy to implement in C++, can be provided in constructor.
46 
47  ## @brief Constructor.
48  # @param action EAction or string of the action to perform, 'NoAction' will not initialise the internal parser.
49  # @param option_parser (not implemented)
50  # @param option_multithread if possible for the action, parsers are run in independent threads
51  def __init__(self, action, option_parser = None, opt_multithread = False): #, load_all_stocks=False):
52  #option to add: load_all_stocks, (to_send_gui_server, case ServerPortfolio), (to_C++,only if C++ call or gui_serfer),
53  # ( to_write_XML == to_save_file == to_save/to_update, depends on the action )
54  #
55  # to_interactive important, X choices:
56  # 1. the caller ask for data to validate, with generator maybe...(caller python/C++), caller knows what to query (caller wait or thread needed)
57  # 2. UpdateStocks proposes validation form(s) (simple console, simple gui in python) when it is ready and blocks there (simplest), can avoid thread.
58  # 3. // send a validation form to the caller (need some wrapper/interface to store caller python/C++) need thread/process for sure
59  # use gui_server ? maybe easier...
60  #
61  # multi_threads (number of threads ?) inside or by caller ? easier to split between thread here
62 
63  self._logger = logging.getLogger('SP.UpdateStocks')
64  self._logger.debug('Init UpdateStocks')
65  self._logger.debug('action: %s', action)
66 
67  # function stringToEAction, can throw a PortfolioError
68  self.e_action = Utils.stringToEAction( action )
69 
70  # maybe option, one fixed parser or not is a good idea ?
71  # e_action NoAction implemented
72  try:
73  self._myparser = ParserFactory.create_parser( self.e_action, option_parser )
74  # raise a parser error
75  except ParserError as ex:
76  print "Catch ParserError form ParserFactory ex.get_format_string()"
77  #raise PortfolioError( "Error in ParserFactory", ex.get_format_string())
78  raise
79 
80  ## @brief Link to the DictionaryStocks singleton.
81  # Could pass the option load_all_stocks if created from here...
83  ## @brief Allow multi-threading for executing _update_data.
84  # @todo check if works for InstValue, HistPrice (DivSplit also).
85  self._multithread = opt_multithread
86  ## @brief Indicates if the new data must be saved into the file system, post-processing stage.
87  # Apply to all types : 'csv' and 'xml'\n
88  # Default is True, for debugging/testing may need to set to False
89  self.b_save = True
90  ## @brief Set interactive mode, the user is required to accept / reject the update of data.
91  self.b_interactive = False
92 
93  ## @brief To set/unset the options b_save and b_interactive.
94  # @param bool_save boolean to set/unset the option to save to file.
95  # @param bool_interactive to set/unset the interative mode
96  def set_option_post(self, bool_save = True, bool_interactive = False):
97  self.b_save = bool_save
98  self.b_interactive = bool_interactive
99 
100  ## @brief Return a new dictionary with the option for post-processing.
101  # An entry 'tk_manager' will be added only if needed (interactive/multithread) and supported by action
102  # @return dictionary with keys: 'to_save', 'interactive' and 'tk_manager'
103  def get_option_post(self):
104  tmp = {'to_save' : self.b_save,
105  'interactive' : self.b_interactive,
106  'tk_manager' : None
107  }
108  return tmp
109 
110 # to make clear, not a bad idea
111  ## @brief Set or reset an action, a correct parser depending on the action.
112  # Try to re-use the same parser object if the same action is required.\n
113  # If not possible, create a new one\n
114  # Called from _update_data\n
115  # @param action_name string or EAction to execute
116  def _set_parser(self, action_name):
117  self._logger.debug("_set_parser")
118  status = ParserFactory().is_compatible( self._myparser, action_name)
119  self._logger.debug("status %s", status)
120 
121  # throw a PortfolioError
122  self.e_action = Utils.stringToEAction( action_name )
123 
124  if status == False:
125  self._myparser = ParserFactory.create_parser( self.e_action )
126 
127  ## @name Wrapper to DictionaryStocks functionalities
128  ## @{
129 
130  ## @brief Return a Stock (or a list of Stock) object(s) from a symbol (or a list of symbols).
131  # May return InvalidStocks, wrapper to DictionaryStocks.get_stocks()
132  def get_stocks(self, stock):
133  return self._dictstocks.get_stocks(stock)
134 
135  ## @brief Print one line strings of the data.
136  # @todo Check arguments all used ? all needed ?
137  def print_dict_stocks(self, action, input_stocks = None, opt_all_stocks = False, opt_header = False ):
138  return self._dictstocks.print_dict_stocks( action, input_stocks, opt_all_stocks, opt_header)
139  ## @}
140 
141 # ############### update functions
142 
143  ## @brief Update stock(s) data according to the specified action.
144  # If a specific stock or list of stocks symbol is provided, the Stock object may be created on the fly\n
145  # If the default None is used, only the Stock objects already loaded in the dictionary are used(option update_all may be added)\n
146  #
147  # The function provides access to any action.
148  # if option save is set (by default) data are saved in their respective file.
149  # If multithread is required with action InstValue or HistPrice, a thread is created for each thread in this function.
150  #
151  # Other post-processes (print, send data to gui_server) must be done by the caller\n
152  #
153  # @param stock symbol (or list of symbols) of the stock, default None update all stocks from the loaded Stocks (not all from config)
154  # @param action string of the action, must be valid as defined in GlobalDicts.EAction. If None use the default specified in constructor.
155  # All cases check the parser is compatible with the action.
156  # @param opt_parser option to use a specific parser(not implemented, use only the default one)
157  def _update_data( self, stock, action = None): #, opt_parser = None ):
158  self._logger.debug("_update_data stocks: %s" % (stock) )
159  self._logger.debug("action to perform: %s" % action )
160  self._logger.debug("self.e_action to perform: %s" % self.e_action )
161 
162  list_stock=[]
163  # messy this option... can call : list_stock = self.get_stock_keys(), implements keyword for stock ALL
164  # if stock is None:
165  # default all_stock = False (need option in update_data? easier to call DictStocks load all in caller?)
166  # option True working, Stock not created there. Not sure so useful...
167  # list_stock = self.get_stock_keys() # all_stocks=True )
168  # if one string is given, a list with one element is returned, otherwise return the initial list
169  # else :
170  list_stock = Utils.to_list( stock )
171 
172 # complex option NoAction ? only use previous parser or change
173  # if None use the default
174  if (action == None) & (self.e_action != EAction.NoAction):
175  pass
176  # load a new parser if needed, re-use existing if compatible
177  elif action != None:
178  self._set_parser( action )
179 
180  assert(self._myparser != None),"Error in _update_data, the parser could not be created"
181  assert(self.e_action != EAction.NoAction),"Error in _update_data, the parser could not be created"
182 
183  # new added pass option to run_parser, to finalise post-processing of the stocks
184  option_post = self.get_option_post()
185 
186 # maybe easier to have run_thread(list_stock, action) # always stock by stock by default, but concurrent
187 # run_singlethread(list_stock, action) #always a list of stock in entry, default for multiple stocks query. option mt= True/False to force and test
188 # multi-thread could be called 2 times for update of Info, made specific run_parser_info in YQL.
189 
190  # Theses actions support only 1 stock, this info could be stored with the action.
191  # we can reuse the parser or implement multi-threading
192  if (self.e_action == EAction.InstValue) | (self.e_action == EAction.HistPrice) | \
193  (self.e_action == EAction.DivSplit) | (self.e_action == EAction.DatesIntro):
194 
195  # create multiple threads, each deal with one Stock only
196  if self._multithread:
197 
198  # run_multithread
199  # if many stocks/ can limit the number of threads ?
200  # if many actions... much more complex, need a pool of threads to reuse ! to forget for now !
201  # maybe make a simpler version, a derived class may implement more options ?
202  self._logger.debug("multithread activated")
203 
204  # interactive and mutlithread, need the TkManager
205  #if self.b_interactive:
206  # option_post['tk_manager'] = TkManager()
207 
208  # if mutlithtread and interactive
209  if self.b_interactive:
210  option_post['tk_manager'] = ManagerTk()
211 
212  # create a first thread, responsible of the pool of threaded parsers
213  t = threading.Thread(target=run_thread_pool_parser, args=(list_stock, self.e_action, option_post)).start()
214 
215  # main thread waits data from parsers
216  if self.b_interactive:
217  option_post['tk_manager'].update_me()
218  # need to wait in a way
219  else:
220  t.join()
221 
222 
223 
224  # like run_thread to do a static function
225  # main thread may
226 # thread_list = []
227 # # alternative
228 # # threads = [threading.Thread(target=loop, args=(f, repeats))
229 # # for i in range(nthreads)]
230 #
231 # for stock in list_stock:
232 # # run_thread makes pre and post processing for the single stock methods
233 # t = threading.Thread(target=self.run_thread, name="thread_"+stock, args = (stock,self.e_action))
234 # thread_list.append(t)
235 #
236 # for th in thread_list:
237 # th.start()
238 #
239 # # master waits all thread to end
240 # for th in thread_list:
241 # th.join()
242 
243  self._logger.debug("mutlithread done")
244 
245  # ###################
246 
247  # all done in one thread, loop over the list
248  # could call the same static function run_thread_parser(stock, e_action, option_post), if clear with set_parser use
249  else :
250  # run_singlethread() parse/ post-process included
251  self._logger.debug("single thread, make a loop")
252 
253  for stock in list_stock :
254 
255  try :
256  self._myparser.run_parser( stock, option_post )
257  # specific error
258  except ParserError as ex:
259  self._logger.error("_update_data caught a ParserError from run_parser: %s" % ex)
260  # should raise a PortfolioError or still continue ?
261  raise
262  # parsing may generate many errors, catch all
263  except Exception as ex:
264  self._logger.error("_update_data caught an Exception from run_parser : %s" % ex)
265  print "error message ",ex
266  raise
267  # maybe better to change to a ParserError or PortfolioError
268 
269  # single_thread, post_process now or after ?
270  #self.post_process(stock, self.e_action)
271 
272  #self.post_process( list_stock, self.e_action)
273 
274  # #################
275  # these actions with default parser support multiple stocks, no need to loop over stock
276  # can force mutlithread to retrieve all Fundamental, can extend _multithread to this case, divide with nb_thread
277  # AllAction ? split the job in different threads (how to join after?)
278  elif (self.e_action == EAction.Info) | (self.e_action == EAction.Fundamental):
279 
280  # run_single_thread
281  # not yet pre/post processing case here, can add later
282  #self.pre_process()
283 
284  # case Info need 2 parsing. If only use of YQL could override run_parser
285  # run_parser_info has been implemented in YQL, may derive an other class
286  try:
287  # in this case makes 2 parsings with YahooYQL
288  # specific case or to make derive class YQL_Info only rewrite run_parser ?
289  if (self.e_action == EAction.Info) & (isinstance( self._myparser, YahooYQL)):
290  self._myparser.run_parser_info( list_stock, option_post )
291  # normal, all other cases
292  else:
293  self._myparser.run_parser( list_stock, option_post )
294 
295  # specific error, PortfolioError ?? to catch simpler !
296  # for test, maybe better
297  except ParserError as ex:
298  self._logger.error("_update_data caught a ParserError from run_parser: %s" % ex)
299  # should raise a PortfolioError or still continue ? can change to PortfolioError, which advantage ?
300  raise
301 
302  except QueryError as ex:
303  self._logger.error("_update_data caught a QueryError from run_parser: %s" % ex)
304  raise
305  # parsing may generate many errors, catch all
306  except Exception as ex:
307  self._logger.error("_update_data caught an Exception from run_parser : %s" % ex)
308  raise
309 
310  # same for post-processing
311  #self.post_process(list_stock, self.e_action)
312 
313 
314 # ################## split pre / run / post-process
315 
316 # moved into Stock, saved_as( e_action, 'csv' ) more general or just HistPrice ?
317 # to delete
318  # Only HistPrice at the moment, do stock by stock in multi or single thread
319 # def pre_process(self, stock, e_action):
320 # print "pre_process"
321 #
322 # if ( self.e_action == EAction.HistPrice ):
323 # # bad way, many loop. Better to call pre_process( one_stock )/run/post-process
324 # self.get_stocks( stock ).last_CSV( e_action.name )
325 
326  # Only HistPrice and Instvalue at the moment, deal only with one stock
327  # All action needed, update_xml slightly more general. PostProcess class, with implementation 'action dependent' avoid if
328  # problem with Validation, need two steps: check_new_data / create dict_interactive
329  # if interactive: call user validation
330  # apply validation
331  #
332  # difficult to have multi-thread and interactive
333 # start to move post-process to Stock, called by run_parser(), need to pass some options, need to deal with multi-thread
334 # def post_process(self, list_stock,e_action):
335 # self._logger.debug("UpdateStocks post_process")
336 # self._logger.debug("stock/e_action: %s/%s" % (list_stock,e_action.name))
337 #
338 # list_stock = Utils.to_list(list_stock)
339 #
340 # opt_post = {
341 # 'interactive': self.b_interactive,
342 # 'to_save' : self.b_save
343 # }
344 #
345 # for stock in list_stock:
346 # self._logger.debug("stock: %s" % stock)
347 # self.get_stocks( stock ).post_process( e_action, opt_post )
348 #
349 # maybe should not be in the class ? not needed in fact
350 # only one stock in this function
351 
352 
353 
354 # ################# End processing
355 
356 # could have an option, opt_force, make a new parsing or not, easier for all modules using DictStocks in Server/RunParser
357 # option GET, opt_parse called only if Server is working
358 
359 # only for tests, to delete
360 # ##### function of test, update_xml is restrictive, and interactive force to split the function
361  ## @brief
362  def update_xml(self, stock):
363  self._logger.debug("Entry update_xml stock %s" % stock)
364 
365  if not self.save_xml:
366  self._logger.debug("update_xml, but save_xml is False, return")
367  return
368  self._logger.debug("mode interactive %s" % self.b_interactive)
369 
370  list_stock = Utils.to_list( stock )
371  # need to read the file
372  for stock_symb in list_stock:
373 
374  obj_stock = self.get_stocks(stock_symb)
375  obj_stock.read_xml()
376 
377  try :
378  obj_stock.check_new_data(self.b_interactive)
379  except PortfolioError as ex:
380  self._logger.error("Caught PortfolioError")
381  raise
382 
383  if self.b_interactive == False:
384 
385  # better to test if empty, but should work and produce the same file, good for unit-test
386  # ok pass with FP test, input == output
387  if any(obj_stock._dict_interactive):
388  obj_stock.save_new_data_to_xml()
389  else:
390  self._logger.debug("dict_interactive is empty, nothing to update")
391 
392  else:
393  print "Interactive to write"
394 
395  # ##### End test function
396 
397 
398  ## @brief Update the stock(s) and return the data as multi lines string.
399  #
400  # Should work for all actions, print one line by stock.\n
401  # Easy to call and parse from C++ or other scripts. Called by default by RunParsers.
402  #
403  # @param list_stock of the stock symbols
404  # @param action
405  # @param opt_header optional boolean, print a header before the data
406  # @param opt_parse if True call an update of the data, if False only print values in memory
407  def get_string_data( self, list_stock, action=None, opt_header=False, opt_parse=True ):
408  self._logger.debug("Entry get_string_data %s opt_parse: %s" % (self.e_action, opt_parse))
409 
410  if opt_parse:
411  # make the update of the new data, for use with option GET not needed
412  self._update_data(list_stock, action)
413 
414  # post-process, create a multi-line string
415  lines = self.print_dict_stocks( self.e_action.name, list_stock, opt_header = opt_header )
416  return lines
417 
418  ## @brief Parse instantaneous value and check the stock is in state 'OPEN'.
419  # Called only at the start of AutoParser.\n
420  # or may call _update_data from AutoParser, and then check with Stock get_state
421  #
422  # @pre Parser must be loaded with InstValue action (done in AutoParser)
423  #
424  # @param stock one unique stock symbol
425  # @return boolean if state is 'OPEN'
426  def check_open(self, stock ):
427  self._logger.debug("Entry check_open")
428  # assert InstValue for the parser
429  self._update_data( stock )
430  #print "state ", DictionaryStocks().get_stocks( stock ).state
431  if self.get_stocks( stock ).state == 'OPEN':
432  return True
433  # else
434  return False
435 
436 ## @name Static functions dealing with thread creation, execution
437 ## @{
438 
439 ## @brief Create and run a pool of thread parsers.
440 # In interactive session, assure the creation of a TkManager and its deletion.
441 # @param stock list of stock symbol
442 # @param e_action EAction to perform
443 # @param option_post option for the post_processing
444 def run_thread_pool_parser(list_stock, e_action, option_post): #run_thread_parser as argument possible, nice for unit-test
445  print "Entry thd_pool_run name", threading.current_thread().name
446 
447  #pydevd.settrace( 5678 )
448  # default
449  ref_tk_manger = None
450 
451  # if interactive session
452  if option_post['interactive'] == True:
453  ref_tk_manager = option_post['tk_manager']
454  assert ref_tk_manager is not None, "Error in option post"
455 
456  thread_list = []
457  for stock in list_stock:
458  t = threading.Thread(target=run_thread_parser, name="thread_"+stock, args = (stock, e_action, option_post))
459  thread_list.append(t)
460 
461  for th in thread_list: th.start()
462  # master waits all thread to end
463  for th in thread_list: th.join()
464 
465  if ref_tk_manager:
466  print "thd_pool all joined, send quit signal"
467  ref_tk_manager.write(["QUIT"])
468  print "thread_pool will die..."
469 
470 ## @brief Create and run the parsing of one action for an unique stock.
471 # Parsing includes pre and post-processing: validation and saving the data to file.
472 # @param stock symbol
473 # @param e_action EAction to perform
474 # @param option_post post-processing option
475 def run_thread_parser(stock, e_action, option_post):
476  logger = logging.getLogger('SP.UpdateStocks.thread_parser')
477  logger.debug("run_thread name %s" % threading.current_thread().name)
478  logger.debug("stock %s", stock)
479  logger.debug("e_action %s", e_action.name)
480  logger.debug("option_post %s" % option_post)
481 
482  # costly, need to create a parser for each thread
483  try :
484  parser = ParserFactory.create_parser( e_action ) #option_parser )
485  except:
486  print "Got an exception "
487  raise
488 
489  # case of Info/YQL ? run_parser_info ? No Info use only one thread
490  try :
491  parser.run_parser( stock, option_post )
492  # specific error
493  except ParserError as ex:
494  logger.error("run_thread_parser caught a ParserError from run_parser: %s" % ex)
495  # should raise a PortfolioError or still continue ?
496  raise
497  # parsing may generate many errors, catch all
498  except Exception as ex:
499  logger.error("run_thread_parser caught an Exception from run_parser: %s" % ex)
500  raise
501  # moved to Stock, run by run_parser
502  # how to deal with interactive if multithread ? need queue/other thread to serialize
503  #self.post_process(stock, e_action)
504 
505  ## @}
506 
507 ## @class ParserFactory
508 # @brief Helper functions, all the functions to create a specific parser from a given action.
509 # Used only by UpdateStocks
511 
512  ## @brief Possible action to perform and the associated parsers.
513  _ACTION = {
514  EAction.Info:['YQL'], # 'YHTML'
515  EAction.HistPrice:['YCSV'],
516  EAction.DatesIntro:['YCSV'], # use an HTML page, keep in same module YahooCSV, later maybe YHTML
517  EAction.DivSplit:['YCSV'], #['YQL'], #or YCSV with_x(seems better
518  EAction.InstValue:['BOURSO'], # Only boursorama, to extend to YHTML
519  EAction.Fundamental:['YCSV','YQL'] # YHTML or YCSV also
520  }
521 
522  # for is_compatible(), need Abstract.type() and inverse dictionary (can be checked on the fly)
523  # YCSV : ['HistPrice','DatesIntro','Fundamental']
524  # YQL : ['Info'], Fundamental
525  # BOURSO : ['InstValue'
526 
527  _logger = None
528 
529  ## @brief Return a specific parser, according to the action.
530  # Action determine the parser to use, the first one in the entry by default
531  # @param e_action EAction to perform
532  # @param option optional parser. By default the first in the list is chosen (not implemented)
533  @classmethod
534  def create_parser( cls, e_action, option = {} ):
535 
536  cls._logger = logging.getLogger( 'SP.ParserFactory' )
537  cls._logger.debug("CreateParser e_action: %s, option: %s" % (e_action.name, option) )
538 
539  if e_action == EAction.NoAction:
540  return None
541  # use only default option
542  try:
543  default_option = cls._ACTION[ e_action ]
544  cls._logger.debug("default_option %s " % default_option)
545  except:
546  cls._logger.critical("Error action %s not in list ACTION or is invalid" % (e_action.name) )
547  raise ParserError( "Cannot create a Parser from the action %s" % e_action.name, \
548  None, e_action.name, None )
549  # check option, check if action compatible with this option
550  # if option and not compatible, warning ?
551  opt_parser = default_option[0]
552 
553  # if default in Action, should test over Action
554  # switch case would be nice
555  # http://bytebaker.com/2008/11/03/switch-case-statement-in-python/
556  # Boursorama supports only InstValue, only one stock
557  if opt_parser == 'BOURSO':
558  assert ( e_action == EAction.InstValue ),"Error in combination Boursorama / %s" % ( e_action )
559  parser = Parser_Bourso( e_action )
560 
561  # for HistPrice, now can have price + dividence with g=v (but not split)
562  # for Fundamental/Info can use explicit list with old api
563  elif opt_parser == 'YCSV':
564  # test all actions, for each check option_yahoo or default
565  # html/'YQL'/x : html default for getting HP
566  parser = YahooCSV( e_action )
567 
568  elif opt_parser == 'YQL':
569  parser = YahooYQL( e_action )
570 
571  else:
572  # should never reach here assert
573  cls.logger.debug("option parser not yet implemented %s" % opt_parser)
574  raise ParserError( "option parser not yet implemented %s" % opt_parser, \
575  'None', e_action.name, 'None' )
576 
577  return parser
578 
579 # parser.source implemented, solve partly the problem ?
580 #
581 # tricky does not know the exact parser used(in case of option, only the previous action), now parser.source is implemented !
582 # to be sure, need to know the exact Parser. Can be in Abstract.type () avoid to test isinstance, ovelroaded by parser
583 # but tricky in the list does not mean we want to use the optional parser...
584 # need to know YQL : [list of actions]
585  # actual_action was not enough
586  @classmethod
587  def is_compatible(cls, actual_parser, new_action_name):
588  cls._logger.debug('is_compatible')
589  cls._logger.debug('test current parser.source: %s', actual_parser.source)
590  print "is_compatible"
591  print "actual_parser.source ", actual_parser.source
592 
593  # if it does not exist, or NoAction ?
594  #if actual_parser.e_action.name == 'NoAction':
595  if actual_parser == None:
596  return False
597 
598  # second quick test
599  if actual_parser.e_action.name == new_action_name:
600  #cls.logger.debug("Same action, return True")
601  print "Same action, return True"
602  return True
603 
604  print "return False"
605  return False
606 
607  # Utils.stringToEAction
608  try :
609  #if type(action) == types.StringType:
610  new_action = EAction[new_action_name]
611  #else: #isisntance(EAction) to check
612  # self.e_action = action
613  except :
614  #cls._logger.critical("Error action %s not in list ACTION or is invalid" % (new_action_name) )
615  raise ParserError( "Cannot create a Parser from the action %s" % new_action_name, \
616  None, new_action_name, None )
617 
618  # to do later, maybe not so important
619  # actual_action
620  # actual_possible_action = cls._ACTION
621 
622 # for testing/debugging purpose
623 # Implements all actions:
624 # - Only one stock InstValue, HistPrice, DivSplit, DatesIntro
625 # - Multiple stocks: Fundamental, Info
626 if __name__ == "__main__":
627 
628  import sys,traceback
629  import GlobalDicts
630 
631  logging.basicConfig(level=logging.DEBUG) #WARNING)
632  logging.getLogger('SP')
633  m_logger = logging.getLogger("SP.main")
634  # To use entry in ROOT_application/data_test (unit_test)
635  #GlobalDicts.PATH_DICT=GlobalDicts.PATH_DICT_TEST
636  try:
637  m_stock = sys.argv[1]
638  m_action = sys.argv[2]
639  # output: 'string','pretty_dict','XML' ?
640  # option interactive/threads/to_save similar to RunParsers
641  except IndexError:
642  print " Only for debugging, should call ./Run_Parsers for all options "
643  print " Usage:"
644  print " UpdateStocks CAC40,FSLR InstValue"
645  print " Action: all actions, thread option"
646  sys.exit(1)
647  m_logger.debug("Entry Main UpdateStocks")
648  #print "\n== Create a NoAction UpdateStocks"
649  #m_no_action = UpdateStocks('NoAction')
650  #print "action ", m_no_action.e_action.name
651  m_update_stocks = None
652  # transform to a list, from format "Stock1,Stock2,.."
653  m_list_stock = m_stock.split(',')
654  m_logger.debug("list_stock: %s", m_list_stock)
655  print "\n== Create UpdateStocks"
656  # assign an action (can be 'NoAction')
657  try:
658  m_update_stocks = UpdateStocks( m_action, opt_multithread=True)
659  except PortfolioError as ex:
660  print "Caught PortfolioError from UpdateStocks()"
661  print ex.get_format_string()
662  sys.exit(1)
663  except Exception as ex:
664  print "Caught Exception ex:",ex
665  sys.exit(1)
666  print "action ", m_update_stocks.e_action.name
667  #sys.exit(1)
668 # #############
669  # basic function, run the parser(threads), but no post-processing, not really... maybe...
670  # could be private
671  #m_update_stocks._update_data( action, list_stock ) #load the list of stock
672  #m_update_stocks._update_data( action ) #default None, but nothing loaded in DictStocks
673  # need some sort of manual post-processing
674  # only print the dictionary as final processing
675  #for stock in list_stock:
676  # print Utils.pretty_dict( DictionaryStocks().get_stocks( stock ).get_dict_data() )
677 # ############ get string format
678 
679 # now action is fixed, with the parser.
680 # To test more to change parser on the fly
681 
682  # b_save, b_interactive
683  m_update_stocks.set_option_post( True, True )
684  try:
685  m_lines = m_update_stocks.get_string_data( m_list_stock, m_action, opt_header=True)
686  #m_lines = no_action.get_string_data( m_list_stock, m_action, opt_header=True)
687  except PortfolioError as ex:
688  print "Catch PortfolioError, ex: %s" % ex
689  print ex.get_format_string()
690  sys.exit(1)
691  except Exception as ex:
692  print "Catch Exception ex: %s" % ex
693  print "traceback:\n", traceback.format_exc(3)
694  sys.exit(1)
695  m_logger.debug("\n==result")
696  print m_lines
697 # ########## Test interactive console for HistPrice
698 # try:
699 # # can make the update from python if no error ??
700 # status = update_stocks._update_data( action, list_stock )
701 #
702 # except PortfolioError as ex:
703 # print "Catch PortfolioError, ex: %s" % ex
704 # print ex.get_format_string()
705 # sys.exit(1)
706 #
707 # except Exception as ex:
708 # print "Catch Exception ex: %s" % ex
709 # sys.exit(1)
710 #
711 # print "\n==result "
712 # print "status ", status
713 #
714 # print "\n get the errors"
715 # error_lines = DictionaryStocks().get_stocks( list_stock ).get_error()
716 #
717 # if error_lines is None:
718 # print "No Error, can update all the data"
719 #
720 # else:
721 # print "Found error, can update some ?? if volume missing can deal with it and check later ??"
722 # for error in error_lines:
723 # print error
724 #
725 # print "\n get the list_csv"
726 # lines_csv = DictionaryStocks().get_stocks( list_stock ).get_dict_data('HistPrice')['list_csv']
727 # for csv in lines_csv:
728 # print csv
729 #
730 # print (30 * '-')
731 # print (" M A I N - M E N U")
732 # print (30 * '-')
733 # print ("1. Save")
734 # print ("2. Discard")
735 # #print ("3. Reboot the server")
736 # print (30 * '-')
737 #
738 # is_valid=0
739 # while not is_valid :
740 #
741 # try :
742 # choice = int ( raw_input('Enter your choice [1-2] : ') )
743 # if (choice < 1) | (choice > 2 ):
744 # raise ValueError( 'an error : ' + str(choice) )
745 # # pass
746 # is_valid = 1 # set it to 1 to validate input and to terminate the while..not loop
747 #
748 # except ValueError, e :
749 # print "e ", e
750 # print ("'%s' is not a valid integer." % e.args[0].split(": ")[1])
751 #
752 # # Take action as per selected menu-option ###
753 # if choice == 1:
754 # print ("Save the data...")
755 # #DictionaryStocks().get_stocks( list_stock[0] ).save_hist_price()
756 # elif choice == 2:
757 # print ("Discard the data...")
758 # pass
759 # #elif choice == 3:
760 # # print ("Rebooting the server...")
761 # else:
762 # print ("Invalid number. Try again...")
def check_open
Parse instantaneous value and check the stock is in state 'OPEN'.
def get_stocks
Return a Stock (or a list of Stock) object(s) from a symbol (or a list of symbols).
Manage and serialize the access to a ValidationTkinter application, necessary when UpdateStock is run...
Definition: Validation.py:346
_dictstocks
Link to the DictionaryStocks singleton.
Definition: UpdateStocks.py:82
b_save
Indicates if the new data must be saved into the file system, post-processing stage.
Definition: UpdateStocks.py:89
def get_option_post
Return a new dictionary with the option for post-processing.
def run_thread_pool_parser
Create and run a pool of thread parsers.
def _set_parser
Set or reset an action, a correct parser depending on the action.
Helper functions, all the functions to create a specific parser from a given action.
Specific parser for querying the french Boursorama website.
Definition: Parser_Bourso.py:1
def run_thread_parser
Create and run the parsing of one action for an unique stock.
def print_dict_stocks
Print one line strings of the data.
Specific parser using Yahoo YQL interface.
Definition: YahooYQL.py:1
def create_parser
Return a specific parser, according to the action.
For graphical user validation before saving the data.
Definition: Validation.py:1
Define custom and specific exceptions for the complete package.
Define the class YahooCSV to use the 'old' Yahoo API.
Definition: YahooCSV.py:1
Derived class specific to the parsers.
b_interactive
Set interactive mode, the user is required to accept / reject the update of data. ...
Definition: UpdateStocks.py:91
_multithread
Allow multi-threading for executing _update_data.
Definition: UpdateStocks.py:85
Container of all Stocks objects, it also reads the static stocks configuration file "dictstocks...
def _update_data
Update stock(s) data according to the specified action.
Global variables for configuration: paths, TCP ports and generic definitions.
Definition: GlobalDicts.py:1
Update data of a list of Stock's (only one action by call is possible).
Definition: UpdateStocks.py:45
def set_option_post
To set/unset the options b_save and b_interactive.
Definition: UpdateStocks.py:96
tuple start
liststocks=(list_stocks,list_stocks_open)
def get_string_data
Update the stock(s) and return the data as multi lines string.
Define singleton class DictionaryStocks, act as the main container of Stocks objects.
Specific parser for querying the Boursorama website, only for instantaneous data. ...
Derived parser class for retrieving divers data from the 'old' Yahoo API.
Definition: YahooCSV.py:38