mirror of
https://github.com/DragonMinded/bemaniutils.git
synced 2026-09-07 18:05:13 -05:00
Display last seen/last played on admin user page, prepare for delete user feature.
This commit is contained in:
@@ -131,8 +131,8 @@ class BaseData:
|
||||
Given a previously-opened session, look up an ID.
|
||||
|
||||
Parameters:
|
||||
session - String identifying a session that was opened by create_session.
|
||||
sesstype - Arbitrary string identifying the session type.
|
||||
session - String identifying a session that was opened by _create_session.
|
||||
sesstype - Arbitrary string identifying the session type, given as the optype to _create_session.
|
||||
|
||||
Returns:
|
||||
ID as an integer if found, or None if the session is expired or doesn't exist.
|
||||
@@ -153,6 +153,7 @@ class BaseData:
|
||||
|
||||
Parameters:
|
||||
opid - ID we wish to start a session for.
|
||||
optype - The session type, which helps distinguish the ID from an identical one of a different ID type.
|
||||
expiration - Number of seconds before this session is invalid.
|
||||
|
||||
Returns:
|
||||
@@ -192,7 +193,8 @@ class BaseData:
|
||||
Destroy a previously-created session.
|
||||
|
||||
Parameters:
|
||||
session - A session string as returned from create_session.
|
||||
session - A session string as returned from _create_session.
|
||||
sesstype - Arbitrary string identifying the session type, given as the optype to _create_session.
|
||||
"""
|
||||
# Remove the session token
|
||||
sql = "DELETE FROM session WHERE session = :session AND type = :sesstype"
|
||||
@@ -201,3 +203,19 @@ class BaseData:
|
||||
# Also weed out any other defunct sessions
|
||||
sql = "DELETE FROM session WHERE expiration < :timestamp"
|
||||
self.execute(sql, {"timestamp": Time.now()}, safe_write_operation=True)
|
||||
|
||||
def _destroy_sessions(self, opid: int, optype: str) -> None:
|
||||
"""
|
||||
Destroy any previously created sessions based on ID and type.
|
||||
|
||||
Parameters:
|
||||
opid - A session's associated ID, the same as would be given to _create_session.
|
||||
optype - A session's associated type, which segments IDs, the same as would be given to _create_session.
|
||||
"""
|
||||
# Remove the session token
|
||||
sql = "DELETE FROM session WHERE id = :opid AND type = :optype"
|
||||
self.execute(sql, {"opid": opid, "optype": optype})
|
||||
|
||||
# Also weed out any other defunct sessions
|
||||
sql = "DELETE FROM session WHERE expiration < :timestamp"
|
||||
self.execute(sql, {"timestamp": Time.now()}, safe_write_operation=True)
|
||||
|
||||
@@ -99,6 +99,29 @@ class GameData(BaseData):
|
||||
result = cursor.mappings().fetchone()
|
||||
return ValidatedDict(self.deserialize(result["data"]))
|
||||
|
||||
def get_all_settings(self, userid: UserID) -> Dict[GameConstants, ValidatedDict]:
|
||||
"""
|
||||
Given a user ID, look up all game-wide settings as a dictionary.
|
||||
|
||||
This is mostly used for the frontend for admins to be able to see when a profile was
|
||||
last played.
|
||||
|
||||
Parameters:
|
||||
userid - Integer identifying a user, as possibly looked up by UserData.
|
||||
|
||||
Returns:
|
||||
A dictionary keyed by GameConstant whose value is a game settings dictionary
|
||||
as returned by get_settings for that game.
|
||||
"""
|
||||
sql = "SELECT game, data FROM game_settings WHERE userid = :userid"
|
||||
cursor = self.execute(sql, {"userid": userid})
|
||||
|
||||
return {
|
||||
GameConstants(result["game"]): ValidatedDict(self.deserialize(result["data"]))
|
||||
for result in cursor.mappings()
|
||||
if result["game"] in GameConstants
|
||||
}
|
||||
|
||||
def put_settings(self, game: GameConstants, userid: UserID, settings: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Given a game and a user ID, save game-wide settings to the DB.
|
||||
|
||||
@@ -103,13 +103,16 @@ def format_user(user: User) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def format_profile(profile: Profile) -> Dict[str, object]:
|
||||
def format_profile(profile: Profile, stats: Dict[GameConstants, ValidatedDict]) -> Dict[str, object]:
|
||||
gamestats = stats.get(profile.game) or ValidatedDict()
|
||||
|
||||
return {
|
||||
"game": profile.game.value,
|
||||
"version": profile.version,
|
||||
"extid": ID.format_extid(profile.extid),
|
||||
"refid": profile.refid,
|
||||
"name": profile.get_str("name", ""),
|
||||
"last_played": gamestats.get_int("last_play_timestamp", 0),
|
||||
}
|
||||
|
||||
|
||||
@@ -387,7 +390,12 @@ def viewuser(userid: int) -> Response:
|
||||
games[game.value][version] = name
|
||||
|
||||
cards = [__format_card(card) for card in g.data.local.user.get_cards(userid)]
|
||||
profiles = [format_profile(profile) for profile in g.data.local.user.get_profiles(userid)]
|
||||
stats = g.data.local.game.get_all_settings(userid)
|
||||
newest = 0
|
||||
for _, playstats in stats.items():
|
||||
newest = max(newest, playstats.get_int("last_play_timestamp", 0))
|
||||
|
||||
profiles = [format_profile(profile, stats) for profile in g.data.local.user.get_profiles(userid)]
|
||||
arcades = g.data.local.machine.get_all_arcades()
|
||||
return render_react(
|
||||
"User",
|
||||
@@ -396,6 +404,7 @@ def viewuser(userid: int) -> Response:
|
||||
"user": {
|
||||
"email": user.email,
|
||||
"username": user.username,
|
||||
"last_played": newest,
|
||||
},
|
||||
"games": games,
|
||||
"cards": cards,
|
||||
@@ -435,9 +444,20 @@ def listuser(userid: int) -> Dict[str, Any]:
|
||||
except CardCipherException:
|
||||
return "????????????????"
|
||||
|
||||
user = g.data.local.user.get_user(userid)
|
||||
cards = [__format_card(card) for card in g.data.local.user.get_cards(userid)]
|
||||
arcades = g.data.local.machine.get_all_arcades()
|
||||
stats = g.data.local.game.get_all_settings(userid)
|
||||
newest = 0
|
||||
for _, playstats in stats.items():
|
||||
newest = max(newest, playstats.get_int("last_play_timestamp", 0))
|
||||
|
||||
return {
|
||||
"user": {
|
||||
"email": user.email,
|
||||
"username": user.username,
|
||||
"last_played": newest,
|
||||
},
|
||||
"cards": cards,
|
||||
"arcades": {arcade.id: arcade.name for arcade in arcades},
|
||||
"balances": {arcade.id: g.data.local.user.get_balance(userid, arcade.id) for arcade in arcades},
|
||||
@@ -1142,7 +1162,8 @@ def removeuserprofile(userid: int) -> Dict[str, Any]:
|
||||
g.data.local.user.delete_profile(profile.game, profile.version, userid)
|
||||
|
||||
# Return new profile list
|
||||
return {"profiles": [format_profile(profile) for profile in g.data.local.user.get_profiles(userid)]}
|
||||
stats = g.data.local.game.get_all_settings(userid)
|
||||
return {"profiles": [format_profile(profile, stats) for profile in g.data.local.user.get_profiles(userid)]}
|
||||
|
||||
|
||||
@admin_pages.route("/users/<int:userid>/cards/add", methods=["POST"])
|
||||
|
||||
@@ -168,13 +168,17 @@ var PASELITransactionEvent = createReactClass({
|
||||
var username = null;
|
||||
var user = null;
|
||||
if (this.props.users) {
|
||||
if (this.props.users[event.userid]) {
|
||||
username = this.props.users[event.userid];
|
||||
}
|
||||
if (username == null) {
|
||||
user = <span className="placeholder">anonymous account</span>;
|
||||
if (event.userid == null) {
|
||||
user = <span className="placeholder">deleted account</span>;
|
||||
} else {
|
||||
user = <span>{username}</span>;
|
||||
if (this.props.users[event.userid]) {
|
||||
username = this.props.users[event.userid];
|
||||
}
|
||||
if (username == null) {
|
||||
user = <span className="placeholder">anonymous account</span>;
|
||||
} else {
|
||||
user = <span>{username}</span>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ var user_management = createReactClass({
|
||||
editing_password: false,
|
||||
new_password1: '',
|
||||
new_password2: '',
|
||||
last_played: window.user.last_played,
|
||||
generating_recovery: false,
|
||||
cards: window.cards,
|
||||
profiles: window.profiles,
|
||||
@@ -46,12 +47,21 @@ var user_management = createReactClass({
|
||||
AJAX.get(
|
||||
Link.get('refresh'),
|
||||
function(response) {
|
||||
// Update key things about the user in case it changes.
|
||||
this.setState({
|
||||
last_played: response.user.last_played,
|
||||
cards: response.cards,
|
||||
balances: response.balances,
|
||||
arcades: response.arcades,
|
||||
events: response.events,
|
||||
});
|
||||
// Only update values if they aren't being edited.
|
||||
if (!this.state.editing_email) {
|
||||
this.setState({email: response.user.email});
|
||||
}
|
||||
if (!this.state.editing_username) {
|
||||
this.setState({username: response.user.username});
|
||||
}
|
||||
// Refresh every 15 seconds
|
||||
setTimeout(this.refreshUser, 5000);
|
||||
}.bind(this)
|
||||
@@ -422,6 +432,14 @@ var user_management = createReactClass({
|
||||
);
|
||||
},
|
||||
|
||||
renderLastPlayed: function() {
|
||||
return (
|
||||
<LabelledSection vertical={true} label="Last Seen">
|
||||
<Timestamp timestamp={this.state.last_played} className="lastseen" />
|
||||
</LabelledSection>
|
||||
);
|
||||
},
|
||||
|
||||
deleteExistingProfile: function(event, refid) {
|
||||
$.confirm({
|
||||
escapeKey: 'Cancel',
|
||||
@@ -474,6 +492,7 @@ var user_management = createReactClass({
|
||||
{this.renderPassword()}
|
||||
{this.renderEmail()}
|
||||
{this.renderPIN()}
|
||||
{this.renderLastPlayed()}
|
||||
</div>
|
||||
<div className="section">
|
||||
<h3>Cards</h3>
|
||||
@@ -533,6 +552,12 @@ var user_management = createReactClass({
|
||||
return profile.name;
|
||||
}.bind(this),
|
||||
},
|
||||
{
|
||||
name: 'Last Played',
|
||||
render: function(profile) {
|
||||
return <Timestamp timestamp={profile.last_played} className="lastseen" />;
|
||||
}.bind(this),
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
render: this.renderDeleteProfileButton,
|
||||
|
||||
Reference in New Issue
Block a user