From 85ca844eb465c8875d61c54bc227ba578417b288 Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Fri, 28 Feb 2025 01:00:04 +0100
Subject: [PATCH 01/11] postgre database fixed

---
 .ven/bin/Activate.ps1  | 247 +++++++++++++++++++++++++++++++++++++++++
 .ven/bin/activate      |  69 ++++++++++++
 .ven/bin/activate.csh  |  26 +++++
 .ven/bin/activate.fish |  69 ++++++++++++
 .ven/bin/pip           |   8 ++
 .ven/bin/pip3          |   8 ++
 .ven/bin/pip3.10       |   8 ++
 .ven/bin/python        |   1 +
 .ven/bin/python3       |   1 +
 .ven/bin/python3.10    |   1 +
 .ven/lib64             |   1 +
 .ven/pyvenv.cfg        |   3 +
 12 files changed, 442 insertions(+)
 create mode 100644 .ven/bin/Activate.ps1
 create mode 100644 .ven/bin/activate
 create mode 100644 .ven/bin/activate.csh
 create mode 100644 .ven/bin/activate.fish
 create mode 100755 .ven/bin/pip
 create mode 100755 .ven/bin/pip3
 create mode 100755 .ven/bin/pip3.10
 create mode 120000 .ven/bin/python
 create mode 120000 .ven/bin/python3
 create mode 120000 .ven/bin/python3.10
 create mode 120000 .ven/lib64
 create mode 100644 .ven/pyvenv.cfg

