ServerPortfolio  2.0
Python parsers and server
 All Classes Namespaces Files Functions Variables Properties Pages
SocketServer_Server.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 
3 ## @package serverportfolio.SocketServer_Server
4 # @brief Server of the application, wait for requests: provide data, force update, etc.
5 #
6 # Last Changed $Id: SocketServer_Server.py 9 2015-04-02 23:27:25Z michael $
7 
8 #
9 # Can be run alone ( just for test ),
10 # will parse last value from internet, but not running autoparser, and quit after 3 seconds.
11 # or loaded and run by Server_Portfolio.py ( main use )
12 
13 # 6th version, adding LastCSVUpdate and UpdateCSV
14 # 5th version, new option RELOAD, reload dictstocks.txt, check if open or not, actualize queueing system
15 # 4th version, can answer to multiple stock like GET CAC40 DJ S&P500
16 # only a call for multiple answer, better for php exec
17 #
18 # 3thd version of a TCP server
19 # can respond to GET CAC40 and get data
20 # can force FORCE CAC40 to run parser and get new data
21 #
22 # with module SocketServer
23 # Possibility to make one thread, answering to request. One unique is certainly enought ??
24 # Data is in memory in a DictStock_Parser
25 # Add more specific class for 2nde version, in order to include it after
26 #
27 # found on internet !!! solution for closing server
28 # If the value of s is set once, and not reinitialized
29 # you could make it a class variable as opposed to an instance variable
30 # of TestServer, and then have the handler retrieve it via a class method
31 # of TestServer in the handler's constructor.
32 
33 import SocketServer, threading
34 import logging
35 import time
36 import sys
37 
38 from serverportfolio.GlobalDicts import HOST, PORT, DEBUG_MODE
39 # dependence could be deleted, only print_dict
40 from serverportfolio.DictionaryStocks import DictionaryStocks
41 from serverportfolio.UpdateStocks import UpdateStocks
42 
43 # only Yahoo needed ? general parser better ?
44 #import YahooAPI
45 
46 ## @class MyThreaded_TCPServer_Parser
47 # @brief The thread Server.
48 #
49 # want this server threaded, but not the Handler (SocketServer.ThreadingMixIn)
50 class MyThreaded_TCPServer_Parser(SocketServer.TCPServer):
51 
52  _server_running = False
53 
54  # for efficiency, but NO SECURE ???
55  allow_reuse_address = True
56  # If we use Threaded Handler, define in class ThreadingMixIn:
57  # seems enought if not threading ??
58  # daemon_threads = True
59 
60  ## @brief Constructor.
61  # Constructor, pass the queue to handler, MyHandler_Parser, and add a message
62  def __init__(self, server_address, RequestHandlerClass, thq=None, message=''):
63  # constructor of the parent, need address and handler
64  SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
65 
66  ## @brief Save a link to the queue for interacting (ex:force update)
67  # available from the handler as self.server._thq
68  self._thq = thq
69 
70  # get child logger from SP, not a thread
71  self.logger = logging.getLogger('SP.TCPServer')
72 
73  # just a message Welcome to print
74  self.message = message
75  print "\n"+self.message+"\n"
76 
77  # keep all for test of logging
78  self.logger.info('info %s',self.message)
79  #self.logger.error('error %s',self.message)
80 
81  ## @brief Called by constructor to activate the server.
82  # May be overridden.
83  #
84  # why ? useful ??
85  def server_activate(self):
86 
87  ## important line ? not by default ?
88  self.socket.listen(self.request_queue_size)
89  MyThreaded_TCPServer_Parser._server_running=True
90  #self.logger.info("TCP server activated")
91  #print "self.request_queue_size ", self.request_queue_size #5 default ??
92  #print dir()
93 
94 ## @class MyHandler_Parser
95 # @brief Make a proper handler class to respond to the message.
96 #
97 # Respond to the client to:
98 # - empty : send back RUNNING or NOT RUNNING
99 # - GET CAC40 [DJ EURUS ..] : read data in dictstocks, get fast
100 # - FORCE CAC40 [DJ .] : force new parsing, longer
101 # (works but order of execution inverse ??)
102 # - UPDATE_CSV : download missing csv data from yahoo, save to the disk
103 # - LAST_CSV_UPDATE : give date ( timestamp ) of the last update
104 # - RELOAD : reread dictstocks, needed when a new stock has been added
105 # - BYE : to stop the server, need to look at shutdown() since v2.6
106 #
107 # general, need to optimize, it works but may not so good
108 #
109 # The RequestHandler class for our server.\n
110 # It is instantiated once per connection to the server\n
111 # override the handle() to get correct return\n
112 class MyHandler_Parser(SocketServer.BaseRequestHandler):
113 
114  # not printed ?? to check
115  # ok printed before handle
116  # need access to DictionaryStocks, in init ? or setup ?
117  # here working, but called at every request. Can create the object in the handle method
118  def setup(self):
119 
120  # log
121  self.logger = logging.getLogger('SP.TCPServerHandler')
122  self.logger.debug("MyHandler_Parser in settup")
123 
124  # originally used, becomes duplicate with update_stocks
125  #self.dict_stocks = DictionaryStocks()
126 
127  # can be created only after the action is known, anyway setup called everytime ??
128 
129  # certainly different action to perform, fix the parser ?
130  # need to create the object, maybe singleton better for a single GET
131  #self.update_stock = UpdateStocks()
132  # or option no parser associated, when needed set_parser delete old one and create a new one...
133  # self.update_stock = UpdateStocks( None )
134 
135  def handle(self):
136 
137  # log
138  self.logger = logging.getLogger('SP.TCPServerHandler')
139 
140  #if DEBUG_MODE:
141  self.logger.debug("Entry Handler_Parser thq: %s", self.server._thq)
142 
143  # self.request is the TCP socket connected to the client
144  self.data = self.request.recv(1024).strip()
145  #print " self.data %s" % self.data
146  #self.logger.debug('%s',self.data)
147  #print "%s wrote " % self.client_address[0]
148 
149  # need a list of self.data
150  tab_data = self.data.rsplit(' ')
151  self.logger.debug("tab_data: %s",tab_data)
152  # name to change GET InstValue ListStock (ALL or empty),
153 
154  method = None
155  action = None
156  list_stock = None
157 
158  size = len(tab_data)
159  #print "size ", size
160 
161  if size > 0:
162  method = tab_data[0]
163  if size > 1:
164  action = tab_data[1]
165  if size > 2:
166  list_stock = tab_data[2:]
167 
168  self.logger.debug("method %s", method)
169  self.logger.debug("action %s", action)
170  self.logger.debug("list_stock %s", list_stock)
171 
172  str_line=''
173 
174  # Check for action, GET returns the data in memory
175  if method == "GET":
176 
177  # option no parser associated, when needed set_parser delete old one and create a new one...
178  # could use DictionaryStock here ...
179  self.update_stock = UpdateStocks( action )
180  str_line = self.update_stock.get_string_data( list_stock, opt_parse = False )
181  str_line=str_line[:-1]
182  self.request.send(str_line)
183 
184  # force the parsing of new data
185  elif method == "FORCE":
186 
187  # option no parser associated, when needed set_parser delete old one and create a new one...
188  self.update_stock = UpdateStocks( action )
189  str_line=self.update_stock.get_string_data( list_stock, opt_parse = True )
190  str_line=str_line[:-1]
191  self.request.send(str_line)
192  #if DEBUG_MODE :
193  #self.logger.debug("SocketSerClient option FORCE")
194  #print self.dict_stocks.dictstocks
195 
196  # if a list of stocks send directly, how to deal with lock ? how to retrieve the answer ?
197  # wait for all locks !
198  # Normal with autoparser running
199 
200 # Do not remember what is it, what was the point ? how to retrieve the data, maybe for some long run ?
201 # Ok, insert a new Stock in the queue, then updated normally, can get the value by GET
202 # like an option ADD_TO_QUEUE/ADD_STOCK
203 
204 # for run parser certainly ? Yes after creation of a Stock
205  if self.server._thq:
206 # #f DEBUG_MODE :
207 # self.logger.debug("thq insert to the queue ")
208  for stock in list_stock:
209  # force insertion to the queue,
210 # # will be locked until answer received
211 # # caller should check for the lock, could be here
212  status = self.server._thq.InsertToQueue( stock, "InstValue")
213  if status:
214  self.logger.info("Inserted to queue %s" % stock)
215  else:
216  self.logger.error("Cannot insert to queue %s" % stock)
217 
218  # if standalone, without autoparser, so need to update sequentially
219  # should not happen at the end
220  #else:
221 
222  #if DEBUG_MODE :
223  #self.logger.debug("thq False in MyHandler_Parser")
224  #print "tab_data[1:] ",tab_data[1:]
225 
226  # still needed one by one, to modify in Update
227  #for stock in tab_data[1:]:
228  # self.dict_stocks.Update( stock )
229  #str_line=self.update_stock.get_string_data('InstValue', tab_data[1:])
230 
231  # todo check this locking system...
232  # need to loop for waiting for all stocks...
233  # locked by InsertToQueue
234  # while self.AllStocksLock( tab_data[1:] ):
235  # pass
236  # print "lock is False"
237 
238  # print new value
239  # str_line = self.dict_stocks.make_print_stocks(tab_data[1:])
240 
241 
242  # add option ADD_STOCKS, REMOVE_STOCKS, RELOAD convenient with web and SQL
243  elif self.data == 'RELOAD':
244  #if DEBUG_MODE :
245  print 'reload dictstocks'
246 
247  # need to change, and save filename !!
248  # obviously problematic with threads ??
249 
250  # should be a function relaod in DictionaryStocks
251  # with possible a real lock ??
252  list_new=self.dict_stocks.ReadStocks('dictstocks.txt')
253  if DEBUG_MODE :
254  print 'list new :', list_new
255  self.dict_stocks.make_print_stocks( None, True )
256 
257  # update list of the queue, if existing
258  if self.server.thq:
259  for stock in list_new:
260  if self.dict_stocks.CheckOpen(stock):
261  self.server.thq.list_active_stock.append(stock)
262  else:
263  self.server.thq.list_inactive_stock.append(stock)
264 
265  if list_new:
266  str_line='Reloaded '
267  for stock in list_new:
268  str_line+= stock
269  self.request.send("Reloaded")
270  else:
271  self.request.send("Nothing changed in dict_stocks")
272 
273  elif tab_data[0] == 'LAST_CSV_UPDATE':
274 
275  if DEBUG_MODE :
276  print " len tab_data ", len(tab_data)
277  print " tab_data ", tab_data
278 
279  #check argument, make better with exception
280  if ( len(tab_data) >= 2 ):
281  stock = tab_data[1]
282  else:
283  print "Argument is missing"
284  self.request.send("Argument is missing")
285  #raise
286  return
287 
288  #print 'LAST_CSV_UPDATE ', stock #tab_data[1]
289  # old version
290  # stock_to_update = YahooAPI.YahooCSV ( tab_data[1] )
291  # new version with derived class
292  # need a call function from DictionaryStocks ?? follow same logic
293 
294  # why stock to update intermediate ? it is a stock simple job ?
295  stock_to_update = YahooAPI.Parser_YahooCSV ( self.dict_stocks, stock ) #tab_data[1] )
296  time_t = stock_to_update.LastCSV()
297 
298  self.request.send(str(time_t))
299  del stock_to_update
300 
301 
302  elif tab_data[0] == 'UPDATE_CSV':
303  if DEBUG_MODE :
304  print 'UPDATE_CSV tab_data ',tab_data
305 
306  if ( len(tab_data) >= 2 ):
307  stock = tab_data[1]
308  else:
309  print "Argument is missing"
310  self.request.send("Argument is missing")
311  #raise
312  return
313 
314  # same need function in DictionaryStocks ?
315  stock_to_update = YahooAPI.Parser_YahooCSV ( self.dict_stocks, stock )
316  # #necessary ?? if stock is running can keep the value in the object
317  # need to not be deleted
318  time_t=stock_to_update.LastCSV()
319  #print " time_t ", time_t
320  retour = stock_to_update.UpdateCSV( time_t )
321 
322  self.request.send( "Updated ret:"+str(retour) )
323  # needed or reused ?
324  del stock_to_update
325 
326  # to add INTRO
327 
328  # to stop the server
329  elif method == 'BYE':
330  print "will stop..."
331  print "Stop thread_handle_queue first"
332 
333  MyThreaded_TCPServer_Parser._server_running = False
334  print "threading enumerate"
335  # print nothing
336  threading.enumerate()
337  self.request.send("Stopping Server")
338 
339  # for testing if server running
340  else:
341  self.request.send("RUNNING")
342 
343  ## @brief check if at least one stock of the list is still locked
344  def AllStocksLock( self, list_stock ):
345 
346  for stock in list_stock:
347  # return True if one is still locked
348  if self.dict_stocks.dictstocks[stock]['lock'] == True :
349  return True
350 
351  return False
352 # To shutdown, need to set _server_running to false
353 
354 ## @brief Function to initialize and run the server.
355 # Can be called by the main thread ( normal behaviour )\n
356 # or as a separate thread like in this main
357 #
358 # server run until class variable MyThreaded_TCPServer_Parser._server_running is True.
359 #
360 # @param host, port hostname and port of the server
361 # @param thq queue associated
362 def run_MyServer( host=HOST, port=PORT, thq=None ):
363 
364  address_server = ( host, port )
365 
366  if DEBUG_MODE :
367  print "run_MyServer"
368  print "address_server ", address_server
369 
370  thq_for_server = None
371  if thq:
372  thq_for_server=thq
373 
374  print "thq_for_server"
375  else:
376  print "thq not given"
377 
378  print "run TCPServer"
379 
380  ## create server and associate the handle
381  # and not, this thread will wait in while loop
382  #server = MyThreaded_TCPServer_Parser((host,port), MyHandler_Parser
383  # ,'Welcome to the MyServer_Portfolio.\n\r')
384  server = MyThreaded_TCPServer_Parser(address_server, MyHandler_Parser
385  , thq_for_server, 'Welcome to Server_Portfolio.' )
386 
387  # by this way control the exit, _server_running accessible by MyHandler (and everywhere in fact)
388  while MyThreaded_TCPServer_Parser._server_running:
389  server.handle_request()
390 
391 # #### main to test, not very good
392 if __name__ == "__main__":
393 
394  print "Version main of SocketServer, autoparser not running"
395 
396  logging.basicConfig(level=logging.DEBUG)
397  logger = logging.getLogger('SP')
398  main_logger = logging.getLogger("SP.main")
399 
400  ## not needed, may be useful for debugging if already one running
401  # just need to change here, default from Global
402  host = HOST
403  port = PORT
404  print "host, port ", host,port
405  address = ( host, port )
406 
407  # Load DictStcoks, alias not used later
408  dict_stocks = DictionaryStocks( load_all_stocks=True )
409 
410  # No option update all the dictionary
411  #dict_stocks.Update()
412 
413  ##liststocks=(list_stocks,list_stocks_open)
414  start=time.time()
415 
416  # wait time update done
417  #time.sleep(10)
418 
419  # without thread, main thread will be waiting in server loop
420  #run_MyServer(address)
421 
422  # here run as a thread, to continue printing information
423  # by Server_Portfolio not needed, will be the MainThread
424  server_thread = threading.Thread(target=run_MyServer, name="t-server",
425  args=(host,port) )
426  # If True, exit the server thread when the main thread terminates
427  # Here default (False)
428  server_thread.setDaemon(False)
429  print "start server_thread "
430  server_thread.start()
431 
432  print "Elapsed Time: %s" % (time.time() - start)
433 
434  # do not exit, not working
435  while (time.time()-start) < 2:
436  pass
437 
438  print " enumerate ", threading.enumerate()
439  print " time passed..."
440  print " server running ", MyThreaded_TCPServer_Parser._server_running
441 
442  print "\nServer loop running in thread:", server_thread.name
443 
Define class UpdateStocks and ParserFactory.
Definition: UpdateStocks.py:1
def server_activate
Called by constructor to activate the server.
def run_MyServer
Function to initialize and run the server.
Container of all Stocks objects, it also reads the static stocks configuration file "dictstocks...
Global variables for configuration: paths, TCP ports and generic definitions.
Definition: GlobalDicts.py:1
_thq
Save a link to the queue for interacting (ex:force update) available from the handler as self...
Update data of a list of Stock's (only one action by call is possible).
Definition: UpdateStocks.py:45
def AllStocksLock
check if at least one stock of the list is still locked
Make a proper handler class to respond to the message.
Define singleton class DictionaryStocks, act as the main container of Stocks objects.