Added fastapi-pagination and audit log download

This commit is contained in:
Saurab-Shrestha9639*969**9858//852 2024-04-23 17:48:13 +05:45
parent 97317b82e0
commit 3282d52bf2
23 changed files with 310 additions and 208 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

122
poetry.lock generated
View file

@ -1225,6 +1225,17 @@ files = [
dnspython = ">=2.0.0" dnspython = ">=2.0.0"
idna = ">=2.0.0" idna = ">=2.0.0"
[[package]]
name = "et-xmlfile"
version = "1.1.0"
description = "An implementation of lxml.xmlfile for the standard library"
optional = false
python-versions = ">=3.6"
files = [
{file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"},
{file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"},
]
[[package]] [[package]]
name = "fastapi" name = "fastapi"
version = "0.110.0" version = "0.110.0"
@ -1276,6 +1287,39 @@ all = ["SQLAlchemy (>=1.4.36,<2.1.0)", "mongoengine (>=0.24.1,<0.28.0)"]
mongoengine = ["mongoengine (>=0.24.1,<0.28.0)"] mongoengine = ["mongoengine (>=0.24.1,<0.28.0)"]
sqlalchemy = ["SQLAlchemy (>=1.4.36,<2.1.0)"] sqlalchemy = ["SQLAlchemy (>=1.4.36,<2.1.0)"]
[[package]]
name = "fastapi-pagination"
version = "0.12.23"
description = "FastAPI pagination"
optional = false
python-versions = "<4.0,>=3.8"
files = [
{file = "fastapi_pagination-0.12.23-py3-none-any.whl", hash = "sha256:a20a05b375bb4ed01355373ae6749e9a61b8bd8248c672d3402840f0f81de7f6"},
{file = "fastapi_pagination-0.12.23.tar.gz", hash = "sha256:c7804e1d02fcb678a43be81e974f88994226099f7e74b70b279b8e79bd8689ef"},
]
[package.dependencies]
fastapi = ">=0.93.0"
pydantic = ">=1.9.1"
typing-extensions = ">=4.8.0,<5.0.0"
[package.extras]
all = ["SQLAlchemy (>=1.3.20)", "asyncpg (>=0.24.0)", "beanie (>=1.25.0)", "bunnet (>=1.1.0,<2.0.0)", "databases (>=0.6.0)", "django (<5.0.0)", "mongoengine (>=0.23.1,<0.29.0)", "motor (>=2.5.1,<4.0.0)", "orm (>=0.3.1)", "ormar (>=0.11.2)", "piccolo (>=0.89,<0.122)", "pony (>=0.7.16,<0.8.0)", "scylla-driver (>=3.25.6,<4.0.0)", "sqlakeyset (>=2.0.1680321678,<3.0.0)", "sqlmodel (>=0.0.8,<0.0.15)", "tortoise-orm (>=0.16.18,<0.21.0)"]
asyncpg = ["SQLAlchemy (>=1.3.20)", "asyncpg (>=0.24.0)"]
beanie = ["beanie (>=1.25.0)"]
bunnet = ["bunnet (>=1.1.0,<2.0.0)"]
databases = ["databases (>=0.6.0)"]
django = ["databases (>=0.6.0)", "django (<5.0.0)"]
mongoengine = ["mongoengine (>=0.23.1,<0.29.0)"]
motor = ["motor (>=2.5.1,<4.0.0)"]
orm = ["databases (>=0.6.0)", "orm (>=0.3.1)"]
ormar = ["ormar (>=0.11.2)"]
piccolo = ["piccolo (>=0.89,<0.122)"]
scylla-driver = ["scylla-driver (>=3.25.6,<4.0.0)"]
sqlalchemy = ["SQLAlchemy (>=1.3.20)", "sqlakeyset (>=2.0.1680321678,<3.0.0)"]
sqlmodel = ["sqlakeyset (>=2.0.1680321678,<3.0.0)", "sqlmodel (>=0.0.8,<0.0.15)"]
tortoise = ["tortoise-orm (>=0.16.18,<0.21.0)"]
[[package]] [[package]]
name = "ffmpy" name = "ffmpy"
version = "0.3.2" version = "0.3.2"
@ -3596,6 +3640,20 @@ files = [
[package.dependencies] [package.dependencies]
numpy = {version = ">=1.23.5", markers = "python_version >= \"3.11\""} numpy = {version = ">=1.23.5", markers = "python_version >= \"3.11\""}
[[package]]
name = "openpyxl"
version = "3.1.2"
description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
optional = false
python-versions = ">=3.6"
files = [
{file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"},
{file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"},
]
[package.dependencies]
et-xmlfile = "*"
[[package]] [[package]]
name = "opentelemetry-api" name = "opentelemetry-api"
version = "1.23.0" version = "1.23.0"
@ -3843,44 +3901,44 @@ files = [
[[package]] [[package]]
name = "pandas" name = "pandas"
version = "2.2.1" version = "2.2.2"
description = "Powerful data structures for data analysis, time series, and statistics" description = "Powerful data structures for data analysis, time series, and statistics"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"},
{file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"},
{file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"},
{file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"},
{file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"},
{file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"},
{file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"},
{file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"},
{file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"},
{file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"},
{file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"},
{file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"},
{file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"},
{file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"},
{file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"},
{file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"},
{file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"},
{file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"},
{file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"},
{file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"},
{file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"},
{file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"},
{file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"},
{file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"},
{file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"},
{file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"},
{file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"},
{file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"},
{file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"},
] ]
[package.dependencies] [package.dependencies]
numpy = {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""} numpy = {version = ">=1.23.2", markers = "python_version == \"3.11\""}
python-dateutil = ">=2.8.2" python-dateutil = ">=2.8.2"
pytz = ">=2020.1" pytz = ">=2020.1"
tzdata = ">=2022.7" tzdata = ">=2022.7"
@ -7157,4 +7215,4 @@ vector-stores-qdrant = ["llama-index-vector-stores-qdrant"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.11,<3.12" python-versions = ">=3.11,<3.12"
content-hash = "57d64e26ba5f7100c0f6d61135808b190a9f6b8c95a6407b984b7cbea244d5f5" content-hash = "d7f6a81832b4a41ebd23b4e2041beb53bc26437212204254c87e33d1a4e68b8d"

View file

@ -3,6 +3,8 @@
import uvicorn import uvicorn
from private_gpt.main import app from private_gpt.main import app
from fastapi_pagination import add_pagination
from private_gpt.settings.settings import settings from private_gpt.settings.settings import settings
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from private_gpt.constants import UPLOAD_DIR from private_gpt.constants import UPLOAD_DIR
@ -12,5 +14,5 @@ from private_gpt.constants import UPLOAD_DIR
# https://github.com/tiangolo/fastapi/discussions/7457#discussioncomment-5141108 # https://github.com/tiangolo/fastapi/discussions/7457#discussioncomment-5141108
app.mount("/static", StaticFiles(directory=UPLOAD_DIR), name="static") app.mount("/static", StaticFiles(directory=UPLOAD_DIR), name="static")
add_pagination(app)
uvicorn.run(app, host="0.0.0.0", port=settings().server.port, log_config=None) uvicorn.run(app, host="0.0.0.0", port=settings().server.port, log_config=None)

View file

@ -54,7 +54,7 @@ def create_app(root_injector: Injector) -> FastAPI:
"http://192.168.1.98", "http://192.168.1.98:3000", "http://localhost:3000","https://globaldocquery.gibl.com.np/", "http://127.0.0.1/", "http://localhost/", "http://192.168.1.98", "http://192.168.1.98:3000", "http://localhost:3000","https://globaldocquery.gibl.com.np/", "http://127.0.0.1/", "http://localhost/",
"http://localhost:80", "http://192.168.1.131", 'http://192.168.1.131:3000' "http://localhost:80", "http://192.168.1.131", 'http://192.168.1.131:3000'
, "http://192.168.1.127", 'http://192.168.1.127:3000' , "http://192.168.1.127", 'http://192.168.1.127:3000'
, "http://192.168.1.70", 'http://192.168.1.70:3000' , "http://192.168.1.89", 'http://192.168.1.89:3000'
], ],
allow_methods=["DELETE", "GET", "POST", "PUT", "OPTIONS", "PATCH"], allow_methods=["DELETE", "GET", "POST", "PUT", "OPTIONS", "PATCH"],
allow_headers=["*"], allow_headers=["*"],

View file

@ -193,7 +193,6 @@ async def create_documents(
`Document Department Association` table with the departments ids for the documents. `Document Department Association` table with the departments ids for the documents.
""" """
department_ids = departments.departments_ids department_ids = departments.departments_ids
print("Department ids: ", department_ids)
file_ingested = crud.documents.get_by_filename( file_ingested = crud.documents.get_by_filename(
db, file_name=file_name) db, file_name=file_name)
if file_ingested: if file_ingested:

View file

@ -1,15 +1,31 @@
from typing import Any, List from typing import Any, List
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from fastapi import APIRouter, Depends, HTTPException, Security, Request from fastapi import APIRouter, Depends, Security
from private_gpt.users import crud, models, schemas
from private_gpt.users.api import deps from private_gpt.users.api import deps
from private_gpt.users.constants.role import Role from private_gpt.users.constants.role import Role
from private_gpt.users import crud, models, schemas from private_gpt.users.utils.export import generate_audit_log_report
router = APIRouter(prefix="/audit", tags=["Audit"])
router = APIRouter(prefix="/audit", tags=["Companies"]) def get_fullname(db: Session, id: int) -> str:
user = crud.user.get_by_id(db, id=id)
return user.username if user else ""
def convert_audit_logs(db: Session, logs: List[Any], username: str = None) -> List[schemas.Audit]:
return [
schemas.Audit(
id=dep.id,
model=dep.model,
username=get_fullname(db, dep.user_id),
details=dep.details,
action=dep.action,
timestamp=dep.timestamp,
ip_address=dep.ip_address,
)
for dep in logs
]
@router.get("", response_model=List[schemas.Audit]) @router.get("", response_model=List[schemas.Audit])
def list_auditlog( def list_auditlog(
@ -21,30 +37,8 @@ def list_auditlog(
scopes=[Role.SUPER_ADMIN["name"]], scopes=[Role.SUPER_ADMIN["name"]],
), ),
) -> List[schemas.Audit]: ) -> List[schemas.Audit]:
"""
Retrieve a list of audit logs with pagination support.
"""
def get_fullname(id):
user = crud.user.get_by_id(db, id=id)
if user:
return user.username
return ""
logs = crud.audit.get_multi_desc(db, skip=skip, limit=limit) logs = crud.audit.get_multi_desc(db, skip=skip, limit=limit)
logs = [ return convert_audit_logs(db, logs)
schemas.Audit(
id=dep.id,
model=dep.model,
username=get_fullname(dep.user_id),
details=dep.details,
action=dep.action,
timestamp=dep.timestamp,
ip_address=dep.ip_address,
)
for dep in logs
]
return logs
@router.get("filter/", response_model=List[schemas.Audit]) @router.get("filter/", response_model=List[schemas.Audit])
def filter_auditlog( def filter_auditlog(
@ -55,30 +49,27 @@ def filter_auditlog(
scopes=[Role.SUPER_ADMIN["name"]], scopes=[Role.SUPER_ADMIN["name"]],
), ),
) -> List[schemas.Audit]: ) -> List[schemas.Audit]:
"""
Retrieve a list of audit logs with pagination support.
"""
def get_fullname(id):
user = crud.user.get_by_id(db, id=id)
if user:
return user.username
return ""
logs = crud.audit.filter(db, obj_in=filter_in) logs = crud.audit.filter(db, obj_in=filter_in)
logs = [ return convert_audit_logs(db, logs)
schemas.Audit(
id=dep.id,
model=dep.model,
username=get_fullname(dep.user_id),
details=dep.details,
action=dep.action,
timestamp=dep.timestamp,
ip_address=dep.ip_address,
)
for dep in logs
]
return logs
@router.get("download/")
def download_auditlog(
db: Session = Depends(deps.get_db),
filter_in= Depends(schemas.ExcelFilter),
current_user: models.User = Security(
deps.get_current_user,
scopes=[Role.SUPER_ADMIN["name"]],
),
):
logs = crud.audit.excel_filter(db, obj_in=filter_in)
username = filter_in.username if filter_in.username else None
logs = convert_audit_logs(db, logs, username)
excel_buffer = generate_audit_log_report(logs, username)
return StreamingResponse(
iter([excel_buffer.getvalue()]),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={"Content-Disposition": "attachment;filename=audit_logs.xlsx"},
)
@router.post("", response_model=schemas.Audit) @router.post("", response_model=schemas.Audit)
def get_auditlog( def get_auditlog(
@ -89,23 +80,5 @@ def get_auditlog(
scopes=[Role.SUPER_ADMIN["name"]], scopes=[Role.SUPER_ADMIN["name"]],
), ),
): ):
"""
Retrieve a single audit log.
"""
def get_fullname(id):
user = crud.user.get_by_id(db, id=id)
if user:
return user.username
return ""
logs = crud.audit.get_by_id(db, id=audit.id) logs = crud.audit.get_by_id(db, id=audit.id)
logs = schemas.Audit( return convert_audit_logs(db, [logs])[0]
id=logs.id,
model=logs.model,
username=get_fullname(logs.user_id),
details=logs.details,
action=logs.action,
timestamp=logs.timestamp,
ip_address=logs.ip_address,
)
return logs

View file

@ -15,8 +15,6 @@ router = APIRouter(prefix="/c", tags=["Chat Histories"])
@router.get("", response_model=list[schemas.ChatHistory]) @router.get("", response_model=list[schemas.ChatHistory])
def list_chat_histories( def list_chat_histories(
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
), ),
@ -26,7 +24,7 @@ def list_chat_histories(
""" """
try: try:
chat_histories = crud.chat.get_chat_history( chat_histories = crud.chat.get_chat_history(
db, user_id=current_user.id, skip=skip, limit=limit) db, user_id=current_user.id)
return chat_histories return chat_histories
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())

View file

@ -4,6 +4,7 @@ from sqlalchemy.orm import Session
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi import APIRouter, Depends, HTTPException, status, Security from fastapi import APIRouter, Depends, HTTPException, status, Security
from fastapi_pagination import Page, paginate
from private_gpt.users.api import deps from private_gpt.users.api import deps
from private_gpt.users.constants.role import Role from private_gpt.users.constants.role import Role
@ -13,21 +14,19 @@ from private_gpt.users import crud, models, schemas
router = APIRouter(prefix="/companies", tags=["Companies"]) router = APIRouter(prefix="/companies", tags=["Companies"])
@router.get("", response_model=List[schemas.Company]) @router.get("", response_model=Page[schemas.Company])
def list_companies( def list_companies(
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
scopes=[Role.SUPER_ADMIN["name"]], scopes=[Role.SUPER_ADMIN["name"]],
), ),
) -> List[schemas.Company]: ) -> Page[schemas.Company]:
""" """
Retrieve a list of companies with pagination support. Retrieve a list of companies with pagination support.
""" """
companies = crud.company.get_multi(db, skip=skip, limit=limit) companies = crud.company.get_multi(db)
return companies return paginate(companies)
@router.post("/create", response_model=schemas.Company) @router.post("/create", response_model=schemas.Company)
@ -54,21 +53,19 @@ def create_company(
) )
@router.get("", response_model=List[schemas.Company]) @router.get("", response_model=Page[schemas.Company])
def list_companies( def list_companies(
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
scopes=[Role.SUPER_ADMIN["name"]], scopes=[Role.SUPER_ADMIN["name"]],
), ),
) -> List[schemas.Company]: ) -> Page[schemas.Company]:
""" """
Retrieve a list of companies with pagination support. Retrieve a list of companies with pagination support.
""" """
companies = crud.company.get_multi(db, skip=skip, limit=limit) companies = crud.company.get_multi(db)
return companies return paginate(companies)
@router.get("/{company_id}", response_model=schemas.Company) @router.get("/{company_id}", response_model=schemas.Company)
def read_company( def read_company(

View file

@ -4,6 +4,8 @@ import traceback
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi_pagination import Page, paginate
from fastapi import APIRouter, Depends, HTTPException, status, Security, Request from fastapi import APIRouter, Depends, HTTPException, status, Security, Request
from private_gpt.users.api import deps from private_gpt.users.api import deps
@ -35,25 +37,23 @@ def log_audit_department(
print(traceback.format_exc()) print(traceback.format_exc())
@router.get("", response_model=list[schemas.Department]) @router.get("", response_model=Page[schemas.Department])
def list_departments( def list_departments(
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
), ),
) -> list[schemas.Department]: ) -> Page[schemas.Department]:
""" """
Retrieve a list of departments with pagination support. Retrieve a list of departments with pagination support.
""" """
try: try:
role = current_user.user_role.role.name if current_user.user_role else None role = current_user.user_role.role.name if current_user.user_role else None
if role == "SUPER_ADMIN": if role == "SUPER_ADMIN":
deps = crud.department.get_multi(db, skip=skip, limit=limit) deps = crud.department.get_multi(db)
else: else:
deps = crud.department.get_multi_department( deps = crud.department.get_multi_department(
db, department_id=current_user.department_id, skip=skip, limit=limit) db, department_id=current_user.department_id)
deps = [ deps = [
schemas.Department( schemas.Department(
@ -64,7 +64,7 @@ def list_departments(
) )
for dep in deps for dep in deps
] ]
return deps return paginate(deps)
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
logger.error(f"There was an error listing the file(s).") logger.error(f"There was an error listing the file(s).")

View file

@ -8,6 +8,7 @@ from datetime import datetime
from typing import Any, List from typing import Any, List
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from sqlalchemy import select from sqlalchemy import select
from fastapi_pagination import Page, paginate
from fastapi import APIRouter, Depends, HTTPException, status, Security, Request, File, UploadFile from fastapi import APIRouter, Depends, HTTPException, status, Security, Request, File, UploadFile
from private_gpt.users.api import deps from private_gpt.users.api import deps
@ -17,28 +18,28 @@ from private_gpt.users import crud, models, schemas
from private_gpt.server.ingest.ingest_router import create_documents, ingest from private_gpt.server.ingest.ingest_router import create_documents, ingest
from private_gpt.users.models.document import MakerCheckerActionType, MakerCheckerStatus from private_gpt.users.models.document import MakerCheckerActionType, MakerCheckerStatus
from private_gpt.components.ocr_components.table_ocr_api import process_both_ocr, process_ocr from private_gpt.components.ocr_components.table_ocr_api import process_both_ocr, process_ocr
from fastapi_filter import FilterDepends, with_prefix
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = APIRouter(prefix='/documents', tags=['Documents']) router = APIRouter(prefix='/documents', tags=['Documents'])
CHECKER = False
ENABLE_MAKER = False
def get_username(db, id): def get_username(db, id):
user = crud.user.get_by_id(db=db, id=id) user = crud.user.get_by_id(db=db, id=id)
return user.username return user.username
CHECKER = True
@router.get("", response_model=List[schemas.DocumentView]) @router.get("")
def list_files( def list_files(
request: Request, request: Request,
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"], Role.OPERATOR["name"]], scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"], Role.OPERATOR["name"]],
) )
): )-> Page[schemas.DocumentView]:
""" """
List the documents based on the role. List the documents based on the role.
""" """
@ -47,10 +48,10 @@ def list_files(
role = current_user.user_role.role.name if current_user.user_role else None role = current_user.user_role.role.name if current_user.user_role else None
if (role == "SUPER_ADMIN") or (role == "OPERATOR"): if (role == "SUPER_ADMIN") or (role == "OPERATOR"):
docs = crud.documents.get_multi_documents( docs = crud.documents.get_multi_documents(
db, skip=skip, limit=limit) db)
else: else:
docs = crud.documents.get_documents_by_departments( docs = crud.documents.get_documents_by_departments(
db, department_id=current_user.department_id, skip=skip, limit=limit) db, department_id=current_user.department_id)
documents = [ documents = [
schemas.DocumentView( schemas.DocumentView(
@ -68,7 +69,7 @@ def list_files(
) )
for doc in docs for doc in docs
] ]
return documents return paginate(documents)
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
logger.error(f"There was an error listing the file(s).") logger.error(f"There was an error listing the file(s).")
@ -78,12 +79,10 @@ def list_files(
) )
@router.get("/pending", response_model=List[schemas.DocumentVerify]) @router.get("/pending", response_model=Page[schemas.DocumentVerify])
def list_pending_files( def list_pending_files(
request: Request, request: Request,
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
scopes=[Role.SUPER_ADMIN["name"], Role.OPERATOR["name"]], scopes=[Role.SUPER_ADMIN["name"], Role.OPERATOR["name"]],
@ -98,7 +97,8 @@ def list_pending_files(
try: try:
docs = crud.documents.get_files_to_verify( docs = crud.documents.get_files_to_verify(
db, department_id=current_user.department_id, skip=skip, limit=limit) db
)
documents = [ documents = [
schemas.DocumentVerify( schemas.DocumentVerify(
@ -114,7 +114,7 @@ def list_pending_files(
) )
for doc in docs for doc in docs
] ]
return documents return paginate(documents)
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
logger.error(f"There was an error listing the file(s).") logger.error(f"There was an error listing the file(s).")
@ -123,13 +123,11 @@ def list_pending_files(
detail="Internal Server Error", detail="Internal Server Error",
) )
@router.get('{department_id}', response_model=List[schemas.DocumentList]) @router.get('{department_id}', response_model=Page[schemas.DocumentList])
def list_files_by_department( def list_files_by_department(
request: Request, request: Request,
department_id: int, department_id: int,
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
scopes=[Role.SUPER_ADMIN["name"]], scopes=[Role.SUPER_ADMIN["name"]],
@ -140,8 +138,8 @@ def list_files_by_department(
''' '''
try: try:
docs = crud.documents.get_documents_by_departments( docs = crud.documents.get_documents_by_departments(
db, department_id=department_id, skip=skip, limit=limit) db, department_id=department_id)
return docs return paginate(docs)
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
logger.error(f"There was an error listing the file(s).") logger.error(f"There was an error listing the file(s).")
@ -151,12 +149,10 @@ def list_files_by_department(
) )
@router.get('/files', response_model=List[schemas.DocumentList]) @router.get('/files', response_model=Page[schemas.DocumentList])
def list_files_by_department( def list_files_by_department(
request: Request, request: Request,
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"], Role.OPERATOR["name"]], scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"], Role.OPERATOR["name"]],
@ -168,8 +164,8 @@ def list_files_by_department(
try: try:
department_id = current_user.department_id department_id = current_user.department_id
docs = crud.documents.get_documents_by_departments( docs = crud.documents.get_documents_by_departments(
db, department_id=department_id, skip=skip, limit=limit) db, department_id=department_id)
return docs return paginate(docs)
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
logger.error(f"There was an error listing the file(s).") logger.error(f"There was an error listing the file(s).")
@ -293,6 +289,7 @@ async def upload_documents(
contents = await file.read() contents = await file.read()
async with aiofiles.open(upload_path, 'wb') as f: async with aiofiles.open(upload_path, 'wb') as f:
await f.write(contents) await f.write(contents)
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
@ -301,6 +298,13 @@ async def upload_documents(
document = await create_documents(db, original_filename, current_user, departments, log_audit) document = await create_documents(db, original_filename, current_user, departments, log_audit)
logger.info( logger.info(
f"{original_filename} is uploaded by {current_user.username} in {departments.departments_ids}") f"{original_filename} is uploaded by {current_user.username} in {departments.departments_ids}")
if not ENABLE_MAKER:
checker_in = schemas.DocumentUpdate(
id=document.id,
status=MakerCheckerStatus.APPROVED.value
)
await verify_documents(request=request, checker_in=checker_in, db=db, log_audit=log_audit, current_user=current_user)
return document return document
except HTTPException: except HTTPException:
@ -338,24 +342,25 @@ async def verify_documents(
detail="Document not found!", detail="Document not found!",
) )
if document.verified:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Document already verified!",
)
if CHECKER: if CHECKER:
print("Current user is checker: ", current_user.checker) if document.verified:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Document already verified!",
)
if not current_user.checker: if not current_user.checker:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
detail="You are not the checker!", detail="You are not the checker!",
) )
if document.uploaded_by == current_user.id: if document.uploaded_by == current_user.id:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
detail="Cannot verify by same user!", detail="Cannot verify by same user!",
) )
unchecked_path = Path(f"{UNCHECKED_DIR}/{document.filename}") unchecked_path = Path(f"{UNCHECKED_DIR}/{document.filename}")

View file

@ -4,6 +4,7 @@ from typing import Any, List
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi_pagination import Page, paginate
from fastapi import APIRouter, Body, Depends, HTTPException, Security, status, Path, Request from fastapi import APIRouter, Body, Depends, HTTPException, Security, status, Path, Request
from private_gpt.users.api import deps from private_gpt.users.api import deps
@ -36,10 +37,8 @@ def log_audit_user(
print(traceback.format_exc()) print(traceback.format_exc())
@router.get("", response_model=List[schemas.User]) @router.get("", response_model=Page[schemas.User])
def read_users( def read_users(
skip: int = 0,
limit: int = 100,
db: Session = Depends(deps.get_db), db: Session = Depends(deps.get_db),
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_user,
@ -53,11 +52,11 @@ def read_users(
if role == "ADMIN": if role == "ADMIN":
users = crud.user.get_by_department_id(db=db, department_id=current_user.department_id, skip=skip, limit=limit) users = crud.user.get_by_department_id(db=db, department_id=current_user.department_id, skip=skip, limit=limit)
else: else:
users = crud.user.get_multi(db, skip=skip, limit=limit) users = crud.user.get_multi(db)
return users return paginate(users)
@router.get("/company/{company_id}", response_model=List[schemas.User]) @router.get("/company/{company_id}", response_model=Page[schemas.User])
def read_users_by_company( def read_users_by_company(
company_id: int = Path(..., title="Company ID", company_id: int = Path(..., title="Company ID",
description="Only for company admin"), description="Only for company admin"),
@ -77,7 +76,7 @@ def read_users_by_company(
) )
users = crud.user.get_multi_by_company_id(db, company_id=company.id) users = crud.user.get_multi_by_company_id(db, company_id=company.id)
return users return paginate(users)
@router.post("", response_model=schemas.User) @router.post("", response_model=schemas.User)

View file

@ -37,6 +37,31 @@ class CRUDAudit(CRUDBase[Audit, AuditCreate, AuditUpdate]):
return query.order_by(desc(self.model.timestamp)).offset(obj_in.skip).limit(obj_in.limit).all() return query.order_by(desc(self.model.timestamp)).offset(obj_in.skip).limit(obj_in.limit).all()
def excel_filter(
self, db: Session, *, obj_in : AuditFilter
) -> List[Audit]:
def get_id(username):
user = crud.user.get_by_name(db, name=username)
if user:
return user.id
return None
query = db.query(Audit)
if obj_in.model:
query = query.filter(Audit.model == obj_in.model)
if obj_in.username:
query = query.filter(Audit.user_id == get_id(obj_in.username))
if obj_in.action:
query = query.filter(Audit.action == obj_in.action)
if obj_in.start_date:
query = query.filter(Audit.timestamp >= obj_in.start_date)
if obj_in.end_date:
query = query.filter(Audit.timestamp <= obj_in.end_date)
return query.order_by(desc(self.model.timestamp)).all()
def get_by_id(self, db: Session, *, id: str) -> Optional[Audit]: def get_by_id(self, db: Session, *, id: str) -> Optional[Audit]:
return db.query(self.model).filter(Audit.id == id).first() return db.query(self.model).filter(Audit.id == id).first()

View file

@ -25,9 +25,9 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
self.model = model self.model = model
def get_multi( def get_multi(
self, db: Session, *, skip: int = 0, limit: int = 100 self, db: Session,
) -> List[ModelType]: ) -> List[ModelType]:
return db.query(self.model).offset(skip).limit(limit).all() return db.query(self.model).all()
def get(self, db: Session, id: int) -> Optional[ModelType]: def get(self, db: Session, id: int) -> Optional[ModelType]:
return db.query(self.model).filter(self.model.id == id).first() return db.query(self.model).filter(self.model.id == id).first()

View file

@ -27,14 +27,12 @@ class CRUDChat(CRUDBase[ChatHistory, ChatHistoryCreate, ChatHistoryCreate]):
return chat_history return chat_history
def get_chat_history( def get_chat_history(
self, db: Session, *,user_id:int, skip: int = 0, limit: int = 100 self, db: Session, *,user_id:int
) -> List[ChatHistory]: ) -> List[ChatHistory]:
return ( return (
db.query(self.model) db.query(self.model)
.filter(ChatHistory.user_id == user_id) .filter(ChatHistory.user_id == user_id)
.order_by(desc(getattr(ChatHistory, 'created_at'))) .order_by(desc(getattr(ChatHistory, 'created_at')))
.offset(skip)
.limit(limit)
.all() .all()
) )

View file

@ -13,13 +13,11 @@ class CRUDDepartments(CRUDBase[Department, DepartmentCreate, DepartmentUpdate]):
return db.query(self.model).filter(Department.name == name).first() return db.query(self.model).filter(Department.name == name).first()
def get_multi_department( def get_multi_department(
self, db: Session, *, department_id: int, skip: int = 0, limit: int = 100 self, db: Session, *, department_id: int
) -> List[Department]: ) -> List[Department]:
return ( return (
db.query(self.model) db.query(self.model)
.filter(Department.id == department_id) .filter(Department.id == department_id)
.offset(skip)
.limit(limit)
.all() .all()
) )
department = CRUDDepartments(Department) department = CRUDDepartments(Department)

View file

@ -7,6 +7,7 @@ from private_gpt.users.models.department import Department
from private_gpt.users.models.document_department import document_department_association from private_gpt.users.models.document_department import document_department_association
from private_gpt.users.crud.base import CRUDBase from private_gpt.users.crud.base import CRUDBase
from typing import Optional, List from typing import Optional, List
from fastapi_pagination import Page, paginate
class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]): class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
@ -17,18 +18,16 @@ class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
return db.query(self.model).filter(Document.filename == file_name).first() return db.query(self.model).filter(Document.filename == file_name).first()
def get_multi_documents( def get_multi_documents(
self, db: Session, *, skip: int = 0, limit: int = 100 self, db: Session,
) -> List[Document]: ) -> List[Document]:
return ( return (
db.query(self.model) db.query(self.model)
.order_by(desc(getattr(Document, 'uploaded_at'))) .order_by(desc(getattr(Document, 'uploaded_at')))
.offset(skip)
.limit(limit)
.all() .all()
) )
def get_documents_by_departments( def get_documents_by_departments(
self, db: Session, *, department_id: int, skip: int = 0, limit: int = 100 self, db: Session, *, department_id: int
) -> List[Document]: ) -> List[Document]:
return ( return (
db.query(self.model) db.query(self.model)
@ -36,24 +35,20 @@ class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
.join(Department) .join(Department)
.filter(document_department_association.c.department_id == department_id) .filter(document_department_association.c.department_id == department_id)
.order_by(desc(getattr(Document, 'uploaded_at'))) .order_by(desc(getattr(Document, 'uploaded_at')))
.offset(skip)
.limit(limit)
.all() .all()
) )
def get_files_to_verify( def get_files_to_verify(
self, db: Session, *, skip: int = 0, limit: int = 100 self, db: Session,
) -> List[Document]: ) -> List[Document]:
return ( return (
db.query(self.model) db.query(self.model)
.filter(Document.status == 'PENDING') .filter(Document.status == 'PENDING')
.offset(skip)
.limit(limit)
.all() .all()
) )
def get_enabled_documents_by_departments( def get_enabled_documents_by_departments(
self, db: Session, *, department_id: int, skip: int = 0, limit: int = 100 self, db: Session, *, department_id: int
) -> List[Document]: ) -> List[Document]:
all_department_id = 1 # department ID for "ALL" is 1 all_department_id = 1 # department ID for "ALL" is 1
@ -73,8 +68,6 @@ class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
), ),
) )
) )
.offset(skip)
.limit(limit)
.all() .all()
) )
@ -85,8 +78,6 @@ class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
action_type: Optional[str] = None, action_type: Optional[str] = None,
status: Optional[str] = None, status: Optional[str] = None,
order_by: Optional[str] = None, order_by: Optional[str] = None,
skip: int = 0,
limit: int = 100
) -> List[Document]: ) -> List[Document]:
query = db.query(Document) query = db.query(Document)
if filename: if filename:
@ -105,6 +96,6 @@ class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
else: else:
query = query.order_by(asc(getattr(Document, 'uploaded_at'))) query = query.order_by(asc(getattr(Document, 'uploaded_at')))
return query.offset(skip).limit(limit).all() return query.all()
documents = CRUDDocuments(Document) documents = CRUDDocuments(Document)

View file

@ -45,9 +45,9 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
return super().update(db, db_obj=db_obj, obj_in=update_data) return super().update(db, db_obj=db_obj, obj_in=update_data)
def get_multi( def get_multi(
self, db: Session, *, skip: int = 0, limit: int = 100, self, db: Session,
) -> List[User]: ) -> List[User]:
return db.query(self.model).offset(skip).limit(limit).all() return db.query(self.model).all()
def authenticate( def authenticate(
self, db: Session, *, email: str, password: str self, db: Session, *, email: str, password: str
@ -67,27 +67,21 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
db: Session, db: Session,
*, *,
account_id: int, account_id: int,
skip: int = 0,
limit: int = 100,
) -> List[User]: ) -> List[User]:
return ( return (
db.query(self.model) db.query(self.model)
.filter(User.account_id == account_id) .filter(User.account_id == account_id)
.offset(skip)
.limit(limit)
.all() .all()
) )
def get_multi_by_company_id( def get_multi_by_company_id(
self, db: Session, *, company_id: str, skip: int = 0, limit: int = 100 self, db: Session, *, company_id: str
) -> List[User]: ) -> List[User]:
return ( return (
db.query(self.model) db.query(self.model)
.join(User.user_role) .join(User.user_role)
.filter(UserRole.company_id == company_id) .filter(UserRole.company_id == company_id)
.options(joinedload(User.user_role).joinedload(UserRole.role)) .options(joinedload(User.user_role).joinedload(UserRole.role))
.offset(skip)
.limit(limit)
.all() .all()
) )
@ -95,13 +89,11 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
return db.query(self.model).filter(User.username == name).first() return db.query(self.model).filter(User.username == name).first()
def get_by_department_id( def get_by_department_id(
self, db: Session, *, department_id: int, skip: int = 0, limit: int = 100 self, db: Session, *, department_id: int
) -> List[User]: ) -> List[User]:
return ( return (
db.query(self.model) db.query(self.model)
.filter(User.department_id == department_id) .filter(User.department_id == department_id)
.offset(skip)
.limit(limit)
.all() .all()
) )

View file

@ -18,7 +18,7 @@ from .documents import (
from .department import ( from .department import (
Department, DepartmentCreate, DepartmentUpdate, DepartmentAdminCreate, DepartmentDelete Department, DepartmentCreate, DepartmentUpdate, DepartmentAdminCreate, DepartmentDelete
) )
from .audit import AuditBase, AuditCreate, AuditUpdate, Audit, GetAudit, AuditFilter from .audit import AuditBase, AuditCreate, AuditUpdate, Audit, GetAudit, AuditFilter, ExcelFilter
from .chat import ( from .chat import (
ChatHistory, ChatHistoryBase, ChatHistoryCreate, ChatHistoryUpdate, ChatDelete, ChatHistory, ChatHistoryBase, ChatHistoryCreate, ChatHistoryUpdate, ChatDelete,
ChatItem, ChatItemBase, ChatItemCreate, ChatItemUpdate, CreateChatHistory ChatItem, ChatItemBase, ChatItemCreate, ChatItemUpdate, CreateChatHistory

View file

@ -42,8 +42,15 @@ class GetAudit(BaseModel):
class AuditFilter(BaseModel): class AuditFilter(BaseModel):
skip: Optional[int], skip: int = 0,
limit: Optional[int], limit: int = 100,
model: Optional[str] = None
username: Optional[str] = None
action: Optional[str] = None
start_date: Optional[datetime] = None
end_date: Optional[datetime] = None
class ExcelFilter(BaseModel):
model: Optional[str] = None model: Optional[str] = None
username: Optional[str] = None username: Optional[str] = None
action: Optional[str] = None action: Optional[str] = None

View file

@ -0,0 +1,57 @@
import io
import pandas as pd
from typing import List, Optional
from private_gpt.users import schemas
def generate_audit_log_report(audit_logs: List[schemas.Audit], username: Optional[str] = None) -> io.BytesIO:
"""
Generate an Excel report of the QuickGPT audit logs.
Args:
audit_logs (List[schemas.Audit]): List of audit logs to include in the report.
username (str): Username for whom the audit log report is generated.
Returns:
io.BytesIO: In-memory Excel file buffer containing the report.
"""
intro_data = [
["Audit Log Report"],
["Date:", pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S")],
[" "],
]
intro_df = pd.DataFrame(intro_data)
if username:
total_login_counts = get_total_login_counts(audit_logs, username)
intro_data.extend([
["Username:", username],
["Total Login Counts:", total_login_counts],
[" "],
])
audit_df = pd.DataFrame([log.dict() for log in audit_logs])
excel_buffer = io.BytesIO()
with pd.ExcelWriter(excel_buffer, engine='xlsxwriter') as writer:
intro_df.to_excel(writer, index=False, header=False)
if username:
audit_df.to_excel(writer, index=False, startrow=len(intro_data) + 2)
else:
audit_df.to_excel(writer, index=False, startrow=len(intro_data))
excel_buffer.seek(0)
return excel_buffer
def get_total_login_counts(audit_logs: List[schemas.Audit], username: str) -> int:
"""
Get the total login counts for the given username.
Args:
audit_logs (List[schemas.Audit]): List of audit logs to search for login events.
username (str): Username to get the login counts for.
Returns:
int: Total number of login events for the given username.
"""
login_events = [log for log in audit_logs if log.model == "login" and log.user == username]
return len(login_events)

View file

@ -54,6 +54,9 @@ aiofiles = "^23.2.1"
timm = "^0.9.16" timm = "^0.9.16"
fastapi-filter = {extras = ["sqlalchemy"], version = "^1.1.0"} fastapi-filter = {extras = ["sqlalchemy"], version = "^1.1.0"}
uuid = "^1.30" uuid = "^1.30"
openpyxl = "^3.1.2"
pandas = "^2.2.2"
fastapi-pagination = "^0.12.23"
[tool.poetry.extras] [tool.poetry.extras]
ui = ["gradio"] ui = ["gradio"]