diff --git a/.ven/bin/Activate.ps1 b/.ven/bin/Activate.ps1
new file mode 100644
index 000000000..b49d77ba4
--- /dev/null
+++ b/.ven/bin/Activate.ps1
@@ -0,0 +1,247 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies: 
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+    [Parameter(Mandatory = $false)]
+    [String]
+    $VenvDir,
+    [Parameter(Mandatory = $false)]
+    [String]
+    $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+    # Revert to original values
+
+    # The prior prompt:
+    if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+        Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+        Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+    }
+
+    # The prior PYTHONHOME:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+        Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+    }
+
+    # The prior PATH:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+        Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+    }
+
+    # Just remove the VIRTUAL_ENV altogether:
+    if (Test-Path -Path Env:VIRTUAL_ENV) {
+        Remove-Item -Path env:VIRTUAL_ENV
+    }
+
+    # Just remove VIRTUAL_ENV_PROMPT altogether.
+    if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+        Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+    }
+
+    # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+    if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+        Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+    }
+
+    # Leave deactivate function in the global namespace if requested:
+    if (-not $NonDestructive) {
+        Remove-Item -Path function:deactivate
+    }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+    [String]
+    $ConfigDir
+) {
+    Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+    # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+    $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+    # An empty map will be returned if no config file is found.
+    $pyvenvConfig = @{ }
+
+    if ($pyvenvConfigPath) {
+
+        Write-Verbose "File exists, parse `key = value` lines"
+        $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+        $pyvenvConfigContent | ForEach-Object {
+            $keyval = $PSItem -split "\s*=\s*", 2
+            if ($keyval[0] -and $keyval[1]) {
+                $val = $keyval[1]
+
+                # Remove extraneous quotations around a string value.
+                if ("'""".Contains($val.Substring(0, 1))) {
+                    $val = $val.Substring(1, $val.Length - 2)
+                }
+
+                $pyvenvConfig[$keyval[0]] = $val
+                Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+            }
+        }
+    }
+    return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+    Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+    Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+    $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+    Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+    Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+    Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+    if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+        Write-Verbose "  Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+        $Prompt = $pyvenvCfg['prompt'];
+    }
+    else {
+        Write-Verbose "  Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+        Write-Verbose "  Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+        $Prompt = Split-Path -Path $venvDir -Leaf
+    }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+    Write-Verbose "Setting prompt to '$Prompt'"
+
+    # Set the prompt to include the env name
+    # Make sure _OLD_VIRTUAL_PROMPT is global
+    function global:_OLD_VIRTUAL_PROMPT { "" }
+    Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+    New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+    function global:prompt {
+        Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+        _OLD_VIRTUAL_PROMPT
+    }
+    $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+    Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+    Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
diff --git a/.ven/bin/activate b/.ven/bin/activate
new file mode 100644
index 000000000..716f363fb
--- /dev/null
+++ b/.ven/bin/activate
@@ -0,0 +1,69 @@
+# This file must be used with "source bin/activate" *from bash*
+# you cannot run it directly
+
+deactivate () {
+    # reset old environment variables
+    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+        PATH="${_OLD_VIRTUAL_PATH:-}"
+        export PATH
+        unset _OLD_VIRTUAL_PATH
+    fi
+    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+        export PYTHONHOME
+        unset _OLD_VIRTUAL_PYTHONHOME
+    fi
+
+    # This should detect bash and zsh, which have a hash command that must
+    # be called to get it to forget past commands.  Without forgetting
+    # past commands the $PATH changes we made may not be respected
+    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
+        hash -r 2> /dev/null
+    fi
+
+    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+        PS1="${_OLD_VIRTUAL_PS1:-}"
+        export PS1
+        unset _OLD_VIRTUAL_PS1
+    fi
+
+    unset VIRTUAL_ENV
+    unset VIRTUAL_ENV_PROMPT
+    if [ ! "${1:-}" = "nondestructive" ] ; then
+    # Self destruct!
+        unset -f deactivate
+    fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+VIRTUAL_ENV=/root/hng_boilerplate_python_fastapi_web/.ven
+export VIRTUAL_ENV
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/"bin":$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+    unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+    _OLD_VIRTUAL_PS1="${PS1:-}"
+    PS1='(.ven) '"${PS1:-}"
+    export PS1
+    VIRTUAL_ENV_PROMPT='(.ven) '
+    export VIRTUAL_ENV_PROMPT
+fi
+
+# This should detect bash and zsh, which have a hash command that must
+# be called to get it to forget past commands.  Without forgetting
+# past commands the $PATH changes we made may not be respected
+if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
+    hash -r 2> /dev/null
+fi
diff --git a/.ven/bin/activate.csh b/.ven/bin/activate.csh
new file mode 100644
index 000000000..eda6dc7ea
--- /dev/null
+++ b/.ven/bin/activate.csh
@@ -0,0 +1,26 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+# Created by Davide Di Blasi <davidedb@gmail.com>.
+# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV /root/hng_boilerplate_python_fastapi_web/.ven
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+    set prompt = '(.ven) '"$prompt"
+    setenv VIRTUAL_ENV_PROMPT '(.ven) '
+endif
+
+alias pydoc python -m pydoc
+
+rehash
diff --git a/.ven/bin/activate.fish b/.ven/bin/activate.fish
new file mode 100644
index 000000000..e32a30e00
--- /dev/null
+++ b/.ven/bin/activate.fish
@@ -0,0 +1,69 @@
+# This file must be used with "source <venv>/bin/activate.fish" *from fish*
+# (https://fishshell.com/); you cannot run it directly.
+
+function deactivate  -d "Exit virtual environment and return to normal shell environment"
+    # reset old environment variables
+    if test -n "$_OLD_VIRTUAL_PATH"
+        set -gx PATH $_OLD_VIRTUAL_PATH
+        set -e _OLD_VIRTUAL_PATH
+    end
+    if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+        set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+        set -e _OLD_VIRTUAL_PYTHONHOME
+    end
+
+    if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+        set -e _OLD_FISH_PROMPT_OVERRIDE
+        # prevents error when using nested fish instances (Issue #93858)
+        if functions -q _old_fish_prompt
+            functions -e fish_prompt
+            functions -c _old_fish_prompt fish_prompt
+            functions -e _old_fish_prompt
+        end
+    end
+
+    set -e VIRTUAL_ENV
+    set -e VIRTUAL_ENV_PROMPT
+    if test "$argv[1]" != "nondestructive"
+        # Self-destruct!
+        functions -e deactivate
+    end
+end
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV /root/hng_boilerplate_python_fastapi_web/.ven
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/"bin $PATH
+
+# Unset PYTHONHOME if set.
+if set -q PYTHONHOME
+    set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+    set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+    # fish uses a function instead of an env var to generate the prompt.
+
+    # Save the current fish_prompt function as the function _old_fish_prompt.
+    functions -c fish_prompt _old_fish_prompt
+
+    # With the original prompt function renamed, we can override with our own.
+    function fish_prompt
+        # Save the return status of the last command.
+        set -l old_status $status
+
+        # Output the venv prompt; color taken from the blue of the Python logo.
+        printf "%s%s%s" (set_color 4B8BBE) '(.ven) ' (set_color normal)
+
+        # Restore the return status of the previous command.
+        echo "exit $old_status" | .
+        # Output the original/"old" prompt.
+        _old_fish_prompt
+    end
+
+    set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+    set -gx VIRTUAL_ENV_PROMPT '(.ven) '
+end
diff --git a/.ven/bin/pip b/.ven/bin/pip
new file mode 100755
index 000000000..4c7fbaca3
--- /dev/null
+++ b/.ven/bin/pip
@@ -0,0 +1,8 @@
+#!/root/hng_boilerplate_python_fastapi_web/.ven/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/.ven/bin/pip3 b/.ven/bin/pip3
new file mode 100755
index 000000000..4c7fbaca3
--- /dev/null
+++ b/.ven/bin/pip3
@@ -0,0 +1,8 @@
+#!/root/hng_boilerplate_python_fastapi_web/.ven/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/.ven/bin/pip3.10 b/.ven/bin/pip3.10
new file mode 100755
index 000000000..4c7fbaca3
--- /dev/null
+++ b/.ven/bin/pip3.10
@@ -0,0 +1,8 @@
+#!/root/hng_boilerplate_python_fastapi_web/.ven/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/.ven/bin/python b/.ven/bin/python
new file mode 120000
index 000000000..b8a0adbbb
--- /dev/null
+++ b/.ven/bin/python
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/.ven/bin/python3 b/.ven/bin/python3
new file mode 120000
index 000000000..ae65fdaa1
--- /dev/null
+++ b/.ven/bin/python3
@@ -0,0 +1 @@
+/usr/bin/python3
\ No newline at end of file
diff --git a/.ven/bin/python3.10 b/.ven/bin/python3.10
new file mode 120000
index 000000000..b8a0adbbb
--- /dev/null
+++ b/.ven/bin/python3.10
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/.ven/lib64 b/.ven/lib64
new file mode 120000
index 000000000..7951405f8
--- /dev/null
+++ b/.ven/lib64
@@ -0,0 +1 @@
+lib
\ No newline at end of file
diff --git a/.ven/pyvenv.cfg b/.ven/pyvenv.cfg
new file mode 100644
index 000000000..0537ffc00
--- /dev/null
+++ b/.ven/pyvenv.cfg
@@ -0,0 +1,3 @@
+home = /usr/bin
+include-system-site-packages = false
+version = 3.10.12

From 13f72fa2b0d49f88a69cf07e009f11e20aabd39e Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sat, 1 Mar 2025 13:04:18 +0100
Subject: [PATCH 02/11] activty-log pagination succesfully added

---
 api/v1/routes/activity_logs.py | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/api/v1/routes/activity_logs.py b/api/v1/routes/activity_logs.py
index 51017efa5..71bd08b71 100644
--- a/api/v1/routes/activity_logs.py
+++ b/api/v1/routes/activity_logs.py
@@ -1,4 +1,4 @@
-from fastapi import APIRouter, Depends, status, HTTPException
+from fastapi import APIRouter, Depends, status, HTTPException, Query
 from fastapi.encoders import jsonable_encoder
 from sqlalchemy.orm import Session
 from api.v1.models.user import User
@@ -30,11 +30,17 @@ async def create_activity_log(
     )
 
 
+    
 @activity_logs.get("", response_model=list[ActivityLogResponse])
-async def get_all_activity_logs(current_user: User = Depends(user_service.get_current_super_admin), db: Session = Depends(get_db)):
-    '''Get all activity logs'''
-
-    activity_logs = activity_log_service.fetch_all(db=db)
+async def get_all_activity_logs(
+    page: int = Query(1, ge=1),
+    limit: int = Query(10, le=100),
+    current_user: User = Depends(user_service.get_current_super_admin), 
+    db: Session = Depends(get_db)
+    ):
+
+    """Get paginated activity logs"""
+    activity_logs = activity_log_service.fetch_all(db=db, page=page, limit=limit)
 
     return success_response(
         status_code=200,

From fba6641a88e47e31952b3600192740724b6a760d Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sat, 1 Mar 2025 13:36:02 +0100
Subject: [PATCH 03/11] fixed: README.md api/v1/services/activity_logs.py

---
 README.md                        | 2 +-
 api/v1/services/activity_logs.py | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 65fc8b168..d667e8cae 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ GRANT ALL PRIVILEGES ON DATABASE hng_fast_api TO user;
 
 **Starting the database**
 after cloning the database, dont run 
-`alembic revision --autogenerate -m 'initial migration'`
+`embic revision --autogenerate -m al'initial migration'`
 but run
 `alembic upgrade head`
 
diff --git a/api/v1/services/activity_logs.py b/api/v1/services/activity_logs.py
index bca5dca6d..6789897ab 100644
--- a/api/v1/services/activity_logs.py
+++ b/api/v1/services/activity_logs.py
@@ -33,7 +33,8 @@ def fetch_all(self, db: Session, **query_params: Optional[Any]):
         
         return query.all()
     
-    def delete_activity_log_by_id(self, db: Session, log_id: str):
+    def delete_activity_log_by_id(self, db: Session, log_id: str) -> bool:
+        """Delete an activity log by ID with error handling"""
         log = db.query(ActivityLog).filter(ActivityLog.id == log_id).first()
 
         if not log:

From 3f10de4d82ddb5eb81aeab7d24fab99d9262037a Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 08:24:01 +0100
Subject: [PATCH 04/11] Updated .gitignore to ignore environment files

---
 .env.sample            |  41 -------
 .gitignore             |   8 ++
 .ven/bin/Activate.ps1  | 247 -----------------------------------------
 .ven/bin/activate      |  69 ------------
 .ven/bin/activate.csh  |  26 -----
 .ven/bin/activate.fish |  69 ------------
 .ven/bin/pip           |   8 --
 .ven/bin/pip3          |   8 --
 .ven/bin/pip3.10       |   8 --
 .ven/bin/python        |   1 -
 .ven/bin/python3       |   1 -
 .ven/bin/python3.10    |   1 -
 .ven/lib64             |   1 -
 .ven/pyvenv.cfg        |   3 -
 alembic/env.py         |  88 ---------------
 15 files changed, 8 insertions(+), 571 deletions(-)
 delete mode 100644 .env.sample
 delete mode 100644 .ven/bin/Activate.ps1
 delete mode 100644 .ven/bin/activate
 delete mode 100644 .ven/bin/activate.csh
 delete mode 100644 .ven/bin/activate.fish
 delete mode 100755 .ven/bin/pip
 delete mode 100755 .ven/bin/pip3
 delete mode 100755 .ven/bin/pip3.10
 delete mode 120000 .ven/bin/python
 delete mode 120000 .ven/bin/python3
 delete mode 120000 .ven/bin/python3.10
 delete mode 120000 .ven/lib64
 delete mode 100644 .ven/pyvenv.cfg
 delete mode 100644 alembic/env.py

diff --git a/.env.sample b/.env.sample
deleted file mode 100644
index 72cfca777..000000000
--- a/.env.sample
+++ /dev/null
@@ -1,41 +0,0 @@
-PYTHON_ENV=dev
-
-DB_TYPE=postgresql
-DB_NAME=test
-DB_USER=username
-DB_PASSWORD=password
-DB_HOST="localhost"
-DB_PORT=5432
-MYSQL_DRIVER=
-DB_URL=postgresql://username:password@localhost:5432/test
-SECRET_KEY = ""
-ALGORITHM = HS256
-ACCESS_TOKEN_EXPIRE_MINUTES = 3000
-JWT_REFRESH_EXPIRY=7
-APP_URL=
-
-GOOGLE_CLIENT_ID=""
-GOOGLE_CLIENT_SECRET=""
-
-FRONTEND_URL='http://127.0.0.1:3000/login-success'
-
-TESTING=''
-
-MAIL_USERNAME=""
-MAIL_PASSWORD=""
-MAIL_FROM="dummy@gmail.com"
-MAIL_PORT=465
-MAIL_SERVER="smtp.gmail.com"
-
-TWILIO_ACCOUNT_SID="MOCK_ACCOUNT_SID"
-TWILIO_AUTH_TOKEN="MOCK_AUTH_TOKEN"
-TWILIO_PHONE_NUMBER="TWILIO_PHONE_NUMBER"
-
-FLUTTERWAVE_SECRET=""
-PAYSTACK_SECRET=""
-
-STRIPE_SECRET_KEY=""
-STRIPE_WEBHOOK_SECRET=""
-
-MAILJET_API_KEY='MAIL JET API KEY'
-MAILJET_API_SECRET='SECRET KEY'
diff --git a/.gitignore b/.gitignore
index c0782126f..91f4f2a25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -173,3 +173,11 @@ cython_debug/
 #.idea/
 
 alembic/versions
+
+# Ignore environment files
+.env
+.env.*
+.ven
+.venv
+env
+alembic/env.py
diff --git a/.ven/bin/Activate.ps1 b/.ven/bin/Activate.ps1
deleted file mode 100644
index b49d77ba4..000000000
--- a/.ven/bin/Activate.ps1
+++ /dev/null
@@ -1,247 +0,0 @@
-<#
-.Synopsis
-Activate a Python virtual environment for the current PowerShell session.
-
-.Description
-Pushes the python executable for a virtual environment to the front of the
-$Env:PATH environment variable and sets the prompt to signify that you are
-in a Python virtual environment. Makes use of the command line switches as
-well as the `pyvenv.cfg` file values present in the virtual environment.
-
-.Parameter VenvDir
-Path to the directory that contains the virtual environment to activate. The
-default value for this is the parent of the directory that the Activate.ps1
-script is located within.
-
-.Parameter Prompt
-The prompt prefix to display when this virtual environment is activated. By
-default, this prompt is the name of the virtual environment folder (VenvDir)
-surrounded by parentheses and followed by a single space (ie. '(.venv) ').
-
-.Example
-Activate.ps1
-Activates the Python virtual environment that contains the Activate.ps1 script.
-
-.Example
-Activate.ps1 -Verbose
-Activates the Python virtual environment that contains the Activate.ps1 script,
-and shows extra information about the activation as it executes.
-
-.Example
-Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
-Activates the Python virtual environment located in the specified location.
-
-.Example
-Activate.ps1 -Prompt "MyPython"
-Activates the Python virtual environment that contains the Activate.ps1 script,
-and prefixes the current prompt with the specified string (surrounded in
-parentheses) while the virtual environment is active.
-
-.Notes
-On Windows, it may be required to enable this Activate.ps1 script by setting the
-execution policy for the user. You can do this by issuing the following PowerShell
-command:
-
-PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
-
-For more information on Execution Policies: 
-https://go.microsoft.com/fwlink/?LinkID=135170
-
-#>
-Param(
-    [Parameter(Mandatory = $false)]
-    [String]
-    $VenvDir,
-    [Parameter(Mandatory = $false)]
-    [String]
-    $Prompt
-)
-
-<# Function declarations --------------------------------------------------- #>
-
-<#
-.Synopsis
-Remove all shell session elements added by the Activate script, including the
-addition of the virtual environment's Python executable from the beginning of
-the PATH variable.
-
-.Parameter NonDestructive
-If present, do not remove this function from the global namespace for the
-session.
-
-#>
-function global:deactivate ([switch]$NonDestructive) {
-    # Revert to original values
-
-    # The prior prompt:
-    if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
-        Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
-        Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
-    }
-
-    # The prior PYTHONHOME:
-    if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
-        Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
-        Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
-    }
-
-    # The prior PATH:
-    if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
-        Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
-        Remove-Item -Path Env:_OLD_VIRTUAL_PATH
-    }
-
-    # Just remove the VIRTUAL_ENV altogether:
-    if (Test-Path -Path Env:VIRTUAL_ENV) {
-        Remove-Item -Path env:VIRTUAL_ENV
-    }
-
-    # Just remove VIRTUAL_ENV_PROMPT altogether.
-    if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
-        Remove-Item -Path env:VIRTUAL_ENV_PROMPT
-    }
-
-    # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
-    if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
-        Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
-    }
-
-    # Leave deactivate function in the global namespace if requested:
-    if (-not $NonDestructive) {
-        Remove-Item -Path function:deactivate
-    }
-}
-
-<#
-.Description
-Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
-given folder, and returns them in a map.
-
-For each line in the pyvenv.cfg file, if that line can be parsed into exactly
-two strings separated by `=` (with any amount of whitespace surrounding the =)
-then it is considered a `key = value` line. The left hand string is the key,
-the right hand is the value.
-
-If the value starts with a `'` or a `"` then the first and last character is
-stripped from the value before being captured.
-
-.Parameter ConfigDir
-Path to the directory that contains the `pyvenv.cfg` file.
-#>
-function Get-PyVenvConfig(
-    [String]
-    $ConfigDir
-) {
-    Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
-
-    # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
-    $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
-
-    # An empty map will be returned if no config file is found.
-    $pyvenvConfig = @{ }
-
-    if ($pyvenvConfigPath) {
-
-        Write-Verbose "File exists, parse `key = value` lines"
-        $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
-
-        $pyvenvConfigContent | ForEach-Object {
-            $keyval = $PSItem -split "\s*=\s*", 2
-            if ($keyval[0] -and $keyval[1]) {
-                $val = $keyval[1]
-
-                # Remove extraneous quotations around a string value.
-                if ("'""".Contains($val.Substring(0, 1))) {
-                    $val = $val.Substring(1, $val.Length - 2)
-                }
-
-                $pyvenvConfig[$keyval[0]] = $val
-                Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
-            }
-        }
-    }
-    return $pyvenvConfig
-}
-
-
-<# Begin Activate script --------------------------------------------------- #>
-
-# Determine the containing directory of this script
-$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
-$VenvExecDir = Get-Item -Path $VenvExecPath
-
-Write-Verbose "Activation script is located in path: '$VenvExecPath'"
-Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
-Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
-
-# Set values required in priority: CmdLine, ConfigFile, Default
-# First, get the location of the virtual environment, it might not be
-# VenvExecDir if specified on the command line.
-if ($VenvDir) {
-    Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
-}
-else {
-    Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
-    $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
-    Write-Verbose "VenvDir=$VenvDir"
-}
-
-# Next, read the `pyvenv.cfg` file to determine any required value such
-# as `prompt`.
-$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
-
-# Next, set the prompt from the command line, or the config file, or
-# just use the name of the virtual environment folder.
-if ($Prompt) {
-    Write-Verbose "Prompt specified as argument, using '$Prompt'"
-}
-else {
-    Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
-    if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
-        Write-Verbose "  Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
-        $Prompt = $pyvenvCfg['prompt'];
-    }
-    else {
-        Write-Verbose "  Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
-        Write-Verbose "  Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
-        $Prompt = Split-Path -Path $venvDir -Leaf
-    }
-}
-
-Write-Verbose "Prompt = '$Prompt'"
-Write-Verbose "VenvDir='$VenvDir'"
-
-# Deactivate any currently active virtual environment, but leave the
-# deactivate function in place.
-deactivate -nondestructive
-
-# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
-# that there is an activated venv.
-$env:VIRTUAL_ENV = $VenvDir
-
-if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
-
-    Write-Verbose "Setting prompt to '$Prompt'"
-
-    # Set the prompt to include the env name
-    # Make sure _OLD_VIRTUAL_PROMPT is global
-    function global:_OLD_VIRTUAL_PROMPT { "" }
-    Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
-    New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
-
-    function global:prompt {
-        Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
-        _OLD_VIRTUAL_PROMPT
-    }
-    $env:VIRTUAL_ENV_PROMPT = $Prompt
-}
-
-# Clear PYTHONHOME
-if (Test-Path -Path Env:PYTHONHOME) {
-    Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
-    Remove-Item -Path Env:PYTHONHOME
-}
-
-# Add the venv to the PATH
-Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
-$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
diff --git a/.ven/bin/activate b/.ven/bin/activate
deleted file mode 100644
index 716f363fb..000000000
--- a/.ven/bin/activate
+++ /dev/null
@@ -1,69 +0,0 @@
-# This file must be used with "source bin/activate" *from bash*
-# you cannot run it directly
-
-deactivate () {
-    # reset old environment variables
-    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
-        PATH="${_OLD_VIRTUAL_PATH:-}"
-        export PATH
-        unset _OLD_VIRTUAL_PATH
-    fi
-    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
-        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
-        export PYTHONHOME
-        unset _OLD_VIRTUAL_PYTHONHOME
-    fi
-
-    # This should detect bash and zsh, which have a hash command that must
-    # be called to get it to forget past commands.  Without forgetting
-    # past commands the $PATH changes we made may not be respected
-    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
-        hash -r 2> /dev/null
-    fi
-
-    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
-        PS1="${_OLD_VIRTUAL_PS1:-}"
-        export PS1
-        unset _OLD_VIRTUAL_PS1
-    fi
-
-    unset VIRTUAL_ENV
-    unset VIRTUAL_ENV_PROMPT
-    if [ ! "${1:-}" = "nondestructive" ] ; then
-    # Self destruct!
-        unset -f deactivate
-    fi
-}
-
-# unset irrelevant variables
-deactivate nondestructive
-
-VIRTUAL_ENV=/root/hng_boilerplate_python_fastapi_web/.ven
-export VIRTUAL_ENV
-
-_OLD_VIRTUAL_PATH="$PATH"
-PATH="$VIRTUAL_ENV/"bin":$PATH"
-export PATH
-
-# unset PYTHONHOME if set
-# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
-# could use `if (set -u; : $PYTHONHOME) ;` in bash
-if [ -n "${PYTHONHOME:-}" ] ; then
-    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
-    unset PYTHONHOME
-fi
-
-if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
-    _OLD_VIRTUAL_PS1="${PS1:-}"
-    PS1='(.ven) '"${PS1:-}"
-    export PS1
-    VIRTUAL_ENV_PROMPT='(.ven) '
-    export VIRTUAL_ENV_PROMPT
-fi
-
-# This should detect bash and zsh, which have a hash command that must
-# be called to get it to forget past commands.  Without forgetting
-# past commands the $PATH changes we made may not be respected
-if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
-    hash -r 2> /dev/null
-fi
diff --git a/.ven/bin/activate.csh b/.ven/bin/activate.csh
deleted file mode 100644
index eda6dc7ea..000000000
--- a/.ven/bin/activate.csh
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file must be used with "source bin/activate.csh" *from csh*.
-# You cannot run it directly.
-# Created by Davide Di Blasi <davidedb@gmail.com>.
-# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
-
-alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
-
-# Unset irrelevant variables.
-deactivate nondestructive
-
-setenv VIRTUAL_ENV /root/hng_boilerplate_python_fastapi_web/.ven
-
-set _OLD_VIRTUAL_PATH="$PATH"
-setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
-
-
-set _OLD_VIRTUAL_PROMPT="$prompt"
-
-if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
-    set prompt = '(.ven) '"$prompt"
-    setenv VIRTUAL_ENV_PROMPT '(.ven) '
-endif
-
-alias pydoc python -m pydoc
-
-rehash
diff --git a/.ven/bin/activate.fish b/.ven/bin/activate.fish
deleted file mode 100644
index e32a30e00..000000000
--- a/.ven/bin/activate.fish
+++ /dev/null
@@ -1,69 +0,0 @@
-# This file must be used with "source <venv>/bin/activate.fish" *from fish*
-# (https://fishshell.com/); you cannot run it directly.
-
-function deactivate  -d "Exit virtual environment and return to normal shell environment"
-    # reset old environment variables
-    if test -n "$_OLD_VIRTUAL_PATH"
-        set -gx PATH $_OLD_VIRTUAL_PATH
-        set -e _OLD_VIRTUAL_PATH
-    end
-    if test -n "$_OLD_VIRTUAL_PYTHONHOME"
-        set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
-        set -e _OLD_VIRTUAL_PYTHONHOME
-    end
-
-    if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
-        set -e _OLD_FISH_PROMPT_OVERRIDE
-        # prevents error when using nested fish instances (Issue #93858)
-        if functions -q _old_fish_prompt
-            functions -e fish_prompt
-            functions -c _old_fish_prompt fish_prompt
-            functions -e _old_fish_prompt
-        end
-    end
-
-    set -e VIRTUAL_ENV
-    set -e VIRTUAL_ENV_PROMPT
-    if test "$argv[1]" != "nondestructive"
-        # Self-destruct!
-        functions -e deactivate
-    end
-end
-
-# Unset irrelevant variables.
-deactivate nondestructive
-
-set -gx VIRTUAL_ENV /root/hng_boilerplate_python_fastapi_web/.ven
-
-set -gx _OLD_VIRTUAL_PATH $PATH
-set -gx PATH "$VIRTUAL_ENV/"bin $PATH
-
-# Unset PYTHONHOME if set.
-if set -q PYTHONHOME
-    set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
-    set -e PYTHONHOME
-end
-
-if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
-    # fish uses a function instead of an env var to generate the prompt.
-
-    # Save the current fish_prompt function as the function _old_fish_prompt.
-    functions -c fish_prompt _old_fish_prompt
-
-    # With the original prompt function renamed, we can override with our own.
-    function fish_prompt
-        # Save the return status of the last command.
-        set -l old_status $status
-
-        # Output the venv prompt; color taken from the blue of the Python logo.
-        printf "%s%s%s" (set_color 4B8BBE) '(.ven) ' (set_color normal)
-
-        # Restore the return status of the previous command.
-        echo "exit $old_status" | .
-        # Output the original/"old" prompt.
-        _old_fish_prompt
-    end
-
-    set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
-    set -gx VIRTUAL_ENV_PROMPT '(.ven) '
-end
diff --git a/.ven/bin/pip b/.ven/bin/pip
deleted file mode 100755
index 4c7fbaca3..000000000
--- a/.ven/bin/pip
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/root/hng_boilerplate_python_fastapi_web/.ven/bin/python3
-# -*- coding: utf-8 -*-
-import re
-import sys
-from pip._internal.cli.main import main
-if __name__ == '__main__':
-    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
-    sys.exit(main())
diff --git a/.ven/bin/pip3 b/.ven/bin/pip3
deleted file mode 100755
index 4c7fbaca3..000000000
--- a/.ven/bin/pip3
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/root/hng_boilerplate_python_fastapi_web/.ven/bin/python3
-# -*- coding: utf-8 -*-
-import re
-import sys
-from pip._internal.cli.main import main
-if __name__ == '__main__':
-    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
-    sys.exit(main())
diff --git a/.ven/bin/pip3.10 b/.ven/bin/pip3.10
deleted file mode 100755
index 4c7fbaca3..000000000
--- a/.ven/bin/pip3.10
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/root/hng_boilerplate_python_fastapi_web/.ven/bin/python3
-# -*- coding: utf-8 -*-
-import re
-import sys
-from pip._internal.cli.main import main
-if __name__ == '__main__':
-    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
-    sys.exit(main())
diff --git a/.ven/bin/python b/.ven/bin/python
deleted file mode 120000
index b8a0adbbb..000000000
--- a/.ven/bin/python
+++ /dev/null
@@ -1 +0,0 @@
-python3
\ No newline at end of file
diff --git a/.ven/bin/python3 b/.ven/bin/python3
deleted file mode 120000
index ae65fdaa1..000000000
--- a/.ven/bin/python3
+++ /dev/null
@@ -1 +0,0 @@
-/usr/bin/python3
\ No newline at end of file
diff --git a/.ven/bin/python3.10 b/.ven/bin/python3.10
deleted file mode 120000
index b8a0adbbb..000000000
--- a/.ven/bin/python3.10
+++ /dev/null
@@ -1 +0,0 @@
-python3
\ No newline at end of file
diff --git a/.ven/lib64 b/.ven/lib64
deleted file mode 120000
index 7951405f8..000000000
--- a/.ven/lib64
+++ /dev/null
@@ -1 +0,0 @@
-lib
\ No newline at end of file
diff --git a/.ven/pyvenv.cfg b/.ven/pyvenv.cfg
deleted file mode 100644
index 0537ffc00..000000000
--- a/.ven/pyvenv.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-home = /usr/bin
-include-system-site-packages = false
-version = 3.10.12
diff --git a/alembic/env.py b/alembic/env.py
deleted file mode 100644
index ba47c0d95..000000000
--- a/alembic/env.py
+++ /dev/null
@@ -1,88 +0,0 @@
-from logging.config import fileConfig
-from sqlalchemy import engine_from_config
-from sqlalchemy import pool
-from alembic import context
-from decouple import config as decouple_config
-from api.v1.models import *
-from api.v1.models.permissions.permissions import Permission
-from api.v1.models.permissions.role_permissions import role_permissions
-from api.v1.models.permissions.user_org_role import user_organisation_roles
-from api.v1.models.permissions.role import Role
-from api.v1.models.associations import Base
-
-
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-if config.config_file_name is not None:
-    fileConfig(config.config_file_name)
-
-database_url = decouple_config('DB_URL')
-
-# Set the SQLAlchemy URL dynamically
-config.set_main_option('sqlalchemy.url', database_url)
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-target_metadata = Base.metadata
-
-# other values from the config, defined by the needs of env.py,
-# can be acquired:
-# my_important_option = config.get_main_option("my_important_option")
-# ... etc.
-
-
-def run_migrations_offline() -> None:
-    """Run migrations in 'offline' mode.
-
-    This configures the context with just a URL
-    and not an Engine, though an Engine is acceptable
-    here as well.  By skipping the Engine creation
-    we don't even need a DBAPI to be available.
-
-    Calls to context.execute() here emit the given string to the
-    script output.
-
-    """
-    url = config.get_main_option("sqlalchemy.url")
-    context.configure(
-        url=url,
-        target_metadata=target_metadata,
-        literal_binds=True,
-        dialect_opts={"paramstyle": "named"},
-    )
-
-    with context.begin_transaction():
-        context.run_migrations()
-
-
-def run_migrations_online() -> None:
-    """Run migrations in 'online' mode.
-
-    In this scenario we need to create an Engine
-    and associate a connection with the context.
-
-    """
-    connectable = engine_from_config(
-        config.get_section(config.config_ini_section, {}),
-        prefix="sqlalchemy.",
-        poolclass=pool.NullPool,
-    )
-
-    with connectable.connect() as connection:
-        context.configure(
-            connection=connection, target_metadata=target_metadata,
-        )
-
-        with context.begin_transaction():
-            context.run_migrations()
-
-
-if context.is_offline_mode():
-    run_migrations_offline()
-else:
-    run_migrations_online()
\ No newline at end of file

