diff options
| -rw-r--r-- | day9/task5/core.py | 82 | ||||
| -rw-r--r-- | day9/task5/main.py | 43 | ||||
| -rw-r--r-- | day9/task5/router.py | 7 | ||||
| -rw-r--r-- | day9/task5/server.py | 23 |
4 files changed, 112 insertions, 43 deletions
diff --git a/day9/task5/core.py b/day9/task5/core.py new file mode 100644 index 0000000..2bf9cf5 --- /dev/null +++ b/day9/task5/core.py @@ -0,0 +1,82 @@ +from abc import ABC, abstractmethod +import json + +from utils import HTTP_STATUS_CODES + + +class Response(ABC): + @property + def status_code(self) -> int: + """ + По дефолту возвращается статус 200 + """ + return 200 + + @property + @abstractmethod + def content_type(self) -> str: + pass + + @property + @abstractmethod + def content(self) -> bytes: + pass + + +class HtmlResponse(Response): + def __init__(self, html, status_code=200): + self._html: str = html + self._code = status_code + + @property + def status_code(self) -> int: + return self._code + + @property + def content_type(self) -> str: + return 'text/html' + + @property + def content(self) -> bytes: + return self._html.encode() + + +class TextFileResponse(Response): + def __init__(self, path, extension): + with open(path, 'rb') as f: + self._content = f.read() + + self._extension = extension + + @property + def content_type(self) -> str: + return f'text/{self._extension}' + + @property + def content(self) -> bytes: + return self._content + + +class ImageResponse(TextFileResponse): + @property + def content_type(self) -> str: + return f'image/{self._extension}' + + +class JsonResponse(Response): + def __init__(self, json_object): + self._json_str = json.dumps(json_object, ensure_ascii=False) + + @property + def content_type(self) -> str: + return 'application/json' + + @property + def content(self) -> bytes: + return self._json_str.encode() + + +class ErrorResponse(HtmlResponse): + def __init__(self, http_code, message=''): + html = f'<center><h1>ERROR {http_code} {HTTP_STATUS_CODES[http_code].upper()}</h1></center><br>{message}' + super().__init__(html, http_code) diff --git a/day9/task5/main.py b/day9/task5/main.py index 019ff8b..1ca94bb 100644 --- a/day9/task5/main.py +++ b/day9/task5/main.py @@ -2,6 +2,8 @@ from router import route from utils import render_template, NOT_FOUND_CODE from config import SERVER_HOST, SERVER_PORT, STATIC_FILES_PATH +from core import ErrorResponse, TextFileResponse, ImageResponse, HtmlResponse, JsonResponse + import logging import csv from sys import stdout @@ -11,38 +13,39 @@ from sys import stdout def return_static(query, *args): if args[0] in ['css', 'js']: try: - with open(STATIC_FILES_PATH + f'/{args[0]}/{args[1]}', encoding='utf-8') as f: - data = f.read() - - return args[0], {'js': 'javascript', 'css': 'css'}[args[0]], data + return TextFileResponse( + path=STATIC_FILES_PATH + f'/{args[0]}/{args[1]}', + extension={'js': 'javascript', 'css': 'css'}.get(args[0]) + ) except (PermissionError, FileNotFoundError): - return '' + return ErrorResponse(404) + elif args[0] == 'images': try: - with open(STATIC_FILES_PATH + f'/{args[0]}/{args[1]}', 'rb') as f: - data = f.read() + return ImageResponse(STATIC_FILES_PATH + f'/{args[0]}/{args[1]}', args[1].split('.')[-1]) - return 'image', args[1].split('.')[-1], data except (PermissionError, FileNotFoundError): - return '' + return ErrorResponse(404) @route('/api/upload', ['POST']) def upload_file(query, *args): + base_html = '<a href="/">Return to main page</a><br>%s' + try: data = query['files'][0]['data'].decode() except (UnicodeDecodeError, KeyError, IndexError): - return '<a href="/">Return to main page</a><br><h1>Error while reading file</h1>' + return ErrorResponse(500, base_html % '<h1>Error while reading file</h1>') permitted_headers = db_column_names() try: data = list(csv.reader(data.strip().splitlines(), delimiter=';', quotechar='"')) if len(data[0]) != len(permitted_headers) or set(data[0]) != set(permitted_headers): - return '<a href="/">Return to main page</a><br><h1>File format error</h1>' + return ErrorResponse(500, base_html % '<h1>File format error</h1>') except IndexError: - return '<a href="/">Return to main page</a><br><h1>File format error</h1>' + return ErrorResponse(500, base_html % '<h1>File format error</h1>') cursor = db.cursor() cursor.execute("START TRANSACTION; DELETE FROM `table_task1`; COMMIT;") @@ -71,7 +74,7 @@ def upload_file(query, *args): ) cursor.close() - return '<a href="/">Return to main page</a><br><h1>File uploaded</h1>' + return HtmlResponse(base_html % '<h1>File uploaded</h1>') @route('/api/update', ['POST']) @@ -91,7 +94,7 @@ def update_post(query, *args): )) cursor.close() - return f'<a href="/">Return to main page</a><br><h1>Database Updated {query}</h1>' + return HtmlResponse('<a href="/">Return to main page</a><br><h1>Database Updated</h1>') @route('/api/delete', ['POST']) @@ -100,7 +103,7 @@ def delete_post(query, *args): cursor = db.cursor() cursor.execute(f'START TRANSACTION; DELETE FROM `table_task1` WHERE service_id="{service_id}"; COMMIT;') - return f'<a href="/">Return to main page</a><br><h1>Database Updated {query}</h1>' + return HtmlResponse('<a href="/">Return to main page</a><br><h1>Database Updated</h1>') @route('/api/add', ['POST']) @@ -120,7 +123,7 @@ def add_post(query, *args): )) cursor.close() - return f'<a href="/">Return to main page</a><br><h1>Database Updated {query}</h1>' + return HtmlResponse('<a href="/">Return to main page</a><br><h1>Database Updated</h1>') @route('/get', ['POST']) @@ -143,7 +146,7 @@ def db_get(query, *args): new_row.append(col) json_content.append(new_row) - return {'headers': table_headers, 'content': json_content} + return JsonResponse({'headers': table_headers, 'content': json_content}) elif query['type'] == 'single_id': cursor.execute(f'SELECT * FROM table_task1 WHERE service_id="{query["service_id"]}";') @@ -157,15 +160,15 @@ def db_get(query, *args): col = str(col) json_content.append(col) - return dict(zip(table_headers, json_content)) + return JsonResponse(dict(zip(table_headers, json_content))) - return NOT_FOUND_CODE + return ErrorResponse(NOT_FOUND_CODE) @route('/') def index_get(query, *args): data = render_template('index.html') - return data + return HtmlResponse(data) def prepare_logger(): diff --git a/day9/task5/router.py b/day9/task5/router.py index c3857f7..0827bb4 100644 --- a/day9/task5/router.py +++ b/day9/task5/router.py @@ -1,5 +1,6 @@ import re from utils import NOT_FOUND_CODE, BAD_REQUEST_CODE +from core import Response, ErrorResponse def route(url_format, methods=None): @@ -12,7 +13,7 @@ def route(url_format, methods=None): match = re.fullmatch(pattern, url) if match is None or len(match.groups()) != pattern.groups: - return BAD_REQUEST_CODE + return ErrorResponse(BAD_REQUEST_CODE) return func(query, *match.groups(), *args, **kwargs) @@ -25,8 +26,8 @@ def route(url_format, methods=None): return wrapper -def run(request): - res = NOT_FOUND_CODE +def run(request) -> Response: + res = ErrorResponse(NOT_FOUND_CODE) method, url = request['method'], request['url'] for url_pattern in _router_tree: diff --git a/day9/task5/server.py b/day9/task5/server.py index 8ee52a2..9ce38cb 100644 --- a/day9/task5/server.py +++ b/day9/task5/server.py @@ -1,9 +1,8 @@ from http.server import HTTPServer, BaseHTTPRequestHandler from urllib.parse import parse_qs -from json import dumps from router import run -from utils import HTTP_STATUS_CODES, parse_multipart_form +from utils import parse_multipart_form import logging @@ -55,24 +54,8 @@ class MyHTTPRequestHandler(BaseHTTPRequestHandler): })) def finalize_request(self, response): - if isinstance(response, int): - self._set_response(response, 'text/html') - response = f'<center><h1>ERROR {response} {HTTP_STATUS_CODES[response].upper()}</h1></center>'.encode('utf-8') - elif isinstance(response, (dict, list)): - self._set_response(200, 'application/json') - response = dumps(response).encode('utf-8') - elif isinstance(response, tuple): - if response[0] == 'image': - self._set_response(200, f'image/{response[1]}') - response = response[2] - elif response[0] in ['css', 'js']: - self._set_response(200, f'text/{response[1]}') - response = response[2].encode('utf-8') - else: - self._set_response(200, 'text/html') - response = response.encode('utf-8') - - self.wfile.write(response) + self._set_response(response.status_code, response.content_type) + self.wfile.write(response.content) def start_server(host, port): |