Work on live score reload with user queue update
This commit is contained in:
parent
8d208feb24
commit
ee619161d2
@ -25,8 +25,6 @@ class UserDetailsController(
|
|||||||
private val userQueueService: UpdateUserQueueService
|
private val userQueueService: UpdateUserQueueService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data class UserDetailsResponse(
|
data class UserDetailsResponse(
|
||||||
val user_details: UserDetails,
|
val user_details: UserDetails,
|
||||||
val queue_details: UserQueueDetails,
|
val queue_details: UserQueueDetails,
|
||||||
|
|||||||
@ -218,10 +218,10 @@ class ImportScores(
|
|||||||
// Update the frontend
|
// Update the frontend
|
||||||
messagingTemplate.convertAndSend(
|
messagingTemplate.convertAndSend(
|
||||||
"/topic/live-user/${userId}",
|
"/topic/live-user/${userId}",
|
||||||
currentQueueDetails
|
UpdateUserQueueService.UserQueueWebsocketPacket(message = "UPDATE_PROGRESS", data = currentQueueDetails)
|
||||||
)
|
)
|
||||||
|
|
||||||
this.insertAndProcessNewScore(topScore.beatmap.id, topScore)
|
this.insertAndProcessNewScore(topScore.beatmap.id, topScore, isUserQueue = true)
|
||||||
}
|
}
|
||||||
current += 1
|
current += 1
|
||||||
}
|
}
|
||||||
@ -518,7 +518,7 @@ class ImportScores(
|
|||||||
dslContext.batch(queries).execute()
|
dslContext.batch(queries).execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertAndProcessNewScore(beatmapId: Int, score: OsuApiModels.Score) {
|
private fun insertAndProcessNewScore(beatmapId: Int, score: OsuApiModels.Score, isUserQueue: Boolean = false) {
|
||||||
// Check if the score is already in the database
|
// Check if the score is already in the database
|
||||||
val scoreExists = dslContext.fetchExists(SCORES, SCORES.REPLAY_ID.eq(score.best_id))
|
val scoreExists = dslContext.fetchExists(SCORES, SCORES.REPLAY_ID.eq(score.best_id))
|
||||||
if (scoreExists) {
|
if (scoreExists) {
|
||||||
@ -627,11 +627,20 @@ class ImportScores(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(processedReplay.ur != null && processedReplay.ur < 25.0) {
|
if(processedReplay.adjusted_ur != null && processedReplay.adjusted_ur < 25.0) {
|
||||||
|
if(isUserQueue) {
|
||||||
|
messagingTemplate.convertAndSend(
|
||||||
|
"/topic/live-user/${score.user_id}",
|
||||||
|
UpdateUserQueueService.UserQueueWebsocketPacket(message = "UPDATE_SCORES")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isUserQueue) {
|
||||||
this.logger.info("Inserting user into queue for update: ${score.user_id}")
|
this.logger.info("Inserting user into queue for update: ${score.user_id}")
|
||||||
this.logger.info("UR: ${processedReplay.ur} on their replay with id = ${score.best_id}")
|
this.logger.info("UR: ${processedReplay.ur} on their replay with id = ${score.best_id}")
|
||||||
this.updateUserQueueService.insertUser(score.user_id)
|
this.updateUserQueueService.insertUser(score.user_id)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (judgement in processedReplay.judgements) {
|
for (judgement in processedReplay.judgements) {
|
||||||
dslContext.insertInto(SCORES_JUDGEMENTS)
|
dslContext.insertInto(SCORES_JUDGEMENTS)
|
||||||
|
|||||||
@ -19,6 +19,11 @@ class UpdateUserQueueService(
|
|||||||
|
|
||||||
private val USER_UPDATE_INTERVAL_HOURS = 4
|
private val USER_UPDATE_INTERVAL_HOURS = 4
|
||||||
|
|
||||||
|
data class UserQueueWebsocketPacket(
|
||||||
|
val message: String,
|
||||||
|
val data: UserQueueDetails? = null
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the user queue details for the given user ID.
|
* Retrieves the user queue details for the given user ID.
|
||||||
*
|
*
|
||||||
@ -38,28 +43,10 @@ class UpdateUserQueueService(
|
|||||||
.limit(1)
|
.limit(1)
|
||||||
.fetchOneInto(OffsetDateTime::class.java)
|
.fetchOneInto(OffsetDateTime::class.java)
|
||||||
|
|
||||||
val lastCompletedUpdateUser = dslContext.select(USERS.SYS_LAST_UPDATE)
|
|
||||||
.from(USERS)
|
|
||||||
.where(USERS.USER_ID.eq(userId))
|
|
||||||
.fetchOneInto(OffsetDateTime::class.java)
|
|
||||||
|
|
||||||
// Select the most recent
|
|
||||||
val lastCompletedUpdate = lastCompletedUpdateQueue?.let {
|
|
||||||
if (lastCompletedUpdateUser != null) {
|
|
||||||
if (lastCompletedUpdateUser.isAfter(lastCompletedUpdateQueue)) {
|
|
||||||
lastCompletedUpdateUser
|
|
||||||
} else {
|
|
||||||
lastCompletedUpdateQueue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastCompletedUpdateQueue
|
|
||||||
}
|
|
||||||
} ?: lastCompletedUpdateUser
|
|
||||||
|
|
||||||
var canUpdate = !isProcessing
|
var canUpdate = !isProcessing
|
||||||
if(lastCompletedUpdate != null) {
|
if(lastCompletedUpdateQueue != null) {
|
||||||
val now = OffsetDateTime.now(ZoneOffset.UTC)
|
val now = OffsetDateTime.now(ZoneOffset.UTC)
|
||||||
val hoursSinceLastUpdate = now.hour - lastCompletedUpdate.hour
|
val hoursSinceLastUpdate = now.hour - lastCompletedUpdateQueue.hour
|
||||||
|
|
||||||
if(hoursSinceLastUpdate < USER_UPDATE_INTERVAL_HOURS)
|
if(hoursSinceLastUpdate < USER_UPDATE_INTERVAL_HOURS)
|
||||||
canUpdate = false
|
canUpdate = false
|
||||||
@ -78,7 +65,7 @@ class UpdateUserQueueService(
|
|||||||
|
|
||||||
return UserQueueDetails(
|
return UserQueueDetails(
|
||||||
isProcessing,
|
isProcessing,
|
||||||
lastCompletedUpdate,
|
lastCompletedUpdateQueue,
|
||||||
canUpdate,
|
canUpdate,
|
||||||
currentProgress?.progressCurrent,
|
currentProgress?.progressCurrent,
|
||||||
currentProgress?.progressTotal
|
currentProgress?.progressTotal
|
||||||
@ -123,7 +110,7 @@ class UpdateUserQueueService(
|
|||||||
// Notify the user that their queue has been processed with fresh info
|
// Notify the user that their queue has been processed with fresh info
|
||||||
messagingTemplate.convertAndSend(
|
messagingTemplate.convertAndSend(
|
||||||
"/topic/live-user/${userId}",
|
"/topic/live-user/${userId}",
|
||||||
this.getUserQueueDetails(userId)
|
UpdateUserQueueService.UserQueueWebsocketPacket(message = "UPDATE_PROGRESS", data = this.getUserQueueDetails(userId))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,5 +26,5 @@
|
|||||||
</div>
|
</div>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
<div class="text-center version">
|
<div class="text-center version">
|
||||||
v20240218
|
v20240222
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -47,14 +47,14 @@
|
|||||||
</a>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!this.userInfo.queue_details.canUpdate">
|
<ng-container *ngIf="!this.userInfo.queue_details.canUpdate">
|
||||||
<span class="btn-info">can't force update now</span>
|
<span class="btn-warning">wait a bit to force update</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<span style="margin-left: 4px">|</span>
|
<span style="margin-left: 4px">|</span>
|
||||||
last update: {{ this.userInfo.queue_details.lastCompletedUpdate ? this.calculateTimeAgo(this.userInfo.queue_details.lastCompletedUpdate) : 'never'}}
|
last update: {{ this.userInfo.queue_details.lastCompletedUpdate ? this.calculateTimeAgo(this.userInfo.queue_details.lastCompletedUpdate) : 'never'}}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #updateProgress>
|
<ng-template #updateProgress>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<span class="btn-warning">updating now!</span> <span style="margin-left: 4px">|</span> progress: {{ this.userInfo.queue_details.progressCurrent ? this.userInfo.queue_details.progressCurrent : "?" }}/{{ this.userInfo.queue_details.progressTotal ? this.userInfo.queue_details.progressTotal : "?" }}
|
<span class="btn-info">updating now!</span> <span style="margin-left: 4px">|</span> progress: {{ this.userInfo.queue_details.progressCurrent != null ? this.userInfo.queue_details.progressCurrent : "?" }}/{{ this.userInfo.queue_details.progressTotal != null ? this.userInfo.queue_details.progressTotal : "?" }}
|
||||||
<ng-container *ngIf="!this.userInfo.queue_details.progressTotal && !this.userInfo.queue_details.progressCurrent; else loading">
|
<ng-container *ngIf="!this.userInfo.queue_details.progressTotal && !this.userInfo.queue_details.progressCurrent; else loading">
|
||||||
<span style="font-weight: bold">(in queue)</span>
|
<span style="font-weight: bold">(in queue)</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -64,6 +64,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-container *ngIf="this.userInfo.suspicious_scores.length > 0">
|
||||||
<h4>Suspicious Scores ({{ this.userInfo.suspicious_scores.length }})</h4>
|
<h4>Suspicious Scores ({{ this.userInfo.suspicious_scores.length }})</h4>
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@ -77,7 +78,7 @@
|
|||||||
<th>
|
<th>
|
||||||
PP
|
PP
|
||||||
</th>
|
</th>
|
||||||
<th>Links</th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -101,7 +102,7 @@
|
|||||||
<a [routerLink]="['/s/' + score.replay_id]" class="btn btn-outline-secondary btn-sm mb-2">
|
<a [routerLink]="['/s/' + score.replay_id]" class="btn btn-outline-secondary btn-sm mb-2">
|
||||||
Details
|
Details
|
||||||
</a>
|
</a>
|
||||||
<a [href]="'https://osu.ppy.sh/scores/osu/' + score.replay_id" class="btn btn-outline-secondary btn-sm"
|
<a [href]="'https://osu.ppy.sh/scores/osu/' + score.replay_id" class="btn btn-outline-secondary btn-sm" style="margin-left: 5px"
|
||||||
target="_blank">
|
target="_blank">
|
||||||
osu!web
|
osu!web
|
||||||
</a>
|
</a>
|
||||||
@ -110,7 +111,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="this.userInfo.similar_replays.length > 0">
|
||||||
<h4 class="mt-2">Similar Replays ({{ this.userInfo.similar_replays.length }})</h4>
|
<h4 class="mt-2">Similar Replays ({{ this.userInfo.similar_replays.length }})</h4>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -163,7 +166,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,6 +21,11 @@ interface UserInfo {
|
|||||||
total_scores: number;
|
total_scores: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UserQueueWebsocketPacket {
|
||||||
|
message: string;
|
||||||
|
data?: UserQueueDetails;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-view-user',
|
selector: 'app-view-user',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
@ -38,8 +43,6 @@ interface UserInfo {
|
|||||||
})
|
})
|
||||||
export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
|
||||||
userUpdateIntervalHours = 4
|
|
||||||
|
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
notFound = false;
|
notFound = false;
|
||||||
userId: string | null = null;
|
userId: string | null = null;
|
||||||
@ -70,10 +73,16 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.activatedRoute.params.subscribe(params => {
|
this.activatedRoute.params.subscribe(params => {
|
||||||
this.userId = params['userId'];
|
this.userId = params['userId'];
|
||||||
if (this.userId) {
|
if (this.userId) {
|
||||||
|
this.loadUser();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadUser(isScoreUpdate = false) {
|
||||||
this.getUserInfo().pipe(
|
this.getUserInfo().pipe(
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
this.userInfo = null;
|
this.userInfo = null;
|
||||||
if(error.status == 404) {
|
if (error.status == 404) {
|
||||||
this.notFound = true;
|
this.notFound = true;
|
||||||
}
|
}
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
@ -85,12 +94,12 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
(response: UserInfo) => {
|
(response: UserInfo) => {
|
||||||
this.notFound = false;
|
this.notFound = false;
|
||||||
this.userInfo = response;
|
this.userInfo = response;
|
||||||
|
if(!isScoreUpdate) {
|
||||||
this.title.setTitle(`${this.userInfo.user_details.username}`);
|
this.title.setTitle(`${this.userInfo.user_details.username}`);
|
||||||
this.subscribeToUser();
|
this.subscribeToUser();
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
@ -136,7 +145,20 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.liveUserSub = this.rxStompService
|
this.liveUserSub = this.rxStompService
|
||||||
.watch(`/topic/live-user/${this.userInfo?.user_details.user_id}`)
|
.watch(`/topic/live-user/${this.userInfo?.user_details.user_id}`)
|
||||||
.subscribe((message: Message) => {
|
.subscribe((message: Message) => {
|
||||||
this.userInfo!.queue_details = JSON.parse(message.body);
|
let queueDetails: UserQueueWebsocketPacket = JSON.parse(message.body);
|
||||||
|
if(queueDetails.message == "UPDATE_SCORES") {
|
||||||
|
this.loadUser(true);
|
||||||
|
} else {
|
||||||
|
if(queueDetails.data != null) {
|
||||||
|
if(queueDetails.data.progressCurrent != null && queueDetails.data.progressTotal != null) {
|
||||||
|
if (queueDetails.data.progressCurrent >= queueDetails.data.progressTotal) {
|
||||||
|
this.loadUser(true);
|
||||||
|
this.liveUserSub?.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.userInfo!.queue_details = queueDetails.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user