From 81df0ecee3606f6692dd53eaf018b53078bdc5ed Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 08:32:38 +0100
Subject: [PATCH 05/11] Restored environment files

---
 alembic/env.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 alembic/env.py

diff --git a/alembic/env.py b/alembic/env.py
new file mode 100644
index 000000000..ba47c0d95
--- /dev/null
+++ b/alembic/env.py
@@ -0,0 +1,88 @@
+from logging.config import fileConfig
+from sqlalchemy import engine_from_config
+from sqlalchemy import pool
+from alembic import context
+from decouple import config as decouple_config
+from api.v1.models import *
+from api.v1.models.permissions.permissions import Permission
+from api.v1.models.permissions.role_permissions import role_permissions
+from api.v1.models.permissions.user_org_role import user_organisation_roles
+from api.v1.models.permissions.role import Role
+from api.v1.models.associations import Base
+
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+if config.config_file_name is not None:
+    fileConfig(config.config_file_name)
+
+database_url = decouple_config('DB_URL')
+
+# Set the SQLAlchemy URL dynamically
+config.set_main_option('sqlalchemy.url', database_url)
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = Base.metadata
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline() -> None:
+    """Run migrations in 'offline' mode.
+
+    This configures the context with just a URL
+    and not an Engine, though an Engine is acceptable
+    here as well.  By skipping the Engine creation
+    we don't even need a DBAPI to be available.
+
+    Calls to context.execute() here emit the given string to the
+    script output.
+
+    """
+    url = config.get_main_option("sqlalchemy.url")
+    context.configure(
+        url=url,
+        target_metadata=target_metadata,
+        literal_binds=True,
+        dialect_opts={"paramstyle": "named"},
+    )
+
+    with context.begin_transaction():
+        context.run_migrations()
+
+
+def run_migrations_online() -> None:
+    """Run migrations in 'online' mode.
+
+    In this scenario we need to create an Engine
+    and associate a connection with the context.
+
+    """
+    connectable = engine_from_config(
+        config.get_section(config.config_ini_section, {}),
+        prefix="sqlalchemy.",
+        poolclass=pool.NullPool,
+    )
+
+    with connectable.connect() as connection:
+        context.configure(
+            connection=connection, target_metadata=target_metadata,
+        )
+
+        with context.begin_transaction():
+            context.run_migrations()
+
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    run_migrations_online()
\ No newline at end of file

