diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bec4dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +venv/ \ No newline at end of file diff --git "a/Flask File Upload Mvc With My Sql And Logging \342\200\224 Step-by-step Documentation.pdf" "b/Flask File Upload Mvc With My Sql And Logging \342\200\224 Step-by-step Documentation.pdf" new file mode 100644 index 0000000..113299f Binary files /dev/null and "b/Flask File Upload Mvc With My Sql And Logging \342\200\224 Step-by-step Documentation.pdf" differ diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000..b05a42d Binary files /dev/null and b/__pycache__/app.cpython-313.pyc differ diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..e7a1437 Binary files /dev/null and b/__pycache__/config.cpython-313.pyc differ diff --git a/__pycache__/database.cpython-313.pyc b/__pycache__/database.cpython-313.pyc new file mode 100644 index 0000000..ba14f23 Binary files /dev/null and b/__pycache__/database.cpython-313.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..87177ce --- /dev/null +++ b/app.py @@ -0,0 +1,36 @@ +import os +import logging +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from config import Config +from database import db + +# Initialize Flask app +app = Flask(__name__) +app.config.from_object(Config) + +# Initialize database +# Configure logging + +os.makedirs(app.config['LOG_FOLDER'], exist_ok=True) +logging.basicConfig( +filename=f"{app.config['LOG_FOLDER']}/app.log", +level=logging.INFO, +format='%(asctime)s [%(levelname)s] %(message)s' +) + +# Register blueprints +from controllers.file_controller import file_bp +from controllers.auth_controller import auth_bp +app.register_blueprint(file_bp) +app.register_blueprint(auth_bp) + + +db.init_app(app) + +# Create tables if not exist +with app.app_context(): + db.create_all() + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..55431d6 --- /dev/null +++ b/config.py @@ -0,0 +1,10 @@ +import os +from dotenv import load_dotenv +load_dotenv() # Load .env variables +class Config: + SQLALCHEMY_DATABASE_URI = (f"mysql+pymysql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@" f"{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}") + SQLALCHEMY_TRACK_MODIFICATIONS = False + UPLOAD_FOLDER = os.getenv('UPLOAD_FOLDER', 'uploads') + LOG_FOLDER = 'logs' + DEBUG = True + SECRET_KEY = os.environ.get('SECRET_KEY') diff --git a/controllers/__pycache__/auth_controller.cpython-313.pyc b/controllers/__pycache__/auth_controller.cpython-313.pyc new file mode 100644 index 0000000..cbec0d0 Binary files /dev/null and b/controllers/__pycache__/auth_controller.cpython-313.pyc differ diff --git a/controllers/__pycache__/file_controller.cpython-313.pyc b/controllers/__pycache__/file_controller.cpython-313.pyc new file mode 100644 index 0000000..345801c Binary files /dev/null and b/controllers/__pycache__/file_controller.cpython-313.pyc differ diff --git a/controllers/auth_controller.py b/controllers/auth_controller.py new file mode 100644 index 0000000..bba8bd3 --- /dev/null +++ b/controllers/auth_controller.py @@ -0,0 +1,98 @@ + +from flask import Blueprint, request, jsonify, current_app +import jwt +import datetime + + +MOCK_USERS = [ + { + "id": 1, + "username": "admin", + "password": "password123", + "name": "Administrator", + "role": "admin" + }, + { + "id": 2, + "username": "user", + "password": "password", + "name": "Regular User", + "role": "user" + } +] + +auth_bp = Blueprint('auth_bp', __name__) + +def _find_user_by_username(username): + """A helper function to find a user in our mock DB.""" + for user in MOCK_USERS: + if user['username'] == username: + return user + return None + +@auth_bp.route('/login', methods=['POST']) +def login(): + """ + Handles the login request. + Validates credentials and returns user details and a JWT on success. + """ + try: + data = request.json + if not data: + return jsonify({"message": "No input data provided"}), 400 + + username = data.get('username') + password = data.get('password') + + if not username or not password: + return jsonify({"message": "Missing username or password"}), 400 + + + user = _find_user_by_username(username) + + if not user: + current_app.logger.warning(f"Failed login attempt. User not found: {username}") + return jsonify({"message": "Username not found"}), 401 + + + if user['password'] != password: + + current_app.logger.warning(f"Wrong password for user: {username}") + return jsonify({"message": "Wrong password"}), 401 + + + payload = { + 'sub': user['id'], + 'name': user['name'], + 'role': user['role'], + 'iat': datetime.datetime.now(), + 'exp': datetime.datetime.now() + datetime.timedelta(hours=1) + } + + + secret_key = current_app.config['SECRET_KEY'] + + # token + token = jwt.encode(payload, secret_key, algorithm="HS256") + + + user_details = { + "id": user['id'], + "username": user['username'], + "name": user['name'], + "role": user['role'] + } + + current_app.logger.info(f"Login successful for user: {username}") + + + return jsonify({ + "message": "Login successful", + "token": token, + "user": user_details + }), 200 + + except Exception as e: + + current_app.logger.error(f"Error during login for user {username}: {e}") + return jsonify({"message": "An internal error occurred"}), 500 \ No newline at end of file diff --git a/controllers/file_controller.py b/controllers/file_controller.py new file mode 100644 index 0000000..b8566e3 --- /dev/null +++ b/controllers/file_controller.py @@ -0,0 +1,60 @@ +from flask import Blueprint, request, jsonify, send_file,current_app +from services.file_service import save_file, update_file, delete_file +from models.uploaded_file import UploadedFile + + +import logging +import uuid +import os + +file_bp = Blueprint('file_bp', __name__) + +# POST — Upload file +@file_bp.route('/upload', methods=['POST']) +def upload_file(): + try: + if 'file' not in request.files: + return jsonify({'message': 'No file uploaded'}), 400 + file = request.files['file'] + folder = os.path.join(current_app.config['UPLOAD_FOLDER'], uuid.uuid4().hex) # new folder per request + uploaded = save_file(file, folder) + return jsonify({'message': 'Upload successful', 'id': uploaded.id, 'path': uploaded.file_path}), 201 + except Exception as e: + logging.error(f"Upload error: {e}") + return jsonify({'message': str(e)}), 400 + +# GET — Retrieve file info or download +@file_bp.route('/file/', methods=['GET']) +def get_file(file_id): + try: + file = UploadedFile.query.get(file_id) + if not file: + return jsonify({'message': 'File not found'}), 404 + # You can test using Postman → GET http://localhost:5000/file/ + return send_file(file.file_path, as_attachment=True) + except Exception as e: + logging.error(f"Get error: {e}") + return jsonify({'message': str(e)}), 400 + +# PUT — Update file +@file_bp.route('/file/', methods=['PUT']) +def update_existing_file(file_id): + try: + if 'file' not in request.files: + return jsonify({'message': 'No file provided'}), 400 + file = request.files['file'] + updated = update_file(file_id, file, current_app.config['UPLOAD_FOLDER']) + return jsonify({'message': 'File updated successfully', 'path': updated.file_path}), 200 + except Exception as e: + logging.error(f"Update error: {e}") + return jsonify({'message': str(e)}), 400 + +# DELETE — Delete file +@file_bp.route('/file/', methods=['DELETE']) +def delete_existing_file(file_id): + try: + delete_file(file_id) + return jsonify({'message': 'File deleted successfully'}), 200 + except Exception as e: + logging.error(f"Delete error: {e}") + return jsonify({'message': str(e)}), 400 \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..2e1eeb6 --- /dev/null +++ b/database.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() \ No newline at end of file diff --git a/file-upload b/file-upload new file mode 160000 index 0000000..ea0fb54 --- /dev/null +++ b/file-upload @@ -0,0 +1 @@ +Subproject commit ea0fb5432eedecad2a28425d2cf00c811408aa00 diff --git a/logs/app.log b/logs/app.log new file mode 100644 index 0000000..8892a68 --- /dev/null +++ b/logs/app.log @@ -0,0 +1,284 @@ +2025-10-19 13:09:58,582 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 13:09:58,582 [INFO] Press CTRL+C to quit +2025-10-19 13:11:56,377 [INFO] 127.0.0.1 - - [19/Oct/2025 13:11:56] "GET /file/1 HTTP/1.1" 404 - +2025-10-19 13:12:04,641 [INFO] 127.0.0.1 - - [19/Oct/2025 13:12:04] "POST /file/1 HTTP/1.1" 405 - +2025-10-19 13:12:21,921 [INFO] 127.0.0.1 - - [19/Oct/2025 13:12:21] "POST /file HTTP/1.1" 404 - +2025-10-19 13:14:00,216 [INFO] 127.0.0.1 - - [19/Oct/2025 13:14:00] "POST /file HTTP/1.1" 404 - +2025-10-19 13:14:03,698 [INFO] 127.0.0.1 - - [19/Oct/2025 13:14:03] "POST /file/1 HTTP/1.1" 405 - +2025-10-19 13:14:24,561 [INFO] 127.0.0.1 - - [19/Oct/2025 13:14:24] "POST /file/1 HTTP/1.1" 405 - +2025-10-19 13:14:33,916 [INFO] 127.0.0.1 - - [19/Oct/2025 13:14:33] "POST /file HTTP/1.1" 404 - +2025-10-19 13:15:19,857 [INFO] 127.0.0.1 - - [19/Oct/2025 13:15:19] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:15:24,869 [INFO] 127.0.0.1 - - [19/Oct/2025 13:15:24] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:15:46,337 [INFO] 127.0.0.1 - - [19/Oct/2025 13:15:46] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:15:55,368 [INFO] 127.0.0.1 - - [19/Oct/2025 13:15:55] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:16:17,766 [INFO] 127.0.0.1 - - [19/Oct/2025 13:16:17] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:17:02,675 [INFO] 127.0.0.1 - - [19/Oct/2025 13:17:02] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:18:27,851 [INFO] 127.0.0.1 - - [19/Oct/2025 13:18:27] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:18:29,633 [INFO] 127.0.0.1 - - [19/Oct/2025 13:18:29] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:19:35,417 [INFO] 127.0.0.1 - - [19/Oct/2025 13:19:35] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:20:30,892 [INFO] 127.0.0.1 - - [19/Oct/2025 13:20:30] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:22:09,038 [INFO] 127.0.0.1 - - [19/Oct/2025 13:22:09] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:22:58,789 [ERROR] Upload error: 'filename' is an invalid keyword argument for UploadedFile +2025-10-19 13:22:58,790 [INFO] 127.0.0.1 - - [19/Oct/2025 13:22:58] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:23:35,607 [ERROR] Upload error: 'filename' is an invalid keyword argument for UploadedFile +2025-10-19 13:23:35,607 [INFO] 127.0.0.1 - - [19/Oct/2025 13:23:35] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:24:48,690 [INFO] 127.0.0.1 - - [19/Oct/2025 13:24:48] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:24:50,756 [INFO] 127.0.0.1 - - [19/Oct/2025 13:24:50] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:27:39,040 [INFO] 127.0.0.1 - - [19/Oct/2025 13:27:39] "GET /file/1 HTTP/1.1" 404 - +2025-10-19 13:28:48,941 [INFO] 127.0.0.1 - - [19/Oct/2025 13:28:48] "GET /file/11 HTTP/1.1" 404 - +2025-10-19 13:28:54,539 [INFO] 127.0.0.1 - - [19/Oct/2025 13:28:54] "GET /file/2 HTTP/1.1" 404 - +2025-10-19 13:28:57,558 [INFO] 127.0.0.1 - - [19/Oct/2025 13:28:57] "GET /file/3 HTTP/1.1" 404 - +2025-10-19 13:29:00,602 [INFO] 127.0.0.1 - - [19/Oct/2025 13:29:00] "GET /file/4 HTTP/1.1" 404 - +2025-10-19 13:29:03,631 [INFO] 127.0.0.1 - - [19/Oct/2025 13:29:03] "GET /file/1 HTTP/1.1" 404 - +2025-10-19 13:31:41,221 [INFO] 127.0.0.1 - - [19/Oct/2025 13:31:41] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:31:53,694 [ERROR] Upload error: 'filename' is an invalid keyword argument for UploadedFile +2025-10-19 13:31:53,695 [INFO] 127.0.0.1 - - [19/Oct/2025 13:31:53] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:35:17,352 [ERROR] Upload error: 'filename' is an invalid keyword argument for UploadedFile +2025-10-19 13:35:17,353 [INFO] 127.0.0.1 - - [19/Oct/2025 13:35:17] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:35:23,211 [INFO] 127.0.0.1 - - [19/Oct/2025 13:35:23] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:35:54,777 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 13:35:54,777 [INFO] Press CTRL+C to quit +2025-10-19 13:38:11,748 [ERROR] Upload error: (pymysql.err.OperationalError) (1054, "Unknown column 'filename' in 'field list'") +[SQL: INSERT INTO uploaded_file (filename, file_path) VALUES (%(filename)s, %(file_path)s)] +[parameters: {'filename': 'fe29cabba4eb4a8bb0c971c2cab891d3_ERD_REPORT.txt', 'file_path': 'uploads\\ae1044410425409898794fcc91b55fc6\\fe29cabba4eb4a8bb0c971c2cab891d3_ERD_REPORT.txt'}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-10-19 13:38:11,749 [INFO] 127.0.0.1 - - [19/Oct/2025 13:38:11] "POST /upload HTTP/1.1" 400 - +2025-10-19 13:44:15,154 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 13:44:15,154 [INFO] Press CTRL+C to quit +2025-10-19 13:45:17,213 [INFO] File saved: uploads\21f23ce5e9ca4acd984937d15d5ae5b8\ef7738c9921640a982a07020043853f3_ERD_REPORT.txt +2025-10-19 13:45:17,218 [INFO] 127.0.0.1 - - [19/Oct/2025 13:45:17] "POST /upload HTTP/1.1" 201 - +2025-10-19 13:45:38,865 [INFO] 127.0.0.1 - - [19/Oct/2025 13:45:38] "GET /file/1 HTTP/1.1" 200 - +2025-10-19 13:47:10,958 [INFO] File saved: uploads\524bb7ea762a4add914c87f3165e1c6d_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 13:47:10,969 [INFO] File updated: ID=1 +2025-10-19 13:47:10,971 [INFO] 127.0.0.1 - - [19/Oct/2025 13:47:10] "PUT /file/1 HTTP/1.1" 200 - +2025-10-19 13:47:17,147 [INFO] File deleted from storage: uploads\524bb7ea762a4add914c87f3165e1c6d_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 13:47:17,161 [INFO] Record deleted: ID=1 +2025-10-19 13:47:17,162 [INFO] 127.0.0.1 - - [19/Oct/2025 13:47:17] "DELETE /file/1 HTTP/1.1" 200 - +2025-10-19 13:48:33,260 [INFO] 127.0.0.1 - - [19/Oct/2025 13:48:33] "DELETE /upload HTTP/1.1" 405 - +2025-10-19 13:48:50,942 [INFO] File saved: uploads\30472a9d9dee45c482ed4b8f4ffd2997\a19db5f610ee425aba07da3946a7a7bf_Flask_Logging_Assignment_Documentation.pdf +2025-10-19 13:48:50,944 [INFO] 127.0.0.1 - - [19/Oct/2025 13:48:50] "POST /upload HTTP/1.1" 201 - +2025-10-19 14:08:46,966 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 14:08:46,966 [INFO] Press CTRL+C to quit +2025-10-19 14:09:24,862 [INFO] File saved: uploads\55b722747a6a4145af345b3c97f6f3c2\160f0a07f1d84fac9aebd3b906bd026f_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 14:09:24,869 [INFO] 127.0.0.1 - - [19/Oct/2025 14:09:24] "POST /upload HTTP/1.1" 201 - +2025-10-19 14:09:44,592 [INFO] 127.0.0.1 - - [19/Oct/2025 14:09:44] "GET /file/4 HTTP/1.1" 200 - +2025-10-19 14:11:30,113 [INFO] File saved: uploads\e09a5ad792ad4bf8ad68d11ffd44ef3a_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 14:11:30,122 [INFO] File updated: ID=4 +2025-10-19 14:11:30,125 [INFO] 127.0.0.1 - - [19/Oct/2025 14:11:30] "PUT /file/4 HTTP/1.1" 200 - +2025-10-19 14:11:44,907 [INFO] File deleted from storage: uploads\e09a5ad792ad4bf8ad68d11ffd44ef3a_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 14:11:44,915 [INFO] Record deleted: ID=4 +2025-10-19 14:11:44,915 [INFO] 127.0.0.1 - - [19/Oct/2025 14:11:44] "DELETE /file/4 HTTP/1.1" 200 - +2025-10-19 14:12:21,636 [INFO] File saved: uploads\f3e5be010ff243f08016c992a1434945\0d3fc979c0e748e8b07b78f4d2bc9663_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 14:12:21,638 [INFO] 127.0.0.1 - - [19/Oct/2025 14:12:21] "POST /upload HTTP/1.1" 201 - +2025-10-19 14:12:43,913 [INFO] File saved: uploads\ea426657e7ad462193cff9d2434c606b_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf +2025-10-19 14:12:43,922 [INFO] File updated: ID=6 +2025-10-19 14:12:43,925 [INFO] 127.0.0.1 - - [19/Oct/2025 14:12:43] "PUT /file/6 HTTP/1.1" 200 - +2025-10-28 15:40:55,217 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 15:40:55,218 [INFO] Press CTRL+C to quit +2025-10-28 15:40:55,224 [INFO] * Restarting with stat +2025-10-28 15:40:57,463 [WARNING] * Debugger is active! +2025-10-28 15:40:57,473 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:47:17,226 [INFO] Login successful for user: admin +2025-10-28 15:47:17,227 [INFO] 127.0.0.1 - - [28/Oct/2025 15:47:17] "POST /login HTTP/1.1" 200 - +2025-10-28 15:47:54,672 [WARNING] Failed login attempt for user: admin +2025-10-28 15:47:54,672 [INFO] 127.0.0.1 - - [28/Oct/2025 15:47:54] "POST /login HTTP/1.1" 401 - +2025-10-28 15:47:56,205 [WARNING] Failed login attempt for user: admin +2025-10-28 15:47:56,207 [INFO] 127.0.0.1 - - [28/Oct/2025 15:47:56] "POST /login HTTP/1.1" 401 - +2025-10-28 15:48:14,178 [WARNING] Failed login attempt for user: admin +2025-10-28 15:48:14,179 [INFO] 127.0.0.1 - - [28/Oct/2025 15:48:14] "POST /login HTTP/1.1" 401 - +2025-10-28 15:48:31,801 [WARNING] Failed login attempt for user: admin +2025-10-28 15:48:31,801 [INFO] 127.0.0.1 - - [28/Oct/2025 15:48:31] "POST /login HTTP/1.1" 401 - +2025-10-28 15:48:37,602 [WARNING] Failed login attempt for user: admin1 +2025-10-28 15:48:37,603 [INFO] 127.0.0.1 - - [28/Oct/2025 15:48:37] "POST /login HTTP/1.1" 401 - +2025-10-28 15:54:31,381 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\auth_controller.py', reloading +2025-10-28 15:54:31,599 [INFO] * Restarting with stat +2025-10-28 15:54:33,420 [WARNING] * Debugger is active! +2025-10-28 15:54:33,430 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:54:49,139 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 15:54:49,139 [INFO] Press CTRL+C to quit +2025-10-28 15:54:49,150 [INFO] * Restarting with stat +2025-10-28 15:54:51,584 [WARNING] * Debugger is active! +2025-10-28 15:54:51,588 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:55:56,241 [INFO] 127.0.0.1 - - [28/Oct/2025 15:55:56] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:34,095 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:34] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:35,257 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:35] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:36,830 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:36] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:37,025 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:37] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:37,677 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:37] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:37,890 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:37] "POST /login HTTP/1.1" 401 - +2025-10-28 15:56:40,564 [INFO] 127.0.0.1 - - [28/Oct/2025 15:56:40] "POST /login HTTP/1.1" 401 - +2025-10-28 15:57:32,191 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\auth_controller.py', reloading +2025-10-28 15:57:32,368 [INFO] * Restarting with stat +2025-10-28 15:57:34,546 [WARNING] * Debugger is active! +2025-10-28 15:57:34,551 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:57:37,732 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\auth_controller.py', reloading +2025-10-28 15:57:38,109 [INFO] * Restarting with stat +2025-10-28 15:57:43,659 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 15:57:43,659 [INFO] Press CTRL+C to quit +2025-10-28 15:57:43,663 [INFO] * Restarting with stat +2025-10-28 15:57:45,151 [WARNING] * Debugger is active! +2025-10-28 15:57:45,160 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:57:56,978 [INFO] 127.0.0.1 - - [28/Oct/2025 15:57:56] "POST /login HTTP/1.1" 401 - +2025-10-28 15:57:58,224 [INFO] 127.0.0.1 - - [28/Oct/2025 15:57:58] "POST /login HTTP/1.1" 401 - +2025-10-28 15:57:58,992 [INFO] 127.0.0.1 - - [28/Oct/2025 15:57:58] "POST /login HTTP/1.1" 401 - +2025-10-28 15:57:59,376 [INFO] 127.0.0.1 - - [28/Oct/2025 15:57:59] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:02,620 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:02] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:09,375 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:09] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:10,541 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:10] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:11,083 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:11] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:11,376 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:11] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:17,359 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:17] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:18,064 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:18] "POST /login HTTP/1.1" 401 - +2025-10-28 15:58:18,736 [INFO] 127.0.0.1 - - [28/Oct/2025 15:58:18] "POST /login HTTP/1.1" 401 - +2025-10-28 15:59:28,893 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\auth_controller.py', reloading +2025-10-28 15:59:29,090 [INFO] * Restarting with stat +2025-10-28 15:59:30,339 [WARNING] * Debugger is active! +2025-10-28 15:59:30,343 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:59:34,004 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 15:59:34,005 [INFO] Press CTRL+C to quit +2025-10-28 15:59:34,008 [INFO] * Restarting with stat +2025-10-28 15:59:35,259 [WARNING] * Debugger is active! +2025-10-28 15:59:35,265 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 15:59:41,190 [INFO] 127.0.0.1 - - [28/Oct/2025 15:59:41] "POST /login HTTP/1.1" 400 - +2025-10-28 15:59:42,571 [INFO] 127.0.0.1 - - [28/Oct/2025 15:59:42] "POST /login HTTP/1.1" 400 - +2025-10-28 15:59:48,430 [WARNING] Failed login attempt for user: asd +2025-10-28 15:59:48,431 [INFO] 127.0.0.1 - - [28/Oct/2025 15:59:48] "POST /login HTTP/1.1" 401 - +2025-10-28 15:59:49,263 [WARNING] Failed login attempt for user: asd +2025-10-28 15:59:49,264 [INFO] 127.0.0.1 - - [28/Oct/2025 15:59:49] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:09,615 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:09,615 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:09] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:10,310 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:10,310 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:10] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:10,716 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:10,716 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:10] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:11,493 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:11,493 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:11] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:13,541 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:13,541 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:13] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:13,824 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:13,824 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:13] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:14,059 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:14,059 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:14] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:14,295 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:14,295 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:14] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:14,685 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:14,685 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:14] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:15,864 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:15,865 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:15] "POST /login HTTP/1.1" 401 - +2025-10-28 16:00:16,645 [WARNING] Failed login attempt for user: username +2025-10-28 16:00:16,645 [INFO] 127.0.0.1 - - [28/Oct/2025 16:00:16] "POST /login HTTP/1.1" 401 - +2025-10-28 16:01:00,977 [INFO] Login successful for user: admin +2025-10-28 16:01:00,978 [INFO] 127.0.0.1 - - [28/Oct/2025 16:01:00] "POST /login HTTP/1.1" 200 - +2025-10-28 16:01:01,852 [INFO] Login successful for user: admin +2025-10-28 16:01:01,853 [INFO] 127.0.0.1 - - [28/Oct/2025 16:01:01] "POST /login HTTP/1.1" 200 - +2025-10-28 16:01:02,532 [INFO] Login successful for user: admin +2025-10-28 16:01:02,533 [INFO] 127.0.0.1 - - [28/Oct/2025 16:01:02] "POST /login HTTP/1.1" 200 - +2025-10-28 16:01:48,100 [INFO] Login successful for user: admin +2025-10-28 16:01:48,101 [INFO] 127.0.0.1 - - [28/Oct/2025 16:01:48] "POST /login HTTP/1.1" 200 - +2025-10-28 16:01:49,339 [INFO] Login successful for user: admin +2025-10-28 16:01:49,340 [INFO] 127.0.0.1 - - [28/Oct/2025 16:01:49] "POST /login HTTP/1.1" 200 - +2025-10-28 16:02:05,624 [WARNING] Failed login attempt for user: admin +2025-10-28 16:02:05,625 [INFO] 127.0.0.1 - - [28/Oct/2025 16:02:05] "POST /login HTTP/1.1" 401 - +2025-10-28 16:02:15,398 [WARNING] Failed login attempt for user: admin1 +2025-10-28 16:02:15,398 [INFO] 127.0.0.1 - - [28/Oct/2025 16:02:15] "POST /login HTTP/1.1" 401 - +2025-10-28 16:03:34,622 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\auth_controller.py', reloading +2025-10-28 16:03:34,832 [INFO] * Restarting with stat +2025-10-28 16:03:37,098 [WARNING] * Debugger is active! +2025-10-28 16:03:37,102 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 16:03:51,134 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 16:03:51,134 [INFO] Press CTRL+C to quit +2025-10-28 16:03:51,137 [INFO] * Restarting with stat +2025-10-28 16:03:52,349 [WARNING] * Debugger is active! +2025-10-28 16:03:52,353 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 16:04:00,427 [WARNING] Failed login attempt. User not found: admin1 +2025-10-28 16:04:00,428 [INFO] 127.0.0.1 - - [28/Oct/2025 16:04:00] "POST /login HTTP/1.1" 401 - +2025-10-28 16:04:10,627 [WARNING] Wrong password for user: admin +2025-10-28 16:04:10,627 [INFO] 127.0.0.1 - - [28/Oct/2025 16:04:10] "POST /login HTTP/1.1" 401 - +2025-10-28 16:07:07,995 [ERROR] Upload error: name 'app' is not defined +2025-10-28 16:07:07,996 [INFO] 127.0.0.1 - - [28/Oct/2025 16:07:07] "POST /upload HTTP/1.1" 400 - +2025-10-28 16:08:02,985 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\file_controller.py', reloading +2025-10-28 16:08:03,291 [INFO] * Restarting with stat +2025-10-28 16:11:38,443 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 16:11:38,444 [INFO] Press CTRL+C to quit +2025-10-28 16:11:38,446 [INFO] * Restarting with stat +2025-10-28 16:11:40,145 [WARNING] * Debugger is active! +2025-10-28 16:11:40,153 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 16:11:47,618 [INFO] File saved: uploads\499e288008254e0087a383457eac0897\fc498595839f4f0b9aa37d8f97e263f7_Flask_FileUpload_Login_Extension.pdf +2025-10-28 16:11:47,628 [INFO] 127.0.0.1 - - [28/Oct/2025 16:11:47] "POST /upload HTTP/1.1" 201 - +2025-10-28 16:12:22,991 [INFO] 127.0.0.1 - - [28/Oct/2025 16:12:22] "PUT /upload/8 HTTP/1.1" 404 - +2025-10-28 16:13:10,461 [INFO] 127.0.0.1 - - [28/Oct/2025 16:13:10] "PUT /upload/8 HTTP/1.1" 404 - +2025-10-28 16:14:11,686 [INFO] 127.0.0.1 - - [28/Oct/2025 16:14:11] "PUT /upload HTTP/1.1" 405 - +2025-10-28 16:14:16,143 [INFO] 127.0.0.1 - - [28/Oct/2025 16:14:16] "PUT /upload/8 HTTP/1.1" 404 - +2025-10-28 16:14:29,591 [INFO] 127.0.0.1 - - [28/Oct/2025 16:14:29] "PUT /upload/8 HTTP/1.1" 404 - +2025-10-28 16:16:18,849 [INFO] 127.0.0.1 - - [28/Oct/2025 16:16:18] "PUT /api/8/update HTTP/1.1" 404 - +2025-10-28 16:16:34,434 [INFO] 127.0.0.1 - - [28/Oct/2025 16:16:34] "PUT /api/file/8/update HTTP/1.1" 404 - +2025-10-28 16:16:35,612 [INFO] 127.0.0.1 - - [28/Oct/2025 16:16:35] "PUT /api/file/8/update HTTP/1.1" 404 - +2025-10-28 16:17:41,048 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\controllers\\file_controller.py', reloading +2025-10-28 16:17:41,179 [INFO] * Restarting with stat +2025-10-28 16:17:42,742 [WARNING] * Debugger is active! +2025-10-28 16:17:42,748 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 16:17:55,461 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-28 16:17:55,462 [INFO] Press CTRL+C to quit +2025-10-28 16:17:55,484 [INFO] * Restarting with stat +2025-10-28 16:17:56,506 [WARNING] * Debugger is active! +2025-10-28 16:17:56,511 [INFO] * Debugger PIN: 127-520-521 +2025-10-28 16:18:10,439 [INFO] 127.0.0.1 - - [28/Oct/2025 16:18:10] "PUT /api/file/8/update HTTP/1.1" 404 - +2025-10-28 16:18:27,196 [INFO] 127.0.0.1 - - [28/Oct/2025 16:18:27] "PUT /update/8 HTTP/1.1" 404 - +2025-10-28 16:18:28,693 [INFO] 127.0.0.1 - - [28/Oct/2025 16:18:28] "PUT /update/8 HTTP/1.1" 404 - +2025-10-28 16:18:48,395 [INFO] File saved: uploads\5ec3b3cc5d4f490e8d950b1a56a2b588_1a.png +2025-10-28 16:18:48,412 [INFO] File updated: ID=8 +2025-10-28 16:18:48,416 [INFO] 127.0.0.1 - - [28/Oct/2025 16:18:48] "PUT /file/8 HTTP/1.1" 200 - +2025-10-28 16:18:59,887 [INFO] File deleted from storage: uploads\5ec3b3cc5d4f490e8d950b1a56a2b588_1a.png +2025-10-28 16:18:59,896 [INFO] Record deleted: ID=8 +2025-10-28 16:18:59,897 [INFO] 127.0.0.1 - - [28/Oct/2025 16:18:59] "DELETE /file/8 HTTP/1.1" 200 - +2025-10-28 16:19:01,790 [ERROR] Delete error: File not found +2025-10-28 16:19:01,791 [INFO] 127.0.0.1 - - [28/Oct/2025 16:19:01] "DELETE /file/8 HTTP/1.1" 400 - +2025-10-28 16:21:33,579 [INFO] File saved: uploads\fd33b9961ff3440fabeaa5ee02aec779\b258ed3b7f754b819061c10badf59404_1a.png +2025-10-28 16:21:33,581 [INFO] 127.0.0.1 - - [28/Oct/2025 16:21:33] "POST /upload HTTP/1.1" 201 - +2025-10-28 16:21:48,082 [INFO] 127.0.0.1 - - [28/Oct/2025 16:21:48] "GET /file/10 HTTP/1.1" 200 - +2025-10-28 16:22:03,607 [INFO] File saved: uploads\46638c545b964cc3947d3deea69998d3_1a.png +2025-10-28 16:22:03,614 [INFO] File updated: ID=10 +2025-10-28 16:22:03,616 [INFO] 127.0.0.1 - - [28/Oct/2025 16:22:03] "PUT /file/10 HTTP/1.1" 200 - +2025-10-28 16:22:09,726 [INFO] File deleted from storage: uploads\46638c545b964cc3947d3deea69998d3_1a.png +2025-10-28 16:22:09,734 [INFO] Record deleted: ID=10 +2025-10-28 16:22:09,734 [INFO] 127.0.0.1 - - [28/Oct/2025 16:22:09] "DELETE /file/10 HTTP/1.1" 200 - +2025-10-28 16:22:13,618 [ERROR] Delete error: File not found +2025-10-28 16:22:13,621 [INFO] 127.0.0.1 - - [28/Oct/2025 16:22:13] "DELETE /file/10 HTTP/1.1" 400 - +2025-11-01 18:30:16,089 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-11-01 18:30:16,089 [INFO] Press CTRL+C to quit +2025-11-01 18:30:16,092 [INFO] * Restarting with stat +2025-11-01 18:30:17,196 [WARNING] * Debugger is active! +2025-11-01 18:30:17,201 [INFO] * Debugger PIN: 127-520-521 +2025-11-01 18:32:51,259 [INFO] Login successful for user: admin +2025-11-01 18:32:51,259 [INFO] 127.0.0.1 - - [01/Nov/2025 18:32:51] "POST /login HTTP/1.1" 200 - +2025-11-01 18:36:29,330 [WARNING] Failed login attempt. User not found: admin6 +2025-11-01 18:36:29,331 [INFO] 127.0.0.1 - - [01/Nov/2025 18:36:29] "POST /login HTTP/1.1" 401 - +2025-11-01 18:36:59,443 [INFO] Login successful for user: user +2025-11-01 18:36:59,444 [INFO] 127.0.0.1 - - [01/Nov/2025 18:36:59] "POST /login HTTP/1.1" 200 - +2025-11-01 18:39:23,810 [INFO] 127.0.0.1 - - [01/Nov/2025 18:39:23] "GET /login HTTP/1.1" 405 - +2025-11-01 18:39:33,995 [INFO] 127.0.0.1 - - [01/Nov/2025 18:39:33] "GET /login HTTP/1.1" 405 - +2025-11-01 18:39:48,643 [INFO] 127.0.0.1 - - [01/Nov/2025 18:39:48] "GET /login HTTP/1.1" 405 - +2025-11-01 18:41:02,531 [INFO] Login successful for user: user +2025-11-01 18:41:02,531 [INFO] 127.0.0.1 - - [01/Nov/2025 18:41:02] "POST /login HTTP/1.1" 200 - +2025-11-01 18:41:32,920 [INFO] File saved: uploads\2051ad1591fc42a7a82f62ee6918a952\ef2e7342d8f64c649fcf3c631537f186_Multi-Network_Based_Approach_for_Drug_Repurposing-1.pdf +2025-11-01 18:41:32,928 [INFO] 127.0.0.1 - - [01/Nov/2025 18:41:32] "POST /upload HTTP/1.1" 201 - +2025-11-01 18:41:43,438 [INFO] 127.0.0.1 - - [01/Nov/2025 18:41:43] "GET /upload/12 HTTP/1.1" 404 - +2025-11-01 18:41:52,226 [INFO] 127.0.0.1 - - [01/Nov/2025 18:41:52] "GET /file/12 HTTP/1.1" 200 - +2025-11-01 18:42:29,927 [ERROR] Update error: File type not allowed +2025-11-01 18:42:29,928 [INFO] 127.0.0.1 - - [01/Nov/2025 18:42:29] "PUT /file/12 HTTP/1.1" 400 - +2025-11-01 18:42:42,431 [ERROR] Update error: File type not allowed +2025-11-01 18:42:42,432 [INFO] 127.0.0.1 - - [01/Nov/2025 18:42:42] "PUT /file/12 HTTP/1.1" 400 - +2025-11-01 18:43:20,764 [INFO] * Detected change in 'D:\\flask_file_upload_mvc\\services\\file_service.py', reloading +2025-11-01 18:43:20,891 [INFO] * Restarting with stat diff --git a/models/__pycache__/uploaded_file.cpython-313.pyc b/models/__pycache__/uploaded_file.cpython-313.pyc new file mode 100644 index 0000000..d14447b Binary files /dev/null and b/models/__pycache__/uploaded_file.cpython-313.pyc differ diff --git a/models/uploaded_file.py b/models/uploaded_file.py new file mode 100644 index 0000000..84fe737 --- /dev/null +++ b/models/uploaded_file.py @@ -0,0 +1,14 @@ +from database import db + +# This model represents an uploaded file record in the database +class UploadedFile(db.Model): + id = db.Column(db.Integer, primary_key=True) + filename = db.Column(db.String(255), nullable=False) + file_path = db.Column(db.String(255), nullable=False) + + def to_json(self): + return { + 'id': self.id, + 'filename': self.filename, + 'filepath': self.filepath + } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a2fbb49 Binary files /dev/null and b/requirements.txt differ diff --git a/services/__pycache__/file_service.cpython-313.pyc b/services/__pycache__/file_service.cpython-313.pyc new file mode 100644 index 0000000..d1ebd61 Binary files /dev/null and b/services/__pycache__/file_service.cpython-313.pyc differ diff --git a/services/file_service.py b/services/file_service.py new file mode 100644 index 0000000..aac90d7 --- /dev/null +++ b/services/file_service.py @@ -0,0 +1,54 @@ +import os +import uuid +from werkzeug.utils import secure_filename +from models.uploaded_file import UploadedFile +from database import db +import logging + +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'pdf', 'txt','docs','docx'} + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def save_file(file, base_folder): + if not allowed_file(file.filename): + raise ValueError('File type not allowed') + + os.makedirs(base_folder, exist_ok=True) + filename = secure_filename(file.filename) + unique_name = f"{uuid.uuid4().hex}_{filename}" + save_path = os.path.join(base_folder, unique_name) + file.save(save_path) + new_file = UploadedFile(filename=unique_name, file_path=save_path) + db.session.add(new_file) + db.session.commit() + logging.info(f"File saved: {save_path}") + return new_file + +def update_file(file_id, new_file, base_folder): + existing = UploadedFile.query.get(file_id) + + if not existing: + raise ValueError('File not found') + if os.path.exists(existing.file_path): + os.remove(existing.file_path) + + new_record = save_file(new_file, base_folder) + existing.filename = new_record.filename + existing.file_path = new_record.file_path + db.session.commit() + + logging.info(f"File updated: ID={file_id}") + return existing + +def delete_file(file_id): + file_record = UploadedFile.query.get(file_id) + if not file_record: + raise ValueError('File not found') + if os.path.exists(file_record.file_path): + os.remove(file_record.file_path) + + logging.info(f"File deleted from storage: {file_record.file_path}") + db.session.delete(file_record) + db.session.commit() + logging.info(f"Record deleted: ID={file_id}") diff --git a/uploads/30472a9d9dee45c482ed4b8f4ffd2997/a19db5f610ee425aba07da3946a7a7bf_Flask_Logging_Assignment_Documentation.pdf b/uploads/30472a9d9dee45c482ed4b8f4ffd2997/a19db5f610ee425aba07da3946a7a7bf_Flask_Logging_Assignment_Documentation.pdf new file mode 100644 index 0000000..429db4e Binary files /dev/null and b/uploads/30472a9d9dee45c482ed4b8f4ffd2997/a19db5f610ee425aba07da3946a7a7bf_Flask_Logging_Assignment_Documentation.pdf differ diff --git a/uploads/ea426657e7ad462193cff9d2434c606b_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf b/uploads/ea426657e7ad462193cff9d2434c606b_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf new file mode 100644 index 0000000..113299f Binary files /dev/null and b/uploads/ea426657e7ad462193cff9d2434c606b_Flask_File_Upload_Mvc_With_My_Sql_And_Logging_Step-by-step_Documentation.pdf differ