Add endpoints for LeaderboardVersions.

Change add_scores.
Change get leaderboards positions.
pull/966/head
Andrey 2023-11-20 10:51:31 +02:00
rodzic 7295b9c023
commit e6b75d59d9
3 zmienionych plików z 746 dodań i 44 usunięć

Wyświetl plik

@ -11,7 +11,7 @@ from hexbytes import HexBytes
import requests # type: ignore import requests # type: ignore
from sqlalchemy.dialects.postgresql import insert from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from sqlalchemy import func, text, or_ from sqlalchemy import func, text, or_, Subquery
from sqlalchemy.engine import Row from sqlalchemy.engine import Row
from web3 import Web3 from web3 import Web3
from web3.types import ChecksumAddress from web3.types import ChecksumAddress
@ -24,6 +24,7 @@ from .models import (
DropperClaim, DropperClaim,
Leaderboard, Leaderboard,
LeaderboardScores, LeaderboardScores,
LeaderboardVersion,
) )
from . import signatures from . import signatures
from .settings import ( from .settings import (
@ -91,6 +92,10 @@ class LeaderboardConfigAlreadyInactive(Exception):
pass pass
class LeaderboardVersionNotFound(Exception):
pass
BATCH_SIGNATURE_PAGE_SIZE = 500 BATCH_SIGNATURE_PAGE_SIZE = 500
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -959,6 +964,25 @@ def refetch_drop_signatures(
return claimant_objects return claimant_objects
def leaderboard_version_filter(
db_session: Session,
leaderboard_id: uuid.UUID,
version_number: Optional[int] = None,
) -> Union[Subquery, int]:
# Subquery to get the latest version number for the given leaderboard
if not version_number:
latest_version = (
db_session.query(func.max(LeaderboardVersion.version_number)).filter(
LeaderboardVersion.leaderboard_id == leaderboard_id,
LeaderboardVersion.published == True,
)
).scalar_subquery()
else:
latest_version = version_number
return latest_version
def get_leaderboard_total_count(db_session: Session, leaderboard_id) -> int: def get_leaderboard_total_count(db_session: Session, leaderboard_id) -> int:
""" """
Get the total number of claimants in the leaderboard Get the total number of claimants in the leaderboard
@ -971,12 +995,17 @@ def get_leaderboard_total_count(db_session: Session, leaderboard_id) -> int:
def get_leaderboard_info( def get_leaderboard_info(
db_session: Session, leaderboard_id: uuid.UUID db_session: Session, leaderboard_id: uuid.UUID, version_number: Optional[int] = None
) -> Row[Tuple[uuid.UUID, str, str, int, Optional[datetime]]]: ) -> Row[Tuple[uuid.UUID, str, str, int, Optional[datetime]]]:
""" """
Get the leaderboard from the database with users count Get the leaderboard from the database with users count
""" """
latest_version = leaderboard_version_filter(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version_number,
)
leaderboard = ( leaderboard = (
db_session.query( db_session.query(
Leaderboard.id, Leaderboard.id,
@ -990,6 +1019,15 @@ def get_leaderboard_info(
LeaderboardScores.leaderboard_id == Leaderboard.id, LeaderboardScores.leaderboard_id == Leaderboard.id,
isouter=True, isouter=True,
) )
.join(
LeaderboardVersion,
LeaderboardVersion.leaderboard_id == Leaderboard.id,
isouter=True,
)
.filter(
LeaderboardVersion.published == True,
LeaderboardVersion.version_number == latest_version,
)
.filter(Leaderboard.id == leaderboard_id) .filter(Leaderboard.id == leaderboard_id)
.group_by(Leaderboard.id, Leaderboard.title, Leaderboard.description) .group_by(Leaderboard.id, Leaderboard.title, Leaderboard.description)
.one() .one()
@ -1078,19 +1116,44 @@ def get_leaderboards(
def get_position( def get_position(
db_session: Session, leaderboard_id, address, window_size, limit: int, offset: int db_session: Session,
leaderboard_id,
address,
window_size,
limit: int,
offset: int,
version_number: Optional[int] = None,
) -> List[Row[Tuple[str, int, int, int, Any]]]: ) -> List[Row[Tuple[str, int, int, int, Any]]]:
""" """
Return position by address with window size Return position by address with window size
""" """
query = db_session.query(
LeaderboardScores.address, latest_version = leaderboard_version_filter(
LeaderboardScores.score, db_session=db_session,
LeaderboardScores.points_data.label("points_data"), leaderboard_id=leaderboard_id,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"), version_number=version_number,
func.row_number().over(order_by=LeaderboardScores.score.desc()).label("number"), )
).filter(LeaderboardScores.leaderboard_id == leaderboard_id)
query = (
db_session.query(
LeaderboardScores.address,
LeaderboardScores.score,
LeaderboardScores.points_data.label("points_data"),
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"),
func.row_number()
.over(order_by=LeaderboardScores.score.desc())
.label("number"),
)
.join(
LeaderboardVersion,
LeaderboardVersion.leaderboard_id == LeaderboardScores.leaderboard_id,
)
.filter(
LeaderboardVersion.published == True,
LeaderboardVersion.version_number == latest_version,
)
.filter(LeaderboardScores.leaderboard_id == leaderboard_id)
)
ranked_leaderboard = query.cte(name="ranked_leaderboard") ranked_leaderboard = query.cte(name="ranked_leaderboard")
@ -1130,11 +1193,25 @@ def get_position(
def get_leaderboard_positions( def get_leaderboard_positions(
db_session: Session, leaderboard_id, limit: int, offset: int db_session: Session,
leaderboard_id,
limit: int,
offset: int,
version_number: Optional[int] = None,
) -> List[Row[Tuple[uuid.UUID, str, int, str, int]]]: ) -> List[Row[Tuple[uuid.UUID, str, int, str, int]]]:
""" """
Get the leaderboard positions Get the leaderboard positions
""" """
# get public leaderboard scores with max version
latest_version = leaderboard_version_filter(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version_number,
)
# Main query
query = ( query = (
db_session.query( db_session.query(
LeaderboardScores.id, LeaderboardScores.id,
@ -1143,8 +1220,13 @@ def get_leaderboard_positions(
LeaderboardScores.points_data, LeaderboardScores.points_data,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"), func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"),
) )
.join(
LeaderboardVersion,
LeaderboardVersion.leaderboard_id == LeaderboardScores.leaderboard_id,
)
.filter(LeaderboardScores.leaderboard_id == leaderboard_id) .filter(LeaderboardScores.leaderboard_id == leaderboard_id)
.order_by(text("rank asc, id asc")) .filter(LeaderboardVersion.published == True)
.filter(LeaderboardVersion.version_number == latest_version)
) )
if limit: if limit:
@ -1157,18 +1239,35 @@ def get_leaderboard_positions(
def get_qurtiles( def get_qurtiles(
db_session: Session, leaderboard_id db_session: Session, leaderboard_id, version_number: Optional[int] = None
) -> Tuple[Row[Tuple[str, float, int]], ...]: ) -> Tuple[Row[Tuple[str, float, int]], ...]:
""" """
Get the leaderboard qurtiles Get the leaderboard qurtiles
https://docs.sqlalchemy.org/en/14/core/functions.html#sqlalchemy.sql.functions.percentile_disc https://docs.sqlalchemy.org/en/14/core/functions.html#sqlalchemy.sql.functions.percentile_disc
""" """
query = db_session.query( latest_version = leaderboard_version_filter(
LeaderboardScores.address, db_session=db_session,
LeaderboardScores.score, leaderboard_id=leaderboard_id,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"), version_number=version_number,
).filter(LeaderboardScores.leaderboard_id == leaderboard_id) )
query = (
db_session.query(
LeaderboardScores.address,
LeaderboardScores.score,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"),
)
.join(
LeaderboardVersion,
LeaderboardVersion.leaderboard_id == LeaderboardScores.leaderboard_id,
)
.filter(
LeaderboardVersion.published == True,
LeaderboardVersion.version_number == latest_version,
)
.filter(LeaderboardScores.leaderboard_id == leaderboard_id)
)
ranked_leaderboard = query.cte(name="ranked_leaderboard") ranked_leaderboard = query.cte(name="ranked_leaderboard")
@ -1192,17 +1291,37 @@ def get_qurtiles(
return q1, q2, q3 return q1, q2, q3
def get_ranks(db_session: Session, leaderboard_id) -> List[Row[Tuple[int, int, int]]]: def get_ranks(
db_session: Session, leaderboard_id, version_number: Optional[int] = None
) -> List[Row[Tuple[int, int, int]]]:
""" """
Get the leaderboard rank buckets(rank, size, score) Get the leaderboard rank buckets(rank, size, score)
""" """
query = db_session.query(
LeaderboardScores.id, latest_version = leaderboard_version_filter(
LeaderboardScores.address, db_session=db_session,
LeaderboardScores.score, leaderboard_id=leaderboard_id,
LeaderboardScores.points_data, version_number=version_number,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"), )
).filter(LeaderboardScores.leaderboard_id == leaderboard_id)
query = (
db_session.query(
LeaderboardScores.id,
LeaderboardScores.address,
LeaderboardScores.score,
LeaderboardScores.points_data,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"),
)
.join(
LeaderboardVersion,
LeaderboardVersion.leaderboard_id == LeaderboardScores.leaderboard_id,
)
.filter(
LeaderboardVersion.published == True,
LeaderboardVersion.version_number == latest_version,
)
.filter(LeaderboardScores.leaderboard_id == leaderboard_id)
)
ranked_leaderboard = query.cte(name="ranked_leaderboard") ranked_leaderboard = query.cte(name="ranked_leaderboard")
@ -1220,10 +1339,18 @@ def get_rank(
rank: int, rank: int,
limit: Optional[int] = None, limit: Optional[int] = None,
offset: Optional[int] = None, offset: Optional[int] = None,
version_number: Optional[int] = None,
) -> List[Row[Tuple[uuid.UUID, str, int, str, int]]]: ) -> List[Row[Tuple[uuid.UUID, str, int, str, int]]]:
""" """
Get bucket in leaderboard by rank Get bucket in leaderboard by rank
""" """
latest_version = leaderboard_version_filter(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version_number,
)
query = ( query = (
db_session.query( db_session.query(
LeaderboardScores.id, LeaderboardScores.id,
@ -1232,6 +1359,14 @@ def get_rank(
LeaderboardScores.points_data, LeaderboardScores.points_data,
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"), func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"),
) )
.join(
LeaderboardVersion,
LeaderboardVersion.leaderboard_id == LeaderboardScores.leaderboard_id,
)
.filter(
LeaderboardVersion.published == True,
LeaderboardVersion.version_number == latest_version,
)
.filter(LeaderboardScores.leaderboard_id == leaderboard_id) .filter(LeaderboardScores.leaderboard_id == leaderboard_id)
.order_by(text("rank asc, id asc")) .order_by(text("rank asc, id asc"))
) )
@ -1377,7 +1512,7 @@ def add_scores(
db_session: Session, db_session: Session,
leaderboard_id: uuid.UUID, leaderboard_id: uuid.UUID,
scores: List[Score], scores: List[Score],
overwrite: bool = False, version_number: int,
normalize_addresses: bool = True, normalize_addresses: bool = True,
): ):
""" """
@ -1397,16 +1532,6 @@ def add_scores(
raise DuplicateLeaderboardAddressError("Dublicated addresses", duplicates) raise DuplicateLeaderboardAddressError("Dublicated addresses", duplicates)
if overwrite:
db_session.query(LeaderboardScores).filter(
LeaderboardScores.leaderboard_id == leaderboard_id
).delete()
try:
db_session.commit()
except:
db_session.rollback()
raise LeaderboardDeleteScoresError("Error deleting leaderboard scores")
for score in scores: for score in scores:
leaderboard_scores.append( leaderboard_scores.append(
{ {
@ -1414,6 +1539,7 @@ def add_scores(
"address": normalizer_fn(score.address), "address": normalizer_fn(score.address),
"score": score.score, "score": score.score,
"points_data": score.points_data, "points_data": score.points_data,
"leaderboard_version_number": version_number,
} }
) )
@ -1675,3 +1801,137 @@ def check_leaderboard_resource_permissions(
return True return True
return False return False
def get_leaderboard_version(
db_session: Session, leaderboard_id: uuid.UUID, version_number: int
) -> LeaderboardVersion:
"""
Get the leaderboard version by id
"""
return (
db_session.query(LeaderboardVersion)
.filter(LeaderboardVersion.leaderboard_id == leaderboard_id)
.filter(LeaderboardVersion.version_number == version_number)
.one()
)
def create_leaderboard_version(
db_session: Session,
leaderboard_id: uuid.UUID,
version_number: Optional[int] = None,
publish: bool = False,
) -> LeaderboardVersion:
"""
Create a leaderboard version
"""
if version_number is None:
latest_version_result = (
db_session.query(func.max(LeaderboardVersion.version_number))
.filter(LeaderboardVersion.leaderboard_id == leaderboard_id)
.one()
)
latest_version = latest_version_result[0]
if latest_version is None:
version_number = 0
else:
version_number = latest_version + 1
leaderboard_version = LeaderboardVersion(
leaderboard_id=leaderboard_id,
version_number=version_number,
published=publish,
)
db_session.add(leaderboard_version)
db_session.commit()
return leaderboard_version
def change_publish_leaderboard_version_status(
db_session: Session, leaderboard_id: uuid.UUID, version_number: int, published: bool
) -> LeaderboardVersion:
"""
Publish a leaderboard version
"""
leaderboard_version = (
db_session.query(LeaderboardVersion)
.filter(LeaderboardVersion.leaderboard_id == leaderboard_id)
.filter(LeaderboardVersion.version_number == version_number)
.one()
)
leaderboard_version.published = published
db_session.commit()
return leaderboard_version
def get_leaderboard_versions(
db_session: Session, leaderboard_id: uuid.UUID
) -> List[LeaderboardVersion]:
"""
Get all leaderboard versions
"""
return (
db_session.query(LeaderboardVersion)
.filter(LeaderboardVersion.leaderboard_id == leaderboard_id)
.all()
)
def delete_leaderboard_version(
db_session: Session, leaderboard_id: uuid.UUID, version_number: int
) -> LeaderboardVersion:
"""
Delete a leaderboard version
"""
leaderboard_version = (
db_session.query(LeaderboardVersion)
.filter(LeaderboardVersion.leaderboard_id == leaderboard_id)
.filter(LeaderboardVersion.version_number == version_number)
.one()
)
db_session.delete(leaderboard_version)
db_session.commit()
return leaderboard_version
def get_leaderboard_version_scores(
db_session: Session,
leaderboard_id: uuid.UUID,
version_number: int,
limit: int,
offset: int,
) -> List[LeaderboardScores]:
"""
Get the leaderboard scores by version number
"""
query = (
db_session.query(
LeaderboardScores.id,
LeaderboardScores.address.label("address"),
LeaderboardScores.score.label("score"),
LeaderboardScores.points_data.label("points_data"),
func.rank().over(order_by=LeaderboardScores.score.desc()).label("rank"),
)
.filter(LeaderboardScores.leaderboard_id == leaderboard_id)
.filter(LeaderboardScores.leaderboard_version_number == version_number)
)
if limit:
query = query.limit(limit)
if offset:
query = query.offset(offset)
return query

Wyświetl plik

@ -442,3 +442,11 @@ class LeaderboardConfigUpdate(BaseModel):
query_name: Optional[str] = None query_name: Optional[str] = None
params: Dict[str, int] params: Dict[str, int]
normalize_addresses: Optional[bool] = None normalize_addresses: Optional[bool] = None
class LeaderboardVersion(BaseModel):
leaderboard_id: UUID
version: int
published: bool
created_at: datetime
updated_at: datetime

Wyświetl plik

@ -94,6 +94,7 @@ async def leaderboard(
limit: int = Query(10), limit: int = Query(10),
offset: int = Query(0), offset: int = Query(0),
db_session: Session = Depends(db.yield_db_session), db_session: Session = Depends(db.yield_db_session),
version: Optional[str] = Query(None, description="Version of the leaderboard."),
) -> List[data.LeaderboardPosition]: ) -> List[data.LeaderboardPosition]:
""" """
Returns the leaderboard positions. Returns the leaderboard positions.
@ -112,7 +113,7 @@ async def leaderboard(
raise EngineHTTPException(status_code=500, detail="Internal server error") raise EngineHTTPException(status_code=500, detail="Internal server error")
leaderboard_positions = actions.get_leaderboard_positions( leaderboard_positions = actions.get_leaderboard_positions(
db_session, leaderboard_id, limit, offset db_session, leaderboard_id, limit, offset, version
) )
result = [ result = [
data.LeaderboardPosition( data.LeaderboardPosition(
@ -604,10 +605,6 @@ async def leaderboard_push_scores(
scores: List[data.Score] = Body( scores: List[data.Score] = Body(
..., description="Scores to put to the leaderboard." ..., description="Scores to put to the leaderboard."
), ),
overwrite: bool = Query(
False,
description="If enabled, this will delete all current scores and replace them with the new scores provided.",
),
normalize_addresses: bool = Query( normalize_addresses: bool = Query(
True, description="Normalize addresses to checksum." True, description="Normalize addresses to checksum."
), ),
@ -635,13 +632,22 @@ async def leaderboard_push_scores(
status_code=403, detail="You don't have access to this leaderboard." status_code=403, detail="You don't have access to this leaderboard."
) )
try:
new_version = actions.create_leaderboard_version(
db_session=db_session,
leaderboard_id=leaderboard_id,
)
except Exception as e:
logger.error(f"Error while creating leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
try: try:
leaderboard_points = actions.add_scores( leaderboard_points = actions.add_scores(
db_session=db_session, db_session=db_session,
leaderboard_id=leaderboard_id, leaderboard_id=leaderboard_id,
scores=scores, scores=scores,
overwrite=overwrite,
normalize_addresses=normalize_addresses, normalize_addresses=normalize_addresses,
version_number=new_version.version_number,
) )
except actions.DuplicateLeaderboardAddressError as e: except actions.DuplicateLeaderboardAddressError as e:
raise EngineHTTPException( raise EngineHTTPException(
@ -658,6 +664,17 @@ async def leaderboard_push_scores(
logger.error(f"Score update failed with error: {e}") logger.error(f"Score update failed with error: {e}")
raise EngineHTTPException(status_code=500, detail="Score update failed.") raise EngineHTTPException(status_code=500, detail="Score update failed.")
try:
actions.change_publish_leaderboard_version_status(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=new_version.version_number,
published=True,
)
except Exception as e:
logger.error(f"Error while updating leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
result = [ result = [
data.LeaderboardScore( data.LeaderboardScore(
leaderboard_id=score["leaderboard_id"], leaderboard_id=score["leaderboard_id"],
@ -881,3 +898,420 @@ async def leaderboard_config_deactivate(
raise EngineHTTPException(status_code=500, detail="Internal server error") raise EngineHTTPException(status_code=500, detail="Internal server error")
return True return True
@app.get(
"/{leaderboard_id}/versions",
response_model=List[data.LeaderboardVersion],
tags=["Authorized Endpoints"],
)
async def leaderboard_versions_list(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> List[data.LeaderboardVersion]:
"""
Get leaderboard versions list.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session,
leaderboard_id=leaderboard_id,
token=token,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard version."
)
try:
leaderboard_versions = actions.get_leaderboard_versions(
db_session=db_session,
leaderboard_id=leaderboard_id,
)
except Exception as e:
logger.error(f"Error while getting leaderboard versions list: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
result = [
data.LeaderboardVersion(
leaderboard_id=version.leaderboard_id,
version=version.version_number,
published=version.published,
created_at=version.created_at,
updated_at=version.updated_at,
)
for version in leaderboard_versions
]
return result
@app.get(
"/{leaderboard_id}/versions/{version}",
response_model=data.LeaderboardVersion,
tags=["Authorized Endpoints"],
)
async def leaderboard_version_handler(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
version: int = Path(..., description="Version of the leaderboard."),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> data.LeaderboardVersion:
"""
Get leaderboard version.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session,
leaderboard_id=leaderboard_id,
token=token,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard."
)
try:
leaderboard_version = actions.get_leaderboard_version(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
except Exception as e:
logger.error(f"Error while getting leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
return data.LeaderboardVersion(
leaderboard_id=leaderboard_version.leaderboard_id,
version=leaderboard_version.version_number,
published=leaderboard_version.published,
created_at=leaderboard_version.created_at,
updated_at=leaderboard_version.updated_at,
)
@app.post(
"/{leaderboard_id}/versions",
response_model=data.LeaderboardVersion,
tags=["Authorized Endpoints"],
)
async def create_leaderboard_version(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
version: int = Query(..., description="Version of the leaderboard."),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> data.LeaderboardVersion:
"""
Create leaderboard version.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session,
leaderboard_id=leaderboard_id,
token=token,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard."
)
try:
leaderboard_version = actions.create_leaderboard_version(
db_session=db_session,
leaderboard_id=leaderboard_id,
version=version,
)
except BugoutResponseException as e:
raise EngineHTTPException(status_code=e.status_code, detail=e.detail)
except actions.LeaderboardConfigNotFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard config not found.",
)
except Exception as e:
logger.error(f"Error while creating leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
return leaderboard_version
@app.put(
"/{leaderboard_id}/versions/{version}",
response_model=data.LeaderboardVersion,
tags=["Authorized Endpoints"],
)
async def update_leaderboard_version_handler(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
version: int = Path(..., description="Version of the leaderboard."),
publish: bool = Query(
False,
description="If enabled, this will publish the leaderboard version.",
),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> data.LeaderboardVersion:
"""
Update leaderboard version.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session,
leaderboard_id=leaderboard_id,
token=token,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard version."
)
try:
leaderboard_version = actions.change_publish_leaderboard_version_status(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version,
published=publish,
)
except Exception as e:
logger.error(f"Error while updating leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
return data.LeaderboardVersion(
leaderboard_id=leaderboard_version.leaderboard_id,
version=leaderboard_version.version_number,
published=leaderboard_version.published,
created_at=leaderboard_version.created_at,
updated_at=leaderboard_version.updated_at,
)
@app.delete(
"/{leaderboard_id}/versions/{version}",
response_model=data.LeaderboardVersion,
tags=["Authorized Endpoints"],
)
async def delete_leaderboard_version_handler(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
version: int = Path(..., description="Version of the leaderboard."),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> data.LeaderboardVersion:
"""
Delete leaderboard version.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session, leaderboard_id=leaderboard_id, token=token
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard version."
)
try:
leaderboard_version = actions.delete_leaderboard_version(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
except Exception as e:
logger.error(f"Error while deleting leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
return data.LeaderboardVersion(
leaderboard_id=leaderboard_version.leaderboard_id,
version=leaderboard_version.version_number,
published=leaderboard_version.published,
created_at=leaderboard_version.created_at,
updated_at=leaderboard_version.updated_at,
)
@app.get(
"/{leaderboard_id}/versions/{version}/scores",
response_model=List[data.LeaderboardPosition],
tags=["Authorized Endpoints"],
)
async def leaderboard_version_scores_handler(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
version: int = Path(..., description="Version of the leaderboard."),
limit: int = Query(10),
offset: int = Query(0),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> List[data.LeaderboardPosition]:
"""
Get leaderboard version scores.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session, leaderboard_id=leaderboard_id, token=token
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard version."
)
try:
leaderboard_version_scores = actions.get_leaderboard_version_scores(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version,
limit=limit,
offset=offset,
)
except Exception as e:
logger.error(f"Error while getting leaderboard version scores: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
result = [
data.LeaderboardPosition(
address=score.address,
score=score.score,
rank=score.rank,
points_data=score.points_data,
)
for score in leaderboard_version_scores
]
return result
@app.put(
"/{leaderboard_id}/versions/{version}/scores",
response_model=List[data.LeaderboardScore],
tags=["Authorized Endpoints"],
)
async def leaderboard_version_push_scores_handler(
request: Request,
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
version: int = Path(..., description="Version of the leaderboard."),
scores: List[data.Score] = Body(
..., description="Scores to put to the leaderboard version."
),
normalize_addresses: bool = Query(
True, description="Normalize addresses to checksum."
),
db_session: Session = Depends(db.yield_db_session),
Authorization: str = AuthHeader,
) -> List[data.LeaderboardScore]:
"""
Put the leaderboard version to the database.
"""
token = request.state.token
try:
access = actions.check_leaderboard_resource_permissions(
db_session=db_session, leaderboard_id=leaderboard_id, token=token
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
if not access:
raise EngineHTTPException(
status_code=403, detail="You don't have access to this leaderboard version."
)
try:
leaderboard_version = actions.get_leaderboard_version(
db_session=db_session,
leaderboard_id=leaderboard_id,
version_number=version,
)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
detail="Leaderboard version not found.",
)
except Exception as e:
logger.error(f"Error while getting leaderboard version: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
try:
leaderboard_points = actions.add_scores(
db_session=db_session,
leaderboard_id=leaderboard_id,
scores=scores,
normalize_addresses=normalize_addresses,
version_number=leaderboard_version.version_number,
)
except actions.DuplicateLeaderboardAddressError as e:
raise EngineHTTPException(
status_code=409,
detail=f"Duplicates in push to database is disallowed.\n List of duplicates:{e.duplicates}.\n Please handle duplicates manualy.",
)
except Exception as e:
logger.error(f"Score update failed with error: {e}")
raise EngineHTTPException(status_code=500, detail="Score update failed.")
result = [
data.LeaderboardScore(
leaderboard_id=score["leaderboard_id"],
address=score["address"],
score=score["score"],
points_data=score["points_data"],
)
for score in leaderboard_points
]
return result