From 092804fe1628904a86b984cb262c4a818e9560a0 Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 10:24:52 +0100
Subject: [PATCH 06/11] Updated ignored files and pushed changes

---
 .gitignore | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/.gitignore b/.gitignore
index 91f4f2a25..74571a98f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -181,3 +181,27 @@ alembic/versions
 .venv
 env
 alembic/env.py
+
+# Ignore environment and virtual environment files
+.env
+.env.*
+.ven
+.venv
+env
+alembic/env.py
+
+# Ignore environment and virtual environment files
+.env
+.env.*
+.ven
+.venv
+env
+alembic/env.py
+
+# Ignore environment and virtual environment files
+.env
+.env.*
+.ven
+.venv
+env
+alembic/env.py

From 9f8ec5689b15d45e75ebc8ede449d4e9523d1947 Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 13:20:41 +0100
Subject: [PATCH 07/11] Revert incorrect change to Alembic migration command in
 README

---
 .gitignore | 24 ------------------------
 README.md  |  2 +-
 2 files changed, 1 insertion(+), 25 deletions(-)

diff --git a/.gitignore b/.gitignore
index 74571a98f..5da21cb23 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,7 +129,6 @@ celerybeat.pid
 # Environments
 .env*
 !.env.sample
-.venv
 .blog_env/
 
 env/
