Team spitfire's part in the super admin dashboard
import psycopg2
import os
import json
from decimal import Decimal
import datetime

from dotenv import load_dotenv

load_dotenv(".env")

# Database connection parameters
db_params = {
    "dbname": os.environ.get("DB_NAME"),
    "user": os.environ.get("DB_USER"),
    "password": os.environ.get("DB_PASSWORD"),
    "host": os.environ.get("DB_HOST"),
    "port": os.environ.get("DB_PORT"),
}

try:
    # Establish a connection to the database
    conn = psycopg2.connect(**db_params)
    # Create a cursor object to execute SQL queries
    cur = conn.cursor()

    # Ask the user for input
    table_name = input("Enter the name of the table to fetch data from: ")

    # Ensure the user input is not empty
    if not table_name:
        raise ValueError("Table name cannot be empty")
    else:
        table_name = table_name.lower()
        # Check if the table exists
        cur.execute(
            """
            SELECT EXISTS (
                SELECT FROM information_schema.tables 
                WHERE table_name = %s
            );
            """,
            (table_name,),
        )
        table_exists = cur.fetchone()[0]
        if not table_exists:
            raise ValueError(f"Table {table_name} does not exist")

    # SQL query to select all users
    select_query = f"SELECT * FROM {table_name};"

    # Execute the query
    cur.execute(select_query)

    # Fetch all rows from the result set
    table = cur.fetchall()

    if not table:
        print(f"The table {table_name} is empty")
        exit()

    # Print the rows in the table
    print(f"Rows in the table {table_name}:")
    for row in table:
        # Create a dictionary to store the row data
        row_data = {}
        for col, value in zip([col[0] for col in cur.description], row):
            # Convert Decimal to float and Fix datetime
            if isinstance(value, Decimal):
                value = float(value)
            elif isinstance(value, datetime.datetime):
                value = value.strftime("%Y-%m-%d %H:%M:%S")
            row_data[col] = value
        # Print the dictionary as well-formatted JSON
        print(json.dumps(row_data, indent=4))

except psycopg2.Error as e:
    print(f"Error connecting to the database: {e}")

finally:
    # Close the cursor and connection
    if cur:
        cur.close()
    if conn:
        conn.close()


fetch_data.py
hngx-org/spitfire-super-admin-one
import logging, os
from flask import jsonify, Blueprint, current_app as app
import datetime
from dotenv import load_dotenv

load_dotenv(".env")

logging.basicConfig(filename='health_check.log', level=logging.ERROR)

health_check_blueprint = Blueprint("health_check", __name__, url_prefix="/api/health")

shop_id = os.environ.get("SHOP_ID")
product_id = os.environ.get("PRODUCT_ID")

health_check_logs = []

ENDPOINTS_TO_CHECK = [
    ("shop_endpoint", "GET", "/api/shop/endpoint"),
    ("get_all_shops", "GET", "/api/shop/all"),
    ("get_shop", "GET", f"/api/shop/{shop_id}"),
    ("get_shop_products", "GET", f"/api/shop/all/products"),
    ("get_shop_products - (by id)", "GET", f"/api/shop/{shop_id}/products"),
    ("ban_vendor", "PUT", f"/api/shop/ban_vendor/{shop_id}"),  
    ("get_banned_vendors", "GET", "/api/shop/banned_vendors"),
    ("unban_vendor", "PUT", f"/api/shop/unban_vendor/{shop_id}"),  
    ("delete_shop", "PATCH",f"/api/shop/delete_shop/{shop_id}"),
    ("get_temporarily_deleted_vendors", "GET", f"/api/shop/temporarily_deleted_vendors"),
    ("restore_shop", "PATCH",f"/api/shop/restore_shop/{shop_id}"),
    ("get_all_shop_logs", "GET","/api/logs/shops"),
    ("download_shop_logs", "GET", "/api/logs/shops/download"),
    ("shop_actions", "GET", "/api/logs/shop/actions"),
    ("get_products", "GET", "/api/product/all"),
    ("get_product", "GET", f"/api/product/{product_id}"),
    ("temporary_delete", "PATCH", f"/api/product/delete_product/{product_id}"),
    ("to_restore_product", "PATCH", f"/api/product/restore_product/{product_id}"),
    ("permanent_delete", "DELETE", f"/api/product/delete_product/{product_id}"),
    ("sanctioned_products", "GET", "/api/product/sanctioned"),
    ("log", "GET", "/api/logs/product/download"),
    ("test_notification", "POST", "/api/notification"),
    
]

@health_check_blueprint.route("/", methods=["GET"])
def health():
    health_results = []

    for endpoint_name, http_method, endpoint_url in ENDPOINTS_TO_CHECK:
        status = check_endpoint(endpoint_url, http_method)
        health_results.append({"endpoint": endpoint_name, "status": status})
        
    # Log health check results along with timestamp
    log_timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_entry = {"timestamp": log_timestamp, "results": health_results}
    health_check_logs.append(log_entry)

    return jsonify(health_results)

@health_check_blueprint.route("/last_check", methods=["GET"])
def last_check():
    # Retrieve the last health check log entry
    if health_check_logs:
        last_check_entry = health_check_logs[-1]
        return jsonify(last_check_entry)
    else:
        return jsonify({"message": "No health check logs available"}), 404

def check_endpoint(endpoint_url, http_method):
    base_url = "https://spitfire-superadmin-1.onrender.com"
    full_url = f"{base_url}{endpoint_url}"
    try:
        if http_method == "GET":
            response = app.test_client().get(full_url)
        elif http_method == "PUT":
            response = app.test_client().put(full_url)
        elif http_method == "POST":
            response = app.test_client().post(full_url)
        elif http_method == "DELETE":
            response = app.test_client().delete(full_url)
        elif http_method == "PATCH":
            response = app.test_client().patch(full_url)
        else:
            return "invalid method"

        response_data = response.data.decode('utf-8')  # Convert bytes to string
        print(f"Response from {full_url}: {response_data}")  # Print the response content

        success_code = [200, 201, 204, 404, 403, 401]
        if response.status_code in success_code:
            return "active"
        else:
            logging.error(f"Error occurred while checking {full_url}. Status Code: {response.status_code}")
            return "inactive"
    except Exception as e:
        logging.error(f"Error occurred while checking {full_url}: {e}")
        return "inactive"

health_check.py
hngx-org/spitfire-super-admin-one
import psycopg2
from psycopg2 import sql
import os
from uuid import uuid4
from dotenv import load_dotenv 
from super_admin_1.models.alternative import Database

load_dotenv(".env")



# Assume 'data' contains the product information you want to insert
data = {
    "id": uuid4().hex,
    "merchant_id": "71a90774-ff7f-4fe7-b37d-fbb33f1c2e20",
    "name": "Fari Couture",
    "policy_confirmation":True,
    "restricted": 'no',
    "admin_status":'pending',
    "is_deleted":'active', 
    "reviewed":True,
    "rating":5.0, 
    "createdAt": "2022-10-17 12:00:00", 
    "updatedAt":"2022-10-17 13:00:00"

}

# Perform the insertion within a context manager block
with Database() as cursor:
    try:
        # Construct the SQL query with placeholders
        insert_query = """
       INSERT INTO shop (id, merchant_id, name, policy_confirmation,restricted, admin_status, is_deleted, reviewed,rating, createdAt, updatedAt)
       VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        """
        # Execute the SQL query with the provided data
        cursor.execute(insert_query, (
            data["id"],
            data["shop_id"],
            data["name"],
            data["policy_confirmation"],
            data["restricted"],
            data["admin_status"],
            data["is_deleted"],
            data["reviewed"],
            data["rating"],
            data["createdAt"],
            data["updatedAt"]
            # ... other data fields ...
        ))

        # Commit the transaction (assuming no exceptions occurred)
        cursor.connection.commit()

        print("Product inserted successfully!")

    except Exception as e:
        # Handle exceptions that might occur during SQL execution
        cursor.connection.rollback()
        print(f"Error inserting product: {e}")


# # Database connection parameters
# db_params = {
#     "dbname": os.environ.get("DB_NAME"),
#     "user": os.environ.get("DB_USER"),
#     "password": os.environ.get("DB_PASSWORD"),
#     "host": os.environ.get("DB_HOST"),
#     "port": os.environ.get("DB_PORT"),
# }

# cur = None
# conn =  None
# try:
#     # Establish a connection to the database
#     conn = psycopg2.connect(**db_params)

#     # Create a cursor object to execute SQL queries
#     cur = conn.cursor()

#     # Insert data to the 'product_category' table
#     # cur.execute(
#     #    """
#     #    INSERT INTO product_category (id, name, parent_category_id, status)
#     #     VALUES ('0', 'spitfire', NULL, 'pending')
#     # """
#     # )

#     # Insert data to the 'user_product_rating' table
#     # cur.execute(
#     #     """
#     #     INSERT INTO user_product_rating (id, user_id, product_id, rating)
#     #     VALUES ('0', 'de73e148-c1dc-41ab-a44a-acbd59542cf6', 'de73e148-c1dc-41ab-a44a-acbd59542cf6', 5)
#     # """
#     # )

#     # conn.commit()

#     # List the columns in 'product_logs' table
#     cur.execute(
#         """
#         SELECT column_name
#         FROM information_schema.columns
#         WHERE table_name = 'product_logs'
#     """
#     )

#     # Fetch the results
#     results = cur.fetchall()

#     # Print the results
#     print(f"Columns in 'product_logs' table: {results}")
#    # SQL query to insert data into the 'product' table
#     insert_query = psycopg2.sql.SQL(
#         """
#         INSERT INTO shop (id, merchant_id, name, policy_confirmation,restricted, admin_status, is_deleted, reviewed,rating, createdAt, updatedAt)
#         VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
#     """
#     )

#     # Data to be inserted
#     data = [
#         (
#             uuid4().hex,
#             "71a90774-ff7f-4fe7-b37d-fbb33f1c2e20",
#             "Fari Couture",
#             True,
#             'no',
#             'pending',
#             'active',
#             True,
#             5.0,
#             "2022-10-17 12:00:00",
#             "2022-10-17 13:00:00",
#         ),
#     ]

#     # # SQL query to insert data into the 'product' table
#     # insert_query = psycopg2.sql.SQL(
#     #     """
#     #     INSERT INTO product (id, shop_id, name, description, quantity, category_id, price, discount_price, tax, admin_status, is_deleted, rating_id,image_id, is_published, currency, createdAt, updatedAt,user_id)
#     #     VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
#     # """
#     # )

#     # # Data to be inserted
#     # data = [
#     #     (
#     #         uuid4().hex,
#     #         "de73e148-c1dc-41ab-a44a-acbd59542cf6",
#     #         "Gucci Dress",
#     #         "A beautiful dress",
#     #         10,
#     #         0,
#     #         100.00,
#     #         90.00,
#     #         5.00,
#     #         "pending",
#     #         "active",
#     #         0,
#     #         True,
#     #         "USD",
#     #         "2022-10-17 12:00:00",
#     #         "2022-10-17 13:00:00",
#     #     ),
#     # ]

#     # Execute the query
#     cur.execute(insert_query, data[0])

#     # Commit the changes to the database
#     conn.commit()

#     table_name = "shop"

#     # Print the results
#     print(f"Inserted data into table {table_name}")
#     print(f"Inserted data: {data}")

# except psycopg2.Error as e:
#     print(f"Error connecting to the database: {e}")

# finally:
#     # Close the cursor and connection
#     if cur:
#         cur.close()
#     if conn:
#         conn.close()


insert_product.py
hngx-org/spitfire-super-admin-one
from super_admin_1 import create_app
from uuid import uuid4

app = create_app()


if __name__ == "__main__":
    app.run(debug=True)

run.py
hngx-org/spitfire-super-admin-one
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask import Flask
from super_admin_1.config import App_Config
from flasgger import Swagger
import yaml

db = SQLAlchemy()


# Create an instance of Swagger
swagger = Swagger()


def create_app():
    """
    Create a new instance of the app with the given configuration.

    :param config_class: configuration class
    :return: app
    """
    # Initialize Flask-

    app = Flask(__name__)
    app.config.from_object(App_Config)
    if app.config["SQLALCHEMY_DATABASE_URI"]:
        print(f"using db")

    # Initialize CORS
    CORS(app, supports_credentials=True)

    # Load Swagger content from the file
    with open("swagger_config.yaml", "r") as file:
        swagger_config = yaml.load(file, Loader=yaml.FullLoader)
    # Initialize Flasgger with the loaded Swagger configuration
    Swagger(app, template=swagger_config)

    # Initialize SQLAlchemy
    db.init_app(app)

    # imports blueprints
    from super_admin_1.shop.routes import shop
    from super_admin_1.logs.routes import logs
    from super_admin_1.products.routes import product
    from super_admin_1.shop.func_helpers import test
    from super_admin_1.errors.handlers import error
    from health_check import health_check_blueprint
    from super_admin_1.notification.routes import notification

    # register blueprint
    app.register_blueprint(error)
    app.register_blueprint(shop)
    app.register_blueprint(logs)
    app.register_blueprint(product)
    app.register_blueprint(test)
    app.register_blueprint(health_check_blueprint)
    app.register_blueprint(notification)

    # create db tables from models if not exists
    with app.app_context():
        db.create_all()

    return app

