Merge pull request #1008 from moonstream-to/leaderboard-metadata-sm

Leaderboard metadata simplify.
pull/1010/head
Andrey Dolgolev 2024-01-30 16:01:45 +02:00 zatwierdzone przez GitHub
commit 11f5252b8e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
5 zmienionych plików z 217 dodań i 47 usunięć

Wyświetl plik

@ -0,0 +1,50 @@
"""leaderboard metadata
Revision ID: 71e888082a6d
Revises: cc80e886e153
Create Date: 2023-11-15 13:21:16.108399
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "71e888082a6d"
down_revision = "cc80e886e153"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"leaderboards",
sa.Column(
"blockchain_ids",
sa.ARRAY(sa.Integer()),
nullable=False,
server_default="{}",
),
)
op.add_column(
"leaderboards",
sa.Column(
"wallet_connect", sa.Boolean(), nullable=False, server_default="false"
),
)
op.add_column(
"leaderboards",
sa.Column(
"columns_names", postgresql.JSONB(astext_type=sa.Text()), nullable=True
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("leaderboards", "columns_names")
op.drop_column("leaderboards", "wallet_connect")
op.drop_column("leaderboards", "blockchain_ids")
# ### end Alembic commands ###

Wyświetl plik

@ -22,7 +22,14 @@ from sqlalchemy.engine import Row
from web3 import Web3
from web3.types import ChecksumAddress
from .data import Score, LeaderboardScore, LeaderboardConfigUpdate, LeaderboardConfig
from .data import (
Score,
LeaderboardScore,
LeaderboardConfigUpdate,
LeaderboardConfig,
LeaderboardPosition,
ColumnsNames,
)
from .contracts import Dropper_interface, ERC20_interface, Terminus_interface
from .models import (
DropperClaimant,
@ -1278,7 +1285,7 @@ def get_leaderboard_score(
def get_leaderboard_positions(
db_session: Session,
leaderboard_id,
leaderboard_id: uuid.UUID,
limit: int,
offset: int,
version_number: Optional[int] = None,
@ -1491,13 +1498,30 @@ def create_leaderboard(
title: str,
description: Optional[str],
token: Optional[Union[uuid.UUID, str]] = None,
wallet_connect: bool = False,
blockchain_ids: List[int] = [],
columns_names: ColumnsNames = None,
) -> Leaderboard:
"""
Create a leaderboard
"""
if columns_names is not None:
columns_names = columns_names.dict()
if not token:
token = uuid.UUID(MOONSTREAM_ADMIN_ACCESS_TOKEN)
try:
leaderboard = Leaderboard(title=title, description=description)
# deduplicate and sort
blockchain_ids = sorted(list(set(blockchain_ids)))
leaderboard = Leaderboard(
title=title,
description=description,
wallet_connect=wallet_connect,
blockchain_ids=blockchain_ids,
columns_names=columns_names,
)
db_session.add(leaderboard)
db_session.commit()
@ -1557,6 +1581,9 @@ def update_leaderboard(
leaderboard_id: uuid.UUID,
title: Optional[str],
description: Optional[str],
wallet_connect: Optional[bool],
blockchain_ids: Optional[List[int]],
columns_names: Optional[ColumnsNames],
) -> Leaderboard:
"""
Update a leaderboard
@ -1570,6 +1597,23 @@ def update_leaderboard(
leaderboard.title = title
if description is not None:
leaderboard.description = description
if wallet_connect is not None:
leaderboard.wallet_connect = wallet_connect
if blockchain_ids is not None:
# deduplicate and sort
blockchain_ids = sorted(list(set(blockchain_ids)))
leaderboard.blockchain_ids = blockchain_ids
if columns_names is not None:
if leaderboard.columns_names is not None:
current_columns_names = ColumnsNames(**leaderboard.columns_names)
for key, value in columns_names.dict(exclude_none=True).items():
setattr(current_columns_names, key, value)
else:
current_columns_names = columns_names
leaderboard.columns_names = current_columns_names.dict()
db_session.commit()

Wyświetl plik

@ -365,11 +365,22 @@ class LeaderboardScore(BaseModel):
points_data: Dict[str, Any]
class ColumnsNames(BaseModel):
rank: Optional[str] = None
address: Optional[str] = None
score: Optional[str] = None
points_data: Optional[str] = None
points_data_fields: Optional[Dict[str, str]] = None
class Leaderboard(BaseModel):
id: UUID
title: str
description: Optional[str] = None
resource_id: Optional[UUID] = None
wallet_connect: bool = False
blockchain_ids: List[int] = Field(default_factory=list)
columns_names: Optional[ColumnsNames] = None
created_at: datetime
updated_at: datetime
@ -385,6 +396,9 @@ class LeaderboardInfoResponse(BaseModel):
class LeaderboardCreateRequest(BaseModel):
title: str
description: Optional[str] = None
wallet_connect: bool = False
blockchain_ids: List[int] = Field(default_factory=list)
columns_names: Optional[ColumnsNames] = None
class LeaderboardCreatedResponse(BaseModel):
@ -392,6 +406,9 @@ class LeaderboardCreatedResponse(BaseModel):
title: str
description: Optional[str] = None
resource_id: Optional[UUID] = None
wallet_connect: bool = False
blockchain_ids: List[int] = Field(default_factory=list)
columns_names: Optional[ColumnsNames] = None
created_at: datetime
updated_at: datetime
@ -404,6 +421,9 @@ class LeaderboardUpdatedResponse(BaseModel):
title: str
description: Optional[str] = None
resource_id: Optional[UUID] = None
wallet_connect: bool = False
blockchain_ids: List[int] = Field(default_factory=list)
columns_names: Optional[ColumnsNames] = None
created_at: datetime
updated_at: datetime
@ -414,6 +434,9 @@ class LeaderboardUpdatedResponse(BaseModel):
class LeaderboardUpdateRequest(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
wallet_connect: bool = False
blockchain_ids: List[int] = Field(default_factory=list)
columns_names: Optional[ColumnsNames] = None
class LeaderboardDeletedResponse(BaseModel):
@ -421,6 +444,9 @@ class LeaderboardDeletedResponse(BaseModel):
title: str
description: Optional[str] = None
resource_id: Optional[UUID] = None
wallet_connect: bool = False
blockchain_ids: List[int] = Field(default_factory=list)
columns_names: Optional[ColumnsNames] = None
created_at: datetime
updated_at: datetime

Wyświetl plik

@ -1,6 +1,7 @@
import uuid
from sqlalchemy import (
ARRAY,
DECIMAL,
VARCHAR,
BigInteger,
@ -335,7 +336,6 @@ class CallRequest(Base):
class Leaderboard(Base): # type: ignore
__tablename__ = "leaderboards"
# __table_args__ = (UniqueConstraint("dropper_contract_id", "address"),)
id = Column(
UUID(as_uuid=True),
@ -347,6 +347,10 @@ class Leaderboard(Base): # type: ignore
title = Column(VARCHAR(128), nullable=False)
description = Column(String, nullable=True)
resource_id = Column(UUID(as_uuid=True), nullable=True, index=True)
blockchain_ids = Column(ARRAY(Integer), nullable=False, default=[])
wallet_connect = Column(Boolean, default=False, nullable=False)
columns_names = Column(JSONB, nullable=True)
created_at = Column(
DateTime(timezone=True), server_default=utcnow(), nullable=False
)

Wyświetl plik

@ -2,7 +2,7 @@
Leaderboard API.
"""
import logging
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Any, Union
from uuid import UUID
from bugout.exceptions import BugoutResponseException
@ -92,9 +92,12 @@ app.add_middleware(
"",
response_model=List[data.LeaderboardPosition],
tags=["Public Endpoints"],
include_in_schema=False,
)
@app.get("/", response_model=List[data.LeaderboardPosition], tags=["Public Endpoints"])
@app.get(
"/",
response_model=List[data.LeaderboardPosition],
tags=["Public Endpoints"],
)
async def leaderboard(
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
limit: int = Query(10),
@ -108,7 +111,7 @@ async def leaderboard(
### Check if leaderboard exists
try:
actions.get_leaderboard_by_id(db_session, leaderboard_id)
leaderboard = actions.get_leaderboard_by_id(db_session, leaderboard_id)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
@ -119,8 +122,9 @@ async def leaderboard(
raise EngineHTTPException(status_code=500, detail="Internal server error")
leaderboard_positions = actions.get_leaderboard_positions(
db_session, leaderboard_id, limit, offset, version
db_session, leaderboard.id, limit, offset, version
)
result = [
data.LeaderboardPosition(
address=position.address,
@ -150,7 +154,6 @@ async def create_leaderboard(
Authorization: str = AuthHeader,
) -> data.LeaderboardCreatedResponse:
"""
Create leaderboard.
"""
@ -162,6 +165,9 @@ async def create_leaderboard(
title=leaderboard.title,
description=leaderboard.description,
token=token,
wallet_connect=leaderboard.wallet_connect,
blockchain_ids=leaderboard.blockchain_ids,
columns_names=leaderboard.columns_names,
)
except actions.LeaderboardCreateError as e:
logger.error(f"Error while creating leaderboard: {e}")
@ -177,12 +183,15 @@ async def create_leaderboard(
# 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
id=created_leaderboard.id,
title=created_leaderboard.title,
description=created_leaderboard.description,
resource_id=created_leaderboard.resource_id,
wallet_connect=created_leaderboard.wallet_connect,
blockchain_ids=created_leaderboard.blockchain_ids,
columns_names=created_leaderboard.columns_names,
created_at=created_leaderboard.created_at,
updated_at=created_leaderboard.updated_at,
)
@ -226,6 +235,9 @@ async def update_leaderboard(
leaderboard_id=leaderboard_id,
title=leaderboard.title,
description=leaderboard.description,
wallet_connect=leaderboard.wallet_connect,
blockchain_ids=leaderboard.blockchain_ids,
columns_names=leaderboard.columns_names,
)
except actions.LeaderboardUpdateError as e:
logger.error(f"Error while updating leaderboard: {e}")
@ -239,12 +251,15 @@ async def update_leaderboard(
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
id=updated_leaderboard.id,
title=updated_leaderboard.title,
description=updated_leaderboard.description,
resource_id=updated_leaderboard.resource_id,
wallet_connect=updated_leaderboard.wallet_connect,
blockchain_ids=updated_leaderboard.blockchain_ids,
columns_names=updated_leaderboard.columns_names,
created_at=updated_leaderboard.created_at,
updated_at=updated_leaderboard.updated_at,
)
@ -299,11 +314,15 @@ async def delete_leaderboard(
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
id=deleted_leaderboard.id,
title=deleted_leaderboard.title,
description=deleted_leaderboard.description,
resource_id=deleted_leaderboard.resource_id,
wallet_connect=deleted_leaderboard.wallet_connect,
blockchain_ids=deleted_leaderboard.blockchain_ids,
columns_names=deleted_leaderboard.columns_names,
created_at=deleted_leaderboard.created_at,
updated_at=deleted_leaderboard.updated_at,
)
@ -336,12 +355,15 @@ async def get_leaderboards(
results = [
data.Leaderboard(
id=leaderboard.id, # type: ignore
title=leaderboard.title, # type: ignore
description=leaderboard.description, # type: ignore
resource_id=leaderboard.resource_id, # type: ignore
created_at=leaderboard.created_at, # type: ignore
updated_at=leaderboard.updated_at, # type: ignore
id=leaderboard.id,
title=leaderboard.title,
description=leaderboard.description,
resource_id=leaderboard.resource_id,
wallet_connect=leaderboard.wallet_connect,
blockchain_ids=leaderboard.blockchain_ids,
columns_names=leaderboard.columns_names,
created_at=leaderboard.created_at,
updated_at=leaderboard.updated_at,
)
for leaderboard in leaderboards
]
@ -453,7 +475,7 @@ async def quartiles(
"""
### Check if leaderboard exists
try:
actions.get_leaderboard_by_id(db_session, leaderboard_id)
leaderboard = actions.get_leaderboard_by_id(db_session, leaderboard_id)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
@ -472,11 +494,32 @@ async def quartiles(
logger.error(f"Error while getting quartiles: {e}")
raise EngineHTTPException(status_code=500, detail="Internal server error")
return data.QuartilesResponse(
percentile_25={"address": q1[0], "score": q1[1], "rank": q1[2]},
percentile_50={"address": q2[0], "score": q2[1], "rank": q2[2]},
percentile_75={"address": q3[0], "score": q3[1], "rank": q3[2]},
)
if len(leaderboard.columns_names) > 0:
result = data.QuartilesResponse(
percentile_25={
"column_1": q1.address,
"column_2": q1.rank,
"column_3": q1.score,
},
percentile_50={
"column_1": q2.address,
"column_2": q2.rank,
"column_3": q2.score,
},
percentile_75={
"column_1": q3.address,
"column_2": q3.rank,
"column_3": q3.score,
},
)
else:
result = data.QuartilesResponse(
percentile_25={"address": q1.address, "rank": q1.rank, "score": q1.score},
percentile_50={"address": q2.address, "rank": q2.rank, "score": q2.score},
percentile_75={"address": q3.address, "rank": q3.rank, "score": q3.score},
)
return result
@app.get(
@ -503,7 +546,7 @@ async def position(
### Check if leaderboard exists
try:
actions.get_leaderboard_by_id(db_session, leaderboard_id)
leaderboard = actions.get_leaderboard_by_id(db_session, leaderboard_id)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
@ -540,7 +583,9 @@ async def position(
@app.get(
"/rank", response_model=List[data.LeaderboardPosition], tags=["Public Endpoints"]
"/rank",
response_model=List[data.LeaderboardPosition],
tags=["Public Endpoints"],
)
async def rank(
leaderboard_id: UUID = Query(..., description="Leaderboard ID"),
@ -556,7 +601,7 @@ async def rank(
### Check if leaderboard exists
try:
actions.get_leaderboard_by_id(db_session, leaderboard_id)
leaderboard = actions.get_leaderboard_by_id(db_session, leaderboard_id)
except NoResultFound as e:
raise EngineHTTPException(
status_code=404,
@ -574,14 +619,15 @@ async def rank(
offset=offset,
version_number=version,
)
results = [
data.LeaderboardPosition(
address=rank_position.address,
score=rank_position.score,
rank=rank_position.rank,
points_data=rank_position.points_data,
address=position.address,
score=position.score,
rank=position.rank,
points_data=position.points_data,
)
for rank_position in leaderboard_rank
for position in leaderboard_rank
]
return results