@@ -174,14 +173,6 @@ cython_debug/
 
 alembic/versions
 
-# Ignore environment files
-.env
-.env.*
-.ven
-.venv
-env
-alembic/env.py
-
 # Ignore environment and virtual environment files
 .env
 .env.*
@@ -190,18 +181,3 @@ alembic/env.py
 env
 alembic/env.py
 
-# Ignore environment and virtual environment files
-.env
-.env.*
-.ven
-.venv
-env
-alembic/env.py
-
-# Ignore environment and virtual environment files
-.env
-.env.*
-.ven
-.venv
-env
-alembic/env.py
diff --git a/README.md b/README.md
index d667e8cae..438708894 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ GRANT ALL PRIVILEGES ON DATABASE hng_fast_api TO user;
 
 **Starting the database**
 after cloning the database, dont run 
-`embic revision --autogenerate -m al'initial migration'`
+`alembic revision --autogenerate -m al'initial migration'`
 but run
 `alembic upgrade head`
 

From e82d3e66ba3293b8107fea1bc040dc1e27438057 Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 13:33:47 +0100
Subject: [PATCH 08/11] Restore .env.sample

---
 .env.sample | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 .env.sample

diff --git a/.env.sample b/.env.sample
new file mode 100644
index 000000000..72cfca777
--- /dev/null
+++ b/.env.sample
@@ -0,0 +1,41 @@
+PYTHON_ENV=dev
+
+DB_TYPE=postgresql
+DB_NAME=test
+DB_USER=username
+DB_PASSWORD=password
+DB_HOST="localhost"
+DB_PORT=5432
+MYSQL_DRIVER=
+DB_URL=postgresql://username:password@localhost:5432/test
+SECRET_KEY = ""
+ALGORITHM = HS256
+ACCESS_TOKEN_EXPIRE_MINUTES = 3000
+JWT_REFRESH_EXPIRY=7
+APP_URL=
+
+GOOGLE_CLIENT_ID=""
+GOOGLE_CLIENT_SECRET=""
+
+FRONTEND_URL='http://127.0.0.1:3000/login-success'
+
+TESTING=''
+
+MAIL_USERNAME=""
+MAIL_PASSWORD=""
+MAIL_FROM="dummy@gmail.com"
+MAIL_PORT=465
+MAIL_SERVER="smtp.gmail.com"
+
+TWILIO_ACCOUNT_SID="MOCK_ACCOUNT_SID"
+TWILIO_AUTH_TOKEN="MOCK_AUTH_TOKEN"
+TWILIO_PHONE_NUMBER="TWILIO_PHONE_NUMBER"
+
+FLUTTERWAVE_SECRET=""
+PAYSTACK_SECRET=""
+
+STRIPE_SECRET_KEY=""
+STRIPE_WEBHOOK_SECRET=""
+
+MAILJET_API_KEY='MAIL JET API KEY'
+MAILJET_API_SECRET='SECRET KEY'

