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