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.
This commit is contained in:
magmaus3 2022-12-09 19:35:07 +01:00
parent 270cae46b2
commit 44b79518a5
Signed by: magmaus3
GPG key ID: 966755D3F4A9B251
3 changed files with 113 additions and 24 deletions

View file

@ -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

View file

@ -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})

View file

@ -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():