summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--day9/task5_vue/backend/core/response_types.py2
-rw-r--r--day9/task5_vue/backend/database/database.py4
-rw-r--r--day9/task5_vue/backend/database/validators.py9
-rw-r--r--day9/task5_vue/backend/database/wrappers.py68
-rw-r--r--day9/task5_vue/backend/views.py67
-rw-r--r--day9/task5_vue/requirements.txtbin496 -> 217 bytes
6 files changed, 90 insertions, 60 deletions
diff --git a/day9/task5_vue/backend/core/response_types.py b/day9/task5_vue/backend/core/response_types.py
index ca3a9f2..4094bf3 100644
--- a/day9/task5_vue/backend/core/response_types.py
+++ b/day9/task5_vue/backend/core/response_types.py
@@ -78,5 +78,5 @@ class JsonResponse(Response):
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}'
+ html = f'<center><h1>ERROR {http_code} {HTTP_STATUS_CODES[http_code].upper()}</h1></center><br><center><h1>{message}</h1></center>'
super().__init__(html, http_code)
diff --git a/day9/task5_vue/backend/database/database.py b/day9/task5_vue/backend/database/database.py
index 44554c7..20af3b4 100644
--- a/day9/task5_vue/backend/database/database.py
+++ b/day9/task5_vue/backend/database/database.py
@@ -27,13 +27,13 @@ def initialize_databases(configs, schemes):
continue
wrappers[config['name']] = wrapper
-
cursor = wrapper.connection.cursor()
for scheme in schemes:
Logger.get_logger().info(f'Preparing table "{scheme.meta["name"]}"...')
- cursor.execute('START TRANSACTION; {} COMMIT;'.format(scheme.get_create_line()))
+ cursor.execute(scheme.get_create_line())
wrapper.schemes[scheme.meta['name']] = scheme
cursor.close()
+ wrapper.connection.commit()
def shutdown_databases():
diff --git a/day9/task5_vue/backend/database/validators.py b/day9/task5_vue/backend/database/validators.py
index 112af2b..4df0fd6 100644
--- a/day9/task5_vue/backend/database/validators.py
+++ b/day9/task5_vue/backend/database/validators.py
@@ -43,6 +43,9 @@ class ValidateLength(Validator):
class ValidateTime(Validator):
@staticmethod
def validate(value, field_object):
+ if not isinstance(value, str):
+ return
+
match = re.fullmatch(r'(\d\d):(\d\d):(\d\d)', value)
if not match:
raise ValidationError('Wrong time format')
@@ -61,6 +64,9 @@ class ValidateTime(Validator):
class ValidateDate(Validator):
@staticmethod
def validate(value, field_object):
+ if not isinstance(value, str):
+ return
+
match = re.fullmatch(r'(\d\d\d\d)-(\d\d)-(\d\d)', value)
if not match:
raise ValidationError('Wrong date format')
@@ -92,6 +98,9 @@ class ValidateDate(Validator):
class ValidateDatetime(Validator):
@staticmethod
def validate(value, field_object):
+ if not isinstance(value, str):
+ return
+
datetime = value.split()
if len(datetime) != 2:
raise ValidationError('Wrong datetime format')
diff --git a/day9/task5_vue/backend/database/wrappers.py b/day9/task5_vue/backend/database/wrappers.py
index 5bb9761..d380b2e 100644
--- a/day9/task5_vue/backend/database/wrappers.py
+++ b/day9/task5_vue/backend/database/wrappers.py
@@ -46,7 +46,7 @@ class MySQLWrapper(Wrapper):
self.connection = MySQLdb.connect(
host=host,
user=username,
- passwd=password,
+ password=password,
db=db_name
)
@@ -54,19 +54,18 @@ class MySQLWrapper(Wrapper):
self.connection.close()
def clear_table(self, table_name):
- cursor = self.connection.cursor()
- cursor.execute(f"START TRANSACTION; DELETE FROM `{table_name}`; COMMIT;")
- cursor.close()
+ with self.connection.cursor() as cursor:
+ cursor.execute(f"DELETE FROM `{table_name}`;")
def get_column_names(self):
- cursor = self.connection.cursor()
- cursor.execute('DESCRIBE table_task1;')
- table_structure = cursor.fetchall()
+ with self.connection.cursor() as cursor:
+ cursor.execute('DESCRIBE table_task1;')
+ table_structure = cursor.fetchall()
+
table_headers = [field[0] for field in table_structure]
return table_headers
def insert_one(self, table_name, data_row: dict):
- cursor = self.connection.cursor()
scheme = self.schemes[table_name]
field_names = []
@@ -74,21 +73,23 @@ class MySQLWrapper(Wrapper):
for field_name, value in data_row.items():
field_names.append(f'`{field_name}`')
- if scheme.fields[field_name].data_type == str:
+ if scheme.fields[field_name].nullable and value is None:
+ values.append('NULL')
+ elif scheme.fields[field_name].data_type == str:
values.append(f'"{value}"')
else:
- values.append(value)
+ values.append(str(value))
- request = "START TRANSACTION; INSERT INTO `{}` ({}) VALUES ({}); COMMIT;".format(
+ request = "INSERT INTO `{}` ({}) VALUES ({});".format(
table_name, ",".join(field_names), ",".join(values)
)
- cursor.execute(request)
- cursor.close()
+ with self.connection.cursor() as cursor:
+ cursor.execute(request)
- def update(self, table_name, expressions, conditions):
- cursor = self.connection.cursor()
+ self.connection.commit()
+ def update(self, table_name, expressions, conditions):
expressions_formatted = []
for field_name, value in expressions.items():
if value != 'NULL' or not value.isnumeric():
@@ -101,48 +102,43 @@ class MySQLWrapper(Wrapper):
value = f'"{value}"'
conditions_formatted.append(f'`{field_name}`={value}')
- cursor.execute("START TRANSACTION; UPDATE `{}` SET {} WHERE {}; COMMIT;".format(
- table_name, ','.join(expressions_formatted), ' AND '.join(conditions_formatted)
- ))
+ with self.connection.cursor() as cursor:
+ cursor.execute("UPDATE `{}` SET {} WHERE {};".format(
+ table_name, ','.join(expressions_formatted), ' AND '.join(conditions_formatted)
+ ))
- cursor.close()
+ self.connection.commit()
def delete_from(self, table_name, conditions):
- cursor = self.connection.cursor()
-
conditions_formatted = []
for field_name, value in conditions.items():
if value != 'NULL' or not value.isnumeric():
value = f'"{value}"'
conditions_formatted.append(f'`{field_name}`={value}')
- cursor.execute("START TRANSACTION; DELETE FROM `{}` WHERE {}; COMMIT;".format(
- table_name, ' AND '.join(conditions_formatted)
- ))
+ with self.connection.cursor() as cursor:
+ cursor.execute("DELETE FROM `{}` WHERE {};".format(
+ table_name, ' AND '.join(conditions_formatted)
+ ))
- cursor.close()
+ self.connection.commit()
def get_data(self, table_name):
- cursor = self.connection.cursor()
+ with self.connection.cursor() as cursor:
+ cursor.execute(f'SELECT * FROM `{table_name}`;')
+ content = list(map(list, cursor.fetchall()))
- cursor.execute(f'SELECT * FROM `{table_name}`;')
- content = list(map(list, cursor.fetchall()))
-
- cursor.close()
return content
def get_rows(self, table_name, conditions):
- cursor = self.connection.cursor()
-
conditions_formatted = []
for field_name, value in conditions.items():
if value != 'NULL' or not value.isnumeric():
value = f'"{value}"'
conditions_formatted.append(f'`{field_name}`={value}')
- cursor.execute(f'SELECT * FROM `{table_name}` WHERE {" AND ".join(conditions_formatted)};')
- content = list(map(list, cursor.fetchall()))
-
- cursor.close()
+ with self.connection.cursor() as cursor:
+ cursor.execute(f'SELECT * FROM `{table_name}` WHERE {" AND ".join(conditions_formatted)};')
+ content = list(map(list, cursor.fetchall()))
return content
diff --git a/day9/task5_vue/backend/views.py b/day9/task5_vue/backend/views.py
index 5c39322..8fb3a36 100644
--- a/day9/task5_vue/backend/views.py
+++ b/day9/task5_vue/backend/views.py
@@ -5,7 +5,10 @@ from backend.core.response_types import ErrorResponse, TextFileResponse, ImageRe
from backend.database.database import get_wrapper_for
from backend.database.validators import ValidationError
+from backend.logger import Logger
+
import csv
+import io
def return_static(query, *args):
@@ -28,36 +31,58 @@ def return_static(query, *args):
def upload_file(query, *args):
- base_html = '<a href="/">Return to main page</a><br>%s'
-
try:
data = query['files'][0]['data'].decode('utf-8')
except (UnicodeDecodeError, KeyError, IndexError):
- return ErrorResponse(500, base_html % '<h1>Error while reading file</h1>')
+ return ErrorResponse(500, 'Error while reading file')
wrapper = get_wrapper_for('mysql')
-
- permitted_headers = wrapper.get_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 ErrorResponse(500, base_html % '<h1>File format error</h1>')
- except IndexError:
- return ErrorResponse(500, base_html % '<h1>File format error</h1>')
-
wrapper.clear_table('table_task1')
-
scheme = wrapper.schemes['table_task1']
- for row in data[1:]:
- insert_values = dict(zip(data[0], row))
- validation_results = scheme.validate(insert_values)
+
+ result = parse_csv(data, scheme)
+ if result is None:
+ return ErrorResponse(500, 'Wrong file format')
+
+ for index, row in enumerate(result):
+ Logger.get_logger().debug(f'Validating')
+ validation_results = scheme.validate(row)
if not validation_results['error']:
- wrapper.insert_one('table_task1', insert_values)
+ Logger.get_logger().debug(f'No errors')
+ wrapper.insert_one('table_task1', row)
else:
- return ErrorResponse(500)
-
- return HtmlResponse(base_html % '<h1>File uploaded</h1>')
+ Logger.get_logger().debug(f'Error {validation_results}')
+ return ErrorResponse(500, f'Validation error\n{validation_results}')
+
+ return HtmlResponse('File uploaded')
+
+
+def parse_csv(data, scheme):
+ reader = csv.DictReader(io.StringIO(data), delimiter=';')
+
+ if set(reader.fieldnames) != set(scheme.fields.keys()):
+ return None
+
+ result = []
+ for row in reader:
+ insert_row = {}
+ for field_name, value in row.items():
+ if scheme.fields[field_name].nullable and value == 'NULL':
+ insert_row[field_name] = None
+ else:
+ data_type = scheme.fields[field_name].data_type
+
+ try:
+ new_value = data_type(value)
+ except ValueError:
+ Logger.get_logger().debug(f'Unable to cast `{value}` to {data_type}')
+ insert_row[field_name] = value
+ else:
+ insert_row[field_name] = new_value
+
+ result.append(insert_row)
+
+ return result
def validate(query, *args):
diff --git a/day9/task5_vue/requirements.txt b/day9/task5_vue/requirements.txt
index fce4102..b0c1791 100644
--- a/day9/task5_vue/requirements.txt
+++ b/day9/task5_vue/requirements.txt
Binary files differ