From 2d125b6e664f17dc9f2fb80a53dff0529b9ddbbe Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 14:43:03 +0100
Subject: [PATCH 09/11] feat: get and fetch activty log added

---
 api/v1/routes/activity_logs.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/api/v1/routes/activity_logs.py b/api/v1/routes/activity_logs.py
index 71bd08b71..71b9efa73 100644
--- a/api/v1/routes/activity_logs.py
+++ b/api/v1/routes/activity_logs.py
@@ -51,6 +51,8 @@ async def get_all_activity_logs(
 @activity_logs.get("/{user_id}", status_code=status.HTTP_200_OK)
 async def fetch_all_users_activity_log(
     user_id: str,
+    page: int = Query(1, ge=1),
+    limit: int = Query(10, le=100),
     db: Session = Depends(get_db),
     current_user: User = Depends(user_service.get_current_super_admin)
 ):

From dff86902aed523c1c2fd69e820729f6ed4807f8f Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Sun, 2 Mar 2025 20:01:26 +0100
Subject: [PATCH 10/11] pagination endpoint tested

---
 tests/v1/activity_logs/test_get_all_logs.py   |  14 ++
 .../test_pagination_activity_log.py           | 126 ++++++++++++++++++
 2 files changed, 140 insertions(+)
 create mode 100644 tests/v1/activity_logs/test_pagination_activity_log.py

diff --git a/tests/v1/activity_logs/test_get_all_logs.py b/tests/v1/activity_logs/test_get_all_logs.py
index 48593f50c..a17a5ec4f 100644
--- a/tests/v1/activity_logs/test_get_all_logs.py
+++ b/tests/v1/activity_logs/test_get_all_logs.py
@@ -104,3 +104,17 @@ def test_get_all_activity_logs_non_super_admin(mock_user_service, mock_db_sessio
                           'Authorization': f'Bearer {access_token}'})
 
     assert response.status_code == status.HTTP_403_FORBIDDEN
