kopia lustrzana https://github.com/bugout-dev/moonstream
commit
f8d067bf7d
|
@ -1,6 +1,6 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from typing import List, Any, Optional, Dict, Union
|
from typing import List, Any, Optional, Dict, Union, Tuple
|
||||||
import uuid
|
import uuid
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ 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_
|
||||||
|
from sqlalchemy.engine import Row
|
||||||
from web3 import Web3
|
from web3 import Web3
|
||||||
from web3.types import ChecksumAddress
|
from web3.types import ChecksumAddress
|
||||||
|
|
||||||
|
@ -64,6 +65,18 @@ class LeaderboardDeleteScoresError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardCreateError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardUpdateError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardDeleteError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
BATCH_SIGNATURE_PAGE_SIZE = 500
|
BATCH_SIGNATURE_PAGE_SIZE = 500
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -208,7 +221,7 @@ def delete_claim(db_session: Session, dropper_claim_id):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
claim = (
|
claim = (
|
||||||
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one()
|
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
db_session.delete(claim)
|
db_session.delete(claim)
|
||||||
|
@ -260,7 +273,7 @@ def activate_drop(db_session: Session, dropper_claim_id: uuid.UUID):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
claim = (
|
claim = (
|
||||||
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one()
|
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
claim.active = True
|
claim.active = True
|
||||||
|
@ -275,7 +288,7 @@ def deactivate_drop(db_session: Session, dropper_claim_id: uuid.UUID):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
claim = (
|
claim = (
|
||||||
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one()
|
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
claim.active = False
|
claim.active = False
|
||||||
|
@ -300,7 +313,7 @@ def update_drop(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
claim = (
|
claim = (
|
||||||
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one()
|
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
if title:
|
if title:
|
||||||
|
@ -619,7 +632,7 @@ def get_drop(db_session: Session, dropper_claim_id: uuid.UUID):
|
||||||
Return particular drop
|
Return particular drop
|
||||||
"""
|
"""
|
||||||
drop = (
|
drop = (
|
||||||
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one()
|
db_session.query(DropperClaim).filter(DropperClaim.id == dropper_claim_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
return drop
|
return drop
|
||||||
|
|
||||||
|
@ -833,7 +846,7 @@ def refetch_drop_signatures(
|
||||||
)
|
)
|
||||||
.join(DropperContract, DropperClaim.dropper_contract_id == DropperContract.id)
|
.join(DropperContract, DropperClaim.dropper_contract_id == DropperContract.id)
|
||||||
.filter(DropperClaim.id == dropper_claim_id)
|
.filter(DropperClaim.id == dropper_claim_id)
|
||||||
).one()
|
).one() # type: ignore
|
||||||
|
|
||||||
if claim.claim_block_deadline is None:
|
if claim.claim_block_deadline is None:
|
||||||
raise DropWithNotSettedBlockDeadline(
|
raise DropWithNotSettedBlockDeadline(
|
||||||
|
@ -932,7 +945,7 @@ def refetch_drop_signatures(
|
||||||
return claimant_objects
|
return claimant_objects
|
||||||
|
|
||||||
|
|
||||||
def get_leaderboard_total_count(db_session: Session, leaderboard_id):
|
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
|
||||||
"""
|
"""
|
||||||
|
@ -943,18 +956,83 @@ def get_leaderboard_total_count(db_session: Session, leaderboard_id):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_leaderboard(db_session: Session, leaderboard_id: uuid.UUID) -> Leaderboard:
|
def get_leaderboard_info(
|
||||||
|
db_session: Session, leaderboard_id: uuid.UUID
|
||||||
|
) -> Row[Tuple[uuid.UUID, str, str, int, Optional[datetime]]]:
|
||||||
"""
|
"""
|
||||||
Get the leaderboard from the database
|
Get the leaderboard from the database with users count
|
||||||
"""
|
"""
|
||||||
|
|
||||||
leaderboard = (
|
leaderboard = (
|
||||||
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one()
|
db_session.query(
|
||||||
|
Leaderboard.id,
|
||||||
|
Leaderboard.title,
|
||||||
|
Leaderboard.description,
|
||||||
|
func.count(LeaderboardScores.id).label("users_count"),
|
||||||
|
func.max(LeaderboardScores.updated_at).label("last_update"),
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
LeaderboardScores,
|
||||||
|
LeaderboardScores.leaderboard_id == Leaderboard.id,
|
||||||
|
isouter=True,
|
||||||
|
)
|
||||||
|
.filter(Leaderboard.id == leaderboard_id)
|
||||||
|
.group_by(Leaderboard.id, Leaderboard.title, Leaderboard.description)
|
||||||
|
.one()
|
||||||
)
|
)
|
||||||
|
|
||||||
return leaderboard
|
return leaderboard
|
||||||
|
|
||||||
|
|
||||||
|
def get_leaderboard_scores_changes(
|
||||||
|
db_session: Session, leaderboard_id: uuid.UUID
|
||||||
|
) -> List[Row[Tuple[int, datetime]]]:
|
||||||
|
"""
|
||||||
|
Return the leaderboard scores changes timeline changes of leaderboard scores
|
||||||
|
"""
|
||||||
|
|
||||||
|
leaderboard_scores_changes = (
|
||||||
|
db_session.query(
|
||||||
|
func.count(LeaderboardScores.address).label("players_count"),
|
||||||
|
# func.extract("epoch", LeaderboardScores.updated_at).label("timestamp"),
|
||||||
|
LeaderboardScores.updated_at.label("date"),
|
||||||
|
)
|
||||||
|
.filter(LeaderboardScores.leaderboard_id == leaderboard_id)
|
||||||
|
.group_by(LeaderboardScores.updated_at)
|
||||||
|
.order_by(LeaderboardScores.updated_at.desc())
|
||||||
|
).all()
|
||||||
|
|
||||||
|
return leaderboard_scores_changes
|
||||||
|
|
||||||
|
|
||||||
|
def get_leaderboard_scores_by_timestamp(
|
||||||
|
db_session: Session,
|
||||||
|
leaderboard_id: uuid.UUID,
|
||||||
|
date: datetime,
|
||||||
|
limit: int,
|
||||||
|
offset: int,
|
||||||
|
) -> List[LeaderboardScores]:
|
||||||
|
"""
|
||||||
|
Return the leaderboard scores by timestamp
|
||||||
|
"""
|
||||||
|
|
||||||
|
leaderboard_scores = (
|
||||||
|
db_session.query(
|
||||||
|
LeaderboardScores.leaderboard_id,
|
||||||
|
LeaderboardScores.address,
|
||||||
|
LeaderboardScores.score,
|
||||||
|
LeaderboardScores.points_data,
|
||||||
|
)
|
||||||
|
.filter(LeaderboardScores.leaderboard_id == leaderboard_id)
|
||||||
|
.filter(LeaderboardScores.updated_at == date)
|
||||||
|
.order_by(LeaderboardScores.score.desc())
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset)
|
||||||
|
)
|
||||||
|
|
||||||
|
return leaderboard_scores
|
||||||
|
|
||||||
|
|
||||||
def get_leaderboards(
|
def get_leaderboards(
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
token: Union[str, uuid.UUID],
|
token: Union[str, uuid.UUID],
|
||||||
|
@ -987,7 +1065,7 @@ 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
|
||||||
):
|
) -> List[Row[Tuple[str, int, int, int, Any]]]:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Return position by address with window size
|
Return position by address with window size
|
||||||
|
@ -1039,7 +1117,7 @@ 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
|
||||||
):
|
) -> List[Row[Tuple[uuid.UUID, str, int, str, int]]]:
|
||||||
"""
|
"""
|
||||||
Get the leaderboard positions
|
Get the leaderboard positions
|
||||||
"""
|
"""
|
||||||
|
@ -1064,7 +1142,9 @@ def get_leaderboard_positions(
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
|
||||||
def get_qurtiles(db_session: Session, leaderboard_id):
|
def get_qurtiles(
|
||||||
|
db_session: Session, leaderboard_id
|
||||||
|
) -> 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
|
||||||
|
@ -1098,7 +1178,7 @@ def get_qurtiles(db_session: Session, leaderboard_id):
|
||||||
return q1, q2, q3
|
return q1, q2, q3
|
||||||
|
|
||||||
|
|
||||||
def get_ranks(db_session: Session, leaderboard_id):
|
def get_ranks(db_session: Session, leaderboard_id) -> List[Row[Tuple[int, int, int]]]:
|
||||||
"""
|
"""
|
||||||
Get the leaderboard rank buckets(rank, size, score)
|
Get the leaderboard rank buckets(rank, size, score)
|
||||||
"""
|
"""
|
||||||
|
@ -1126,7 +1206,7 @@ def get_rank(
|
||||||
rank: int,
|
rank: int,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
offset: Optional[int] = None,
|
offset: Optional[int] = None,
|
||||||
):
|
) -> List[Row[Tuple[uuid.UUID, str, int, str, int]]]:
|
||||||
"""
|
"""
|
||||||
Get bucket in leaderboard by rank
|
Get bucket in leaderboard by rank
|
||||||
"""
|
"""
|
||||||
|
@ -1157,33 +1237,114 @@ def get_rank(
|
||||||
return positions
|
return positions
|
||||||
|
|
||||||
|
|
||||||
def create_leaderboard(db_session: Session, title: str, description: str):
|
def create_leaderboard(
|
||||||
|
db_session: Session,
|
||||||
|
title: str,
|
||||||
|
description: Optional[str],
|
||||||
|
token: Optional[Union[uuid.UUID, str]] = None,
|
||||||
|
) -> Leaderboard:
|
||||||
"""
|
"""
|
||||||
Create a leaderboard
|
Create a leaderboard
|
||||||
"""
|
"""
|
||||||
|
|
||||||
leaderboard = Leaderboard(title=title, description=description)
|
if not token:
|
||||||
db_session.add(leaderboard)
|
token = uuid.UUID(MOONSTREAM_ADMIN_ACCESS_TOKEN)
|
||||||
|
try:
|
||||||
|
leaderboard = Leaderboard(title=title, description=description)
|
||||||
|
db_session.add(leaderboard)
|
||||||
|
db_session.commit()
|
||||||
|
|
||||||
|
resource = create_leaderboard_resource(
|
||||||
|
leaderboard_id=str(leaderboard.id),
|
||||||
|
token=token,
|
||||||
|
)
|
||||||
|
|
||||||
|
leaderboard.resource_id = resource.id
|
||||||
|
|
||||||
|
db_session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
db_session.rollback()
|
||||||
|
logger.error(f"Error creating leaderboard: {e}")
|
||||||
|
raise LeaderboardCreateError(f"Error creating leaderboard: {e}")
|
||||||
|
|
||||||
|
return leaderboard
|
||||||
|
|
||||||
|
|
||||||
|
def delete_leaderboard(
|
||||||
|
db_session: Session, leaderboard_id: uuid.UUID, token: uuid.UUID
|
||||||
|
) -> Leaderboard:
|
||||||
|
"""
|
||||||
|
Delete a leaderboard
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
leaderboard = (
|
||||||
|
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one() # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
if leaderboard.resource_id is not None:
|
||||||
|
try:
|
||||||
|
bc.delete_resource(
|
||||||
|
token=token,
|
||||||
|
resource_id=leaderboard.resource_id,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error deleting leaderboard resource: {e}")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"Leaderboard {leaderboard_id} has no resource id. Skipping. Better delete it manually."
|
||||||
|
)
|
||||||
|
|
||||||
|
db_session.delete(leaderboard)
|
||||||
|
db_session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
db_session.rollback()
|
||||||
|
logger.error(e)
|
||||||
|
raise LeaderboardDeleteError(f"Error deleting leaderboard: {e}")
|
||||||
|
|
||||||
|
return leaderboard
|
||||||
|
|
||||||
|
|
||||||
|
def update_leaderboard(
|
||||||
|
db_session: Session,
|
||||||
|
leaderboard_id: uuid.UUID,
|
||||||
|
title: Optional[str],
|
||||||
|
description: Optional[str],
|
||||||
|
) -> Leaderboard:
|
||||||
|
"""
|
||||||
|
Update a leaderboard
|
||||||
|
"""
|
||||||
|
|
||||||
|
leaderboard = (
|
||||||
|
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one() # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
if title is not None:
|
||||||
|
leaderboard.title = title
|
||||||
|
if description is not None:
|
||||||
|
leaderboard.description = description
|
||||||
|
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
return leaderboard.id
|
return leaderboard
|
||||||
|
|
||||||
|
|
||||||
def get_leaderboard_by_id(db_session: Session, leaderboard_id):
|
def get_leaderboard_by_id(db_session: Session, leaderboard_id) -> Leaderboard:
|
||||||
"""
|
"""
|
||||||
Get the leaderboard by id
|
Get the leaderboard by id
|
||||||
"""
|
"""
|
||||||
return db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one()
|
return db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def get_leaderboard_by_title(db_session: Session, title):
|
def get_leaderboard_by_title(db_session: Session, title) -> Leaderboard:
|
||||||
"""
|
"""
|
||||||
Get the leaderboard by title
|
Get the leaderboard by title
|
||||||
"""
|
"""
|
||||||
return db_session.query(Leaderboard).filter(Leaderboard.title == title).one()
|
return db_session.query(Leaderboard).filter(Leaderboard.title == title).one() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def list_leaderboards(db_session: Session, limit: int, offset: int):
|
def list_leaderboards(
|
||||||
|
db_session: Session, limit: int, offset: int
|
||||||
|
) -> List[Row[Tuple[uuid.UUID, str, str]]]:
|
||||||
"""
|
"""
|
||||||
List all leaderboards
|
List all leaderboards
|
||||||
"""
|
"""
|
||||||
|
@ -1265,8 +1426,7 @@ def add_scores(
|
||||||
|
|
||||||
|
|
||||||
def create_leaderboard_resource(
|
def create_leaderboard_resource(
|
||||||
leaderboard_id: uuid.UUID,
|
leaderboard_id: str, token: Union[Optional[uuid.UUID], str] = None
|
||||||
token: Optional[uuid.UUID] = None,
|
|
||||||
) -> BugoutResource:
|
) -> BugoutResource:
|
||||||
resource_data: Dict[str, Any] = {
|
resource_data: Dict[str, Any] = {
|
||||||
"type": LEADERBOARD_RESOURCE_TYPE,
|
"type": LEADERBOARD_RESOURCE_TYPE,
|
||||||
|
@ -1275,19 +1435,22 @@ def create_leaderboard_resource(
|
||||||
|
|
||||||
if token is None:
|
if token is None:
|
||||||
token = MOONSTREAM_ADMIN_ACCESS_TOKEN
|
token = MOONSTREAM_ADMIN_ACCESS_TOKEN
|
||||||
|
try:
|
||||||
resource = bc.create_resource(
|
resource = bc.create_resource(
|
||||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
token=token,
|
||||||
application_id=MOONSTREAM_APPLICATION_ID,
|
application_id=MOONSTREAM_APPLICATION_ID,
|
||||||
resource_data=resource_data,
|
resource_data=resource_data,
|
||||||
timeout=10,
|
timeout=10,
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise LeaderboardCreateError(f"Error creating leaderboard resource: {e}")
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
|
|
||||||
def assign_resource(
|
def assign_resource(
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
leaderboard_id: uuid.UUID,
|
leaderboard_id: uuid.UUID,
|
||||||
|
user_token: Union[uuid.UUID, str],
|
||||||
resource_id: Optional[uuid.UUID] = None,
|
resource_id: Optional[uuid.UUID] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
@ -1295,19 +1458,17 @@ def assign_resource(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
leaderboard = (
|
leaderboard = (
|
||||||
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one()
|
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
if leaderboard.resource_id is not None:
|
|
||||||
raise Exception("Leaderboard already has a resource")
|
|
||||||
|
|
||||||
if resource_id is not None:
|
if resource_id is not None:
|
||||||
leaderboard.resource_id = resource_id
|
leaderboard.resource_id = resource_id
|
||||||
else:
|
else:
|
||||||
# Create resource via admin token
|
# Create resource via admin token
|
||||||
|
|
||||||
resource = create_leaderboard_resource(
|
resource = create_leaderboard_resource(
|
||||||
leaderboard_id=leaderboard_id,
|
leaderboard_id=str(leaderboard_id),
|
||||||
|
token=user_token,
|
||||||
)
|
)
|
||||||
|
|
||||||
leaderboard.resource_id = resource.id
|
leaderboard.resource_id = resource.id
|
||||||
|
@ -1330,7 +1491,9 @@ def list_leaderboards_resources(
|
||||||
return query.all()
|
return query.all()
|
||||||
|
|
||||||
|
|
||||||
def revoke_resource(db_session: Session, leaderboard_id: uuid.UUID):
|
def revoke_resource(
|
||||||
|
db_session: Session, leaderboard_id: uuid.UUID
|
||||||
|
) -> Optional[uuid.UUID]:
|
||||||
"""
|
"""
|
||||||
Revoke a resource handler to a leaderboard
|
Revoke a resource handler to a leaderboard
|
||||||
"""
|
"""
|
||||||
|
@ -1338,7 +1501,7 @@ def revoke_resource(db_session: Session, leaderboard_id: uuid.UUID):
|
||||||
# TODO(ANDREY): Delete resource via admin token
|
# TODO(ANDREY): Delete resource via admin token
|
||||||
|
|
||||||
leaderboard = (
|
leaderboard = (
|
||||||
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one()
|
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
if leaderboard.resource_id is None:
|
if leaderboard.resource_id is None:
|
||||||
|
@ -1354,12 +1517,12 @@ def revoke_resource(db_session: Session, leaderboard_id: uuid.UUID):
|
||||||
|
|
||||||
def check_leaderboard_resource_permissions(
|
def check_leaderboard_resource_permissions(
|
||||||
db_session: Session, leaderboard_id: uuid.UUID, token: uuid.UUID
|
db_session: Session, leaderboard_id: uuid.UUID, token: uuid.UUID
|
||||||
):
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the user has permissions to access the leaderboard
|
Check if the user has permissions to access the leaderboard
|
||||||
"""
|
"""
|
||||||
leaderboard = (
|
leaderboard = (
|
||||||
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one()
|
db_session.query(Leaderboard).filter(Leaderboard.id == leaderboard_id).one() # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
permission_url = f"{bc.brood_url}/resources/{leaderboard.resource_id}/holders"
|
permission_url = f"{bc.brood_url}/resources/{leaderboard.resource_id}/holders"
|
||||||
|
|
|
@ -378,3 +378,53 @@ class LeaderboardInfoResponse(BaseModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
title: str
|
title: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
|
users_count: int
|
||||||
|
last_updated_at: Optional[datetime] = None
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardCreateRequest(BaseModel):
|
||||||
|
title: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardCreatedResponse(BaseModel):
|
||||||
|
id: UUID
|
||||||
|
title: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
resource_id: Optional[UUID] = None
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardUpdatedResponse(BaseModel):
|
||||||
|
id: UUID
|
||||||
|
title: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
resource_id: Optional[UUID] = None
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardUpdateRequest(BaseModel):
|
||||||
|
title: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardDeletedResponse(BaseModel):
|
||||||
|
id: UUID
|
||||||
|
title: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
resource_id: Optional[UUID] = None
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardScoresChangesResponse(BaseModel):
|
||||||
|
players_count: int
|
||||||
|
date: datetime
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
"""
|
"""
|
||||||
Leaderboard API.
|
Leaderboard API.
|
||||||
"""
|
"""
|
||||||
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from web3 import Web3
|
from web3 import Web3
|
||||||
from fastapi import FastAPI, Request, Depends, Response
|
from fastapi import FastAPI, Request, Depends, Response, Query, Path, Body
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
@ -33,12 +34,17 @@ leaderboad_whitelist = {
|
||||||
f"/leaderboard/{DOCS_TARGET_PATH}": "GET",
|
f"/leaderboard/{DOCS_TARGET_PATH}": "GET",
|
||||||
"/leaderboard/openapi.json": "GET",
|
"/leaderboard/openapi.json": "GET",
|
||||||
"/leaderboard/info": "GET",
|
"/leaderboard/info": "GET",
|
||||||
|
"/leaderboard/scores/changes": "GET",
|
||||||
"/leaderboard/quartiles": "GET",
|
"/leaderboard/quartiles": "GET",
|
||||||
"/leaderboard/count/addresses": "GET",
|
"/leaderboard/count/addresses": "GET",
|
||||||
"/leaderboard/position": "GET",
|
"/leaderboard/position": "GET",
|
||||||
"/leaderboard": "GET",
|
"/leaderboard": "GET",
|
||||||
|
"/leaderboard/": "GET",
|
||||||
"/leaderboard/rank": "GET",
|
"/leaderboard/rank": "GET",
|
||||||
"/leaderboard/ranks": "GET",
|
"/leaderboard/ranks": "GET",
|
||||||
|
"/scores/changes": "GET",
|
||||||
|
"/leaderboard/docs": "GET",
|
||||||
|
"/leaderboard/openapi.json": "GET",
|
||||||
}
|
}
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
|
@ -62,16 +68,21 @@ app.add_middleware(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/info", response_model=data.LeaderboardInfoResponse)
|
@app.get("", response_model=List[data.LeaderboardPosition])
|
||||||
async def get_leadeboard(
|
@app.get("/", response_model=List[data.LeaderboardPosition])
|
||||||
leaderboard_id: UUID,
|
async def leaderboard(
|
||||||
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
|
limit: int = Query(10),
|
||||||
|
offset: int = Query(0),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> data.LeaderboardInfoResponse:
|
) -> List[data.LeaderboardPosition]:
|
||||||
"""
|
"""
|
||||||
Returns leaderboard info.
|
Returns the leaderboard positions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
### Check if leaderboard exists
|
||||||
try:
|
try:
|
||||||
leaderboard = actions.get_leaderboard(db_session, leaderboard_id)
|
actions.get_leaderboard_by_id(db_session, leaderboard_id)
|
||||||
except NoResultFound as e:
|
except NoResultFound as e:
|
||||||
raise EngineHTTPException(
|
raise EngineHTTPException(
|
||||||
status_code=404,
|
status_code=404,
|
||||||
|
@ -81,10 +92,174 @@ async def get_leadeboard(
|
||||||
logger.error(f"Error while getting leaderboard: {e}")
|
logger.error(f"Error while getting leaderboard: {e}")
|
||||||
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
return data.LeaderboardInfoResponse(
|
leaderboard_positions = actions.get_leaderboard_positions(
|
||||||
id=leaderboard.id,
|
db_session, leaderboard_id, limit, offset
|
||||||
title=leaderboard.title,
|
)
|
||||||
description=leaderboard.description,
|
result = [
|
||||||
|
data.LeaderboardPosition(
|
||||||
|
address=position.address,
|
||||||
|
score=position.score,
|
||||||
|
rank=position.rank,
|
||||||
|
points_data=position.points_data,
|
||||||
|
)
|
||||||
|
for position in leaderboard_positions
|
||||||
|
]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("", response_model=data.LeaderboardCreatedResponse)
|
||||||
|
@app.post("/", response_model=data.LeaderboardCreatedResponse)
|
||||||
|
async def create_leaderboard(
|
||||||
|
request: Request,
|
||||||
|
leaderboard: data.LeaderboardCreateRequest = Body(...),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
|
) -> data.LeaderboardCreatedResponse:
|
||||||
|
"""
|
||||||
|
|
||||||
|
Create leaderboard.
|
||||||
|
"""
|
||||||
|
|
||||||
|
token = request.state.token
|
||||||
|
|
||||||
|
try:
|
||||||
|
created_leaderboard = actions.create_leaderboard(
|
||||||
|
db_session,
|
||||||
|
title=leaderboard.title,
|
||||||
|
description=leaderboard.description,
|
||||||
|
token=token,
|
||||||
|
)
|
||||||
|
except actions.LeaderboardCreateError as e:
|
||||||
|
logger.error(f"Error while creating leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Leaderboard creation failed. Please try again.",
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while creating leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
# Add resource to the leaderboard
|
||||||
|
|
||||||
|
return data.LeaderboardCreatedResponse(
|
||||||
|
id=created_leaderboard.id, # type: ignore
|
||||||
|
title=created_leaderboard.title, # type: ignore
|
||||||
|
description=created_leaderboard.description, # type: ignore
|
||||||
|
resource_id=created_leaderboard.resource_id, # type: ignore
|
||||||
|
created_at=created_leaderboard.created_at, # type: ignore
|
||||||
|
updated_at=created_leaderboard.updated_at, # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/{leaderboard_id}", response_model=data.LeaderboardUpdatedResponse)
|
||||||
|
async def update_leaderboard(
|
||||||
|
request: Request,
|
||||||
|
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
|
||||||
|
leaderboard: data.LeaderboardUpdateRequest = Body(...),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
|
) -> data.LeaderboardUpdatedResponse:
|
||||||
|
"""
|
||||||
|
Update leaderboard.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 access != True:
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=403, detail="You don't have access to this leaderboard."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
updated_leaderboard = actions.update_leaderboard(
|
||||||
|
db_session=db_session,
|
||||||
|
leaderboard_id=leaderboard_id,
|
||||||
|
title=leaderboard.title,
|
||||||
|
description=leaderboard.description,
|
||||||
|
)
|
||||||
|
except actions.LeaderboardUpdateError as e:
|
||||||
|
logger.error(f"Error while updating leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Leaderboard update failed. Please try again.",
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while updating leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
return data.LeaderboardUpdatedResponse(
|
||||||
|
id=updated_leaderboard.id, # type: ignore
|
||||||
|
title=updated_leaderboard.title, # type: ignore
|
||||||
|
description=updated_leaderboard.description, # type: ignore
|
||||||
|
resource_id=updated_leaderboard.resource_id, # type: ignore
|
||||||
|
created_at=updated_leaderboard.created_at, # type: ignore
|
||||||
|
updated_at=updated_leaderboard.updated_at, # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/{leaderboard_id}", response_model=data.LeaderboardDeletedResponse)
|
||||||
|
async def delete_leaderboard(
|
||||||
|
request: Request,
|
||||||
|
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
|
) -> data.LeaderboardDeletedResponse:
|
||||||
|
"""
|
||||||
|
Delete leaderboard.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 access != True:
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=403, detail="You don't have access to this leaderboard."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
deleted_leaderboard = actions.delete_leaderboard(
|
||||||
|
db_session=db_session,
|
||||||
|
leaderboard_id=leaderboard_id,
|
||||||
|
token=token,
|
||||||
|
)
|
||||||
|
except actions.LeaderboardDeleteError as e:
|
||||||
|
logger.error(f"Error while deleting leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Leaderboard deletion failed. Please try again.",
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while deleting leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
return data.LeaderboardDeletedResponse(
|
||||||
|
id=deleted_leaderboard.id, # type: ignore
|
||||||
|
title=deleted_leaderboard.title, # type: ignore
|
||||||
|
description=deleted_leaderboard.description, # type: ignore
|
||||||
|
created_at=deleted_leaderboard.created_at, # type: ignore
|
||||||
|
updated_at=deleted_leaderboard.updated_at, # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,12 +286,12 @@ async def get_leaderboards(
|
||||||
|
|
||||||
results = [
|
results = [
|
||||||
data.Leaderboard(
|
data.Leaderboard(
|
||||||
id=leaderboard.id,
|
id=leaderboard.id, # type: ignore
|
||||||
title=leaderboard.title,
|
title=leaderboard.title, # type: ignore
|
||||||
description=leaderboard.description,
|
description=leaderboard.description, # type: ignore
|
||||||
resource_id=leaderboard.resource_id,
|
resource_id=leaderboard.resource_id, # type: ignore
|
||||||
created_at=leaderboard.created_at,
|
created_at=leaderboard.created_at, # type: ignore
|
||||||
updated_at=leaderboard.updated_at,
|
updated_at=leaderboard.updated_at, # type: ignore
|
||||||
)
|
)
|
||||||
for leaderboard in leaderboards
|
for leaderboard in leaderboards
|
||||||
]
|
]
|
||||||
|
@ -126,7 +301,7 @@ async def get_leaderboards(
|
||||||
|
|
||||||
@app.get("/count/addresses", response_model=data.CountAddressesResponse)
|
@app.get("/count/addresses", response_model=data.CountAddressesResponse)
|
||||||
async def count_addresses(
|
async def count_addresses(
|
||||||
leaderboard_id: UUID,
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> data.CountAddressesResponse:
|
) -> data.CountAddressesResponse:
|
||||||
"""
|
"""
|
||||||
|
@ -150,9 +325,64 @@ async def count_addresses(
|
||||||
return data.CountAddressesResponse(count=count)
|
return data.CountAddressesResponse(count=count)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/info", response_model=data.LeaderboardInfoResponse)
|
||||||
|
async def leadeboard_info(
|
||||||
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
|
) -> data.LeaderboardInfoResponse:
|
||||||
|
"""
|
||||||
|
Returns leaderboard info.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
leaderboard = actions.get_leaderboard_info(db_session, leaderboard_id)
|
||||||
|
except NoResultFound as e:
|
||||||
|
raise EngineHTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail="Leaderboard not found.",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while getting leaderboard: {e}")
|
||||||
|
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
return data.LeaderboardInfoResponse(
|
||||||
|
id=leaderboard.id,
|
||||||
|
title=leaderboard.title,
|
||||||
|
description=leaderboard.description,
|
||||||
|
users_count=leaderboard.users_count,
|
||||||
|
last_updated_at=leaderboard.last_update,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/scores/changes")
|
||||||
|
async def get_scores_changes(
|
||||||
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
|
) -> List[data.LeaderboardScoresChangesResponse]:
|
||||||
|
"""
|
||||||
|
Returns the score history for the given address.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
scores = actions.get_leaderboard_scores_changes(db_session, leaderboard_id)
|
||||||
|
except actions.LeaderboardIsEmpty:
|
||||||
|
raise EngineHTTPException(status_code=204, detail="Leaderboard is empty.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while getting scores: {e}")
|
||||||
|
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
return [
|
||||||
|
data.LeaderboardScoresChangesResponse(
|
||||||
|
players_count=score.players_count,
|
||||||
|
date=score.date,
|
||||||
|
)
|
||||||
|
for score in scores
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@app.get("/quartiles", response_model=data.QuartilesResponse)
|
@app.get("/quartiles", response_model=data.QuartilesResponse)
|
||||||
async def quartiles(
|
async def quartiles(
|
||||||
leaderboard_id: UUID,
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> data.QuartilesResponse:
|
) -> data.QuartilesResponse:
|
||||||
"""
|
"""
|
||||||
|
@ -188,12 +418,14 @@ async def quartiles(
|
||||||
|
|
||||||
@app.get("/position", response_model=List[data.LeaderboardPosition])
|
@app.get("/position", response_model=List[data.LeaderboardPosition])
|
||||||
async def position(
|
async def position(
|
||||||
leaderboard_id: UUID,
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
address: str,
|
address: str = Query(..., description="Address to get position for."),
|
||||||
window_size: int = 1,
|
window_size: int = Query(1, description="Amount of positions up and down."),
|
||||||
limit: int = 10,
|
limit: int = Query(10),
|
||||||
offset: int = 0,
|
offset: int = Query(0),
|
||||||
normalize_addresses: bool = True,
|
normalize_addresses: bool = Query(
|
||||||
|
True, description="Normalize addresses to checksum."
|
||||||
|
),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> List[data.LeaderboardPosition]:
|
) -> List[data.LeaderboardPosition]:
|
||||||
"""
|
"""
|
||||||
|
@ -233,52 +465,12 @@ async def position(
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@app.get("", response_model=List[data.LeaderboardPosition])
|
|
||||||
@app.get("/", response_model=List[data.LeaderboardPosition])
|
|
||||||
async def leaderboard(
|
|
||||||
leaderboard_id: UUID,
|
|
||||||
limit: int = 10,
|
|
||||||
offset: int = 0,
|
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
|
||||||
) -> List[data.LeaderboardPosition]:
|
|
||||||
"""
|
|
||||||
Returns the leaderboard positions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
### Check if leaderboard exists
|
|
||||||
try:
|
|
||||||
actions.get_leaderboard_by_id(db_session, leaderboard_id)
|
|
||||||
except NoResultFound as e:
|
|
||||||
raise EngineHTTPException(
|
|
||||||
status_code=404,
|
|
||||||
detail="Leaderboard not found.",
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error while getting leaderboard: {e}")
|
|
||||||
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
leaderboard_positions = actions.get_leaderboard_positions(
|
|
||||||
db_session, leaderboard_id, limit, offset
|
|
||||||
)
|
|
||||||
result = [
|
|
||||||
data.LeaderboardPosition(
|
|
||||||
address=position.address,
|
|
||||||
score=position.score,
|
|
||||||
rank=position.rank,
|
|
||||||
points_data=position.points_data,
|
|
||||||
)
|
|
||||||
for position in leaderboard_positions
|
|
||||||
]
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/rank", response_model=List[data.LeaderboardPosition])
|
@app.get("/rank", response_model=List[data.LeaderboardPosition])
|
||||||
async def rank(
|
async def rank(
|
||||||
leaderboard_id: UUID,
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
rank: int = 1,
|
rank: int = Query(1, description="Rank to get."),
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = Query(None),
|
||||||
offset: Optional[int] = None,
|
offset: Optional[int] = Query(None),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> List[data.LeaderboardPosition]:
|
) -> List[data.LeaderboardPosition]:
|
||||||
"""
|
"""
|
||||||
|
@ -314,7 +506,8 @@ async def rank(
|
||||||
|
|
||||||
@app.get("/ranks", response_model=List[data.RanksResponse])
|
@app.get("/ranks", response_model=List[data.RanksResponse])
|
||||||
async def ranks(
|
async def ranks(
|
||||||
leaderboard_id: UUID, db_session: Session = Depends(db.yield_db_session)
|
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
|
||||||
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> List[data.RanksResponse]:
|
) -> List[data.RanksResponse]:
|
||||||
"""
|
"""
|
||||||
Returns the leaderboard rank buckets overview with score and size of bucket.
|
Returns the leaderboard rank buckets overview with score and size of bucket.
|
||||||
|
@ -347,38 +540,39 @@ async def ranks(
|
||||||
@app.put("/{leaderboard_id}/scores", response_model=List[data.LeaderboardScore])
|
@app.put("/{leaderboard_id}/scores", response_model=List[data.LeaderboardScore])
|
||||||
async def leaderboard_push_scores(
|
async def leaderboard_push_scores(
|
||||||
request: Request,
|
request: Request,
|
||||||
leaderboard_id: UUID,
|
leaderboard_id: UUID = Path(..., description="Leaderboard ID"),
|
||||||
scores: List[data.Score],
|
scores: List[data.Score] = Body(
|
||||||
overwrite: bool = False,
|
..., description="Scores to put to the leaderboard."
|
||||||
normalize_addresses: bool = True,
|
),
|
||||||
|
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(
|
||||||
|
True, description="Normalize addresses to checksum."
|
||||||
|
),
|
||||||
db_session: Session = Depends(db.yield_db_session),
|
db_session: Session = Depends(db.yield_db_session),
|
||||||
) -> List[data.LeaderboardScore]:
|
) -> List[data.LeaderboardScore]:
|
||||||
"""
|
"""
|
||||||
Put the leaderboard to the database.
|
Put the leaderboard to the database.
|
||||||
"""
|
"""
|
||||||
|
token = request.state.token
|
||||||
access = actions.check_leaderboard_resource_permissions(
|
|
||||||
db_session=db_session,
|
|
||||||
leaderboard_id=leaderboard_id,
|
|
||||||
token=request.state.token,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not access:
|
|
||||||
raise EngineHTTPException(
|
|
||||||
status_code=403, detail="You don't have access to this leaderboard."
|
|
||||||
)
|
|
||||||
|
|
||||||
### Check if leaderboard exists
|
|
||||||
try:
|
try:
|
||||||
actions.get_leaderboard_by_id(db_session, leaderboard_id)
|
access = actions.check_leaderboard_resource_permissions(
|
||||||
|
db_session=db_session,
|
||||||
|
leaderboard_id=leaderboard_id,
|
||||||
|
token=token,
|
||||||
|
)
|
||||||
except NoResultFound as e:
|
except NoResultFound as e:
|
||||||
raise EngineHTTPException(
|
raise EngineHTTPException(
|
||||||
status_code=404,
|
status_code=404,
|
||||||
detail="Leaderboard not found.",
|
detail="Leaderboard not found.",
|
||||||
)
|
)
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error while getting leaderboard: {e}")
|
if not access:
|
||||||
raise EngineHTTPException(status_code=500, detail="Internal server error")
|
raise EngineHTTPException(
|
||||||
|
status_code=403, detail="You don't have access to this leaderboard."
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
leaderboard_points = actions.add_scores(
|
leaderboard_points = actions.add_scores(
|
||||||
|
|
Ładowanie…
Reference in New Issue