Move logic for calculating play statistics into common module and backend base module.
This commit is contained in:
parent
bcfd4fcf24
commit
bb7916d3c4
@ -2,7 +2,7 @@ from abc import ABC
|
||||
import traceback
|
||||
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
|
||||
|
||||
from bemani.common import Model, ValidatedDict, Profile, GameConstants, Time
|
||||
from bemani.common import Model, ValidatedDict, Profile, PlayStatistics, GameConstants, Time
|
||||
from bemani.data import Config, Data, UserID, RemoteUser
|
||||
|
||||
|
||||
@ -318,7 +318,7 @@ class Base(ABC):
|
||||
raise Exception('Trying to save a remote profile locally!')
|
||||
self.data.local.user.put_profile(self.game, self.version, userid, profile)
|
||||
|
||||
def update_play_statistics(self, userid: UserID, extra_stats: Optional[Dict[str, Any]]=None) -> None:
|
||||
def update_play_statistics(self, userid: UserID, stats: Optional[PlayStatistics] = None) -> None:
|
||||
"""
|
||||
Given a user ID, calculate new play statistics.
|
||||
|
||||
@ -327,18 +327,19 @@ class Base(ABC):
|
||||
|
||||
Parameters:
|
||||
userid - The user ID we are binding the profile for.
|
||||
stats - A play statistics object we should store extra data from.
|
||||
"""
|
||||
if RemoteUser.is_remote(userid):
|
||||
raise Exception('Trying to save remote statistics locally!')
|
||||
|
||||
# We store the play statistics in a series-wide settings blob so its available
|
||||
# across all game versions, since it isn't game-specific.
|
||||
settings = self.get_play_statistics(userid)
|
||||
settings = self.data.local.game.get_settings(self.game, userid) or ValidatedDict({})
|
||||
|
||||
if extra_stats is not None:
|
||||
for key in extra_stats:
|
||||
if stats is not None:
|
||||
for key in stats:
|
||||
# Make sure we don't override anything we manage here
|
||||
if key in [
|
||||
if key in {
|
||||
'total_plays',
|
||||
'today_plays',
|
||||
'total_days',
|
||||
@ -346,14 +347,14 @@ class Base(ABC):
|
||||
'last_play_timestamp',
|
||||
'last_play_date',
|
||||
'consecutive_days',
|
||||
]:
|
||||
}:
|
||||
continue
|
||||
# Safe to copy over
|
||||
settings[key] = extra_stats[key]
|
||||
settings[key] = stats[key]
|
||||
|
||||
settings.replace_int('total_plays', settings.get_int('total_plays') + 1)
|
||||
settings.replace_int('first_play_timestamp', settings.get_int('first_play_timestamp', int(Time.now())))
|
||||
settings.replace_int('last_play_timestamp', int(Time.now()))
|
||||
settings.replace_int('first_play_timestamp', settings.get_int('first_play_timestamp', Time.now()))
|
||||
settings.replace_int('last_play_timestamp', Time.now())
|
||||
|
||||
last_play_date = settings.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
@ -363,13 +364,13 @@ class Base(ABC):
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
# We already played today, add one
|
||||
# We already played today, add one.
|
||||
settings.replace_int('today_plays', settings.get_int('today_plays') + 1)
|
||||
else:
|
||||
# We played on a new day, so count total days up
|
||||
# We played on a new day, so count total days up.
|
||||
settings.replace_int('total_days', settings.get_int('total_days') + 1)
|
||||
|
||||
# We haven't played yet today, reset to one
|
||||
# We played only once today (the play we are saving).
|
||||
settings.replace_int('today_plays', 1)
|
||||
if (
|
||||
last_play_date[0] == yesterday_play_date[0] and
|
||||
@ -379,7 +380,7 @@ class Base(ABC):
|
||||
# We played yesterday, add one to consecutive days
|
||||
settings.replace_int('consecutive_days', settings.get_int('consecutive_days') + 1)
|
||||
else:
|
||||
# We haven't played yet today or yesterday, reset consecutive days
|
||||
# We haven't played yesterday, so we have only one consecutive day.
|
||||
settings.replace_int('consecutive_days', 1)
|
||||
settings.replace_int_array('last_play_date', 3, today_play_date)
|
||||
|
||||
@ -413,7 +414,7 @@ class Base(ABC):
|
||||
settings = ValidatedDict()
|
||||
return settings
|
||||
|
||||
def get_play_statistics(self, userid: UserID) -> ValidatedDict:
|
||||
def get_play_statistics(self, userid: UserID) -> PlayStatistics:
|
||||
"""
|
||||
Given a user ID, get the play statistics.
|
||||
|
||||
@ -433,8 +434,93 @@ class Base(ABC):
|
||||
consecutive_days - Number of consecutive days played at this time.
|
||||
"""
|
||||
if RemoteUser.is_remote(userid):
|
||||
return ValidatedDict({})
|
||||
return PlayStatistics(
|
||||
self.game,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Time.now(),
|
||||
Time.now(),
|
||||
)
|
||||
|
||||
# Grab the last saved settings and today's date.
|
||||
settings = self.data.local.game.get_settings(self.game, userid)
|
||||
today_play_date = Time.todays_date()
|
||||
yesterday_play_date = Time.yesterdays_date()
|
||||
if settings is None:
|
||||
return ValidatedDict({})
|
||||
return settings
|
||||
return PlayStatistics(
|
||||
self.game,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Time.now(),
|
||||
Time.now(),
|
||||
)
|
||||
|
||||
# Calculate whether we are on our first play of the day or not.
|
||||
last_play_date = settings.get_int_array('last_play_date', 3)
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
# We last played today, so the total days and today plays are accurate
|
||||
# as stored.
|
||||
today_count = settings.get_int('today_plays', 0)
|
||||
total_days = settings.get_int('total_days', 1)
|
||||
consecutive_days = settings.get_int('consecutive_days', 1)
|
||||
else:
|
||||
if (
|
||||
last_play_date[0] != 0 and
|
||||
last_play_date[1] != 0 and
|
||||
last_play_date[2] != 0
|
||||
):
|
||||
# We've played before but not today, so the total days is
|
||||
# the stored count plus today.
|
||||
total_days = settings.get_int('total_days') + 1
|
||||
else:
|
||||
# We've never played before, so the total days is just 1.
|
||||
total_days = 1
|
||||
|
||||
if (
|
||||
last_play_date[0] == yesterday_play_date[0] and
|
||||
last_play_date[1] == yesterday_play_date[1] and
|
||||
last_play_date[2] == yesterday_play_date[2]
|
||||
):
|
||||
# We've played before, and it was yesterday, so today is the
|
||||
# next consecutive day. So add the current value and today.
|
||||
consecutive_days = settings.get_int('consecutive_days') + 1
|
||||
else:
|
||||
# This is the first consecutive day, we've either never played
|
||||
# or we played a bunch but in the past before yesterday.
|
||||
consecutive_days = 1
|
||||
|
||||
# We haven't played yet today.
|
||||
today_count = 0
|
||||
|
||||
# Grab any extra settings that a game may have stored here.
|
||||
extra_settings: Dict[str, Any] = {
|
||||
key: value for (key, value) in settings.items()
|
||||
if key not in {
|
||||
'total_plays',
|
||||
'today_plays',
|
||||
'total_days',
|
||||
'first_play_timestamp',
|
||||
'last_play_timestamp',
|
||||
'last_play_date',
|
||||
'consecutive_days',
|
||||
}
|
||||
}
|
||||
|
||||
return PlayStatistics(
|
||||
self.game,
|
||||
settings.get_int('total_plays') + 1,
|
||||
today_count + 1,
|
||||
total_days,
|
||||
consecutive_days,
|
||||
settings.get_int('first_play_timestamp', Time.now()),
|
||||
settings.get_int('last_play_timestamp', Time.now()),
|
||||
extra_settings,
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
from bemani.backend.ddr.base import DDRBase
|
||||
from bemani.common import Time, Profile, intish
|
||||
from bemani.common import Profile, intish
|
||||
from bemani.data import Score, UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -272,19 +272,9 @@ class DDRGameLoadDailyHandler(DDRBase):
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Day play counts
|
||||
last_play_date = play_stats.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = play_stats.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
daycount = Node.void('daycount')
|
||||
game.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(today_count))
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unclear how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
|
@ -333,19 +333,9 @@ class DDR2013(
|
||||
workout.set_attribute('disp', '1')
|
||||
|
||||
# Daily play counts
|
||||
last_play_date = play_stats.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = play_stats.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
daycount = Node.void('daycount')
|
||||
root.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(today_count))
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unknown how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
|
@ -388,19 +388,9 @@ class DDR2014(
|
||||
totalcalorie.set_attribute('total', str(total))
|
||||
|
||||
# Daily play counts
|
||||
last_play_date = play_stats.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = play_stats.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
daycount = Node.void('daycount')
|
||||
root.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(today_count))
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unknown how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
|
@ -408,19 +408,9 @@ class DDRX3(
|
||||
workout.set_attribute('disp', '1')
|
||||
|
||||
# Daily play counts
|
||||
last_play_date = play_stats.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = play_stats.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
daycount = Node.void('daycount')
|
||||
root.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(today_count))
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unknown how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
|
@ -12,7 +12,7 @@ from bemani.backend.museca.common import (
|
||||
MusecaGameSaveMusicHandler,
|
||||
MusecaGameShopHandler,
|
||||
)
|
||||
from bemani.common import Time, VersionConstants, Profile, ID
|
||||
from bemani.common import VersionConstants, Profile, ID
|
||||
from bemani.data import UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -215,19 +215,9 @@ class Museca1(
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0)))
|
||||
game.add_child(Node.u32('daily_count', today_count))
|
||||
game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0)))
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
|
@ -13,7 +13,7 @@ from bemani.backend.museca.common import (
|
||||
MusecaGameShopHandler,
|
||||
)
|
||||
from bemani.backend.museca.museca1 import Museca1
|
||||
from bemani.common import Time, VersionConstants, Profile, ID
|
||||
from bemani.common import VersionConstants, Profile, ID
|
||||
from bemani.data import UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -354,19 +354,9 @@ class Museca1Plus(
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0)))
|
||||
game.add_child(Node.u32('daily_count', today_count))
|
||||
game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0)))
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
|
@ -6,7 +6,7 @@ from typing import Dict, List, Optional
|
||||
from bemani.backend.popn.base import PopnMusicBase
|
||||
from bemani.backend.popn.lapistoria import PopnMusicLapistoria
|
||||
|
||||
from bemani.common import Time, Profile, VersionConstants
|
||||
from bemani.common import Profile, VersionConstants
|
||||
from bemani.data import UserID, Link
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -546,20 +546,10 @@ class PopnMusicEclale(PopnMusicBase):
|
||||
|
||||
# player statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
account.add_child(Node.s16('total_play_cnt', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s16('today_play_cnt', today_count))
|
||||
account.add_child(Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0)))
|
||||
account.add_child(Node.s16('total_days', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s16('total_play_cnt', statistics.total_plays))
|
||||
account.add_child(Node.s16('today_play_cnt', statistics.today_plays))
|
||||
account.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
||||
account.add_child(Node.s16('total_days', statistics.total_days))
|
||||
account.add_child(Node.s16('interval_day', 0))
|
||||
|
||||
# Set up info node
|
||||
|
@ -6,7 +6,7 @@ from bemani.backend.popn.base import PopnMusicBase
|
||||
from bemani.backend.popn.tunestreet import PopnMusicTuneStreet
|
||||
|
||||
from bemani.backend.base import Status
|
||||
from bemani.common import Profile, VersionConstants, Time, ID
|
||||
from bemani.common import Profile, VersionConstants, ID
|
||||
from bemani.data import Score, Link, UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -120,19 +120,9 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
|
||||
# Statistics section and scores section
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
base.add_child(Node.s32('total_play_cnt', statistics.get_int('total_plays', 0)))
|
||||
base.add_child(Node.s16('today_play_cnt', today_count))
|
||||
base.add_child(Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0)))
|
||||
base.add_child(Node.s32('total_play_cnt', statistics.total_plays))
|
||||
base.add_child(Node.s16('today_play_cnt', statistics.today_plays))
|
||||
base.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
||||
|
||||
# Number of rivals that are active for this version.
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
|
@ -6,7 +6,7 @@ from bemani.backend.popn.base import PopnMusicBase
|
||||
from bemani.backend.popn.sunnypark import PopnMusicSunnyPark
|
||||
|
||||
from bemani.backend.base import Status
|
||||
from bemani.common import Profile, VersionConstants, Time, ID
|
||||
from bemani.common import Profile, VersionConstants, ID
|
||||
from bemani.data import UserID, Link
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -340,20 +340,10 @@ class PopnMusicLapistoria(PopnMusicBase):
|
||||
|
||||
# Statistics section and scores section
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
account.add_child(Node.s16('total_play_cnt', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s16('today_play_cnt', today_count))
|
||||
account.add_child(Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0)))
|
||||
account.add_child(Node.s16('total_days', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s16('total_play_cnt', statistics.total_plays))
|
||||
account.add_child(Node.s16('today_play_cnt', statistics.today_plays))
|
||||
account.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
||||
account.add_child(Node.s16('total_days', statistics.total_days))
|
||||
account.add_child(Node.s16('interval_day', 0))
|
||||
|
||||
# Number of rivals that are active for this version.
|
||||
|
@ -6,7 +6,7 @@ from bemani.backend.popn.base import PopnMusicBase
|
||||
from bemani.backend.popn.fantasia import PopnMusicFantasia
|
||||
|
||||
from bemani.backend.base import Status
|
||||
from bemani.common import Profile, VersionConstants, Time, ID
|
||||
from bemani.common import Profile, VersionConstants, ID
|
||||
from bemani.data import UserID, Link
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -81,19 +81,9 @@ class PopnMusicSunnyPark(PopnMusicBase):
|
||||
|
||||
# Statistics section and scores section
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
base.add_child(Node.s32('total_play_cnt', statistics.get_int('total_plays', 0)))
|
||||
base.add_child(Node.s16('today_play_cnt', today_count))
|
||||
base.add_child(Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0)))
|
||||
base.add_child(Node.s32('total_play_cnt', statistics.total_plays))
|
||||
base.add_child(Node.s16('today_play_cnt', statistics.today_plays))
|
||||
base.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
||||
|
||||
# Number of rivals that are active for this version.
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
|
@ -796,7 +796,6 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
||||
root.add_child(account)
|
||||
account.add_child(Node.string('g_pm_id', self.format_extid(profile.extid)))
|
||||
account.add_child(Node.string('name', profile.get_str('name', 'なし')))
|
||||
account.add_child(Node.s16('tutorial', profile.get_int('tutorial')))
|
||||
account.add_child(Node.s16('area_id', profile.get_int('area_id')))
|
||||
account.add_child(Node.s16('use_navi', profile.get_int('use_navi')))
|
||||
account.add_child(Node.s16('read_news', profile.get_int('read_news')))
|
||||
@ -817,6 +816,53 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
||||
account.add_child(Node.s32('player_point', profile.get_int('player_point', 300)))
|
||||
account.add_child(Node.s32_array('power_point_list', profile.get_int_array('power_point_list', 20, [-1] * 20)))
|
||||
|
||||
# Tutorial handling is all sorts of crazy in UsaNeko. the tutorial flag
|
||||
# is split into two values. The game uses the flag modulo 100 for standard
|
||||
# tutorial progress, and the flag divided by 100 for the hold note tutorial.
|
||||
# The hold note tutorial will activate the first time you choose a song with
|
||||
# hold notes in it, regardless of whether you say yes/no. The total times you
|
||||
# have ever played Pop'n Music also factors in for some screens. The enumerated
|
||||
# values are as follows:
|
||||
#
|
||||
# Lower values:
|
||||
# 0 - Should not be used, presenting this to the game causes buggy behavior.
|
||||
# 1 - User has not been prompted to choose any tutorials. Prompts the user for the
|
||||
# menu tutorial. If the user selects "no" then moves the tutorial state to
|
||||
# "2" at the end of the round. If the user selects "yes" then moves the
|
||||
# tutorial state to "3" immediately and starts the menu tutorial. If the total
|
||||
# play count for this user is "1" when this value is hit, the game will bug
|
||||
# out and play the hold note tutorial and then crash.
|
||||
# 2 - Prompt the user on the mode select screen asking them if they want to see
|
||||
# the menu tutorial. If the user selects "no" then moves the tutorial state
|
||||
# to "8" immediately. If the user selects "yes" then moves the tutorial state
|
||||
# to "3" immediately. If the total play count for this user is "1" when this value
|
||||
# is hit, then the game will bug out and play the hold note tutorial and then crash.
|
||||
# 3 - Display some tutorial elements on most screens, and then advance the tutorial
|
||||
# state to "4" on profile save.
|
||||
# 4 - Display some tutorial elements on most screens, and then advance the tutorial
|
||||
# state to "5" on profile save.
|
||||
# 5 - Display some tutorial elements on most screens, and then prompt user with a
|
||||
# repeat tutorial question. If the user selects "no" then moves the tutorial
|
||||
# state to "8". If the user selects "yes" then moves the tutorial state to "3".
|
||||
# 6 - Do nothing, display nothing, but advance the tutorial state to "7" at the
|
||||
# end of the game. It seems that nothing requests this state.
|
||||
# 7 - Display guide information prompt on the option select screen. Game moves
|
||||
# this to "8" after this tutorial has been displayed.
|
||||
# 8 - Do not display any more tutorial stuff, this is a terminal state.
|
||||
#
|
||||
# Upper values:
|
||||
# 0 - Should not be used, presenting this to the game causes buggy behavior.
|
||||
# 1 - Hold note tutorial has not been activated yet and will be displayed when
|
||||
# the player chooses a song with hold notes. Game moves this to "2" after this
|
||||
# tutorial has been activated.
|
||||
# 2 - Hold note tutorial was displayed to the user, but the mini-tutorial showing
|
||||
# the hold note indicator that pops up after the hold note tutorial has not
|
||||
# been displayed yet. Presumably this is just in case you play a hold note
|
||||
# song on your last stage. Game moves this to "3" after this tutorial has been
|
||||
# displayed.
|
||||
# 3 - All hold note tutorials are finished, this is a terminal state.
|
||||
account.add_child(Node.s16('tutorial', profile.get_int('tutorial')))
|
||||
|
||||
# Stuff we never change
|
||||
account.add_child(Node.s8('staff', 0))
|
||||
account.add_child(Node.s16('item_type', 0))
|
||||
@ -837,20 +883,10 @@ class PopnMusicUsaNeko(PopnMusicBase):
|
||||
|
||||
# Player statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
account.add_child(Node.s16('total_play_cnt', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s16('today_play_cnt', today_count))
|
||||
account.add_child(Node.s16('consecutive_days', statistics.get_int('consecutive_days', 0)))
|
||||
account.add_child(Node.s16('total_days', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s16('total_play_cnt', statistics.total_plays))
|
||||
account.add_child(Node.s16('today_play_cnt', statistics.today_plays))
|
||||
account.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
||||
account.add_child(Node.s16('total_days', statistics.total_days))
|
||||
account.add_child(Node.s16('interval_day', 0))
|
||||
|
||||
# Number of rivals that are active for this version.
|
||||
|
@ -705,26 +705,14 @@ class ReflecBeatColette(ReflecBeatBase):
|
||||
pdata = Node.void('pdata')
|
||||
root.add_child(pdata)
|
||||
|
||||
# Account time info
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
|
||||
account = Node.void('account')
|
||||
pdata.add_child(account)
|
||||
account.add_child(Node.s32('usrid', profile.extid))
|
||||
account.add_child(Node.s32('tpc', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s32('dpc', today_count))
|
||||
account.add_child(Node.s32('tpc', statistics.total_plays))
|
||||
account.add_child(Node.s32('dpc', statistics.today_plays))
|
||||
account.add_child(Node.s32('crd', 1))
|
||||
account.add_child(Node.s32('brd', 1))
|
||||
account.add_child(Node.s32('tdc', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s32('tdc', statistics.total_days))
|
||||
account.add_child(Node.s32('intrvld', 0))
|
||||
account.add_child(Node.s16('ver', 5))
|
||||
account.add_child(Node.u64('pst', 0))
|
||||
|
@ -927,27 +927,15 @@ class ReflecBeatGroovin(ReflecBeatBase):
|
||||
pdata = Node.void('pdata')
|
||||
root.add_child(pdata)
|
||||
|
||||
# Account time info
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
|
||||
# Account info
|
||||
account = Node.void('account')
|
||||
pdata.add_child(account)
|
||||
account.add_child(Node.s32('usrid', profile.extid))
|
||||
account.add_child(Node.s32('tpc', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s32('dpc', today_count))
|
||||
account.add_child(Node.s32('tpc', statistics.total_plays))
|
||||
account.add_child(Node.s32('dpc', statistics.today_plays))
|
||||
account.add_child(Node.s32('crd', 1))
|
||||
account.add_child(Node.s32('brd', 1))
|
||||
account.add_child(Node.s32('tdc', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s32('tdc', statistics.total_days))
|
||||
account.add_child(Node.s32('intrvld', 0))
|
||||
account.add_child(Node.s16('ver', 1))
|
||||
account.add_child(Node.u64('pst', 0))
|
||||
|
@ -563,23 +563,12 @@ class ReflecBeatLimelight(ReflecBeatBase):
|
||||
base.add_child(Node.s32('pc', profile.get_int('pc')))
|
||||
base.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
||||
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
|
||||
con = Node.void('con')
|
||||
pdata.add_child(con)
|
||||
con.add_child(Node.s32('day', today_count))
|
||||
con.add_child(Node.s32('cnt', statistics.get_int('total_plays')))
|
||||
con.add_child(Node.s32('total_cnt', statistics.get_int('total_plays')))
|
||||
con.add_child(Node.s32('last', statistics.get_int('last_play_timestamp')))
|
||||
con.add_child(Node.s32('day', statistics.today_plays))
|
||||
con.add_child(Node.s32('cnt', statistics.total_plays))
|
||||
con.add_child(Node.s32('total_cnt', statistics.total_plays))
|
||||
con.add_child(Node.s32('last', statistics.last_play_timestamp))
|
||||
con.add_child(Node.s32('now', Time.now()))
|
||||
|
||||
team = Node.void('team')
|
||||
|
@ -321,22 +321,11 @@ class ReflecBeat(ReflecBeatBase):
|
||||
base.add_child(Node.s16('ap', profile.get_int('ap')))
|
||||
base.add_child(Node.s32('flag', profile.get_int('flag')))
|
||||
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
|
||||
con = Node.void('con')
|
||||
pdata.add_child(con)
|
||||
con.add_child(Node.s32('day', today_count))
|
||||
con.add_child(Node.s32('cnt', statistics.get_int('total_plays')))
|
||||
con.add_child(Node.s32('last', statistics.get_int('last_play_timestamp')))
|
||||
con.add_child(Node.s32('day', statistics.today_plays))
|
||||
con.add_child(Node.s32('cnt', statistics.total_plays))
|
||||
con.add_child(Node.s32('last', statistics.last_play_timestamp))
|
||||
con.add_child(Node.s32('now', Time.now()))
|
||||
|
||||
team = Node.void('team')
|
||||
|
@ -310,18 +310,6 @@ class ReflecBeatVolzza(ReflecBeatVolzzaBase):
|
||||
pdata = Node.void('pdata')
|
||||
root.add_child(pdata)
|
||||
|
||||
# Account time info
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
|
||||
# Previous account info
|
||||
previous_version = self.previous_version()
|
||||
if previous_version:
|
||||
@ -333,11 +321,11 @@ class ReflecBeatVolzza(ReflecBeatVolzzaBase):
|
||||
account = Node.void('account')
|
||||
pdata.add_child(account)
|
||||
account.add_child(Node.s32('usrid', profile.extid))
|
||||
account.add_child(Node.s32('tpc', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s32('dpc', today_count))
|
||||
account.add_child(Node.s32('tpc', statistics.total_plays))
|
||||
account.add_child(Node.s32('dpc', statistics.today_plays))
|
||||
account.add_child(Node.s32('crd', 1))
|
||||
account.add_child(Node.s32('brd', 1))
|
||||
account.add_child(Node.s32('tdc', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s32('tdc', statistics.total_days))
|
||||
account.add_child(Node.s32('intrvld', 0))
|
||||
account.add_child(Node.s16('ver', 0))
|
||||
account.add_child(Node.u64('pst', 0))
|
||||
|
@ -330,18 +330,6 @@ class ReflecBeatVolzza2(ReflecBeatVolzzaBase):
|
||||
pdata = Node.void('pdata')
|
||||
root.add_child(pdata)
|
||||
|
||||
# Account time info
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
|
||||
# Previous account info
|
||||
previous_version = self.previous_version()
|
||||
if previous_version:
|
||||
@ -353,11 +341,11 @@ class ReflecBeatVolzza2(ReflecBeatVolzzaBase):
|
||||
account = Node.void('account')
|
||||
pdata.add_child(account)
|
||||
account.add_child(Node.s32('usrid', profile.extid))
|
||||
account.add_child(Node.s32('tpc', statistics.get_int('total_plays', 0)))
|
||||
account.add_child(Node.s32('dpc', today_count))
|
||||
account.add_child(Node.s32('tpc', statistics.total_plays))
|
||||
account.add_child(Node.s32('dpc', statistics.today_plays))
|
||||
account.add_child(Node.s32('crd', 1))
|
||||
account.add_child(Node.s32('brd', 1))
|
||||
account.add_child(Node.s32('tdc', statistics.get_int('total_days', 0)))
|
||||
account.add_child(Node.s32('tdc', statistics.total_days))
|
||||
account.add_child(Node.s32('intrvld', 0))
|
||||
account.add_child(Node.s16('ver', 0))
|
||||
account.add_child(Node.bool('succeed', succeeded))
|
||||
|
@ -3,7 +3,7 @@ import copy
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from bemani.backend.sdvx.gravitywars import SoundVoltexGravityWars
|
||||
from bemani.common import ID, Time, Profile
|
||||
from bemani.common import ID, Profile
|
||||
from bemani.data import UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -3149,19 +3149,9 @@ class SoundVoltexGravityWarsSeason1(
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0)))
|
||||
game.add_child(Node.u32('daily_count', today_count))
|
||||
game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0)))
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
|
@ -3,7 +3,7 @@ import copy
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
from bemani.backend.sdvx.gravitywars import SoundVoltexGravityWars
|
||||
from bemani.common import ID, Time, Profile
|
||||
from bemani.common import ID, Profile
|
||||
from bemani.data import Score, UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -3970,19 +3970,9 @@ class SoundVoltexGravityWarsSeason2(
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0)))
|
||||
game.add_child(Node.u32('daily_count', today_count))
|
||||
game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0)))
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
|
@ -5,7 +5,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
from bemani.backend.ess import EventLogHandler
|
||||
from bemani.backend.sdvx.base import SoundVoltexBase
|
||||
from bemani.backend.sdvx.gravitywars import SoundVoltexGravityWars
|
||||
from bemani.common import ID, Time, Profile, VersionConstants
|
||||
from bemani.common import ID, Profile, VersionConstants
|
||||
from bemani.data import Score, UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -3858,19 +3858,9 @@ class SoundVoltexHeavenlyHaven(
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0)))
|
||||
game.add_child(Node.u32('today_count', today_count))
|
||||
game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0)))
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('today_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
|
||||
# Also exists but we don't support:
|
||||
# - day_count: Number of days where this user had at least one play.
|
||||
|
@ -5,7 +5,7 @@ from typing import Any, Dict, List, Optional
|
||||
from bemani.backend.ess import EventLogHandler
|
||||
from bemani.backend.sdvx.base import SoundVoltexBase
|
||||
from bemani.backend.sdvx.booth import SoundVoltexBooth
|
||||
from bemani.common import Time, Profile, VersionConstants, ID
|
||||
from bemani.common import Profile, VersionConstants, ID
|
||||
from bemani.data import UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -2304,19 +2304,9 @@ class SoundVoltexInfiniteInfection(
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
last_play_date = statistics.get_int_array('last_play_date', 3)
|
||||
today_play_date = Time.todays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
today_count = statistics.get_int('today_plays', 0)
|
||||
else:
|
||||
today_count = 0
|
||||
game.add_child(Node.u32('play_count', statistics.get_int('total_plays', 0)))
|
||||
game.add_child(Node.u32('daily_count', today_count))
|
||||
game.add_child(Node.u32('play_chain', statistics.get_int('consecutive_days', 0)))
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
|
@ -1,5 +1,5 @@
|
||||
from bemani.common.model import Model
|
||||
from bemani.common.validateddict import ValidatedDict, Profile, intish
|
||||
from bemani.common.validateddict import ValidatedDict, Profile, PlayStatistics, intish
|
||||
from bemani.common.http import HTTP
|
||||
from bemani.common.constants import APIConstants, GameConstants, VersionConstants, DBConstants, BroadcastConstants
|
||||
from bemani.common.card import CardCipher, CardCipherException
|
||||
@ -14,6 +14,7 @@ __all__ = [
|
||||
"Model",
|
||||
"ValidatedDict",
|
||||
"Profile",
|
||||
"PlayStatistics",
|
||||
"HTTP",
|
||||
"APIConstants",
|
||||
"GameConstants",
|
||||
|
@ -454,3 +454,37 @@ class Profile(ValidatedDict):
|
||||
self.version = version
|
||||
self.refid = refid
|
||||
self.extid = extid
|
||||
|
||||
|
||||
class PlayStatistics(ValidatedDict):
|
||||
"""
|
||||
A special case of a ValidatedDict, a play statistics object is guaranteed
|
||||
to also contain several values representing last play times, total play times,
|
||||
and the like.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
game: GameConstants,
|
||||
total_plays: int,
|
||||
today_plays: int,
|
||||
total_days: int,
|
||||
consecutive_days: int,
|
||||
first_play_timestamp: int,
|
||||
last_play_timestamp: int,
|
||||
extra_values: Dict[str, Any] = {},
|
||||
) -> None:
|
||||
super().__init__(extra_values or {})
|
||||
self.game = game
|
||||
# How many actual profiles saves have we registered across all games in this series.
|
||||
self.total_plays = total_plays
|
||||
# How many actual profile saves have we registered today, so far.
|
||||
self.today_plays = today_plays
|
||||
# How many total days that we have registered at least one profile save.
|
||||
self.total_days = total_days
|
||||
# How many consecutive days in a row we registered at least one profile save.
|
||||
self.consecutive_days = consecutive_days
|
||||
# The timestamp of the very first play session, in seconds.
|
||||
self.first_play_timestamp = first_play_timestamp
|
||||
# The timestamp of the very last play session, in seconds.
|
||||
self.last_play_timestamp = last_play_timestamp
|
||||
|
@ -86,7 +86,7 @@ class APIData(APIProviderInterface, BaseData):
|
||||
cursor = self.execute(
|
||||
sql,
|
||||
{
|
||||
'timestamp': int(Time.now()),
|
||||
'timestamp': Time.now(),
|
||||
'name': name,
|
||||
'token': str(uuid.uuid4()),
|
||||
},
|
||||
@ -175,7 +175,7 @@ class APIData(APIProviderInterface, BaseData):
|
||||
cursor = self.execute(
|
||||
sql,
|
||||
{
|
||||
'timestamp': int(Time.now()),
|
||||
'timestamp': Time.now(),
|
||||
'uri': uri,
|
||||
'token': token,
|
||||
},
|
||||
|
@ -88,7 +88,7 @@ class NetworkData(BaseData):
|
||||
The ID of the newly created entry.
|
||||
"""
|
||||
sql = "INSERT INTO news (timestamp, title, body) VALUES (:timestamp, :title, :body)"
|
||||
cursor = self.execute(sql, {'timestamp': int(Time.now()), 'title': title, 'body': body})
|
||||
cursor = self.execute(sql, {'timestamp': Time.now(), 'title': title, 'body': body})
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_news(self, newsid: int) -> Optional[News]:
|
||||
|
260
bemani/tests/test_PlayStats.py
Normal file
260
bemani/tests/test_PlayStats.py
Normal file
@ -0,0 +1,260 @@
|
||||
# vim: set fileencoding=utf-8
|
||||
import unittest
|
||||
from freezegun import freeze_time
|
||||
from typing import Dict, Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
from bemani.backend.base import Base
|
||||
from bemani.common import GameConstants, Time, ValidatedDict
|
||||
from bemani.data import UserID
|
||||
|
||||
|
||||
# Make the normally-abstract base instantiable so we can test it.
|
||||
class InstantiableBase(Base):
|
||||
game = GameConstants.BISHI_BASHI
|
||||
version = -1
|
||||
name = "Test Class"
|
||||
|
||||
|
||||
# Make an easier mock implementation of load/save stats.
|
||||
def mock_stats(existing_value: Dict[str, Any]) -> Mock:
|
||||
data = Mock()
|
||||
data.local = Mock()
|
||||
data.local.game = Mock()
|
||||
data.local.game.get_settings = Mock(return_value=ValidatedDict(existing_value) if existing_value else None)
|
||||
data.local.game.put_settings = Mock()
|
||||
return data
|
||||
|
||||
|
||||
def saved_stats(mock: Mock) -> ValidatedDict:
|
||||
return ValidatedDict(mock.local.game.put_settings.call_args.args[2])
|
||||
|
||||
|
||||
class TestPlayStats(unittest.TestCase):
|
||||
|
||||
def test_get_brand_new_profile(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
stats = None
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
|
||||
self.assertEqual(settings.game, GameConstants.BISHI_BASHI)
|
||||
self.assertEqual(settings.total_plays, 1)
|
||||
self.assertEqual(settings.today_plays, 1)
|
||||
self.assertEqual(settings.total_days, 1)
|
||||
self.assertEqual(settings.consecutive_days, 1)
|
||||
self.assertEqual(settings.first_play_timestamp, Time.now())
|
||||
self.assertEqual(settings.last_play_timestamp, Time.now())
|
||||
|
||||
def test_put_brand_new_profile(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
stats = None
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
base.update_play_statistics(UserID(1337), settings)
|
||||
new_settings = saved_stats(data)
|
||||
|
||||
self.assertEqual(new_settings.get_int('total_plays'), 1)
|
||||
self.assertEqual(new_settings.get_int('today_plays'), 1)
|
||||
self.assertEqual(new_settings.get_int('total_days'), 1)
|
||||
self.assertEqual(new_settings.get_int('consecutive_days'), 1)
|
||||
self.assertEqual(new_settings.get_int('first_play_timestamp'), Time.now())
|
||||
self.assertEqual(new_settings.get_int('last_play_timestamp'), Time.now())
|
||||
self.assertEqual(new_settings.get_int_array('last_play_date', 3), Time.todays_date())
|
||||
|
||||
def test_get_played_today(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
play_date = Time.todays_date()
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'today_plays': 420,
|
||||
'total_days': 10,
|
||||
'first_play_timestamp': 1234567890,
|
||||
'last_play_timestamp': 9876543210,
|
||||
'consecutive_days': 69,
|
||||
'last_play_date': [play_date[0], play_date[1], play_date[2]],
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
|
||||
self.assertEqual(settings.game, GameConstants.BISHI_BASHI)
|
||||
self.assertEqual(settings.total_plays, 1235)
|
||||
self.assertEqual(settings.today_plays, 421)
|
||||
self.assertEqual(settings.total_days, 10)
|
||||
self.assertEqual(settings.consecutive_days, 69)
|
||||
self.assertEqual(settings.first_play_timestamp, 1234567890)
|
||||
self.assertEqual(settings.last_play_timestamp, 9876543210)
|
||||
|
||||
def test_put_played_today(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
play_date = Time.todays_date()
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'today_plays': 420,
|
||||
'total_days': 10,
|
||||
'first_play_timestamp': 1234567890,
|
||||
'last_play_timestamp': 1234567890,
|
||||
'consecutive_days': 69,
|
||||
'last_play_date': [play_date[0], play_date[1], play_date[2]],
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
base.update_play_statistics(UserID(1337), settings)
|
||||
new_settings = saved_stats(data)
|
||||
|
||||
self.assertEqual(new_settings.get_int('total_plays'), 1235)
|
||||
self.assertEqual(new_settings.get_int('today_plays'), 421)
|
||||
self.assertEqual(new_settings.get_int('total_days'), 10)
|
||||
self.assertEqual(new_settings.get_int('consecutive_days'), 69)
|
||||
self.assertEqual(new_settings.get_int('first_play_timestamp'), 1234567890)
|
||||
self.assertEqual(new_settings.get_int('last_play_timestamp'), Time.now())
|
||||
self.assertEqual(new_settings.get_int_array('last_play_date', 3), Time.todays_date())
|
||||
|
||||
def test_get_played_yesterday(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
play_date = Time.yesterdays_date()
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'today_plays': 420,
|
||||
'total_days': 10,
|
||||
'first_play_timestamp': 1234567890,
|
||||
'last_play_timestamp': 9876543210,
|
||||
'consecutive_days': 69,
|
||||
'last_play_date': [play_date[0], play_date[1], play_date[2]],
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
|
||||
self.assertEqual(settings.game, GameConstants.BISHI_BASHI)
|
||||
self.assertEqual(settings.total_plays, 1235)
|
||||
self.assertEqual(settings.today_plays, 1)
|
||||
self.assertEqual(settings.total_days, 11)
|
||||
self.assertEqual(settings.consecutive_days, 70)
|
||||
self.assertEqual(settings.first_play_timestamp, 1234567890)
|
||||
self.assertEqual(settings.last_play_timestamp, 9876543210)
|
||||
|
||||
def test_put_played_yesterday(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
play_date = Time.yesterdays_date()
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'today_plays': 420,
|
||||
'total_days': 10,
|
||||
'first_play_timestamp': 1234567890,
|
||||
'last_play_timestamp': 1234567890,
|
||||
'consecutive_days': 69,
|
||||
'last_play_date': [play_date[0], play_date[1], play_date[2]],
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
base.update_play_statistics(UserID(1337), settings)
|
||||
new_settings = saved_stats(data)
|
||||
|
||||
self.assertEqual(new_settings.get_int('total_plays'), 1235)
|
||||
self.assertEqual(new_settings.get_int('today_plays'), 1)
|
||||
self.assertEqual(new_settings.get_int('total_days'), 11)
|
||||
self.assertEqual(new_settings.get_int('consecutive_days'), 70)
|
||||
self.assertEqual(new_settings.get_int('first_play_timestamp'), 1234567890)
|
||||
self.assertEqual(new_settings.get_int('last_play_timestamp'), Time.now())
|
||||
self.assertEqual(new_settings.get_int_array('last_play_date', 3), Time.todays_date())
|
||||
|
||||
def test_get_played_awhile_ago(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'today_plays': 420,
|
||||
'total_days': 10,
|
||||
'first_play_timestamp': 1234567890,
|
||||
'last_play_timestamp': 9876543210,
|
||||
'consecutive_days': 69,
|
||||
'last_play_date': [2010, 4, 20],
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
|
||||
self.assertEqual(settings.game, GameConstants.BISHI_BASHI)
|
||||
self.assertEqual(settings.total_plays, 1235)
|
||||
self.assertEqual(settings.today_plays, 1)
|
||||
self.assertEqual(settings.total_days, 11)
|
||||
self.assertEqual(settings.consecutive_days, 1)
|
||||
self.assertEqual(settings.first_play_timestamp, 1234567890)
|
||||
self.assertEqual(settings.last_play_timestamp, 9876543210)
|
||||
|
||||
def test_put_played_awhile_ago(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'today_plays': 420,
|
||||
'total_days': 10,
|
||||
'first_play_timestamp': 1234567890,
|
||||
'last_play_timestamp': 1234567890,
|
||||
'consecutive_days': 69,
|
||||
'last_play_date': [2010, 4, 20],
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
base.update_play_statistics(UserID(1337), settings)
|
||||
new_settings = saved_stats(data)
|
||||
|
||||
self.assertEqual(new_settings.get_int('total_plays'), 1235)
|
||||
self.assertEqual(new_settings.get_int('today_plays'), 1)
|
||||
self.assertEqual(new_settings.get_int('total_days'), 11)
|
||||
self.assertEqual(new_settings.get_int('consecutive_days'), 1)
|
||||
self.assertEqual(new_settings.get_int('first_play_timestamp'), 1234567890)
|
||||
self.assertEqual(new_settings.get_int('last_play_timestamp'), Time.now())
|
||||
self.assertEqual(new_settings.get_int_array('last_play_date', 3), Time.todays_date())
|
||||
|
||||
def test_get_extra_settings(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'key': 'value',
|
||||
'int': 1337,
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
|
||||
self.assertEqual(settings.get_int('int'), 1337)
|
||||
self.assertEqual(settings.get_str('key'), 'value')
|
||||
self.assertEqual(settings.get_int('total_plays'), 0)
|
||||
|
||||
def test_put_extra_settings(self) -> None:
|
||||
with freeze_time('2021-08-24'):
|
||||
stats = {
|
||||
'total_plays': 1234,
|
||||
'key': 'value',
|
||||
'int': 1337,
|
||||
}
|
||||
data = mock_stats(stats)
|
||||
base = InstantiableBase(data, Mock(), Mock())
|
||||
|
||||
settings = base.get_play_statistics(UserID(1337))
|
||||
settings.replace_int('int', 420)
|
||||
settings.replace_int('int2', 69)
|
||||
settings.replace_int('total_plays', 37)
|
||||
base.update_play_statistics(UserID(1337), settings)
|
||||
|
||||
new_settings = saved_stats(data)
|
||||
|
||||
self.assertEqual(new_settings.get_int('int'), 420)
|
||||
self.assertEqual(new_settings.get_str('key'), 'value')
|
||||
self.assertEqual(new_settings.get_int('int2'), 69)
|
||||
self.assertEqual(new_settings.get_int('total_plays'), 1235)
|
Loading…
x
Reference in New Issue
Block a user