async def model_delete_all(request, model_id): try: await cfg.models[model_id]["model"].delete.where(True).gino.status() message = f"All objects in {model_id} was deleted" flash_message = (message, "success") log_history_event(request, message, f"all, model_id: {model_id}") except asyncpg.exceptions.ForeignKeyViolationError as e: flash_message = (e.args, "error") return await model_view_table(request, model_id, flash_message)
async def upload_from_csv_data(upload_file: File, file_name: Text, request: Request, model_id: Text): with TextIOWrapper(BytesIO(upload_file.body)) as read_obj: request, is_success = await insert_data_from_csv_rows( read_obj, model_id, request) if is_success: log_history_event( request, f"Upload data from CSV from file {file_name} to model {model_id}", "system: upload_csv", ) return request, is_success
async def init_db_run(request: Request): data = literal_eval(request.form["data"][0]) count = 0 for _, value in data.items(): if isinstance(value, int): count += value await drop_and_recreate_all_tables() message = f"{count} object was deleted. DB was Init from Scratch" request.ctx.flash(message, "success") log_history_event(request, message, "system: init_db") return jinja.render("init_db.html", request, data=await count_elements_in_db())
async def model_edit_post(request, model_id): model_data = cfg.models[model_id] model = model_data["model"] columns_data = model_data["columns_data"] previous_id: Dict = utils.extract_obj_id_from_query( dict(request.query_args)["_id"]) previous_id: Dict = utils.correct_types(previous_id, columns_data, no_default=True) request_params = { key: request.form[key][0] if request.form[key][0] != "None" else None for key in request.form } request_params = utils.prepare_request_params(request_params, model_id, model_data) try: if not model_data["identity"]: old_obj = previous_id await update_all_by_params(request_params, previous_id, model) obj = request_params else: obj = await get_by_params(previous_id, model) old_obj = obj.to_dict() await obj.update(**request_params).apply() obj = obj.to_dict() changes = utils.get_changes(old_obj, obj) new_obj_id = utils.get_obj_id_from_row(model_data, request_params, previous_id) message = f"Object with id {previous_id} was updated." if changes: message += f'Changes: from {changes["from"]} to {changes["to"]}' request.ctx.flash(message, "success") log_history_event(request, message, previous_id) except asyncpg.exceptions.ForeignKeyViolationError as e: request.ctx.flash( f"ForeignKey error. " f"Impossible to edit field for row {previous_id}, " f"because exists objects that depend on it. {e}", "error", ) except asyncpg.exceptions.UniqueViolationError: request.ctx.flash( f"{model_id.capitalize()} with such id already exists", "error") except asyncpg.exceptions.NotNullViolationError as e: column = e.args[0].split("column")[1].split("violates")[0] request.ctx.flash(f"Field {column} cannot be null", "error") return await render_add_or_edit_form(request, model_id, new_obj_id)
async def presets(request: Request): # json content/type expected preset_path = request.json.get("preset") preset_id = request.json.get("preset_id") if preset_id: preset = get_preset_by_id(preset_id) if not preset: answer = { "error": f"Could not find preset with id {preset_id} in presets folder {cfg.presets_folder}. " } return response.json(answer, status=422) presets_folder = cfg.presets_folder else: if not preset_path: answer = { "error": 'You must provide "preset" field with path to preset yml file or preset_id. ' } return response.json(answer, status=422) preset = read_yaml(preset_path) presets_folder = os.path.dirname(preset_path) with_drop = "drop" in request.json if with_drop: await drop_and_recreate_all_tables() try: for model_id, file_path in preset["files"].items(): request.ctx.flash_messages = [] # TODO(ehborisov): handle is_success and errors properly? request, is_success = await insert_data_from_csv_file( os.path.join(presets_folder, file_path), model_id.lower(), request) logger.debug(str(request.ctx.flash_messages)) if "drop" in request.json: message = "DB was dropped & Preset was success loaded" else: message = "Preset was loaded" result = response.json({"status": f"{message}"}, status=200) log_history_event( request, f"Loaded preset {preset['id']}{' with DB drop' if with_drop else ''}", "system: load_preset", ) except FileNotFoundError: answer = {"error": f"Wrong file path in Preset {preset['name']}."} result = response.json(answer, status=422) return result
async def model_delete(request, model_id): """ route for delete item per row """ model_data = cfg.models[model_id] model = model_data["model"] request_params = {key: request.form[key][0] for key in request.form} obj_id = utils.get_obj_id_from_row(model_data, request_params) try: if not model_data["identity"]: await delete_all_by_params(obj_id, model) else: obj = await get_by_params(obj_id, model) await obj.delete() message = f"Object with {obj_id} was deleted" flash_message = (message, "success") log_history_event(request, message, obj_id) except asyncpg.exceptions.ForeignKeyViolationError as e: flash_message = (str(e.args), "error") return await model_view_table(request, model_id, flash_message)
async def model_copy(request, model_id): """ route for copy item per row """ request_params = {elem: request.form[elem][0] for elem in request.form} base_obj_id = utils.extract_obj_id_from_query(request_params["_id"]) try: new_obj_key = await create_object_copy(model_id, base_obj_id, cfg.models[model_id]) message = f"Object with {base_obj_id} key was copied as {new_obj_key}" flash_message = (message, "success") log_history_event(request, message, new_obj_key) except asyncpg.exceptions.UniqueViolationError as e: flash_message = ( f"Duplicate in Unique column Error during copy: {e.args}. \n" f"Try to rename existed id or add manual.", "error", ) except asyncpg.exceptions.ForeignKeyViolationError as e: flash_message = (e.args, "error") return await model_view_table(request, model_id, flash_message)
async def sql_query_run(request): result = [] if not request.form.get("sql_query"): request.ctx.flash("SQL query cannot be empty", "error") else: sql_query = request.form["sql_query"][0] try: result = await cfg.app.db.status(cfg.app.db.text(sql_query)) log_history_event(request, f"Query run '{sql_query}'", "system: sql_run") except asyncpg.exceptions.PostgresSyntaxError as e: request.ctx.flash(f"{e.args}", "error") except asyncpg.exceptions.UndefinedTableError as e: request.ctx.flash(f"{e.args}", "error") if result: return jinja.render("sql_runner.html", request, columns=result[1], result=result[1]) else: return jinja.render("sql_runner.html", request)
async def presets_use(request: Request): preset = utils.get_preset_by_id(request.form["preset"][0]) with_drop = "with_db" in request.form if with_drop: await drop_and_recreate_all_tables() request.ctx.flash("DB was successful Dropped", "success") try: for model_id, file_path in preset["files"].items(): request, is_success = await insert_data_from_csv_file( os.path.join(cfg.presets_folder, file_path), model_id.lower(), request) for message in request.ctx.flash_messages: request.ctx.flash(*message) history_message = ( f"Loaded preset {preset['id']} {' with DB drop' if with_drop else ''}" ) log_history_event(request, history_message, "system: load_preset") except FileNotFoundError: request.ctx.flash(f"Wrong file path in Preset {preset['name']}.", "error") return jinja.render("presets.html", request, presets=utils.get_presets()["presets"])
async def model_add(request, model_id): model_data = cfg.models[model_id] request_params = {key: request.form[key][0] for key in request.form} request_params = utils.prepare_request_params(request_params, model_id, model_data) not_filled = [ x for x in model_data["required_columns"] if x not in request_params ] if not_filled: request.ctx.flash(f"Fields {not_filled} required. Please fill it", "error") else: try: obj = await model_data["model"].create(**request_params) obj_id = utils.get_obj_id_from_row(model_data, obj.to_dict()) message = f"Object with {obj_id} was added." request.ctx.flash(message, "success") log_history_event(request, message, obj_id) except ( asyncpg.exceptions.StringDataRightTruncationError, ValueError, asyncpg.exceptions.ForeignKeyViolationError, ) as e: request.ctx.flash(e.args, "error") except asyncpg.exceptions.UniqueViolationError: request.ctx.flash( f"{model_id.capitalize()} with such id already exists", "error") except asyncpg.exceptions.NotNullViolationError as e: column = e.args[0].split("column")[1].split("violates")[0] request.ctx.flash(f"Field {column} cannot be null", "error") except asyncpg.exceptions.UndefinedTableError: request.ctx.flash( f"Somebody stole the table. Table {model_id} does not exist", "error") return await render_add_or_edit_form(request, model_id)
async def model_deepcopy(request, model_id): """ Recursively creates copies for the whole chain of entities, referencing the given model and instance id through the foreign keys. :param request: :param model_id: :return: """ request_params = {key: request.form[key][0] for key in request.form} columns_data = cfg.models[model_id]["columns_data"] base_obj_id = utils.extract_obj_id_from_query(request_params["_id"]) try: # todo: fix deepcopy new_id = utils.extract_obj_id_from_query(request_params["new_id"]) new_id = utils.correct_types(new_id, columns_data) except ValueError as e: request.ctx.flash(e, "error") return await render_model_view(request, model_id) try: async with cfg.app.db.acquire() as conn: async with conn.transaction() as _: new_base_obj_id = await deepcopy_recursive( cfg.models[model_id]["model"], base_obj_id, new_id=new_id, model_data=cfg.models[model_id], ) if isinstance(new_base_obj_id, tuple): request.ctx.flash(new_base_obj_id, "error") else: message = f"Object with {request_params['_id']} was deepcopied with new id {new_base_obj_id}" request.ctx.flash(message, "success") log_history_event(request, message, new_base_obj_id) except asyncpg.exceptions.PostgresError as e: request.ctx.flash(e.args, "error") return await render_model_view(request, model_id)