From 44b79518a539c584f6e9db29dcebb13e70f332c5 Mon Sep 17 00:00:00 2001 From: magmaus3 Date: Fri, 9 Dec 2022 19:35:07 +0100 Subject: [PATCH] Add user rating support! It took me too long to complete it, I hope this thing will work for more than 10 minutes. It also breaks when user rated a level that was already rated. --- customiwmserver/data_types.py | 4 ++ customiwmserver/database.py | 2 + customiwmserver/main.py | 131 +++++++++++++++++++++++++++------- 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/customiwmserver/data_types.py b/customiwmserver/data_types.py index 525e5eb..704d945 100644 --- a/customiwmserver/data_types.py +++ b/customiwmserver/data_types.py @@ -53,6 +53,10 @@ class User(BaseModel): Username: str = "magmaus3" Email: str = "user@example.com" + # Ratings have to be stored in the following format: {mapID: rating}, + # where rating is either 1 or 5, or None. + Ratings: dict = {} + class Notification(BaseModel): """Pydantic model for Notifications diff --git a/customiwmserver/database.py b/customiwmserver/database.py index b790674..2b68cc4 100644 --- a/customiwmserver/database.py +++ b/customiwmserver/database.py @@ -37,6 +37,8 @@ def auth_check(Authorization): - wrongpass = wrong password - [dictionary] = query """ + if Authorization is None: + return False, "noauth" username, password = Authorization.split(":") query = user_collection.find_one({"Username": username}) diff --git a/customiwmserver/main.py b/customiwmserver/main.py index 57cb814..b7a5582 100644 --- a/customiwmserver/main.py +++ b/customiwmserver/main.py @@ -2,6 +2,7 @@ from datetime import datetime from fastapi import FastAPI, Form, File, UploadFile, Header, HTTPException, Body from fastapi.responses import PlainTextResponse from fastapi.exceptions import RequestValidationError +from pydantic import BaseModel from starlette.exceptions import HTTPException as StarletteHTTPException from typing import Union, Optional, Any @@ -166,23 +167,33 @@ async def getMap(mapID: int): @app.post("/api/v1/map/{mapID}/start") -async def startMap(mapID: int): - query = db.maps_collection.find_one({"ID": mapID}) - del query["_id"] +async def startMap( + mapID: int, + Authorization: Union[str, None] = Header(default=None) + ): + authcheck = db.auth_check(Authorization) + if not authcheck[0] and authcheck[1] == "nouser": + raise HTTPException(404, detail="User not found") + elif not authcheck[0] and authcheck[1] == "wrongpass": + raise HTTPException(403, detail="Wrong password") + elif authcheck[0]: + userData = types.User(**authcheck[1]) + query = db.maps_collection.find_one({"ID": mapID}) + del query["_id"] - returned_resp = { - "BestDeaths": 0, - "BestPlaytime": 0, - "Clear": False, - "CurMap": query, - "Difficulty": 0, - "Followed": False, - "Played": True, - "Rating": 5, - "TagIDs": "1,8,9", - "TagNames": "Boss/Avoidance,Music,Art", - } - return returned_resp + returned_resp = { + "BestDeaths": 0, + "BestPlaytime": 0, + "Clear": False, + "CurMap": query, + "Difficulty": 0, + "Followed": False, + "Played": True, + "Rating": userData.Ratings[str(mapID)] if str(mapID) in userData.Ratings else -1, + "TagIDs": query["TagIDs"], + "TagNames": query["TagNames"], + } + return returned_resp @app.post("/api/v1/map/{mapID}/stop") @@ -213,12 +224,10 @@ async def stopMapPlay( ) if query is not None: del query["_id"] - print(__import__("json").dumps(query, indent=4)) BestUserTime = None BestTime = None if query is not None and "Leaderboard" in query: - print("-" * 3) for i in query["Leaderboard"]: if i["UserID"] == userData.ID: if BestUserTime is None or BestUserTime > i["BestPlaytime"]: @@ -227,10 +236,7 @@ async def stopMapPlay( BestTime = i["BestPlaytime"] if len(query["Leaderboard"]) <= 1 and i["UserID"] != userData.ID: FirstClear = True - print(BestUserTime, BestTime) if BestUserTime is None or playtime < BestUserTime: - print(BestUserTime) - updateQuery = db.maps_collection.update_one( {"ID": mapID}, {"$pull": {"Leaderboard": {"UserID": userData.ID}}, @@ -271,7 +277,6 @@ async def stopMapPlay( }, ) if BestTime is None or playtime < BestTime: - print(BestTime, playtime) NewMapRecord = True hook.execute_hooks( @@ -368,6 +373,84 @@ async def upload_map( return {"MapCode": MapCode} # raise HTTPException(501) +class Rating(BaseModel): + Rating: int + +@app.post("/api/v1/map/{mapID}/rating") +async def rateMap(mapID: int, Rating: Rating, Authorization: Union[str, None] = Header(default=None)): + authcheck = db.auth_check(Authorization) + if not authcheck[0] and authcheck[1] == "nouser": + raise HTTPException(404, detail="User not found") + elif not authcheck[0] and authcheck[1] == "wrongpass": + raise HTTPException(403, detail="Wrong password") + elif authcheck[0]: + userData = types.User(**authcheck[1]) + + userRating = userData.Ratings[str(mapID)] if str(mapID) in userData.Ratings else 0 + print(userRating, type(userRating)) + + + if Rating.Rating == 5: + if userRating is not None and userRating == 5: + raise HTTPException(400, detail="Map already rated! Set rating in request to -1 to unrate.") + additional = {} + if userRating == 1: + additional = {"NumThumbsDown": -1} + query = db.maps_collection.update_one({"ID": mapID}, {"$inc": {"NumThumbsUp":1, "NumRatings": 1, **additional}}) + userRating = 5 + elif Rating.Rating == 1: + if userRating is not None and userRating == 1: + raise HTTPException(400, detail="Map already rated! Set rating in request to -1 to unrate.") + additional = {} + if userRating == 5: + additional = {"NumThumbsUp": -1} + query = db.maps_collection.update_one({"ID": mapID}, {"$inc": {"NumThumbsDown": 1, "NumRatings": 1, **additional}}) + userRating = 1 + elif Rating.Rating == -1: + if userRating is None or userRating == -1: + raise HTTPException(400, detail="Map is not rated!") + key = "" + if userRating == 5: + key = "NumThumbsUp" + elif userRating == 1: + key = "NumThumbsDown" + else: + raise HTTPException(400, detail=f"Previous rating was not 5 or 1. It was {userRating} [{repr(type(userRating))}]") + query = db.maps_collection.update_one({"ID": mapID}, {"$inc": {"NumRatings": -1, key: -1}}) + userRating = -1 + query = db.maps_collection.find_one({"ID": mapID}) + + print(userRating, "+") + userQuery = db.user_collection.update_one({"ID": userData.ID}, {"$set": {f"Ratings.{mapID}": userRating} }) + print(userQuery.raw_result) + + return { + "Exists": True, + "TagIDs": query["TagIDs"], + "TagNames": query["TagNames"], + "UserMap": { + "BestDeaths": 0, + "BestFullPlaytime": 0, + "BestFullPlaytimeTime": None, + "BestPlaytime": 0, + "BestPlaytimeTime": None, + "BestReplay": "", + "Clear": False, + "Difficulty": 0, + "FirstClearInvalid": False, + "FirstClearPlaytime": 0, + "FirstClearTime": False, + "FirstDeathTimeValid": False, + "FirstDeaths": -1, + "FirstPlayRecorded": True, + "FirstPlaytime": -1, + "FullClear": False, + "MapID": mapID, + "Played": True, + "Rating": userRating, + "UserID": userData.ID + } + } @app.get("/api/v1/map/{mapID}/besttimes/{maxEntries}") async def getMapLeaderboard(mapID: int, maxEntries: int = 5): @@ -497,14 +580,14 @@ async def reportContent( async def featuredlist(): """Returns the list id of the weekly levels list.""" # FIXME Add playlists - return + raise HTTPException(404, detail="Not available due to lack of playlist support.") @app.get("/api/v1/followcheck") async def followcheck(): """Check, if creators that the user follows uploaded new levels.""" # FIXME: Stub - return 1 + raise HTTPException(404, detail="Not available due to follows not being implemented.") def start():