summaryrefslogtreecommitdiff
path: root/day9
diff options
context:
space:
mode:
Diffstat (limited to 'day9')
-rw-r--r--day9/task5/database.py29
-rw-r--r--day9/task5/index.html2
-rw-r--r--day9/task5/main.py187
-rw-r--r--day9/task5/router.py40
-rw-r--r--day9/task5/server.py47
-rw-r--r--day9/task5/utils.py107
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}