super_admin_1/__init__.py
hngx-org/spitfire-super-admin-one
import os
from dotenv import load_dotenv


load_dotenv(".env")


class App_Config:
    SECRET_KEY = os.environ.get("SECRET_KEY", "test")
    SQLALCHEMY_DATABASE_URI = os.environ.get(
        "SQLALCHEMY_DATABASE_URI", "sqlite:///test.db"
    )
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ECHO = False

super_admin_1/config.py
hngx-org/spitfire-super-admin-one
from super_admin_1 import db  

def query_one_filtered(table, **kwargs):
    """Query a single item from the table based on filters."""
    return db.session.query(table).filter_by(**kwargs).first()


def query_all_filtered(table, **kwargs):
    """Query all items from the table based on filters."""
    return db.session.query(table).filter_by(**kwargs).all()


def query_one(table):
    """Query a single item from the table."""
    return db.session.query(table).first()


def query_all(table):
    """Query all items from the table."""
    return db.session.query(table).all()


def query_paginated(table, page):
    """Query paginated items from the table."""
    per_page = 15
    return db.session.query(table).order_by(table.date_created.desc()).paginate(page, per_page, False)


def query_paginate_filtered(table, page, **kwargs):
    """Query paginated items from the table based on filters."""
    per_page = 15
    return db.session.query(table).filter_by(**kwargs).order_by(table.date_created.desc()).paginate(page, per_page, False)

super_admin_1/db_utils.py
hngx-org/spitfire-super-admin-one
from flask import Blueprint, jsonify

error = Blueprint("error", __name__)


class CustomError(Exception):
    """Exception class for custom errors"""

    def __init__(self, error, code, message):
        """constructor for custom error class

        Args:
            error (_type_): Error Name
            code (_type_): HTTP error code
            message (_type_): error message
        """
        self.error = error
        self.code = code
        self.message = message



@error.app_errorhandler(CustomError)
def custom_error(error):
    """app error handler for custom errors"""
    return (
        jsonify({"error": error.error, "message": error.message}),
        error.code,
    )


