diff --git a/main.py b/main.py index dfdd131..cb1f1c7 100755 --- a/main.py +++ b/main.py @@ -10,7 +10,6 @@ activities_router, groups_router, trophies_router, - exports_router, ) from fastapi import FastAPI from database import close_mongo_connection, connect_to_mongo @@ -40,7 +39,6 @@ ) app.include_router(groups_router.router, prefix="/api/group", tags=["groups"]) app.include_router(trophies_router.router, prefix="/api/trophy", tags=["trophies"]) -app.include_router(exports_router.router, prefix="/api/export", tags=["exports"]) # Custom exception handler for internal server errors diff --git a/routers/exports_router.py b/routers/exports_router.py index e319aa9..7587dbc 100644 --- a/routers/exports_router.py +++ b/routers/exports_router.py @@ -1,112 +1,244 @@ -from h11 import Request -from typings.export import Export, ExportFormat, ExportStatus, ExportResponse -from fastapi import APIRouter, Depends, HTTPException, Response -from bson import ObjectId -from database import connect_to_mongo, db -from util.time_export import calculate, json2csv, json2xlsx -from utils import get_current_user -from uuid import uuid4 -import datetime -import asyncio - -router = APIRouter() - -exports: list[ExportResponse] = [] - - -async def export_time(export: Export, id: str): - if export.start is not None and export.end is not None: - start = datetime.datetime.fromisoformat(export.start) - end = datetime.datetime.fromisoformat(export.end) - users = await db.zvms.users.find().to_list(None) - timequery = ( - {"$gte": start, "$lte": end} if start is not None and end is not None else {} - ) - normal_activities = await db.zvms.activities.find( - {"type": {"$ne": "special"}, "date": timequery} - ).to_list(None) - special_activities = await db.zvms.activities.find( - {"type": "special", "date": timequery} - ).to_list(None) - prize_activities = await db.zvms.activities.find( - {"type": "prize", "date": timequery} - ).to_list(None) - groups = await db.zvms.groups.find().to_list(None) - trophies = await db.zvms.trophies.find({"time": timequery}).to_list(None) - prize_full = 10.0 - discount = False - result = await calculate( - users, - normal_activities, - special_activities, - prize_activities, - trophies, - prize_full, - discount, - groups, - ) - if export.format == ExportFormat.csv: - result = json2csv(result) - elif export.format == ExportFormat.xlsx: - result = json2xlsx(result) - return result - - -@router.get("") -async def get_exports(current_user: dict = Depends(get_current_user)): - if "admin" not in current_user["per"]: - raise HTTPException(status_code=403, detail="Permission denied") - return {"status": "ok", "code": 200, "data": [export.id for export in exports]} - - -@router.post("") -async def create_export(export: Export, current_user: dict = Depends(get_current_user)): - # if "admin" not in current_user["per"] or "inspector" not in current_user["per"]: - # raise HTTPException(status_code=403, detail="Permission denied") - if export.collection != "time": - raise HTTPException(status_code=400, detail="Collection export unsupported") - id = str(uuid4()) - print(f"Exporting time data to {export.format} with id {id}...") - exports.append( - ExportResponse( - id=id, - status=ExportStatus.pending, - data=None, - url="", - error=None, - format=export.format, - ) - ) - await export_time(export, id) - return {"status": "ok", "code": 201, "data": id} - - -@router.get("/{export_id}") -async def get_export(export_id: str): - for export in exports: - if export.id == export_id: - result = { - "status": "ok", - "code": 200, - "data": { - "id": export.id, - "status": export.status, - "url": export.url, - "format": export.format, - "error": export.error, - }, - } - if export.status == ExportStatus.completed: - exports.remove(export) - return result - raise HTTPException(status_code=404, detail="Export not found") - - -@router.get("/{export_id}/download") -async def download_export(export_id: str): - for export in exports: - if export.id == export_id: - if export.status != ExportStatus.completed: - raise HTTPException(status_code=404, detail="Export not found") - if export.format == ExportFormat.json: - return export.data +# import pandas as pd +# from h11 import Request +# from pydantic import BaseModel +# +# from typings.export import Export, ExportFormat, ExportStatus, ExportResponse +# from fastapi import APIRouter, Depends, HTTPException, Response +# from bson import ObjectId +# from database import connect_to_mongo, db +# from util.time_export import calculate, json2csv, json2xlsx +# from utils import get_current_user +# from uuid import uuid4 +# import datetime +# import asyncio +# +# router = APIRouter() +# +# exports: list[ExportResponse] = [] +# +# +# @router.post('') +# async def create_exports( +# # mode: Export, +# # user=Depends(get_current_user) +# ): +# pipeline = \ +# [ +# { +# '$lookup': { +# 'from': 'activities', +# 'let': { +# 'userIdStr': { +# '$toString': '$_id' +# } +# }, +# 'pipeline': [ +# { +# '$unwind': '$members' +# }, { +# '$match': { +# '$expr': { +# '$and': [ +# { +# '$eq': [ +# '$members._id', '$$userIdStr' +# ] +# }, { +# '$eq': [ +# '$members.status', 'effective' +# ] +# } +# ] +# } +# } +# } +# ], +# 'as': 'user_activities' +# } +# }, { +# '$addFields': { +# 'groupObjectIds': { +# '$map': { +# 'input': '$group', +# 'as': 'groupIdStr', +# 'in': { +# '$toObjectId': '$$groupIdStr' +# } +# } +# } +# } +# }, { +# '$lookup': { +# 'from': 'groups', +# 'let': { +# 'userGroups': '$groupObjectIds' +# }, +# 'pipeline': [ +# { +# '$match': { +# '$expr': { +# '$and': [ +# { +# '$in': [ +# '$_id', '$$userGroups' +# ] +# }, { +# '$eq': [ +# '$type', 'class' +# ] +# } +# ] +# } +# } +# }, { +# '$project': { +# 'name': 1 +# } +# } +# ], +# 'as': 'user_classes' +# } +# }, { +# '$unwind': '$user_activities' +# }, { +# '$group': { +# '_id': { +# 'userId': '$_id', +# 'mode': '$user_activities.members.mode', +# 'name': '$name', +# 'userIdNumber': '$id', +# 'class': { +# '$arrayElemAt': [ +# '$user_classes.name', 0 +# ] +# } +# }, +# 'totalDuration': { +# '$sum': '$user_activities.members.duration' +# } +# } +# }, { +# '$group': { +# '_id': '$_id.userId', +# 'name': { +# '$first': '$_id.name' +# }, +# 'id': { +# '$first': '$_id.userIdNumber' +# }, +# 'class': { +# '$first': '$_id.class' +# }, +# 'modes': { +# '$push': { +# 'mode': '$_id.mode', +# 'totalDuration': '$totalDuration' +# } +# } +# } +# }, { +# '$project': { +# '_id': 0, +# 'id': 1, +# 'name': 1, +# 'class': 1, +# 'on_campus': { +# '$cond': { +# 'if': { +# '$in': [ +# 'on-campus', '$modes.mode' +# ] +# }, +# 'then': { +# '$arrayElemAt': [ +# { +# '$filter': { +# 'input': '$modes', +# 'as': 'mode', +# 'cond': { +# '$eq': [ +# '$$mode.mode', 'on-campus' +# ] +# } +# } +# }, 0 +# ] +# }, +# 'else': { +# 'mode': 'on-campus', +# 'totalDuration': 0 +# } +# } +# }, +# 'off_campus': { +# '$cond': { +# 'if': { +# '$in': [ +# 'off-campus', '$modes.mode' +# ] +# }, +# 'then': { +# '$arrayElemAt': [ +# { +# '$filter': { +# 'input': '$modes', +# 'as': 'mode', +# 'cond': { +# '$eq': [ +# '$$mode.mode', 'off-campus' +# ] +# } +# } +# }, 0 +# ] +# }, +# 'else': { +# 'mode': 'off-campus', +# 'totalDuration': 0 +# } +# } +# }, +# 'social_practice': { +# '$cond': { +# 'if': { +# '$in': [ +# 'social-practice', '$modes.mode' +# ] +# }, +# 'then': { +# '$arrayElemAt': [ +# { +# '$filter': { +# 'input': '$modes', +# 'as': 'mode', +# 'cond': { +# '$eq': [ +# '$$mode.mode', 'social-practice' +# ] +# } +# } +# }, 0 +# ] +# }, +# 'else': { +# 'mode': 'social-practice', +# 'totalDuration': 0 +# } +# } +# } +# } +# }, { +# '$project': { +# 'id': 1, +# 'name': 1, +# 'class': 1, +# 'on-campus': '$on_campus.totalDuration', +# 'off-campus': '$off_campus.totalDuration', +# 'social-practice': '$social_practice.totalDuration' +# } +# } +# ] +# result = await db.zvms.activities.aggregate(pipeline).to_list(None) +# df = pd.DataFrame(result) +# print(df) +# return 123 diff --git a/routers/imports_router.py b/routers/imports_router.py new file mode 100644 index 0000000..e69de29 diff --git a/routers/merge_router.py b/routers/merge_router.py new file mode 100644 index 0000000..e69de29 diff --git a/routers/users_router.py b/routers/users_router.py index a2a9dec..8cb1dbb 100755 --- a/routers/users_router.py +++ b/routers/users_router.py @@ -7,6 +7,7 @@ compulsory_temporary_token, get_current_user, validate_object_id, + string_to_option_object_id ) from database import db from util.cert import get_hashed_password_by_cert, validate_by_cert @@ -29,6 +30,12 @@ async def auth_user(auth: AuthUser): if mode is None: mode = "long" + if string_to_option_object_id(id) is None: + users = await db.zvms.users.find({"id": id}).to_list(None) + if len(users) != 1: + raise HTTPException(status_code=404, detail="The id of the user is not found, or there are multiple users with the same id.") + id = str(users[0]["_id"]) + result = await validate_by_cert(id, credential, mode) return { diff --git a/util/cert.py b/util/cert.py index c830e78..46a28d8 100644 --- a/util/cert.py +++ b/util/cert.py @@ -90,15 +90,16 @@ async def validate_by_cert(id: str, cert: str, type: Optional[str] = "long"): # in a minute if time < datetime.datetime.now().timestamp() - 60: raise HTTPException(status_code=401, detail="Token expired") - try: - user = await db.zvms.users.find_one({"_id": ObjectId(id)}) - if checkpw( - bytes(auth_field["password"], "utf-8"), bytes(user["password"], "utf-8") - ): - return jwt_encode(id, await get_user_permissions(user), type=type) - raise HTTPException(status_code=401, detail="Password incorrect") - except: + founded = await db.zvms.users.find({"_id": ObjectId(id)}).to_list(None) + if len(founded) == 0: raise HTTPException(status_code=404, detail="User not found") + user = founded[0] + if checkpw( + bytes(auth_field["password"], "utf-8"), bytes(user["password"], "utf-8") + ): + return jwt_encode(id, await get_user_permissions(user), type=type) + else: + raise HTTPException(status_code=403, detail="Password incorrect") async def get_hashed_password_by_cert(cert: str): diff --git a/utils.py b/utils.py index 8040851..5fb0fe9 100755 --- a/utils.py +++ b/utils.py @@ -27,6 +27,12 @@ def validate_object_id(id: str): raise HTTPException(status_code=400, detail="Invalid Object ID") return _id +def string_to_option_object_id(id: str): + try : + _id = ObjectId(id) + except: + return None + return _id async def get_user(oid: str): """