server.py
author Eugen Sawin <sawine@me73.com>
Mon, 20 Feb 2012 23:41:50 +0100
changeset 2 b25c62d210f5
parent 0 a2f88c3bd824
permissions -rw-r--r--
Added pid file writing.
     1 #! /usr/bin/env python3
     2 """
     3 Name: 
     4 Author: Eugen Sawin <sawine@me73.com>
     5 """
     6 
     7 import socketserver
     8 import logging
     9 import os
    10 import os.path
    11 import shelve
    12 import json
    13 import re
    14 import urllib.request
    15 import subprocess
    16 import time
    17 from operator import itemgetter
    18 from urllib.parse import urlparse
    19 from urllib.request import urlopen
    20 from tempfile import NamedTemporaryFile
    21 from http.server import BaseHTTPRequestHandler, HTTPServer
    22 from pseudoword import Database
    23 
    24 try:
    25     from argparse import ArgumentParser
    26 except ImportError:
    27     from external.argparse import ArgumentParser
    28 
    29 ENCODING = 'utf-8'
    30 
    31 def parse_args():
    32     parser = ArgumentParser(description='')
    33     parser.add_argument('-p', type=int, default=8080, help='port')
    34     parser.add_argument('-a', default='localhost', help='host address')
    35     parser.add_argument('-l', default='log/server.log', help='log file')
    36     parser.add_argument('-w', default='data/names', help='word database')
    37     parser.add_argument('-d', default='httpdocs', help='http docs path')
    38     parser.add_argument('--pid', default=None, help='pid file')
    39     return parser.parse_args()	
    40 
    41 
    42 def main():       
    43     args = parse_args()
    44     logging.basicConfig(filename=args.l, level=logging.DEBUG,
    45                         format='[%(levelname)s@%(asctime)s] %(message)s',
    46                         datefmt='%d.%m.%Y %I:%M:%S')
    47     if args.pid:
    48         with open(args.pid, 'w') as f:
    49             f.write(str(os.getpid()))
    50     db = Database(args.w)
    51     server = Server(args.a, args.p, args.d, db, GetHandler)
    52     server.run()
    53 
    54 
    55 class GetHandler(BaseHTTPRequestHandler):
    56 
    57     def do_GET(self):
    58         request = Request(self.path, self.server.docs_path, self.server.db)
    59         logging.info('Request: %s' % request)
    60         code, content_type, data = request()
    61         self.send_response(code)       
    62         self.send_header('Content-type', content_type)
    63         self.end_headers()
    64         self.wfile.write(data)
    65         
    66     def log_message(self, format, *args):
    67         pass
    68 
    69 		
    70 class Server(HTTPServer):
    71 
    72     def __init__(self, host, port, docs_path, db, handler):
    73         HTTPServer.__init__(self, (host, port), handler)
    74         self.host = host
    75         self.port = port
    76         self.docs_path = docs_path
    77         self.db = db
    78 
    79     def run(self):
    80         logging.info('Server ready and listening at port {0}.'.format(self.port))
    81         self.serve_forever()
    82 
    83 
    84 class Request:    
    85 
    86     @classmethod
    87     def _content_type(cls, doc):
    88         if doc.endswith('.html'):
    89             return 'text/html'
    90         elif doc.endswith('.css'):
    91             return 'text/css'
    92         elif doc.endswith('.js'):
    93             return 'application/javascript'
    94         elif doc.endswith('.ttf'):           
    95             return 'application/octet-stream'
    96         else:            
    97             return 'text/plain'
    98             
    99     def __init__(self, query, docs_path, db):       
   100         self.docs_path = docs_path
   101         self.db = db
   102         self.parsed = urlparse(query)
   103         self.args = dict()
   104         if len(self.parsed.query):
   105             self.args = dict([a.split('=') for a in self.parsed.query.split('&')])
   106         if self.parsed.path[1:] in commands:
   107             self.__class__ = commands[self.parsed.path[1:]]
   108         else:
   109             self.__class__ = WebRequest     
   110             self.args['doc'] = self.parsed.path    
   111 
   112     def __str__(self):
   113         return '{0}({1})'.format(str(self.__class__).partition('.')[-1].rstrip('">'),
   114                            ', '.join(['{0}: {1}'.format(k, str(v))
   115                                       for k, v in self.args.items()]))
   116 
   117 
   118 def client_rating(rating):
   119     return int(rating * 1000)
   120 
   121 
   122 class WordsRequest(Request):
   123 
   124     def __call__(self):   
   125         w1, w2 = self.db.pick_words()    
   126         data = '{{"words":[["{w1}", {r1}], ["{w2}", {r2}]]}}'.format(w1=w1[0],
   127                                                                      r1=w1[1], 
   128                                                                      w2=w2[0],
   129                                                                      r2=w2[1])
   130         return (200, 'application/javascript', bytes(data, ENCODING))
   131 
   132 
   133 class RateRequest(Request):
   134 
   135     def __call__(self):
   136         w3 = self.args['bn']
   137         w4 = self.args['wn']
   138         w3, w4 = self.db.rate(w3, w4)
   139         w1, w2 = self.db.pick_words()
   140         data = '{{"words":[["{w1}",{r1}],["{w2}",{r2}],'.format(w1=w1[0], r1=client_rating(w1[1]), 
   141                                                                    w2=w2[0], r2=client_rating(w2[1]))
   142         data += '["{w3}",{r3}],["{w4}",{r4}]]}}'.format(w3=w3[0], r3=client_rating(w3[1]), 
   143                                                            w4=w4[0], r4=client_rating(w4[1]))
   144         return (200, 'application/javascript', bytes(data, ENCODING))
   145 
   146 
   147 commands = {'words': WordsRequest,
   148             'rate': RateRequest}
   149 
   150 
   151 class WebRequest(Request):
   152 
   153     def __call__(self):
   154         if self.args['doc'] == '/':
   155             self.args['doc'] = '/index.html'
   156         content_type = self._content_type(self.args['doc'])
   157         data = bytes('', ENCODING)
   158         rel_path = self.docs_path + self.args['doc']
   159         if os.path.exists(rel_path):
   160             with open(rel_path, 'r+b') as f:
   161                 data = f.read()
   162             code = 200
   163         else:
   164             code = 404
   165         return (code, content_type, data)
   166 
   167 
   168 if __name__ == '__main__':
   169 	main()