Spaces:
Running
Running
import json | |
import logging | |
import hashlib | |
from dataclasses import dataclass, field, InitVar, asdict | |
from typing import List, Dict, Union | |
from datetime import datetime, date | |
import pandas as pd | |
from google.cloud import bigquery | |
class Player: | |
player_backend_user_id: str | |
player_nickname: str = field(default=None) | |
player_group: int = field(default=None) | |
partners: List[str] = field(default_factory=list) | |
badges: List[str] = field(default_factory=list) | |
rewards_status: Union[str, Dict[str, Dict[str, Union[bool, str]]]] = field( | |
default_factory=dict | |
) | |
adventure_logs: List[str] = field(default_factory=list) | |
total_active_days: int = field(default=0) | |
total_gained_scores: int = field(default=0) | |
total_finished_contents: int = field(default=0) | |
finished_videos_before_campaign: List[str] = field(default_factory=list) | |
created_at_date: datetime.date = field(default_factory=date.today) | |
updated_at_date: datetime.date = field(default_factory=date.today) | |
available_achievements: InitVar[list] = field(default=None) | |
init: InitVar[bool] = field(default=False) | |
def __post_init__(self, available_achievements: List, init: bool): | |
# If is_init is True, it means that the player is newly created | |
# and the rewards_status is not yet initialized | |
if init: | |
self.assign_group() | |
self.add_adventure_log("你並未參與這次與狐貍貓的冒險,請在下次活動中參與冒險吧!") | |
if available_achievements is not None: | |
self.rewards_status.update( | |
{ | |
achievement_key: { | |
"is_completed": False, | |
"is_issued": False, | |
} | |
for achievement_key in available_achievements | |
} | |
) | |
self.serialize_rewards_status() | |
else: | |
self.badges = self.badges.tolist() | |
self.partners = self.partners.tolist() | |
self.adventure_logs = self.adventure_logs.tolist() | |
self.finished_videos_before_campaign = ( | |
self.finished_videos_before_campaign.tolist() | |
) | |
self.assign_weekly_partner() | |
# Serialization and Deserialization is for content type of rewards_status | |
# rewards_status have to be serialized before inserting into BigQuery | |
def serialize_rewards_status(self): | |
self.rewards_status = json.dumps(self.rewards_status) | |
def deserialize_rewards_status(self): | |
self.rewards_status = json.loads(self.rewards_status) | |
def add_partner(self, partner: str): | |
if partner and partner not in self.partners: | |
self.partners.append(partner) | |
logging.info( | |
f"Player {self.player_backend_user_id} has a new partner: {partner}" | |
) | |
def add_badge(self, badge: str): | |
if badge and badge not in self.badges: | |
self.badges.append(badge) | |
logging.info( | |
f"Player {self.player_backend_user_id} earned a new badge: {badge}" | |
) | |
def add_adventure_log(self, log: str): | |
self.adventure_logs.append(log) | |
logging.info( | |
f"Player {self.player_backend_user_id} has a new adventure log: {log}" | |
) | |
def update_total_gained_scores(self, gained_scores: int): | |
self.total_gained_scores = gained_scores | |
def update_total_finished_contents(self, finished_contents_count: int): | |
self.total_finished_contents = finished_contents_count | |
def update_total_active_days(self, active_days: int): | |
self.total_active_days = active_days | |
def update_rewards_status( | |
self, key: str, value: bool, target=["is_completed", "is_issued"] | |
): | |
self.deserialize_rewards_status() | |
self.rewards_status[key][target] = value | |
self.serialize_rewards_status() | |
def hash_user_id(self): | |
hashed = hashlib.sha256(self.player_backend_user_id.encode()).hexdigest() | |
hash_int = int(hashed, 16) | |
return hash_int % 40 | |
def assign_group(self): | |
self.player_group = self.hash_user_id() | |
partner_group = self.player_group // 10 | |
group_base_partners = { | |
0: "phoenix_1", | |
1: "pegasus_1", | |
2: "dragon_1", | |
3: "griffin_1", | |
} | |
self.add_partner(group_base_partners[partner_group]) | |
def assign_weekly_partner(self): | |
event_start_date = datetime(2023, 12, 4).date() | |
current_date = datetime.now().date() | |
weeks_elapsed = (current_date - event_start_date).days // 7 | |
if weeks_elapsed not in range(0, 4): | |
return | |
group_base_partners = { | |
0: "phoenix", | |
1: "pegasus", | |
2: "dragon", | |
3: "griffin", | |
} | |
base_partner = group_base_partners[self.player_group // 10] | |
stage_partner = ( | |
f"{base_partner}_{weeks_elapsed + 1}" # +1 to start from stage 1 | |
) | |
if stage_partner not in self.partners: | |
self.add_partner(stage_partner) | |
def display_player_info(self): | |
logging.info(f"Player Backend User ID: {self.player_backend_user_id}") | |
logging.info(f"Player Group: {self.player_group}") | |
logging.info(f"Partners: {self.partners}") | |
logging.info(f"Badges: {self.badges}") | |
logging.info(f"Adventure Logs: {self.adventure_logs}") | |
logging.info(f"Rewards Status: {self.rewards_status}") | |
logging.info(f"Total Gained Scores: {self.total_gained_scores}") | |
logging.info(f"Total Finished Contents: {self.total_finished_contents}") | |
logging.info(f"Total Active Days: {self.total_active_days}") | |
logging.info(f"Created At: {self.created_at_date}") | |
logging.info(f"Updated At: {self.updated_at_date}") | |
def get_incomplete_rewards(self): | |
self.deserialize_rewards_status() | |
incomplete_rewards = list( | |
{ | |
k: v for k, v in self.rewards_status.items() if not v["is_completed"] | |
}.keys() | |
) | |
self.serialize_rewards_status() | |
return incomplete_rewards | |
def get_big_query_schema(): | |
return [ | |
bigquery.SchemaField("player_backend_user_id", "STRING", mode="REQUIRED"), | |
bigquery.SchemaField("player_group", "INTEGER", mode="NULLABLE"), | |
bigquery.SchemaField("partners", "STRING", mode="REPEATED"), | |
bigquery.SchemaField("badges", "STRING", mode="REPEATED"), | |
bigquery.SchemaField("rewards_status", "STRING", mode="REQUIRED"), | |
bigquery.SchemaField("adventure_logs", "STRING", mode="REPEATED"), | |
bigquery.SchemaField("total_active_days", "INTEGER", mode="REQUIRED"), | |
bigquery.SchemaField("total_gained_scores", "INTEGER", mode="REQUIRED"), | |
bigquery.SchemaField("total_finished_contents", "INTEGER", mode="REQUIRED"), | |
bigquery.SchemaField("created_at_date", "DATE", mode="REQUIRED"), | |
bigquery.SchemaField("updated_at_date", "DATE", mode="REQUIRED"), | |
] | |
def from_dict(series: pd.Series) -> "Player": | |
data = series.copy() | |
return Player(**data) | |
def to_dict(self) -> Dict: | |
data = asdict(self) | |
# Convert datetime.date objects to string | |
for date_field in ["created_at_date", "updated_at_date"]: | |
if data.get(date_field): | |
data[date_field] = data[date_field].strftime("%Y-%m-%d") | |
return data | |