+
+
+@pytest.mark.usefixture("mock_db_session", "mock_user_service")
+def test_fetch_all_pagination(test_client):
+    response = test_client.get("/activity-logs?page=1&limit=5")
+    assert response.status_code == 200
+    data = response.json()
+
+    assert len(data["data"]) == 5  # Ensure it returns exactly 5 logs
+
+    # Verify sorting by created_at in descending order
+    timestamps = [log["created_at"] for log in data["data"]]
+    assert timestamps == sorted(timestamps, reverse=True)
+    
\ No newline at end of file
diff --git a/tests/v1/activity_logs/test_pagination_activity_log.py b/tests/v1/activity_logs/test_pagination_activity_log.py
new file mode 100644
index 000000000..5a41f847b
--- /dev/null
+++ b/tests/v1/activity_logs/test_pagination_activity_log.py
@@ -0,0 +1,126 @@
+import pytest
+from fastapi.testclient import TestClient
+from unittest.mock import patch, MagicMock
+from main import app  # Assuming your main app file is main.py
+from api.v1.models.user import User
+from api.v1.services.user import user_service
+from api.v1.models.activity_logs import ActivityLog # Ensure you import your ActivityLog MODEL, not schema
+from uuid_extensions import uuid7
+from api.db.database import get_db
+from fastapi import status
+from datetime import datetime, timezone, timedelta
+from typing import List
+
+
+@pytest.mark.usefixtures("mock_db_session", "mock_user_service")
+def test_get_all_activity_logs_default_pagination(mock_user_service, mock_db_session):
+    """Test default pagination (no page/limit params)."""
+    mock_user = create_mock_user(mock_user_service, mock_db_session)
+    access_token = user_service.create_access_token(user_id=str(uuid7()))
+
+    # Mock 12 activity logs to test default limit of 10
+    mock_logs = [
+        create_mock_activity_log(str(uuid7()), str(uuid7()), f"action_{i}", datetime.now(timezone.utc))
+        for i in range(12)
+    ]
+    mock_db_session.query.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = mock_logs[:10] # Mock first 10 for page 1
+
+    response = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'})
+
+    assert response.status_code == status.HTTP_200_OK
+    response_data = response.json()
+    assert isinstance(response_data, list)
+    assert len(response_data) == 10  # Verify default limit is 10
+
+
+@pytest.mark.usefixtures("mock_db_session", "mock_user_service")
+def test_get_all_activity_logs_limit_5(mock_user_service, mock_db_session):
+    """Test setting limit to 5."""
+    mock_user = create_mock_user(mock_user_service, mock_db_session)
+    access_token = user_service.create_access_token(user_id=str(uuid7()))
+
+    mock_logs = [
+        create_mock_activity_log(str(uuid7()), str(uuid7()), f"action_{i}", datetime.now(timezone.utc))
+        for i in range(7) # Mock 7 logs, limit should return max 5
+    ]
+    mock_db_session.query.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = mock_logs[:5] # Mock first 5 for limit 5
+
+
+    response = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'}, params={'limit': 5})
+
+    assert response.status_code == status.HTTP_200_OK
+    response_data = response.json()
+    assert isinstance(response_data, list)
+    assert len(response_data) == 5 # Verify limit is respected
+
+
+@pytest.mark.usefixtures("mock_db_session", "mock_user_service")
+def test_get_all_activity_logs_page_2_limit_5(mock_user_service, mock_db_session):
+    """Test getting page 2 with limit 5."""
+    mock_user = create_mock_user(mock_user_service, mock_db_session)
+    access_token = user_service.create_access_token(user_id=str(uuid7()))
+
+    mock_logs = [
+        create_mock_activity_log(str(uuid7()), str(uuid7()), f"action_{i}", datetime.now(timezone.utc))
+        for i in range(12) # Mock 12 logs
+    ]
+    mock_db_session.query.return_value.order_by.return_value.offset.return_value.limit.return_value.all.side_effect = [ # Use side_effect to return different data for each page request
+        mock_logs[:5], # Page 1 (items 0-4)
+        mock_logs[5:10], # Page 2 (items 5-9)
+        [] # Page 3 and beyond (empty list)
+    ]
+
+    response_page1 = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'}, params={'page': 1, 'limit': 5})
+    response_page2 = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'}, params={'page': 2, 'limit': 5})
+    response_page3 = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'}, params={'page': 3, 'limit': 5})
+
+
+    assert response_page1.status_code == status.HTTP_200_OK
+    assert len(response_page1.json()) == 5
+    assert response_page2.status_code == status.HTTP_200_OK
+    assert len(response_page2.json()) == 5
+    assert response_page3.status_code == status.HTTP_200_OK
+    assert len(response_page3.json()) == 0 # Page 3 should be empty
+
+
+# Add more pagination test cases here, like testing page beyond last page, etc.
+
+
+@pytest.mark.usefixtures("mock_db_session", "mock_user_service")
+def test_get_all_activity_logs_empty(mock_user_service, mock_db_session):
+    """Test for fetching all activity logs with no data."""
+    mock_user = create_mock_user(mock_user_service, mock_db_session)
+    access_token = user_service.create_access_token(user_id=str(uuid7()))
+    mock_db_session.query.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [] # Mock empty list for no data
+    response = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'})
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.json() == [] # Verify empty list when no data
+
+
+@pytest.mark.usefixtures("mock_db_session", "mock_user_service")
+def test_get_all_activity_logs_with_data(mock_user_service, mock_db_session):
+    """Test for fetching all activity logs with data."""
+    mock_user = create_mock_user(mock_user_service, mock_db_session)
+    access_token = user_service.create_access_token(user_id=str(uuid7()))
+
+    mock_logs = [create_mock_activity_log(str(uuid7()), str(uuid7()), "profile Update", datetime.now(timezone.utc))]
+    mock_db_session.query.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = mock_logs # Mock a single log
+
+    response = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'})
+
+    assert response.status_code == status.HTTP_200_OK
+    response_data = response.json()
+    assert isinstance(response_data, list)
+    assert len(response_data) == 1 # Verify one log returned
+
+
+@pytest.mark.usefixtures("mock_db_session", "mock_user_service")
+def test_get_all_activity_logs_non_super_admin(mock_user_service, mock_db_session):
+    """Test for fetching all activity logs as a non-super admin user."""
+    mock_user = create_mock_user(
+        mock_user_service, mock_db_session, is_superadmin=False)
+    access_token = user_service.create_access_token(user_id=str(uuid7()))
+    response = client.get(ACTIVITY_LOGS_ENDPOINT, headers={'Authorization': f'Bearer {access_token}'})
+
+    assert response.status_code 

