diff options
| author | Andrew <saintruler@gmail.com> | 2019-07-06 14:36:55 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2019-07-06 14:36:55 +0400 |
| commit | 1e6967f8c4f1ef64947d7f2b95268339d78db454 (patch) | |
| tree | b2ebf1ccb160e3c58f277dfec4bae8f83b03ec45 /day9 | |
| parent | f1a923860c02c69d9e67d15da24f90d7306223e0 (diff) | |
WIP: Изменена структура сервера.
Diffstat (limited to 'day9')
| -rw-r--r-- | day9/task5/database.py | 29 | ||||
| -rw-r--r-- | day9/task5/index.html | 2 | ||||
| -rw-r--r-- | day9/task5/main.py | 187 | ||||
| -rw-r--r-- | day9/task5/router.py | 40 | ||||
| -rw-r--r-- | day9/task5/server.py | 47 | ||||
| -rw-r--r-- | day9/task5/utils.py | 107 |
6 files changed, 269 insertions, 143 deletions
diff --git a/day9/task5/database.py b/day9/task5/database.py new file mode 100644 index 0000000..067c481 --- /dev/null +++ b/day9/task5/database.py @@ -0,0 +1,29 @@ +import MySQLdb +from config import * + + +# В файле config.py создайте соответствующие переменные +db = MySQLdb.connect( + host=HOST, + user=USERNAME, + passwd=PASSWORD, + db=DATABASE_NAME +) + +db.cursor().execute( + ''' + CREATE TABLE IF NOT EXISTS `table_task1` ( + `service_id` int(11) NOT NULL AUTO_INCREMENT, + `servtype` varchar(20) NOT NULL DEFAULT 'hosting', + `subtype` varchar(32) NOT NULL DEFAULT '', + `user_id` bigint(20) NOT NULL, + `referrer_user_id` bigint(20) NOT NULL, + `state` varchar(1) NOT NULL DEFAULT 'N', + `creation_date` date NOT NULL DEFAULT '0000-01-01', + `creation_time` time NOT NULL DEFAULT '00:00:00', + `creation_request_sent_date` datetime DEFAULT NULL, + `notified_about_expiration` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`service_id`) + ) ENGINE=InnoDB AUTO_INCREMENT=35109400 DEFAULT CHARSET=utf8; + ''' +)
\ No newline at end of file diff --git a/day9/task5/index.html b/day9/task5/index.html index 20e2a3d..dc99964 100644 --- a/day9/task5/index.html +++ b/day9/task5/index.html @@ -1,7 +1,7 @@ <html lang="ru"> <head> - <meta charset="utf-8"> + <meta charset="UTF-8"> <title>"table_task1" table view</title> <style type="text/css"> table { diff --git a/day9/task5/main.py b/day9/task5/main.py index 4655c3f..8ed3b50 100644 --- a/day9/task5/main.py +++ b/day9/task5/main.py @@ -1,163 +1,66 @@ -from http.server import HTTPServer, BaseHTTPRequestHandler -from urllib.parse import parse_qs -import re +from router import route +from utils import render_template, parse_query +from database import db +from config import SERVER_HOST, SERVER_PORT -import MySQLdb -from config import * +@route('/update', ['POST']) +def update_post(query, *args): + expressions, conditions = parse_query(query) + cursor = db.cursor() + cursor.execute('UPDATE `table_task1` SET {} WHERE {}'.format( + expressions, conditions + )) + result = cursor.fetchall() + cursor.close() -def render_template(path, **kwargs): - with open(path) as f: - template = f.read() + return f'<h1>UPDATE: {result}</h1>' - for key in kwargs: - template = template.replace(f'%%{key}%%', kwargs[key]) - return template +@route('/delete', ['POST']) +def delete_post(query, *args): + return f'<h1>DELETE: {query}</h1>' -def parse_query(query): - parsed_query = {'expression': {}, 'condition': {}} +@route('/add', ['POST']) +def add_post(query, *args): + return f'<h1>ADD: {query}</h1>' - pattern = re.compile(r'(expression|condition)(\d+)_(key|value)') - for key, (value,) in query.items(): - match = pattern.fullmatch(key) - if match is not None: - index = int(match.group(2)) - if index not in parsed_query[match.group(1)]: - parsed_query[match.group(1)][index] = {} +@route('/') +def index_get(query, *args): + cursor = db.cursor() + cursor.execute('DESCRIBE table_task1;') + table_structure = cursor.fetchall() - parsed_query[match.group(1)][index][match.group(3)] = value + cursor.execute('SELECT * FROM table_task1;') + content = cursor.fetchall() - expressions = [] - for expression in parsed_query['expression'].values(): - expressions.append('`{}`="{}"'.format( - expression['key'], expression['value'] - )) - expressions = ','.join(expressions) + cursor.close() - conditions = [] - for condition in parsed_query['condition'].values(): - conditions.append('`{}`="{}"'.format( - condition['key'], condition['value'] - )) - conditions = ' AND '.join(conditions) + heading = [] + for column in [field[0] for field in table_structure]: + heading.append(f'<th>{column}</th>') + heading = '<thead><tr>\n%s\n</tr></thead>' % '\n'.join(heading) - return {'expressions': expressions, 'conditions': conditions} + rows = [] + for row_index, row in enumerate(content): + formatted = [] + for field_index, field in enumerate(row): + color = 'odd' if (field_index % 2 + row_index % 2) % 2 == 0 else 'even' + formatted.append(f'<td class={color}>{field}</td>') -class MyHTTPRequestHandler(BaseHTTPRequestHandler): - def update_post(self, query): - expressions, conditions = parse_query(query) + rows.append('<tr>{}</tr>'.format(''.join(formatted))) - cursor = db.cursor() - cursor.execute('UPDATE `table_task1` SET {} WHERE {}'.format( - expressions, conditions - )) - result = cursor.fetchall() - cursor.close() + body = '<tbody>{}</tbody>'.format("\n".join(rows)) - return f'<h1>UPDATE: {result}</h1>' + data = render_template('index.html', heading=heading, body=body) - def delete_post(self, query): - return f'<h1>DELETE: {query}</h1>' + return data - def add_post(self, query): - return f'<h1>ADD: {query}</h1>' - def index_get(self): - cursor = db.cursor() - cursor.execute('DESCRIBE table_task1;') - table_structure = cursor.fetchall() - - cursor.execute('SELECT * FROM table_task1;') - content = cursor.fetchall() - - cursor.close() - - heading = [] - for column in [field[0] for field in table_structure]: - heading.append(f'<th>{column}</th>') - heading = '<thead><tr>\n%s\n</tr></thead>' % '\n'.join(heading) - - rows = [] - for row_index, row in enumerate(content): - formatted = [] - for field_index, field in enumerate(row): - color = 'odd' if (field_index % 2 + row_index % 2) % 2 == 0 else 'even' - - formatted.append(f'<td class={color}>{field}</td>') - - rows.append('<tr>{}</tr>'.format(''.join(formatted))) - - body = '<tbody>{}</tbody>'.format("\n".join(rows)) - - data = render_template('index.html', heading=heading, body=body) - - return data - - def _set_response(self): - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - - def do_GET(self): - if self.path == '/': - response = self.index_get() - else: - response = '<center><h1>ERROR 404 NOT FOUND</h1></center>' - - self._set_response() - self.wfile.write(response.encode('utf-8')) - - def do_POST(self): - content_length = int(self.headers['Content-Length']) - post_data = parse_qs(self.rfile.read(content_length).decode('utf-8')) - - if self.path == '/update': - response = self.update_post(post_data) - - elif self.path == '/delete': - response = self.delete_post(post_data) - - elif self.path == '/add': - response = self.add_post(post_data) - - else: - response = '<center><h1>ERROR 404 NOT FOUND</h1></center>' - - self._set_response() - self.wfile.write(response.encode('utf-8')) - - -# В файле config.py создайте соответствующие переменные -db = MySQLdb.connect( - host=HOST, - user=USERNAME, - passwd=PASSWORD, - db=DATABASE_NAME -) - -db.cursor().execute( - ''' - CREATE TABLE IF NOT EXISTS `table_task1` ( - `service_id` int(11) NOT NULL AUTO_INCREMENT, - `servtype` varchar(20) NOT NULL DEFAULT 'hosting', - `subtype` varchar(32) NOT NULL DEFAULT '', - `user_id` bigint(20) NOT NULL, - `referrer_user_id` bigint(20) NOT NULL, - `state` varchar(1) NOT NULL DEFAULT 'N', - `creation_date` date NOT NULL DEFAULT '0000-01-01', - `creation_time` time NOT NULL DEFAULT '00:00:00', - `creation_request_sent_date` datetime DEFAULT NULL, - `notified_about_expiration` smallint(6) NOT NULL DEFAULT '0', - PRIMARY KEY (`service_id`) - ) ENGINE=InnoDB AUTO_INCREMENT=35109400 DEFAULT CHARSET=utf8; - ''' -) - -server_address = ("", 8000) -httpd = HTTPServer(server_address, MyHTTPRequestHandler) -httpd.serve_forever() +if __name__ == '__main__': + from server import start_server + start_server(SERVER_HOST, SERVER_PORT) diff --git a/day9/task5/router.py b/day9/task5/router.py new file mode 100644 index 0000000..c3857f7 --- /dev/null +++ b/day9/task5/router.py @@ -0,0 +1,40 @@ +import re +from utils import NOT_FOUND_CODE, BAD_REQUEST_CODE + + +def route(url_format, methods=None): + if methods is None: + methods = ['GET'] + + def wrapper(func): + def inner(url, query, *args, **kwargs): + pattern = re.compile(url_format) + match = re.fullmatch(pattern, url) + + if match is None or len(match.groups()) != pattern.groups: + return BAD_REQUEST_CODE + + return func(query, *match.groups(), *args, **kwargs) + + _router_tree[url_format] = _router_tree.get(url_format, {}) + for method in methods: + _router_tree[url_format][method] = inner + + return inner + + return wrapper + + +def run(request): + res = NOT_FOUND_CODE + + method, url = request['method'], request['url'] + for url_pattern in _router_tree: + if re.fullmatch(url_pattern, url) and method in _router_tree[url_pattern]: + res = _router_tree[url_pattern][method](url, request['query']) + break + + return res + + +_router_tree = {} diff --git a/day9/task5/server.py b/day9/task5/server.py new file mode 100644 index 0000000..d9fdc78 --- /dev/null +++ b/day9/task5/server.py @@ -0,0 +1,47 @@ +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import parse_qs + +from router import run +from utils import HTTP_STATUS_CODES + + +class MyHTTPRequestHandler(BaseHTTPRequestHandler): + def _set_response(self, code): + self.send_response(code) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_GET(self): + self.finalize_request(run({ + 'url': self.path, + 'method': 'GET', + 'query': {} + })) + + def do_POST(self): + content_length = int(self.headers['Content-Length']) + post_data = parse_qs(self.rfile.read(content_length).decode('utf-8')) + + for key in post_data: + post_data[key] = post_data[key][0] + + self.finalize_request(run({ + 'url': self.path, + 'method': 'POST', + 'query': post_data + })) + + def finalize_request(self, response): + if isinstance(response, int): + self._set_response(response) + response = f'<center><h1>ERROR {response} {HTTP_STATUS_CODES[response].upper()}</h1></center>' + else: + self._set_response(200) + + self.wfile.write(response.encode('utf-8')) + + +def start_server(host, port): + server_address = (host, port) + httpd = HTTPServer(server_address, MyHTTPRequestHandler) + httpd.serve_forever() diff --git a/day9/task5/utils.py b/day9/task5/utils.py new file mode 100644 index 0000000..5a48a9f --- /dev/null +++ b/day9/task5/utils.py @@ -0,0 +1,107 @@ +import re + +HTTP_STATUS_CODES = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi Status", + 226: "IM Used", # see RFC 3229 + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", # unused + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", # see RFC 2324 + 421: "Misdirected Request", # see RFC 7540 + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", # see RFC 6585 + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 449: "Retry With", # proprietary MS extension + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 507: "Insufficient Storage", + 510: "Not Extended", +} + +SUCCESS_CODE = 200 +BAD_REQUEST_CODE = 400 +NOT_FOUND_CODE = 404 +METHOD_NOT_ALLOWED_CODE = 405 + + +def render_template(path, **kwargs): + with open(path, encoding='utf-8') as f: + template = f.read() + + for key in kwargs: + template = template.replace(f'%%{key}%%', kwargs[key]) + + return template + + +def parse_query(query): + parsed_query = {'expression': {}, 'condition': {}} + + pattern = re.compile(r'(expression|condition)(\d+)_(key|value)') + + for key, (value,) in query.items(): + match = pattern.fullmatch(key) + if match is not None: + index = int(match.group(2)) + if index not in parsed_query[match.group(1)]: + parsed_query[match.group(1)][index] = {} + + parsed_query[match.group(1)][index][match.group(3)] = value + + expressions = [] + for expression in parsed_query['expression'].values(): + expressions.append('`{}`="{}"'.format( + expression['key'], expression['value'] + )) + expressions = ','.join(expressions) + + conditions = [] + for condition in parsed_query['condition'].values(): + conditions.append('`{}`="{}"'.format( + condition['key'], condition['value'] + )) + conditions = ' AND '.join(conditions) + + return {'expressions': expressions, 'conditions': conditions} |