@error.app_errorhandler(400)
def bad_request(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 400


@error.app_errorhandler(401)
def Unauthorized(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 401


@error.app_errorhandler(403)
def Forbidden(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 403


@error.app_errorhandler(404)
def resource_not_found(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 404


@error.app_errorhandler(405)
def method_not_allowed(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return (
        jsonify({"error": error.name, "message": error.description}),
        405,
    )


@error.app_errorhandler(422)
def cant_process(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 422


# pylint: disable=function-redefined
@error.app_errorhandler(429)
def cant_process(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 429


@error.app_errorhandler(500)
def server_error(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": "Its not you its us"}), 500

super_admin_1/errors/handlers.py
hngx-org/spitfire-super-admin-one
import os
import logging
from datetime import date
from super_admin_1 import db
from super_admin_1.models.alternative import Database
from super_admin_1.models.product_logs import ProductLogs

if os.path.isdir('logs') is False:
    os.mkdir('logs')

log_file_name=f'logs/product_log_report_{date.today().strftime("%Y_%m_%d")}.log'

# Configure the logging module
logging.basicConfig(
    filename=f'logs/server_logs_{date.today().strftime("%Y_%m_%d")}.log',
    level=logging.DEBUG,
    format="%(asctime)s - %(levelname)s - %(message)s",
)

logger = logging.getLogger(__name__)
# logger.setLevel(logging.INFO)

def generate_log_file():
    """Generate a log file"""
    try:
        all_logs = db.session.execute(db.select(ProductLogs)).scalars().all()
        for log in all_logs:
            log_message = f"Admin '{log.user_id}' performed action: '{log.action}'\
                on product with Id '{log.product_id}' time: {log.log_date}"
            logging.info(log_message)
        return log_file_name
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return None


def register_action(user_id, action, product_id):
    """log the admin action"""
    try:
        log = ProductLogs(user_id=user_id, action=action, product_id=product_id)
        log.insert()
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")


def generate_log_file_d():
    """Generate a log file using pure sql queries"""
    try:
        query = """
            SELECT * FROM product_logs LIMIT 50;
        """
        with Database() as cursor:
            cursor.execute(query)
            all_logs = cursor.fetchall()
            if len(all_logs) <= 0:
                return False
            if os.path.isfile(log_file_name):
                os.remove(log_file_name)
            for log in all_logs:
                # print(f"type: {type(log)}")
                _ , user_id, action, product_id, log_date = log
                log_message = f"Admin '{user_id}' performed action: '{action}' on product with Id '{product_id}' at time: '{log_date}'\n"
                try:
                    with open(log_file_name, 'a') as log_file:
                        log_file.write(log_message)
                except Exception as error:
                    logger.error(f"{type(error).__name__}: {error}")
                    return None
        return log_file_name
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return None


def register_action_d(user_id, action, product_id, reason = None):
    """log the admin action using raw queries"""
    try:
        query = """
            INSERT INTO product_logs (user_id, action, product_id)
            VALUES (%s, %s, %s);
        """
        with Database() as cursor:
            cursor.execute(query, (user_id, action, product_id))
        if reason is not None:
            pass
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return None

super_admin_1/logs/product_action_logger.py
hngx-org/spitfire-super-admin-one
import os
from datetime import date
from utils import admin_required
from flask import Blueprint, jsonify, send_file,request
from super_admin_1.models.shop_logs import ShopsLogs
from super_admin_1.logs.product_action_logger import generate_log_file_d, logger


logs = Blueprint("logs", __name__, url_prefix="/api/logs")


@logs.route("/shops", defaults={"shop_id": None})
@logs.route("/shops/<shop_id>")
@admin_required(request=request)
def get_all_shop_logs(shop_id):
    """Get all shop logs"""
    if not shop_id:
        return (
            jsonify(
                {
                    "message": "success",
                    "logs": [
                        log.format() if log else [] for log in ShopsLogs.query.all()
                    ],
                }
            ),
            200,
        )

    return (
        jsonify(
            {
                "message": "success",
                "logs": [
                    log.format() if log else []
                    for log in ShopsLogs.query.filter_by(shop_id=shop_id).all()
                ],
            }
        ),
        200,
    )

@logs.route("/shops/download", defaults={"shop_id": None})
@logs.route("/shops/<shop_id>/download")
@admin_required(request=request)
def download_shop_logs(shop_id):
    """Download all shop logs"""
    logs = []
    if not shop_id:
        logs = [log.format() if log else [] for log in ShopsLogs.query.all()]
    else:
        logs = [
            log.format() if log else []
            for log in ShopsLogs.query.filter_by(shop_id=shop_id).all()
        ]
    # Create a temporary file to store the strings
    temp_file_path = f"{os.path.abspath('.')}/temp_file.txt"
    with open(temp_file_path, "w") as temp_file:
        temp_file.write("\n".join(logs))

    response = send_file(
        temp_file_path, as_attachment=True, download_name="shoplogs.txt"
    )
    os.remove(temp_file_path)

    return response

@logs.route("/shop/actions", methods=["GET"])
@admin_required(request=request)
def shop_actions():
    data = ShopsLogs.query.all()
    return jsonify([action.format_json() for action in data]), 200

@logs.route("/product/download")
@admin_required(request=request)
def log():
    """Download product logs"""
    try:
        filename = generate_log_file_d()
        if filename is False:
            return {
                "message": "No log entry exists"
            }, 200
        path = os.path.abspath(filename)
        return send_file(path), 200
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return jsonify(
            {
                "message": "Could not download audit logs",
                "error": f"{error.__doc__}"
            }
        ), 500


@logs.route("/server/download")
def server_log():
    """Download server logs"""
    try:
        filename = f'logs/server_logs_{date.today().strftime("%Y_%m_%d")}.log'
        if filename is False:
            return {
                "message": "No log entry exists"
            }, 204
        path = os.path.abspath(filename)
        return send_file(path), 200
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return jsonify(
            {
                "message": "Could not download server logs",
                "error": f"{error}"
            }
        ), 500

super_admin_1/logs/routes.py
hngx-org/spitfire-super-admin-one
import psycopg2
import os
from dotenv import load_dotenv

load_dotenv(".env")


class Database:
    def __init__(self):
        # Get database configuration from environment variables
        self.dbname = os.environ.get("DB_NAME")
        self.user = os.environ.get("DB_USER")
        self.password = os.environ.get("DB_PASSWORD")
        self.host = os.environ.get("DB_HOST")
        self.port = os.environ.get("DB_PORT")

    def __enter__(self):
        self.connection = psycopg2.connect(
            dbname=self.dbname,
            user=self.user,
            password=self.password,
            host=self.host,
            port=self.port,
            sslmode = "require"
        )
        self.cursor = self.connection.cursor()
        return self.cursor

    def __exit__(self, exc_type, *args):
        try:
            if exc_type is not None:
                self.connection.rollback()  # Rollback the transaction if an exception occurred
            else:
                self.connection.commit()  # Commit the transaction if no exception occurred
        finally:
            self.cursor.close()
            self.connection.close()


# import psycopg2, os
# from psycopg2 import sql


# # ++++++++++++++++++++++++++++++THIS IS A TEST SCENARIO!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# class Database:
#     def __init__(self, dbname, user, password, host, port):
#         self.connection = psycopg2.connect(
#             dbname=dbname,
#             user=user,
#             password=password,
#             host=host,
#             port=port
#         )
#         self.cursor = self.connection.cursor()

#     def execute_query(self, query, params=None):
#         try:
#             if params:
# self.cursor.execute(query, params)
#             else:
#                 self.cursor.execute(query)
#             self.connection.commit()
#         except Exception as error:
#             self.connection.rollback()
#             print("Error executing query:", error)

#     def fetch_data(self, query, params=None):
#         try:
#             if params:
#                 self.cursor.execute(query, params)
#             else:
#                 self.cursor.execute(query)
#             return self.cursor.fetchall()
#         except Exception as error:
#             print("Error fetching data:", error)

#     def close_connection(self):
#         self.cursor.close()
#         self.connection.close()

# # Example usage:
# if __name__ == "__main__":
#     # Database configuration
#     host = os.environ.get("host")
#     dbname = os.environ.get("dbname")
#     user = os.environ.get("user")
#     port = os.environ.get("port")
#     password =  os.environ.get("password")

#     # Create a Database instance
#     db = Database(dbname, user, password, host, port)

#     # Example queries
#     create_table_query = """
#     CREATE TABLE IF NOT EXISTS users (
#         id SERIAL PRIMARY KEY,
#         username VARCHAR(255),
#         email VARCHAR(255)
#     )
#     """

#     insert_data_query = """
#     INSERT INTO users (username, email) VALUES (%s, %s)
#     """

#     select_data_query = "SELECT * FROM public.shop"

#     # Execute queries
#     # db.execute_query(create_table_query)
#     # db.execute_query(insert_data_query, ("john_doe", "john@example.com"))
#     # db.execute_query(insert_data_query, ("jane_smith", "jane@example.com"))

#     # Fetch and print data
#     shop = db.fetch_data(select_data_query)
#     print("shop:")
#     print(shop)
#     for user in shop:
#         print(user)

#     # Close the database connection
#     db.close_connection()

super_admin_1/models/alternative.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""
Base template for the Event driven application
"""
from super_admin_1 import db
from uuid import uuid4
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime


def get_uuid():
    """Generate a unique id using uuid4()"""
    return uuid4().hex


# Create a base model class that will contain common functionality
class BaseModel(db.Model):
    """BaseClass for all models"""

    # Make this class abstract so it won't be mapped to a database table
    __abstract__ = True

    # Define a primary key column with a default value of a generated UUID
    id = db.Column(db.String(60), primary_key=True, default=get_uuid(), unique=True, nullable=False)
    createdAt = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), nullable=False)
    updatedAt = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), onupdate=db.func.current_timestamp())
    
    
    def __init__(self):
      self.id = get_uuid()
      self.createdAt = datetime.utcnow()
      self.updatedAt = datetime.utcnow()

    def insert(self):
        """Insert the current object into the database"""
        db.session.add(self)
        db.session.commit()

    def update(self):
        """Update the current object in the database"""
        self.updatedAt = datetime.utcnow()
        db.session.commit()

    def delete(self):
        """Delete the current object from the database"""
        db.session.delete(self)
        db.session.commit()

    def format(self):
        """Format the object's attributes as a dictionary"""
        # This method should be overridden in subclasses
        raise NotImplementedError(
            "Subclasses must implement the 'format' method"
        )

super_admin_1/models/base.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Template for the User Class"""

from super_admin_1 import db
from super_admin_1.models.base import BaseModel


class Product(BaseModel):
    """ Product class"""
    __tablename__ = "product"

    name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.String(255), nullable=False)
    quantity = db.Column(db.BigInteger, nullable=False)
    price = db.Column(db.Numeric(10, 2), nullable=False)
    discount_price = db.Column(db.Numeric(10, 2), nullable=False)
    tax = db.Column(db.Numeric(10, 2), nullable=False)
    admin_status = db.Column(db.Enum('pending', 'reviewed', 'approved', 'blacklisted',
                             'suspended', name="ADMIN_STATUS"), server_default="pending", nullable=False)
    is_deleted = db.Column(db.Enum(
        "active", "temporary", name="product_status"), server_default="active", nullable=False)
    is_published = db.Column(db.Boolean, nullable=False, default=False)
    currency = db.Column(db.String(16), nullable=False)
    user_id = db.Column(db.String(60), db.ForeignKey(
        "user.id"))
    shop_id = db.Column(db.String(60), db.ForeignKey(
        "shop.id"), nullable=False)
    rating_id = db.Column(db.Integer, db.ForeignKey(
        "user_product_rating.id"), nullable=False)
    category_id = db.Column(db.Integer, db.ForeignKey(
        "product_category.id"))


    def __init__(self, shop_id, rating_id, user_id, category_id, name, description, quantity, price, discount_price, tax, admin_status, is_deleted, currency, is_published=False):
        """object constructor"""
        super().__init__()
        self.shop_id = shop_id
        self.rating_id = rating_id
        self.user_id  = user_id
        self.category_id = category_id
        self.name = name
        self.description = description
        self.quantity = quantity
        self.price = price
        self.discount_price = discount_price
        self.tax = tax
        self.admin_status = admin_status or "pending"
        self.is_published = is_published
        self.is_deleted = is_deleted or "active"
        self.currency = currency

    def __repr__(self):
        """official object representation"""
        return {
            "id": self.id,
            "shop_id": self.shop_id,
            "user_id": self.user_id,
            "rating_id": self.rating_id,
            "category_id": self.category_id,
            "name": self.name,
            "description": self.description,
            "quantity": self.quantity,
            "price": self.price,
            "discount_price": self.discount_price,
            "tax": self.tax,
            "admin_status": self.admin_status,
            "is_published": self.is_published,
            "is_deleted": self.is_deleted,
            "currency": self.currency,
            "createdAt": self.createdAt,
            "updatedAt": self.updatedAt,
        }

    def format(self):
        """Format the object's attributes as a dictionary"""
        return {
            "id": self.id,
            "shop_id": self.shop_id,
            "rating_id": self.rating_id,
            "user_id": self.user_id,
            "category_id": self.category_id,
            "name": self.name,
            "description": self.description,
            "quantity": self.quantity,
            "price": self.price,
            "discount_price": self.discount_price,
            "tax": self.tax,
            "admin_status": self.admin_status,
            "is_deleted": self.is_deleted,
            "is_published": self.is_published,
            "currency": self.currency,
            "createdAt": self.createdAt,
            "updatedAt": self.updatedAt,
        }

super_admin_1/models/product.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Template for the Product_category Class"""

from super_admin_1 import db
from super_admin_1.models.base import BaseModel


class Product_category(BaseModel):
    """Product category class"""
    __tablename__ = "product_category"
    updatedAt = None
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    product_id = db.Column(db.String(60), db.ForeignKey( "product.id", ondelete="CASCADE"), nullable=False)
    user_id = db.Column(db.String(60), db.ForeignKey( "user.id", ondelete="CASCADE"), nullable=False)
    name = db.Column(db.String(225), nullable=False)

    def __init__(self, name):
        """Object constructor"""
        super().__init__()
        self.name = name
    
    def __repr__(self):
        """official object representation"""
        return f"(id: {self.id}, name: {self.name})"
    
    def format(self):
        """Format the object's attributes as a dictionary"""
        return ({
            "id": self.id,
            "name": self.name
        })
super_admin_1/models/product_category.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Template for the product image Class"""

from super_admin_1 import db
from super_admin_1.models.base import BaseModel


class Product_Image(BaseModel):
    """Product_Image class"""
    __tablename__ = "product_image"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    product_id = db.Column(db.String(60), db.ForeignKey("product.id"), nullable=False)
    url = db.Column(db.String(255), nullable=False)

    def __init__(self, product_id, url):
        """Object constructor"""
        super().__init__()
        self.product_id = product_id
        self.url = url

    def __repr__(self):
        """official object representation"""
        return f"(product_id: {self.product_id}, url: {self.url})"
    
    def format(self):
        """Format the object's attributes as a dictionary"""
        return ({
            "product_id": self.product_id,
            "url": self.url,
            "createdAt": self.createdAt,
            "updatedAt": self.updatedAt,
        })
super_admin_1/models/product_image.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""The model for the product_logs table"""
from super_admin_1 import db
from super_admin_1.models.base import BaseModel


class ProductLogs(BaseModel):
    """create the product_logs table"""

    __tablename__ = "product_logs"
    id = db.Column(db.Integer(), primary_key=True, nullable=False)
    user_id = db.Column(
        db.String(60), db.ForeignKey("user.id"), nullable=False, primary_key=True
    )
    action = db.Column(db.String(20), nullable=False)
    product_id = db.Column(
        db.String(60), db.ForeignKey("product.id"), nullable=False, primary_key=True
    )
    log_date = db.Column(db.DateTime(), nullable=False)

    def __init__(self, user_id, action, product_id):
        """Instantiate a model"""
        self.user_id = user_id
        self.action = action
        self.product_id = product_id

    def insert(self):
        """Insert the current object into the database"""
        db.session.add(self)
        db.session.commit()

    def format(self):
        """Not needed"""
        pass
super_admin_1/models/product_logs.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Template for the Shop Class"""
from super_admin_1 import db
from super_admin_1.models.base import BaseModel
from datetime import datetime

class Shop(BaseModel):
  """Shop class"""
  __tablename__ = "shop"
  merchant_id = db.Column(db.String(60), db.ForeignKey("user.id"), nullable=False)
  name = db.Column(db.String(255))
  policy_confirmation = db.Column(db.Boolean)
  restricted = db.Column(db.Enum('no', 'temporary', 'permanent', name="restricted"), server_default="no")
  admin_status = db.Column(db.Enum('pending', 'reviewed', 'approved', 'suspended', 'blacklisted', name="ADMIN_STATUS"), server_default="pending", nullable=False)
  is_deleted = db.Column(db.Enum("active", "temporary", name="shop_status"), server_default="active")
  reviewed = db.Column(db.Boolean)
  rating = db.Column(db.Numeric(10, 2))

  
  def __init__(self, merchant_id, name, policy_confirmation, restricted, admin_status, is_deleted, reviewed, rating):
    """object constructor"""
    super().__init__()
    self.merchant_id = merchant_id
    self.name = name
    self.policy_confirmation = policy_confirmation
    self.restricted = restricted or "no"
    self.admin_status = admin_status or "pending"
    self.is_deleted = is_deleted or "active"
    self.reviewed = reviewed 
    self.rating = rating


  def __repr__(self):
    """official object representation"""
    return f"<Shop(id={self.id}, merchant_id={self.merchant_id}, name={self.name}, admin_status={self.admin_status}, is_deleted={self.is_deleted}, policy_confirmation={self.policy_confirmation}, restricted={self.restricted}, reviewed={self.reviewed}, rating={self.rating})>"


  def format(self):
    """Format the object's attributes as a dictionary"""
    return ({
      "id": self.id,
      "merchant_id": self.merchant_id,
      "name": self.name,
      "policy_confirmation": self.policy_confirmation,
      "restricted": self.restricted,
      "admin_status": self.admin_status,
      "is_deleted": self.is_deleted,
      "reviewed": self.reviewed,
      "rating": self.rating,
      "createdAt": self.createdAt,
      "updatedAt": self.updatedAt,
    })

super_admin_1/models/shop.py
hngx-org/spitfire-super-admin-one
# from super_admin_1 import db
# from datetime import datetime


# class ShopLog(db.Model):
#     """Shop log class"""

#     __tablename__ = "shop_logs"

#     id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
#     user_id = db.Column(db.String(60), db.ForeignKey(
#         "user.id"), nullable=False)
#     action = db.Column(db.String(20), nullable=False)
#     shop_id = db.Column(db.String(60), db.ForeignKey(
#         "shop.id"), nullable=False)
#     log_date = db.Column(
#         db.DateTime(), default=datetime.utcnow, nullable=False)

#     def __init__(self, user_id, action, shop_id):
#         self.user_id = user_id
#         self.action = action
#         self.shop_id = shop_id
#         self.log_date = datetime.utcnow()

#     def __repr__(self):
#         return "user_id: {}, action: {}, shop_id: {}, log_date: {}".format(self.user_id, self.action, self.shop_id, self.log_date)

#     def insert(self):
#         """Insert the current object into the database"""
#         db.session.add(self)
#         db.session.commit()

#     def format(self):
#         return {
#             "user_id": self.user_id,
#             "action": self.action,
#             "shop_id": self.shop_id,
#             "log_date": self.log_date
#         }

super_admin_1/models/shop_log.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Model for Shops Logs Table"""
from datetime import datetime
from super_admin_1 import db
from super_admin_1.models.base import BaseModel


class ShopsLogs(db.Model):
    """
    ShopsLogs Model.
    """

    __tablename__ = "shop_logs"
    id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)

    shop_id = db.Column(db.String(60), db.ForeignKey("shop.id"), nullable=False)

    user_id = db.Column(db.String(60), db.ForeignKey("user.id"), nullable=False)

    action = db.Column(db.String(20), nullable=False)

    log_date = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False)

    def __init__(self, shop_id, user_id, action):
        self.shop_id = shop_id

        self.user_id = user_id

        self.action = action

    def __repr__(self):
        return f"{format(self.log_date)}: User({self.user_id}) {self.action} on Shop({self.shop_id})"

    def insert(self):
        """Insert the current object into the database"""
        db.session.add(self)
        db.session.commit()

    def update(self):
        """Update the current object in the database"""
        db.session.commit()

    def delete(self):
        """Delete the current object from the database"""
        db.session.delete(self)
        db.session.commit()

    def format(self):
        return f"{format(self.log_date)}: User({self.user_id}) {self.action} on Shop({self.shop_id})"

    def format_json(self):
        return {
            "user_id": self.user_id,
            "action": self.action,
            "shop_id": self.shop_id,
            "log_date": self.log_date,
        }

super_admin_1/models/shop_logs.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Template for the Shop Class"""
from super_admin_1 import db
from super_admin_1.models.base import BaseModel
from datetime import datetime


class User(BaseModel):
  """User class"""
  __tablename__ = "user"
  updatedAt = None
  username = db.Column(db.String(255), nullable=False)
  first_name = db.Column(db.String(255), nullable=False)
  last_name = db.Column(db.String(255), nullable=False)
  email = db.Column(db.String(255), nullable=False)
  role_id = db.Column(db.Integer)
  section_order = db.Column(db.Text)
  password = db.Column(db.String(255))
  provider = db.Column(db.String(255))
  phone_number = db.Column(db.String(255))
  is_verified = db.Column(db.Boolean, default=False)
  two_factor_auth = db.Column(db.Boolean, default=False)
  location = db.Column(db.String(255))
  country = db.Column(db.String(255))
  profile_pic = db.Column(db.Text)
  profile_cover_photo = db.Column(db.Text)
  refresh_token = db.Column(db.String(255), nullable=False)
  
  shop = db.relationship("Shop", backref=db.backref("user", lazy=True), cascade="all, delete")
  
  def __init__(self, username, first_name, last_name, email, phone_number, role_id, section_order, password, provider, profile_pic, profile_cover_photo, refresh_token, location, country, is_verified=False, two_factor_auth=False):
    """Initializes a new instance of the class.

    Args:
        username (str): The username of the user.
        first_name (str): The first name of the user.
        last_name (str): The last name of the user.
        email (str): The email of the user.
        section_order (list): The section order of the user.
        password (str): The password of the user.
        provider (str): The provider of the user.
        profile_pic (str): The profile picture of the user.
        refresh_token (str): The refresh token of the user.
        location (str): The location of the user.
        country (str): The country of the user.
        is_verified (bool): The is_verified of the user.
        two_factor_auth (bool): The two_factor_auth of the user.
    """
    super().__init__()
    self.updated_at = None
    self.username = username
    self.first_name = first_name
    self.last_name = last_name
    self.email = email
    self.refresh_token = refresh_token
    self.role_id = role_id
    self.section_order = section_order
    self.password = password
    self.provider = provider
    self.phone_number = phone_number
    self.is_verified = is_verified
    self.two_factor_auth = two_factor_auth
    self.location = location
    self.country = country
    self.profile_pic = profile_pic 
    self.profile_cover_photo = profile_cover_photo
    
  def __repr__(self):
    """ official object representation"""
    return (
        f"<User(id={self.id}, username={self.username}, first_name={self.first_name}, "
        f"last_name={self.last_name}, email={self.email}, "
        f"refresh_token={self.refresh_token}, role_id={self.role_id}, "
        f"section_order={self.section_order}, password={self.password}, "
        f"provider={self.provider}, phone_number={self.phone_number}, "
        f"is_verified={self.is_verified}, two_factor_auth={self.two_factor_auth}, "
        f"location={self.location}, country={self.country}, "
        f"profile_pic={self.profile_pic}"
    )
    
  def format(self):
    """ Format the object's attributes as a dictionary"""
    return ({
        "id": self.id,
        "username": self.username,
        "first_name": self.first_name,
        "last_name": self.last_name,
        "email": self.email,
        "role_id": self.role_id,
        "section_order": self.section_order,
        "password": self.password,
        "provider": self.provider,
        "phone_number": self.phone_number,
        "is_verified": self.is_verified,
        "two_factor_auth": self.two_factor_auth,
        "location": self.location,
        "country": self.country,
        "profile_pic": self.profile_pic,
        "profile_cover_photo": self.profile_cover_photo,
        "refresh_token": self.refresh_token,
        "createdAt": self.createdAt
    })
super_admin_1/models/user.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""Template for the user_product_rating Class"""

from super_admin_1 import db
from super_admin_1.models.base import BaseModel


class User_Product_Rating(BaseModel):
    """User_Product_Rating class"""
    __tablename__ = "user_product_rating"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.String(60), db.ForeignKey("user.id"), nullable=False)
    product_id = db.Column(db.String(60), db.ForeignKey("product.id"), nullable=False)
    rating = db.Column(db.Integer, nullable=False)


    def __init__(self, user_id, product_id, rating):
        """Object constructor"""
        super().__init__()
        self.user_id = user_id
        self.product_id = product_id
        self.rating = rating

    def __repr__(self):
        """official object representation"""
        return f"(user_id: {self.user_id}, product_id: {self.product_id}, rating: {self.rating})"
    
    def format(self):
        """Format the object's attributes as a dictionary"""
        return ({
            "user_id": self.user_id,
            "product_id": self.product_id,
            "rating": self.rating,
            "created_At": self.created_At,
            "updated_At": self.updated_At,
        })
    

super_admin_1/models/user_product_rating.py
hngx-org/spitfire-super-admin-one
url_mapping = {
    "unban": "/api/v1/store/suspension-lifted"
}
super_admin_1/notification/config.py
hngx-org/spitfire-super-admin-one
"""A set of functions to notify a user"""
import requests
from super_admin_1.models.alternative import Database
from super_admin_1.logs.product_action_logger import logger
from super_admin_1.notification.config import url_mapping


class CustomError(Exception):
    def __init__(self, message):
        super().__init__(message)


def get_field_value(field: str, table: str, filter: str, value: str) -> str:
    """Get a particular field value from the db, works for simple get query

    Args:
        field (str): the column on the table you want to get
        table (str): the table in the database
        filter (str): the filtering condition
        value (str): the filtering value

    Returns:
        str: the value from the database
    """
    try:
        query = """SELECT %s FROM %s WHERE %s = %s"""
        with Database() as cursor:
            cursor.execute(query, (field, table, filter, value))
            query_value: str = cursor.fetchone()
        print(type(query_value))
        if query_value is None:
            raise CustomError("A value could not be gotten")
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        raise CustomError("A value could not be gotten")

    return query_value

def notify(action: str, **kwargs: str) -> dict:
    """Notify of an action

    Args:
        user_id (str): id of the admin user.
        action (str): the action being taken on a shop or products.
        **kwargs (dict): A dictionary of keyword arguments.
    
    Keyword Args:
        product_id (str): id of the product being acted on.
        shop_id (str): id of the shop being acted on.
        reason (str): reason for the action.
    
    Returns:
        dict: To signify success, failure or error
            - success (bool): signifies email sent
            - data (dict): details of the successful mail sent
            - error (bool): signifies error during execution
    """
    email_request_base_url = "https://team-titan.mrprotocoll.me"
    try:
        data: dict = {}
        # update the data dictionary with the available keys
        if kwargs.get("product_id", None):
            product_name, shop_id = get_field_value(field="name, shop_id", table="product",
                                                    filter="id", value=kwargs.get("product_id"))
            data["product_name"] = product_name
            merchant_id = get_field_value(field="merchant_id", table="shop", filter="id", value=shop_id)
        else:
            store_name, merchant_id = get_field_value(field="name, merchant_id", table="shop",
                                        filter="id", value=kwargs.get("shop_id"))
            data["store_name"] = store_name
        if kwargs.get("reason", None):
            data["reason"] = kwargs.get("reason")
        # query the database to get the recipient email
        email, name = get_field_value(field="email, name", table="public.user",
                                      filter="id", value=merchant_id)
        data["recipient"] = email
        data["name"] = name
        
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")

    try:
        endpoint = url_mapping.get(action)
        response = requests.post(f"{email_request_base_url}/{endpoint}", json=data)
        if response.status_code != 200:
            return {
                "success": False,
                "data": {},
                "error": False
            }
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return {
            "success": False,
            "data": {},
            "error": True
        }

    return {
        "success": True,
        "data": {
            "recipient": email,
            "name": name,
            "action": action,
            "affected_object": product_name | store_name
        },
        "error": False
    }

def notify_test(name: str, email: str, store_name: str) -> dict:
    
    email_request_url = "https://team-titan.mrprotocoll.me/api/v1/store/suspension-lifted"
    try:
        
        data: dict = {
            "name": name,
            "recipient": email,
            "store_name": store_name,
            # "skill": "Content Writer",
            # "badge_name": "Content Writing",
            # "user_profile_link": "https://example.com"
        }
       
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")

    try:
        response = requests.post(email_request_url, json=data)
        print(f"status code:{response.status_code}")
        if response.status_code != 200:
            return {
                "success": False,
                "error": False
            }
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return {
            "success": False,
            "error": True
        }

    return {
        "success": True,
        "error": False
    }

super_admin_1/notification/notification_helper.py
hngx-org/spitfire-super-admin-one
"""Endpoint to test notification capability"""
from flask import Blueprint, request, jsonify
from super_admin_1.notification.notification_helper import notify, notify_test
from super_admin_1.logs.product_action_logger import logger

notification = Blueprint('notification', __name__, url_prefix='/api/notification')

@notification.route("/", methods=["POST"])
def test_notification():
    """send a mail to a user"""
    acceptable_keys = ["product_id", "shop_id", "reason", "action"]
    try:
        data = request.get_json()
        if len(data.keys()) > 3:
            return jsonify(
                {
                    "message": "Too many keys",
                    "error": "Unnecessary key-value pair present"
                }
            ), 400
        for key in data.keys():
            if key not in acceptable_keys:
                return jsonify(
                    {
                        "message": "Invalid key",
                        "error": f"{key} is not a valid key"
                    }
                ), 400
        if "product_id" in data.keys() and "shop_id" in data.keys():
            return jsonify(
                {
                    "message": "Bad Request",
                    "error": "product_id and shop_id cannot co-exist"
                }
            ), 400

        # response = notify(action=data.get("action"), **data)
        response = notify_test("Wonderful", "adeonederful20@gmail.com", "okay_store")
        print(f"response: {response}")
        if response.get("success") is False:
            return jsonify(
                {
                    "message": "Email not sent",
                    "error": "messaging service is down"
                }
            ), 200
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
        return jsonify(
            {
                "message": "an error occured during execution, try again",
                "error": f"{type(error).__name__}"
            }
        ), 500
    
    return jsonify(
        {
            "message": "Email sent successfully",
            "data": response.get("data", None)

        }
    )

super_admin_1/notification/routes.py
hngx-org/spitfire-super-admin-one
from pydantic import BaseModel
from uuid import UUID


class IdSchema(BaseModel):
    id: UUID

super_admin_1/products/product_schemas.py
hngx-org/spitfire-super-admin-one
from flask import Blueprint, jsonify
from super_admin_1 import db
from super_admin_1.models.alternative import Database
from super_admin_1.models.product import Product
from super_admin_1.logs.product_action_logger import (
    register_action_d,
    logger,
)

from super_admin_1.products.product_schemas import IdSchema
from pydantic import ValidationError
from super_admin_1.logs.product_action_logger import register_action_d, logger
from utils import  raise_validation_error


product = Blueprint("product", __name__, url_prefix="/api/product")

# WORKS
@product.route("/all", methods=["GET"])
# @admin_required(request=request)
def get_products(user_id):
    """get information related to a product

    Returns:
       dict: A JSON response with the appropriate status code and message.
           - If the products are returned successfully:
               - Status code: 200
               - Body:
                   - "message": "all products request successful"
                   - "data": []
           - If an exception occurs during the get process:
               - Status code: 500
               - Body:
                   - "error": "Internal Server Error"
                   - "message": [error message]
    """
    try:
        products = Product.query.all()
        return jsonify(
                {
                    "message": "all products request successful",
                    "data": [product.format() for product in products],
                }
            ), 200
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500


# WORKS 
@product.route("/<product_id>", methods=["GET"])
# @admin_required(request=request)
def get_product( product_id):
    """get information related to a product

    Args:
        product_id (uuid): The unique identifier of the product.

     Returns:
        dict: A JSON response with the appropriate status code and message.
            - If the product is returned successfully:
                - Status code: 200
                - Body:
                    - "message": "the product request successful"
                    - "data": []
            - If the product with the given ID does not exist:
                - Status code: 404
                - Body:
                    - "error": "not found"
                    - "message": "invalid product id"
            - If an exception occurs during the get process:
                - Status code: 500
                - Body:
                    - "error": "Internal Server Error"
                    - "message": [error message]
    """
    try:
        product_id = IdSchema(id=product_id)
        product_id = product_id.id
        product = Product.query.filter_by(id=product_id).first()
        if not product:
            return jsonify({"error": "Not found", "message": "Product Not Found"}), 404

        return jsonify(
                {
                    "message": "the product request successful",
                    "data": [product.format()],
                }
            ), 200
    except ValidationError as e:
        raise_validation_error(e)
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500
    


# NOT WORKING ORM ISSUE
@product.route("/sanction/<product_id>", methods=["PATCH"])
# @admin_required(request=request)
def to_sanction_product( product_id):
    """sanctions a product by setting their
    is_deleted attribute  to "temporary"
    admin_status attribute to "blacklisted"
    Args:
        product_id (string)
    returns:
        JSON response with status code and message:
        -success(HTTP 200): product is sanctioned successfully
        -success(HTTP 200): if the product with provided not marked as sanctioned
        -failure(HTTP 404): if the product with provided id does not exist
        -failure(HTTP 500): if there is any server error
    """
    try:
        product_id = IdSchema(id=product_id)
        product_id = product_id.id
    except ValidationError as e:
        raise_validation_error(e)

    product = Product.query.filter_by(id=product_id).first()
    if not product:
        return jsonify(
            {"error": "Product Not Found", "message": "Product does not exist"}
        ), 404

    if product.is_deleted == "temporary" and product.admin_status == "suspended":
        return jsonify(
            {
                "error": "Conflict",
                "message": "Product has already been sanctioned"
                }
                ), 409

    # Start a transaction
    db.session.begin_nested()

    # Update product attributes
    product.admin_status = "suspended"
    product.is_deleted = "temporary"

    # Commit the transaction
    db.session.commit()

    # ========================Log and notify the owner of the sanctioning action====================
    try:
        register_action_d(user_id, "Product Sanction", product_id)
        notify(action="sanction", product_id=product_id)
    except Exception as error:
        logger.error(f"{type(error).__name__}: {error}")
    # ==============================================================================================

    return jsonify(
        {
            "data": product.format(),
            "message": "Product sanctioned successfully",
        }
    ), 200





#WORKS
@product.route("/product_statistics", methods=["GET"])
# @admin_required(request=request)
def get_product_statistics():
    """
    Returns statistics about the products, including the total number of all products, the total number of sanctioned
    products, and the total number of deleted products.

    :return: A JSON response containing product statistics.
    :rtype: dict
    """
    try:
        all_products = Product.query.count()
        sanctioned_products = Product.query.filter_by(
            admin_status="suspended", is_deleted="temporary"
        ).count()
        deleted_products = Product.query.filter_by(is_deleted="temporary").count()

        statistics = {
            "total_products": all_products,
            "total_sanctioned_products": sanctioned_products,
            "total_deleted_products": deleted_products,
        }

        return jsonify({"status": "Success", "product_statistics": statistics}), 200

    except Exception as exc:
        return jsonify(
                {
                    "error": "Bad request",
                    "message": "Something went wrong while retrieving product statistics: {exc}",
                }
            ), 400




#Not fully working
@product.route("/restore_product/<product_id>", methods=["PATCH"])
# @admin_required(request=request)
def to_restore_product( product_id):
    """restores a temporarily deleted product by setting their is_deleted
        attribute from "temporary" to "active"
    Args:
        product_id (string)
    returns:
        JSON response with status code and message:
        -success(HTTP 200): product restored successfully

        -success(HTTP 200): if the product with provproduct_ided not marked as deleted
        -failure(HTTP 404): if the product with provproduct_ided product_id does not exist

        -success(HTTP 200): if the product with provided not marked as deleted
        -failure(HTTP 404): if the product with provided id does not exist

    """
    try:
        product_id = IdSchema(id=product_id)
        product_id = product_id.id
    except ValidationError as e:
        raise_validation_error(e)

    try:
        product = Product.query.filter_by(id=product_id).first()
        print(product)
        if not product:
            return jsonify(
                    {
                        "error": "Product Not Found",
                        "message": " Product Already deleted",
                    }
                ), 404

        if product.is_deleted == "temporary":
            if product.admin_status == "suspended" or product.admin_status == "approved":
                product.admin_status = "approved"
                product.is_deleted = "active"
                db.session.commit()
                register_action_d(
                    user_id,
                    "Restore Temporary Deletion",
                    product_id,
                )

                return jsonify(
                    {
                        'message': 'product restored successfully',
                        "data": product.format()
                        }
                        ), 201
        else:
            return jsonify({"message": "product is not marked as deleted"}), 200
    except Exception as exc:
        logger.error(f"{type(exc).__name__}: {exc}")
        return jsonify(
            {
                "error": "Bad request",
                "message": "Something went wrong while performing this Action",
            }
        ), 400


# WORKS
@product.route("delete_product/<product_id>", methods=["PATCH"])
# @admin_required(request=request)
def temporary_delete( product_id):
    """
    Deletes a product temporarily by updating the 'is_deleted' field of the product in the database to 'temporary'.
    Logs the action in the product_logs table.

    Args:
        product_id (str): The product_id of the product to be temporarily deleted.

    Returns:
        dict: A JSON response with the appropriate status code and message.
            - If the product is successfully temporarily deleted:
                - Status code: 204
                - Body:
                    - "message": "Product temporarily deleted"
                    - "data": null
            - If the product with the given product_id does not exist:
                - Status code: 404
                - Body:
                    - "error": "Not Found"
                    - "message": "Product not found"
            - If an exception occurs during the logging process:
                - Status code: 500
                - Body:
                    - "error": "Internal Server Error"
                    - "message": [error message]
    """
    select_query = """
                        SELECT * FROM public.product
                        WHERE id=%s;"""

    delete_query = """UPDATE product
                        SET is_deleted = 'temporary'
                        WHERE id = %s;"""
    update_query = """
            UPDATE "product"
            SET "is_deleted" = 'temporary', 
            WHERE "id" = %s
            RETURNING *;  -- Return the updated row
        """

    try:
        product_id = IdSchema(id=product_id)
        product_id = product_id.id
    except ValidationError as e:
        raise_validation_error(e)
    try:
        with Database() as db:
            db.execute(select_query, (product_id,))
            selected_product = db.fetchone()
            if len(selected_product) == 0:
                return jsonify({"error": "Not Found", "message": "Product not found"}), 404
            if selected_product[10] == "temporary":
                return jsonify(
                        {
                            "error": "Conflict",
                            "message": "Action already carried out on this Product",
                        }
                    ), 409

            db.execute(delete_query, (product_id,))

            # data = request.get_json()
            # reason = data.get("reason")

            # if not reason:
            #     return jsonify({"error": "Supply a reason for deleting this product."}), 400

            try:
                register_action_d(user_id, "Temporary Deletion", product_id)
            except Exception as log_error:
                logger.error(f"{type(log_error).__name__}: {log_error}")

        return jsonify(
                {
                    "message": "Product temporarily deleted",
                    # "reason": reason,
                    "data": None,
                }
            ), 204

    except Exception as e:
        print("here")
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500

#WORKS
@product.route("delete_product/<product_id>", methods=["DELETE"])
# @admin_required(request=request)
def permanent_delete( product_id):
    """
    Deletes a product permanently from the database.

    Args:
        user_id (int): The ID of the user performing the deletion.
        product_id (str): The UUID of the product to be deleted.

    Returns:
        A JSON response indicating the success or failure of the deletion.
        If the `product_id` is not a valid UUID, return a JSON response with a "Bad Request" error and a message indicating the unsupported data type.
        If the product is not found in the database, return a JSON response with a "Not Found" error and a message indicating that the product was not found.
        If there is an error while executing the DELETE query or logging the action, return a JSON response with a "Server Error" error and a message indicating the error.
        If the deletion is successful, return a JSON response with a "Product permanently deleted" message and a null data field.
    """

    try:
        product_id = IdSchema(id=product_id)
        product_id = product_id.id
    except ValidationError as e:
        raise_validation_error(e)

    try:
        with Database() as db:
            check_query = "SELECT * FROM product WHERE id = %s;"
            db.execute(check_query, (product_id,))
            product = db.fetchone()

            if len(product) == 0:
                return jsonify({"error": "Not Found", "message": "Product not found"}), 404

            delete_query = """DELETE FROM product WHERE id = %s;"""
            db.execute(delete_query, (product_id,))

            # log and notify of deletion
            try:
                register_action_d(user_id, "Permanent Deletion", product_id)
                notify(action="deletion", product_id=product_id)
            except Exception as error:
                logger.error(f"{type(error).__name__}: {error}")

        return jsonify({"message": "Product permanently deleted", "data": None}), 204
    except Exception as exc:
        return jsonify({"error": "Server Error", "message": str(exc)}), 500


# Define a route to get all temporarily deleted products

#WORKS
@product.route("/temporarily_deleted_products", methods=["GET"], strict_slashes=False)
# @admin_required(request=request)
def get_temporarily_deleted_products():
    """
    Retrieve temporarily deleted products.
    This endpoint allows super admin users to retrieve a list of products that have been temporarily deleted.
    Returns:
        JSON response with status and message:
        - Success (HTTP 200): A list of temporarily deleted products and their details.
        - Success (HTTP 200): A message indicating that no products have been temporarily deleted.
        - Error (HTTP 500): If an error occurs during the retrieving process.
    Permissions:
        - Only accessible to super admin users.
    Note:
        - The list includes the details of products that have been temporarily deleted.
        - If no products have been temporarily deleted, a success message is returned.
    """
    try:
        # Query the database for all temporarily_deleted_products
        temporarily_deleted_products = Product.query.filter_by(
            is_deleted="temporary"
        ).all()

        # Calculate the total count of temporarily deleted products
        total_count = len(temporarily_deleted_products)

        # Check if no products have been temporarily deleted
        if not temporarily_deleted_products:
            return jsonify(
                    {
                        "message": "No products have been temporarily deleted, Yet!",
                        "data": total_count,
                    }
                ), 200

        # Create a list with Product details
        products_list = [product.format() for product in temporarily_deleted_products]

        # Return the list with all attributes of the temporarily_deleted_products
        return jsonify(
                {
                    "message": "All temporarily deleted products retrieved successfully",
                    "data": {
                                    "temporarily_deleted_products": products_list,
                                     "count": total_count,
                    }
                }
            ), 200
    except Exception as e:
        # Handle any exceptions that may occur during the retrieving process
        return jsonify({"status": "Error", "message": str(e)})

# @product.route("/remove_sanction/<product_id>", methods=["PATCH"])
# # @admin_required(request=request)
# def to_remove_sanction_product(user_id, product_id):
#     """remove sanctions on a product by setting their
#     is_deleted attribute from "temporary" to "active"
#     admin_status attribute from "suspended" to "approved"
#     Args:
#         product_id (string)
#     returns:
#         JSON response with status code and message:
#         -success(HTTP 200): product sanctioned is removed successfully
#         -success(HTTP 200): if the product with provided not marked as sanctioned
#         -failure(HTTP 404): if the product with provided id does not exist
#     """
#     try:
#         product_id = IdSchema(id=product_id)
#         product_id = product_id.id
#     except ValidationError as e:
#         raise_validation_error(e)

#     try:
#         product = Product.query.filter_by(id=product_id).first()
#         if not product:
#             return jsonify(
#                     {
#                         "error": "Conflict",
#                         "message": " Product Already deleted",
#                     }
#                 ), 409

#         if product.is_deleted == "temporary" and product.admin_status == "suspended":
#             try:
#                 # Start a transaction
#                 db.session.begin_nested()

#                 # Update product attributes to remove the sanction
#                 product.admin_status = "approved"
#                 product.is_deleted = "active"

#                 # Commit the transaction
#                 db.session.commit()

#                 # Log the removal of the sanction
#                 try:
#                     register_action_d(
#                         user_id,
#                         "Product Sanction removal",
#                         product_id,
#                     )
#                 except Exception as log_error:
#                     return jsonify({"error": "Logging Error", "message": str(log_error)}), 500

#                 return jsonify(
#                         {
#                             "data": product.format(),
#                             "message": "Sanction removed successfully",
#                         }
#                     ), 200

#             except Exception as e:
#                 db.session.rollback()
#                 return jsonify({"error": "Internal Server Error", "message": str(e)}), 500
#         else:
#             return jsonify({"message": "product is not marked as sanctioned"}), 200
#     except Exception as exc:
#         return jsonify(
#                 {
#                     "error": "Bad request {}".format(exc),
#                     "message": "Something went wrong while performing this Action",
#                 }
#             ), 400



# @product.route("/sanctioned_products/", methods=["GET"])
# # @admin_required(request=request)
# def get_sanctioned_products(user_id):
#     """
#     Retrieves the details of sanctioned products.

#     :return: A JSON response containing the details of the sanctioned product.
#     :rtype: dict
#     """
#     try:
#         products = Product.query.all()
#         if not products:
#             return jsonify(
#                     {
#                         "error": "Product Not Found",
#                         "message": " Product Already deleted",
#                     }
#             ), 404
#         santioned_product_list = []
#         for product in products:
#             if (
#                 product.admin_status == "suspended"
#                 and product.is_deleted == "temporary"
#             ):
#                 santioned_product_list.append(product.format())

#         return jsonify(
#                 {
#                     "status": "Success",
#                     "message": "Sanctioned products returned successfully",
#                     "data": santioned_product_list
#                 }
#         ), 200

#     except Exception as exc:
#         return jsonify(
#                 {
#                     "error": "Bad request {}".format(exc),
#                     "message": "Something went wrong while performing this Action",
#                 }
#         ), 400

super_admin_1/products/routes.py
hngx-org/spitfire-super-admin-one
#!/usr/bin/env python3
"""API Template for the Shop-driven Operation"""

import os
import uuid
from flask import Blueprint, jsonify, request, abort, send_file
from super_admin_1 import db
from super_admin_1.models.shop import Shop
from super_admin_1.models.user import User
from super_admin_1.shop.shoplog_helpers import ShopLogs
from super_admin_1.models.product import Product
import os
from super_admin_1.models.shop_logs import ShopsLogs
from utils import admin_required
#from health_check import check_services_health

test = Blueprint("test", __name__, url_prefix="/api/test")


# ============================== MY HELPER FUNCTON ================================
@test.route('/user/create', methods=['POST'])
#@check_services_health
#@admin_required(request)
def create_user():
    """Create a new user"""
    if not request.get_json():
        abort(400)
    data_fields = [
        "username",
        "first_name",
        "last_name",
        "email",
        "section_order",
        "password",
        "is_verified",
        "two_factor_auth",
        "provider",
        "profile_pic",
        "refresh_token",
    ]
    for field in data_fields:
        if field not in request.get_json():
            abort(400)
    user = User(**request.get_json())
    db.session.add(user)
    db.session.commit()
    return jsonify(user.format()), 201


@test.route("/user/<user_id>/shop", methods=["POST"])
#@check_services_health
#@admin_required(request)
def create_shop(user_id):
    """Create a new shop"""
    if not request.get_json():
        abort(400)
    data_fields = ["name", "policy_confirmation", "reviewed", "rating"]
    for field in data_fields:
        if field not in request.get_json():
            abort(400)
        else:
            continue
    data = request.get_json()
    data["merchant_id"] = user_id
    shop = Shop(**data)
    db.session.add(shop)
    db.session.commit()

    """
  The following logs the action in the shop_log db
  """
    get_shop_id = shop.id
    action = ShopLogs(shop_id=get_shop_id, user_id=data["merchant_id"])
    action.log_shop_created()
    return jsonify(shop.format()), 201


@test.route('/shop/<shop_id>/product', methods=['POST'])
@admin_required(request=request)
def create_product(shop_id):
    """ Create a new product"""
    if not request.get_json():
        abort(400)

    data = request.get_json()
    data["shop_id"] = shop_id
    product = Product(**data)
    db.session.add(product)
    db.session.commit()

    return jsonify(product.format()), 201


# get request for shop
@test.route('/', methods=['GET'], strict_slashes=False, defaults={'shop_id': None})
@test.route('/<shop_id>', methods=['GET'])
@admin_required(request=request)
def get_shop(shop_id):
    """Get a shop or all shop"""
    if shop_id:
        return jsonify(Shop.query.filter_by(id=shop_id).first().format()), 200
    else:
        return jsonify([shop.format() for shop in Shop.query.all()]), 200


# Get request for user
@test.route("/user", methods=["GET"], strict_slashes=False, defaults={"user_id": None})
@test.route("/user/<user_id>", methods=["GET"])
@admin_required(request=request)
def get_user(user_id=None):
    """Get all users"""
    if user_id:
        return jsonify(User.query.filter_by(id=user_id).first().format()), 200
    else:
        return jsonify([user.format() for user in User.query.all()]), 200


# Delete user object
@test.route("/user/<user_id>", methods=["DELETE"])
@admin_required(request=request)
def delete_user(user_id):
    """Delete a user"""
    user = User.query.filter_by(id=user_id).first()
    if not user:
        abort(404)
    db.session.delete(user)
    db.session.commit()
    return jsonify({"message": "User deleted"}), 200


# Define a route to get all vendors, including all their details
@test.route("/all_vendors", methods=["GET"])
def get_all_vendors():
    try:
        # Retrieve all vendors from the database
        vendors = Shop.query.all()

        # Check if there are vendors to return
        if not vendors:
            return jsonify({"message": "No vendors found."}), 200

        # Prepare the list of vendors with all their details
        vendor_list = [vendor.format() for vendor in vendors]

        return jsonify({"vendors": vendor_list}), 200
    except Exception as e:
        return jsonify({"status": "Error", "message": str(e)})


# Define a route to temporarily delete a vendor
@test.route("/delete_vendor/<string:vendor_id>", methods=["DELETE"])
def temporarily_delete_vendor(vendor_id):
    try:
        # Check if the vendor_id is a valid UUID (assuming vendor IDs are UUIDs)
        if not is_valid_uuid(vendor_id):
            return jsonify({"error": "Invalid vendor ID format."}), 400

        # Find the vendor by ID and set their status to "temporary" deleted
        vendor = Shop.query.filter_by(id=vendor_id).first()
        if vendor:
            vendor.is_deleted = "temporary"
            db.session.commit()
            return (
                jsonify(
                    {
                        "status": "Success",
                        "message": "Vendor temporarily deleted successfully.",
                    }
                ),
                200,
            )
        else:
            return jsonify({"error": "Vendor not found."}), 404
    except Exception as e:
        return jsonify({"status": "Error", "message": str(e)})


# Helper function to check if a string is a valid UUID
def is_valid_uuid(uuid_string):
    try:
        uuid.UUID(uuid_string, version=4)
        return True
    except ValueError:
        return False


# ======================================== HELPER FUNCTION END=============================================

super_admin_1/shop/func_helpers.py
hngx-org/spitfire-super-admin-one
from flask import Blueprint, jsonify, send_file, request
import os, uuid
from super_admin_1.models.alternative import Database
from super_admin_1 import db
from super_admin_1.models.shop import Shop
from super_admin_1.models.shop_logs import ShopsLogs
from super_admin_1.models.product import Product
from super_admin_1.models.user import User
from super_admin_1.shop.shoplog_helpers import ShopLogs
from sqlalchemy.exc import SQLAlchemyError
from super_admin_1.shop.shop_schemas import IdSchema
from pydantic import ValidationError
from utils import admin_required, raise_validation_error
from sqlalchemy import func


shop = Blueprint("shop", __name__, url_prefix="/api/shop")


# TEST
@shop.route("/endpoint", methods=["GET"])
@admin_required(request=request)
def shop_endpoint(user_id):

    """
    Handle GET requests to the shop endpoint.

    Returns:
        jsonify: A JSON response indicating the success of the request.
    """
    response_data = {"message": "This is the shop endpoint under /api/shop/endpoint"}
    return jsonify(response_data), 200

#WORKS
@shop.route("/totals", methods=["GET"])
@admin_required(request=request)
def shop_total(user_id):
    data = []
    shops = Shop.query.all()
    banned_shops = Shop.query.filter_by(
        admin_status='suspended', restricted='temporary').count()
    deleted_shops = Shop.query.filter_by(is_deleted="temporary").count()

    total_data = {
        "total_shops": len(shops),
        "total_banned_shops": banned_shops,
        "total_deleted_shops": deleted_shops,
    }
    data.append(total_data)
    return jsonify({"message": "total related to shops", "data": data})

#WORKS
@shop.route("/all/specific", methods=["GET"])
@admin_required(request=request)
def get_specific_shops_info(user_id=None):
    """get specific information to all shops needed by the FE (This endpoint is specific to the FE request)

     Returns:
        dict: A JSON response with the appropriate status code and message.
            - If the shops are returned successfully:
                - Status code: 200
                - Body:
                    - "message": "all shops request successful"
                    - "data": []
            - If an exception occurs during the get process:
                - Status code: 500
                - Body:
                    - "error": "Internal Server Error"
                    - "message": [error message]
    """

    shops = Shop.query.all()
    data = []

    def check_status(shop):
        if shop.admin_status == "suspended" and shop.restricted == "temporary":
            return "Banned"
        if (shop.admin_status == "approved" or shop.admin_status == "pending") and shop.is_deleted == "active":
            return "Active"
        if shop.is_deleted == "temporary":
            return "Deleted"

    try:
        for shop in shops:
            products = Product.query.filter_by(shop_id=shop.id).all()
            merchant_name = f"{shop.user.first_name} {shop.user.last_name}"
            date = shop.createdAt.strftime("%d-%m-%Y")
            shop_data = {
                "createdAt": date,
                "shop_id": shop.id,
                "merchant_id": shop.merchant_id,
                "name": merchant_name,
                "email": shop.user.email,
                "status": check_status(shop),
                "total_products": len(products)
            }
            data.append(shop_data)
        return jsonify({"message": "all shops specific information", "data": data})
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500

#WORKS
@shop.route("/specific/<shop_id>", methods=["GET"])
@admin_required(request=request)
def get_specific_shop_info(user_id, shop_id):
    """get specific information to a shop needed by the FE (This endpoint is specific to the FE request)

    Returns:
        dict: A JSON response with the appropriate status code and message.
            - If the shop is returned successfully:
                - Status code: 200
                - Body:
                    - "message": "the shop request successful"
                    - "data": []
            - If the shop with the given ID does not exist:
                - Status code: 404
                - Body:
                    - "error": "not found"
                    - "message": "invalid shop id"
            - If an exception occurs during the get process:
                - Status code: 500
                - Body:
                    - "error": "Internal Server Error"
                    - "message": [error message]
    """
    try:
        shop_id=IdSchema(id=shop_id)
        shop_id=shop_id.id
    except ValidationError as e:
        raise_validation_error(e)
    shop = Shop.query.filter_by(id=shop_id).first()
    data = []

    if not shop:
        return jsonify({"error": "not found", "message": "invalid shop id"}), 404

    def check_status(shop):
        if shop.admin_status == "suspended" and shop.restricted == "temporary":
            return "Banned"
        if (shop.admin_status == "approved" or shop.admin_status == "pending") and shop.is_deleted == "active":
            return "Active"
        if shop.is_deleted == "temporary":
            return "Deleted"

    try:
        products = Product.query.filter_by(shop_id=shop.id).all()
        merchant_name = f"{shop.user.first_name} {shop.user.last_name}"
        date = shop.createdAt.strftime("%d-%m-%Y")
        shop_data = {
            "createdAt": date,
            "shop_id": shop.id,
            "merchant_id": shop.merchant_id,
            "name": merchant_name,
            "email": shop.user.email,
            "status": check_status(shop),
            "total_products": len(products),
            "products": [{"currency": product.currency, "discount_price": product.discount_price, "product_id": product.id, "name": product.name, "price": product.price, "image_id": product.image_id, "rating_id": product.rating_id} for product in products]
        }
        #  "image_id": product.image_id, "rating_id": product.rating_id
        data.append(shop_data)
        return jsonify({"message": "shop specific information", "data": data})
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500

#WORKS
@shop.route("/all", methods=["GET"])
@admin_required(request=request)
def get_shops(user_id):
    """get information related to all shops

    Returns:
       dict: A JSON response with the appropriate status code and message.
           - If the shops are returned successfully:
               - Status code: 200
               - Body:
                   - "message": "all shops request successful"
                   - "data": []
           - If an exception occurs during the get process:
               - Status code: 500
               - Body:
                   - "error": "Internal Server Error"
                   - "message": [error message]
    """
    try:
        shops = Shop.query.all()
        return (
            jsonify(
                {
                    "message": "all shops request successful",
                    "data": [shop.format() for shop in shops],
                }
            ),
            200,
        )
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500


#WORKS
@shop.route("/<shop_id>", methods=["GET"])
@admin_required(request=request)
def get_shop(user_id, shop_id):
    """get information related to a shop

    Args:
        shop_id (uuid): The unique identifier of the shop/vendor.

     Returns:
        dict: A JSON response with the appropriate status code and message.
            - If the shop is returned successfully:
                - Status code: 200
                - Body:
                    - "message": "the shop request successful"
                    - "data": []
            - If the shop with the given ID does not exist:
                - Status code: 404
                - Body:
                    - "error": "not found"
                    - "message": "invalid shop id"
            - If an exception occurs during the get process:
                - Status code: 500
                - Body:
                    - "error": "Internal Server Error"
                    - "message": [error message]
    """
    try:
        shop_id=IdSchema(id=shop_id)
        shop_id=shop_id.id
    except ValidationError as e:
        raise_validation_error(e)

    try:
        shop = Shop.query.filter_by(id=shop_id).first()

        if not shop:
            return jsonify({"error": "Not found", "message": "Shop Not Found"}), 404

        return (
            jsonify(
                {"message": "the shop request successful", "data": [shop.format()]}
            ),
            200,
        )
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500

#WORK (Changes in product model and endpoint)
@shop.route("/all/products", methods=["GET"])
@admin_required(request=request)
def get_shops_products(user_id):
    """get information related to all shops, their products, and total products

    Returns:
       dict: A JSON response with the appropriate status code and message.
           - If the shop is returned successfully:
               - Status code: 200
               - Body:
                   - "message": "successful request for shops and their products"
                   - "data": []
           - If an exception occurs during the get process:
               - Status code: 500
               - Body:
                   - "error": "Internal Server Error"
                   - "message": [error message]
    """
    shop_products = []
    shops = Shop.query.all()
    try:
        for shop in shops:
            products = Product.query.filter_by(shop_id=shop.id).all()
            shop_data = {
                "admin_status": shop.admin_status,
                "createdAt": shop.createdAt,
                "id": shop.id,
                "is_deleted": shop.is_deleted,
                "merchant_id": shop.merchant_id,
                "shop_name": shop.name,
                "policy_confirmation": shop.policy_confirmation,
                "rating": shop.rating,
                "restricted": shop.restricted,
                "reviewed": shop.reviewed,
                "updatedAt": shop.updatedAt,
                "total_products": len(products),
                "products": [
                    {
                        "admin_status": product.admin_status,
                        "category_id": product.category_id,
                        "createdAt": product.createdAt,
                        "currency": product.currency,
                        "description": product.description,
                        "discount_price": product.discount_price,
                        "product_id": product.id,
                        #"image_id": product.image_id,
                        "rating_id": product.rating_id,
                        "is_deleted": product.is_deleted,
                        "is_published": product.is_published,
                        "name": product.name,
                        "price": product.price,
                        "quantity": product.quantity,
                        "tax": product.tax,
                        "updatedAt": product.updatedAt,
                    }
                    for product in products
                ],
            }
            #  "image_id": product.image_id, "rating_id": product.rating_id
            shop_products.append(shop_data)
        return jsonify(
            {
                "message": "successful request for shops and their products",
                "data": shop_products,
            }
        )
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500

#WORKS (Changes in product model and endpoint)
@shop.route("/<shop_id>/products", methods=["GET"])
@admin_required(request=request)
def get_shop_products(user_id, shop_id):
    """get information related to a shop, it's products and total products

    Args:
        shop_id (uuid): The unique identifier of the shop/vendor.

     Returns:
        dict: A JSON response with the appropriate status code and message.
            - If the shop is returned successfully:
                - Status code: 200
                - Body:
                    - "message": "successful request for the shop products"
                    - "data": []
            - If the shop with the given ID does not exist:
                - Status code: 404
                - Body:
                    - "error": "not found"
                    - "message": "invalid shop id"
            - If an exception occurs during the get process:
                - Status code: 500
                - Body:
                    - "error": "Internal Server Error"
                    - "message": [error message]
    """
    try:
        
        shop_id=IdSchema(id=shop_id)
        shop_id=shop_id.id
        shop = Shop.query.filter_by(id=shop_id).first()
        shop_products = []
        if not shop:
            return jsonify({"error": "not found", "message": "invalid shop id"}), 404

    except ValidationError as e:
        raise_validation_error(e)


    try:
        products = Product.query.filter_by(shop_id=shop.id).all()
        shop_data = {
            "admin_status": shop.admin_status,
            "createdAt": shop.createdAt,
            "id": shop.id,
            "is_deleted": shop.is_deleted,
            "merchant_id": shop.merchant_id,
            "shop_name": shop.name,
            "policy_confirmation": shop.policy_confirmation,
            "rating": shop.rating,
            "restricted": shop.restricted,
            "reviewed": shop.reviewed,
            "updatedAt": shop.updatedAt,
            "total_products": len(products),
            "products": [
                {
                    "admin_status": product.admin_status,
                    "category_id": product.category_id,
                    "createdAt": product.createdAt,
                    "currency": product.currency,
                    "description": product.description,
                    "discount_price": product.discount_price,
                    "product_id": product.id,
                    #"image_id": product.image_id,
                    "rating_id": product.rating_id,
                    "is_deleted": product.is_deleted,
                    "is_published": product.is_published,
                    "name": product.name,
                    "price": product.price,
                    "quantity": product.quantity,
                    "tax": product.tax,
                    "updatedAt": product.updatedAt,
                }
                for product in products
            ],
        }
        #  "image_id": product.image_id, "rating_id": product.rating_id
        shop_products.append(shop_data)
        return jsonify(
            {
                "message": "successful request for the shop products",
                "data": shop_products,
            }
        )
    except Exception as e:
        return jsonify({"error": "Internal Server Error", "message": str(e)}), 500

#WORKS
@shop.route("/ban_vendor/<vendor_id>", methods=["PUT"])
@admin_required(request=request)
def ban_vendor(user_id, vendor_id):
    """
    Handle PUT requests to ban a vendor by updating their shop data.

    Args:
        vendor_id (uuid): The unique identifier of the vendor to be banned.

    Returns:
        jsonify: A JSON response containing the status of the vendor banning operation.
    """
    try:
        # Check if the vendor is already banned
        check_query = """
            SELECT "restricted" FROM "shop"
            WHERE "id" = %s
        """
        vendor_id = IdSchema(id=vendor_id)
        vendor_id = vendor_id.id
        with Database() as cursor:
            cursor.execute(check_query, (vendor_id,))
            current_state = cursor.fetchone()

        if current_state and current_state[0] == "temporary":
            return (
                jsonify(
                    {
                        "error": "Conflict",
                        "message": "Action already carried out on this Shop",
                    }
                ),
                409,
            )

        # Extract the reason from the request payload
        data = request.get_json()
        reason = data.get("reason")

        if not reason:
            return (
                jsonify(
                    {
                        "error": "Bad Request",
                        "message": "Supply the reason for banning this vendor.",
                    }
                ),
                400,
            )

        # Proceed with banning the vendor
        update_query = """
            UPDATE "shop"
            SET "restricted" = 'temporary', 
                "admin_status" = 'suspended'
            WHERE "id" = %s
            RETURNING *;  -- Return the updated row
        """
        with Database() as cursor:
            cursor.execute(update_query, (vendor_id,))
            updated_vendor = cursor.fetchone()

        if updated_vendor:
            vendor_details = {
                "id": updated_vendor[0],
                "merchant_id": updated_vendor[1],
                "name": updated_vendor[2],
                "policy_confirmation": updated_vendor[3],
                "restricted": updated_vendor[4],
                "admin_status": updated_vendor[5],
                "is_deleted": updated_vendor[6],
                "reviewed": updated_vendor[7],
                "rating": float(updated_vendor[8]),
                "created_at": str(updated_vendor[9]),
                "updated_at": str(updated_vendor[10]),
            }
            return jsonify({
                "message": "Vendor account banned temporarily.",
                "vendor_details": vendor_details,
                "reason": reason,
                "data": vendor_details
            }), 201
        
        else:
            return jsonify({"error": "Vendor not found."}), 404
    except ValidationError as e:
        raise_validation_error(e)
    except Exception as e:
        print(str(e))
        return jsonify({"error": "Internal Server Error"}), 500

#WORKS
@shop.route("/banned_vendors", methods=["GET"])
@admin_required(request=request)
def get_banned_vendors(user_id):

    try:
        # Perform a database query to retrieve all banned vendors
        query = """
            SELECT * FROM "shop"
            WHERE "restricted" = 'temporary' AND "admin_status" = 'suspended'
        """

        with Database() as cursor:
            cursor.execute(query)
            banned_vendors = cursor.fetchall()

        # Prepare the response data
        banned_vendors_list = []
        for vendor in banned_vendors:
            vendor_details = {
                "id": vendor[0],
                "merchant_id": vendor[1],
                "name": vendor[2],
                "policy_confirmation": vendor[3],
                "restricted": vendor[4],
                "admin_status": vendor[5],
                "is_deleted": vendor[6],
                "reviewed": vendor[7],
                "rating": float(vendor[8]),
                "created_at": str(vendor[9]),
                "updated_at": str(vendor[10]),
            }
            banned_vendors_list.append(vendor_details)

        # Return the list of banned vendors in the response
        return jsonify({
            "message": "Banned vendors retrieved successfully.",
            "data": banned_vendors_list
        }), 200

    except Exception as e:
        print(str(e))
        return jsonify({"error": "Internal Server Error"}), 500


# Define a route to unban a vendor
#WORKS
@shop.route("/unban_vendor/<vendor_id>", methods=["PUT"])
@admin_required(request=request)
def unban_vendor(user_id, vendor_id):

    """
    Unban a vendor by setting their 'restricted' and 'admin_status' fields.

    Args:
        vendor_id (string): The unique identifier of the vendor to unban.

    Returns:
        JSON response with status and message:
        - Success (HTTP 200): Vendor unbanned successfully.
        - Error (HTTP 404): If the vendor with the provided ID is not found.
        - Error (HTTP 500): If an error occurs during the database operation.

    Note:
    - This endpoint is used to unban a vendor by updating their 'restricted' and 'admin_status' fields.
    - The 'restricted' field is set to 'no' to indicate that the vendor is no longer restricted.
    - The 'admin_status' field is set to 'approved' to indicate that the vendor's status has been updated.
    - Proper authentication and authorization checks should be added to secure this endpoint.
    """
    try:
        try:
            vendor_id = IdSchema(id=vendor_id)
            vendor_id = vendor_id.id
        except ValidationError as e:
            raise_validation_error(e)

        # Search the database for the vendor with the provided vendor_id
        vendor = Shop.query.filter_by(id=vendor_id).first()
        # If the vendor with the provided ID doesn't exist, return a 404 error
        if not vendor:
            return jsonify({"Error": "Not Found", "message": "Vendor not found."}), 404

        # Check if the shop associated with the vendor is active
        # if vendor.is_deleted != "active":
        #    return jsonify(
        #            {
        #                "Error": "Bad Request",
        #                "message": "Vendor's shop is not active. Cannot unban.",
        #            }
        #        ), 400

        # Check if the vendor is already unbanned
        if vendor.restricted == "no":
            return (
                jsonify({"status": "Error", "message": "Vendor is already unbanned."}),
                400,
            )

            return (
                jsonify(
                    {"Error": "Conflict", "message": "Vendor is already unbanned."}
                ),
                409,
            )

        # Unban the vendor by setting 'restricted' to 'no' and
        # updating 'admin_status' to 'approved'
        vendor.restricted = "no"
        vendor.admin_status = "approved"

        # Commit the changes to the database
        db.session.commit()

        # Construct vendor details for the response
        vendor_details = {
            "id": vendor.id,
            "merchant_id": vendor.merchant_id,
            "name": vendor.name,
            "policy_confirmation": vendor.policy_confirmation,
            "restricted": vendor.restricted,
            "admin_status": vendor.admin_status,
            "is_deleted": vendor.is_deleted,
            "reviewed": vendor.reviewed,
            "rating": float(vendor.rating),
            "created_at": str(vendor.created_at),
            "updated_at": str(vendor.updated_at),
        }

        # Return a success message
        return (
            jsonify(
                {
                    "message": "Vendor unbanned successfully.",
                    "vendor_details": vendor_details,
                }
            ),
            200,
        )
    except SQLAlchemyError as e:
        # If an error occurs during the database operation, roll back the transaction
        db.session.rollback()
        return jsonify({"status": "Error.", "message": str(e)}), 500


@shop.route("restore_shop/<shop_id>", methods=["PATCH"])
@admin_required(request=request)
def restore_shop(user_id, shop_id):

    """restores a deleted shop by setting their "temporary" to "active" fields
    Args:
        shop_id (string)
    returns:
        JSON response with status code and message:
        -success(HTTP 200):shop restored successfully
        -success(HTTP 200): if the shop with provided not marked as deleted
    """
    try:
        shop_id = IdSchema(id=shop_id)
        shop_id = shop_id.id
    except ValidationError as e:
        raise_validation_error(e)
    try:
        shop = Shop.query.filter_by(id=shop_id).first()
    except Exception as e:
        if not shop:
            return jsonify({"Error": "Not Found", "Message": "Shop Not Found"}), 404

    # change the object attribute from temporary to active
    if shop.is_deleted == "temporary":
        shop.is_deleted = "active"
        try:
            db.session.commit()

            """
            The following logs the action in the shop_log db
            """
            get_user_id = shop.user.id
            action = ShopLogs(shop_id=shop_id, user_id=get_user_id)
            action.log_shop_deleted(delete_type="active")

            return (
                jsonify(
                    {"message": "shop restored successfully", "data": shop.format()}
                ),
                201,
            )
        except Exception as e:
            return jsonify(
                {
                    "Error": "Internal Server Error",
                    "message": str(e),
                }
            )
    else:
        return (
            jsonify(
                {
                    "error": "Conflict",
                    "message": "Action already carried out on this Shop",
                }
            ),
            409,
        )



@shop.route("delete_shop/<shop_id>", methods=["PATCH"], strict_slashes=False)
@admin_required(request=request)
def delete_shop(user_id, shop_id):
    """Delete a shop and cascade temporary delete action"""
    try:
        shop_id = IdSchema(id=shop_id)
        shop_id = shop_id.id
    except ValidationError as e:
        raise_validation_error(e)
    # verify if shop exists
    try:
        shop = Shop.query.filter_by(id=shop_id).first()
    except Exception as e:
        if not shop:
            return jsonify({"error": "Not Found", "message": "Shop not found"}), 404
    # check if shop is temporary
    if shop.is_deleted == "temporary":
        return (
            jsonify(
                {
                    "error": "Conflict",
                    "message": "Action already carried out on this Shop",
                }
            ),
            409,
        )
    data = request.get_json()
    reason = data.get("reason")

    if not reason:
        return (
            jsonify({"error": "Supply a reason for temporarily deleting this shop"}),
            400,
        )

        return (
            jsonify(
                {
                    "error": "Bad Request",
                    "message": "Supply a reason for temporarily deleting this shop",
                }
            ),
            400,
        )

    # delete shop temporarily
    shop.is_deleted = "temporary"
    # Cascade the temporary delete action to associated products
    products = Product.query.filter_by(shop_id=shop_id).all()
    for product in products:
        product.is_deleted = 'temporary'
        db.session.add(product)

    db.session.commit()

    """
    The following logs the action in the shop_log db
    """
    get_user_id = shop.user.id
    action = ShopLogs(shop_id=shop_id, user_id=get_user_id)
    action.log_shop_deleted(delete_type="temporary")

    return jsonify({'message': "Shop and associated products temporarily deleted" , "reason": reason}), 204
  


# delete shop object permanently out of the DB

@shop.route("delete_shop/<shop_id>", methods=["DELETE"])
@admin_required(request=request)
def perm_del(user_id, shop_id):
    """Delete a shop"""
    try:
        shop_id = IdSchema(id=shop_id)
        shop_id = shop_id.id
    except ValidationError as e:
        raise_validation_error(e)

    """ Delete a shop permanently also while shop is deleted all the 
    product associated with it will also be deleted permanently from the shop"""
    try:
        shop = Shop.query.filter_by(id=shop_id).first()
        if not shop:
           return jsonify({'message':'Shop not found'}), 400
        #access associated products     
        products = Product.query.filter_by(shop_id=shop_id).all()
        # access reviews for each product and delete them one by one
        for product in products:
            db.session.delete(product)
            db.session.commit()
        
        db.session.delete(shop)
        db.session.commit()
        return jsonify({'message': 'Shop and associated products deleted permanently'}), 200
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': 'Internal Server Error', 'message': str(e)}), 500

# Define a route to get all temporarily deleted vendors
@shop.route("/temporarily_deleted_vendors", methods=["GET"], strict_slashes=False)
@admin_required(request=request)
def get_temporarily_deleted_vendors(user_id):
    """
    Retrieve temporarily deleted vendors.

    This endpoint allows super admin users to retrieve a list of vendors who have been temporarily deleted.

    Returns:
        JSON response with status and message:
        - Success (HTTP 200): A list of temporarily deleted vendors and their details.
        - Success (HTTP 200): A message indicating that no vendors have been temporarily deleted.
        - Error (HTTP 500): If an error occurs during the retrieving process.

    Permissions:
        - Only accessible to super admin users.

    Note:
        - The list includes the details of vendors who have been temporarily deleted.
        - If no vendors have been temporarily deleted, a success message is returned.
    """
    try:
        # Query the database for all temporarily_deleted_vendors
        temporarily_deleted_vendors = Shop.query.filter_by(is_deleted="temporary").all()

        # Calculate the total count of temporarily deleted vendors
        total_count = len(temporarily_deleted_vendors)

        # Check if no vendors have been temporarily deleted
        if not temporarily_deleted_vendors:
            return (
                jsonify(
                    {
                        "status": "Success",
                        "message": "No vendors have been temporarily deleted",
                        "count": total_count,
                    }
                ),
                200,
            )

        # Create a list with vendors details
        vendors_list = [vendor.format() for vendor in temporarily_deleted_vendors]

        # Return the list with all attributes of the temporarily_deleted_vendors
        return (
            jsonify(
                {
                    "status": "Success",
                    "message": "All temporarily deleted vendors retrieved successfully",
                    "temporarily_deleted_vendors": vendors_list,
                    "count": total_count,
                }
            ),
            200,
        )

    except Exception as e:
        # Handle any exceptions that may occur during the retrieving process
        return jsonify({"status": "Error", "message": str(e)})


# Define a route to get details of a temporarily deleted vendor based on his/her ID
@shop.route(
    "/temporarily_deleted_vendor/<string:vendor_id>",
    methods=["GET"],
    strict_slashes=False,
)
@admin_required(request=request)
def get_temporarily_deleted_vendor(user_id,vendor_id):
    """
    Retrieve details of a temporarily deleted vendor based on its ID.

    Args:
        vendor_id (string): The unique identifier of the vendor to retrieve.

    Returns:
        JSON response with status and message:
        - Success (HTTP 200): Details of the temporarily deleted vendor.
        - Error (HTTP 404): If the vendor with the provided ID is not found or not temporarily deleted.
        - Error (HTTP 500): If an error occurs during the retrieval process.

    Permissions:
        - Only accessible to super admin users.

    Note:
        - This endpoint allows super admin users to retrieve the details of a temporarily deleted vendor based on his/her ID.
    """
    try:
        try:
            vendor_id = IdSchema(id=vendor_id)
            vendor_id = vendor_id.id
        except ValidationError as e:
            raise_validation_error(e)

        # Query the database for the vendor with the provided vendor_id that is temporarily deleted
        temporarily_deleted_vendor = Shop.query.filter_by(
            id=vendor_id, is_deleted="temporary"
        ).first()

        # If the vendor with the provided ID doesn't exist or is not temporarily deleted, return a 404 error
        if not temporarily_deleted_vendor:
            return (
                jsonify(
                    {
                        "status": "Error",
                        "message": "Temporarily deleted vendor not found.",
                    }
                ),
                404,
            )

        # Return the details of the temporarily deleted vendor
        vendor_details = temporarily_deleted_vendor.format()

        return (
            jsonify(
                {
                    "status": "Success",
                    "message": "Temporarily deleted vendor details retrieved successfully",
                    "temporarily_deleted_vendor": vendor_details,
                }
            ),
            200,
        )

    except SQLAlchemyError as e:
        # Handle any exceptions that may occur during the retrieval process
        db.session.rollback()
        return jsonify({"status": "Error", "message": str(e)}), 500


logs = Blueprint("logs", __name__, url_prefix="/api/logs")


@logs.route("/shops", defaults={"shop_id": None})
@logs.route("/shops/<shop_id>")
@admin_required(request=request)
def get_all_shop_logs(user_id,shop_id):
    """Get all shop logs"""
    if not shop_id:
        return (
            jsonify(
                {
                    "message": "success",
                    "logs": [
                        log.format() if log else [] for log in ShopsLogs.query.all()
                    ],
                }
            ),
            200,
        )

    return (
        jsonify(
            {
                "message": "success",
                "logs": [
                    log.format() if log else []
                    for log in ShopsLogs.query.filter_by(shop_id=shop_id).all()
                ],
            }
        ),
        200,
    )


@logs.route("/shops/download", defaults={"shop_id": None})
@logs.route("/shops/<shop_id>/download")
@admin_required(request=request)
def download_shop_logs(user_id, shop_id):
    """Download all shop logs"""
    logs = []
    if not shop_id:
        logs = [log.format() if log else [] for log in ShopsLogs.query.all()]
    else:
        logs = [
            log.format() if log else []
            for log in ShopsLogs.query.filter_by(shop_id=shop_id).all()
        ]
    # Create a temporary file to store the strings
    temp_file_path = f"{os.path.abspath('.')}/temp_file.txt"
    with open(temp_file_path, "w") as temp_file:
        temp_file.write("\n".join(logs))

    response = send_file(
        temp_file_path, as_attachment=True, download_name="shoplogs.txt"
    )
    os.remove(temp_file_path)

    return response


@logs.route("/shop/actions", methods=["GET"])
@admin_required(request=request)
def shop_actions(user_id):
    data = ShopsLogs.query.all()
    return jsonify([action.format_json() for action in data]), 200


@shop.route("/sanctioned", methods=["GET"])
# @admin_required(request=request)
def sanctioned_shop():
    """
    Get all sanctioned products from the database.

    Args:
      None

    Returns:
      A JSON response containing a message and a list of dictionary objects representing the sanctioned shop.
      If no shop are found, the message will indicate that and the object will be set to None.
    """
    data = []
    # get all the product object, filter by is_delete = temporay and rue and admin_status = "suspended"
    query = Shop.query.filter(
        Shop.admin_status == "suspended",
    )

    # if the query is empty
    if not query.all():

        return jsonify({"message": "No shops found", "object": None}), 200

        return jsonify({
            "message": "No shops found",
            "object": None
        }), 200

    # populate the object to a list of dictionary object
    for obj in query:
        data.append(obj.format())
    return jsonify({
        "message": "All sanctioned shops",
        "object": data
    }), 200


super_admin_1/shop/routes.py
hngx-org/spitfire-super-admin-one
from pydantic import BaseModel
from uuid import UUID


class IdSchema(BaseModel):
    id: UUID

super_admin_1/shop/shop_schemas.py
hngx-org/spitfire-super-admin-one
from super_admin_1.models.shop_logs import ShopsLogs


# see sample usage in delete shop route
class ShopLogs:
    """
    Shop Logs

    :param admin_id: uuid of admin
    :param shop_id: uuid of shop
    """

    def __init__(self, user_id, shop_id):
        self.user_id = user_id

        self.shop_id = shop_id

    def log_shop_created(self):
        """
        logs new shop created
        """

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action="shop created",
        )
        shop.insert()

    def log_shop_deleted(self, delete_type="temporary"):
        """
        logs shop deleted

        :param delete_type: type of delete action (active or temporary)
        """

        if delete_type not in ["active", "temporary"]:
            raise ValueError("invalid delete type only (active or temporary)")

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action=f"shop {delete_type}",
        )
        shop.insert()

    def log_shop_reviewed(self):
        """
        logs a shop passed review
        """

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action="shop reviewed",
        )
        shop.insert()

    def log_shop_restricted(self, restrict_type="temporary"):
        """
        logs shop restricted

        :param restrict_type: type of restriction action ('no', 'temporary', 'permanent')
        """

        if restrict_type not in ["no", "temporary", "permanent"]:
            raise ValueError(
                "invalid restrict type only ('no', 'temporary', 'permanent')"
            )

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action=f"{restrict_type} restricted on",
        )
        shop.insert()

    def log_shop_admin_status(self, status_type="pending"):
        """
        logs admin status of shop

        :param status_type: type of status action ('pending', 'review', 'approved', 'blacklist')
        """

        if status_type not in ["no", "temporary", "permanent"]:
            raise ValueError(
                "invalid status type only ('pending', 'review', 'approved', 'blacklist')"
            )

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action=f"marked {status_type} admin status on",
        )
        shop.insert()

    def log_shop_ban(self):
        """
        logs shop ban
        """

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action="shop banned",
        )
        shop.insert()

    def log_shop_unban(self):
        """
        logs shop ban
        """

        shop = ShopsLogs(
            shop_id=self.shop_id,
            user_id=self.user_id,
            action="shop unbanned",
        )
        shop.insert()

super_admin_1/shop/shoplog_helpers.py
hngx-org/spitfire-super-admin-one
import json
import pytest
from flask import Flask
from super_admin_1.products.delete_product import product_delete

@pytest.fixture
def app():
    app = Flask(__name__)
    app.register_blueprint(product_delete)
    return app

@pytest.fixture
def client(app):
    return app.test_client()

def test_temporary_delete(client):
    # Create a test product ID
    test_product_id = "550e8400-e29b-41d4-a716-446655440003"

    # Test case 1: Successful temporary deletion
    response = client.patch(f'/api/product/{test_product_id}')
    assert response.status_code == 204
    data = json.loads(response.data)
    assert data['message'] == 'Product temporarily deleted'
    assert data['data'] is None

    # Test case 2: Product not found
    response = client.patch('/api/product/nonexistent_id')
    assert response.status_code == 404
    data = json.loads(response.data)
    assert data['error'] == 'Not Found'
    assert data['message'] == 'Product not found'

    # Test case 3: Internal Server Error during logging
    with pytest.raises(Exception):
        client.patch(f'/api/product/{test_product_id}')

def test_permanent_delete(client):
    # Create a test product ID
    test_product_id = "test_product_id"

    # Test case 1: Successful permanent deletion
    response = client.delete(f'/api/product/{test_product_id}')
    assert response.status_code == 204
    data = json.loads(response.data)
    assert data['message'] == 'Product permanently deleted'
    assert data['data'] is None

    # Test case 2: Product not found
    response = client.delete('/api/product/nonexistent_id')
    assert response.status_code == 404
    data = json.loads(response.data)
    assert data['error'] == 'Not Found'
    assert data['message'] == 'Product not found'

    # Test case 3: Internal Server Error during logging
    with pytest.raises(Exception):
        client.delete(f'/api/product/{test_product_id}')

def test_get_products(client):
    # Test case 1: Successful retrieval of products
    response = client.get('/api/product/')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert isinstance(data, list)

def test_log(client):
    # Test case 1: Successful log file download
    response = client.get('/api/product/download/log')
    assert response.status_code == 200


super_admin_1/tests/product-test.py
hngx-org/spitfire-super-admin-one
import json
import pytest
from flask import Flask
from super_admin_1 import shop, create_app
from super_admin_1.models.alternative import Database

# Create a Flask app for testing
@pytest.fixture
def app():
    app = Flask(__name__)
    app.config['TESTING'] = True
    return app

# Create a test client for the app
@pytest.fixture
def client(app):
    base_url = 'https://spitfire-superadmin-1.onrender.com'
    client = app.test_client()
    client.base_url = base_url
    return client

# Define a test for the GET /api/shop/endpoint route
def test_shop_endpoint(client):
    response = client.get('/api/shop/endpoint')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert data['message'] == 'This is the shop endpoint under /api/shop/endpoint'

# Define a test for the PUT /api/shop/ban_vendor/<uuid:user_id> route
def test_ban_vendor(client, monkeypatch):
    def mock_cursor():
        return

    monkeypatch.setattr(Database, '__enter__', mock_cursor)

    user_id = '6d49f12f-2295-423e-9de3-f945ee82d7f0'
    response = client.put(f'/api/shop/ban_vendor/{user_id}')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert data['message'] == 'Vendor account banned temporarily.'

# Define a test for the GET /api/shop/banned_vendors route
def test_get_banned_vendors(client, monkeypatch):
    # Mock the Database class or relevant dependencies if needed
    def mock_cursor():
        return

    monkeypatch.setattr(Database, '__enter__', mock_cursor)

    response = client.get('/api/shop/banned_vendors')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert 'banned_vendors' in data


if __name__ == '__main__':
    pytest.main()

super_admin_1/tests/shop-test.py
hngx-org/spitfire-super-admin-one
import json
import pytest
from super_admin_1 import create_app
from super_admin_1.models.alternative import Database


# Create a Flask app for testing
@pytest.fixture
def app():
    app = create_app()
    app.config['TESTING'] = True
    return app

# Create a test client for the app
@pytest.fixture
def client(app):
    return app.test_client()

# Define a test for the GET /api/shop/endpoint route
def test_shop_endpoint(client):
    response = client.get('/api/shop/endpoint')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert data['message'] == 'This is the shop endpoint under /api/shop/endpoint'

# Define a test for the PUT /api/shop/ban_vendor/<uuid:user_id> route
def test_ban_vendor(client, monkeypatch):
    def mock_cursor():
        return

    monkeypatch.setattr(Database, '__enter__', mock_cursor)

    user_id = '550e8400-e29b-41d4-a716-446655440002'
    response = client.put(f'/api/shop/ban_vendor/{user_id}')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert data['message'] == 'Vendor account banned temporarily.'

# Define a test for the GET /api/shop/banned_vendors route
def test_get_banned_vendors(client, monkeypatch):
    # Mock the Database class or relevant dependencies if needed
    def mock_cursor():
        return

    monkeypatch.setattr(Database, '__enter__', mock_cursor)

    response = client.get('/api/shop/banned_vendors')
    assert response.status_code == 200
    data = json.loads(response.data)
    assert 'banned_vendors' in data

if __name__ == '__main__':
    pytest.main()

super_admin_1/tests/vendors-test.py
hngx-org/spitfire-super-admin-one
import pytest
from super_admin_1 import create_app
import json

@pytest.fixture
def app():
    app = create_app()
    app.config['TESTING'] = True
    return app

@pytest.fixture
def client(app):
    return app.test_client()

def test_protected_resource_without_authentication(client):
    response = client.get("/api/shop/endpoint")
    assert response.status_code == 401
    data = json.loads(response.data)
    assert data["error"] == "Unauthorized"
    assert data["message"] == "You are not logged in"

super_admin_1/tests/wrapper-test.py
hngx-org/spitfire-super-admin-one
from functools import wraps
from super_admin_1.errors.handlers import Unauthorized, Forbidden, CustomError
import requests

def admin_required(request=None):
    def super_admin_required(func):
        @wraps(func)
        def get_user_role( *args, **kwargs):
            auth_url = "https://auth.akuya.tech/api/authorize"
            if not request:
                raise CustomError(error="Unauthorized", code=401,  message="You are not logged in//")
            auth_header = request.headers.get("Authorization")
            if not auth_header:
                raise CustomError(error="Unauthorized", code=401,  message="You are not logged in")
            token = None
            if auth_header.startswith("Bearer"):
                token = auth_header.split(" ")[1]
                print(token)

            response = requests.post(
                auth_url,
                {
                    "token": token,
                    "role": "admin",
                },
            )
            #print("Authentication Service Response:", response.json())

            if response.status_code != 200:
                raise CustomError(error="Bad Request", code=400,  message="Something went wrong while Authenticating this User")

            user_data = response.json()

            if not user_data.get("authorized"):
                raise CustomError(error="Forbidden", code=403,  message="No Permissions to access the requested resource")

            user_id = user_data.get("user")["id"]

            return func(user_id,  *args, **kwargs)

        return get_user_role
    return super_admin_required


def raise_validation_error(error):
    msg = []
    for err in error.errors():
        msg.append({
            "field": err["loc"][0],
            "error":err["msg"]
        })
    raise CustomError("Bad Request",400,msg)

utils.py
hngx-org/spitfire-super-admin-one