From f2f8f67ff9d6965400dcddc44f14f8f75e85258a Mon Sep 17 00:00:00 2001
From: Dafinci01 <Davehacker382gmail.com>
Date: Tue, 4 Mar 2025 15:10:57 +0100
Subject: [PATCH 11/11] feat: Pagination seervice layer added

---
 api/v1/services/activity_logs.py | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/api/v1/services/activity_logs.py b/api/v1/services/activity_logs.py
index 6789897ab..b8b44c392 100644
--- a/api/v1/services/activity_logs.py
+++ b/api/v1/services/activity_logs.py
@@ -1,39 +1,43 @@
 from sqlalchemy.orm import Session
 from fastapi import HTTPException, status
 from sqlalchemy.exc import SQLAlchemyError
+from sqlalchemy import desc  
 from api.v1.models.activity_logs import ActivityLog
 from typing import Optional, Any
 
-
-
 class ActivityLogService:
     """Activity Log service"""
 
     def create_activity_log(self, db: Session, user_id: str, action: str):
         """Creates a new activity log"""
-
         activity_log = ActivityLog(user_id=user_id, action=action)
         db.add(activity_log)
         db.commit()
         db.refresh(activity_log)
         return activity_log
 
-    def fetch_all(self, db: Session, **query_params: Optional[Any]):
-        """Fetch all products with option tto search using query parameters"""
-
+    def fetch_all(self, db: Session, page: int, limit: int, user_id: Optional[str] = None, **query_params: Optional[Any]):
+        """Fetch all products with option to search using query parameters"""
+        offset = (page - 1) * limit
         query = db.query(ActivityLog)
 
-        # Enable filter by query parameter
+        if user_id:  # Filter by user id 
+            query = query.filter(ActivityLog.user_id == user_id)
+
+        # Enable filter by arbitrary query parameters
         if query_params:
             for column, value in query_params.items():
                 if hasattr(ActivityLog, column) and value:
                     query = query.filter(
                         getattr(ActivityLog, column).ilike(f"%{value}%")
                     )
-        
-        return query.all()
-    
-    def delete_activity_log_by_id(self, db: Session, log_id: str) -> bool:
+
+       
+        query = query.order_by(desc(ActivityLog.timestamp))
+        paginated_logs = query.offset(offset).limit(limit).all()
+        return paginated_logs 
+
+    def delete_activity_log_by_id(self, db: Session, log_id: str) -> dict:
         """Delete an activity log by ID with error handling"""
         log = db.query(ActivityLog).filter(ActivityLog.id == log_id).first()
 
@@ -48,5 +52,4 @@ def delete_activity_log_by_id(self, db: Session, log_id: str) -> bool:
 
         return {"status": "success", "detail": f"Activity log with ID {log_id} deleted successfully"}
 
-
 activity_log_service = ActivityLogService()