Reworked some fields to be timestamp but tz aware, added how long ago the user queue update was

This commit is contained in:
nise.moe 2024-02-22 13:36:06 +01:00
parent 63be57dfc8
commit 8d208feb24
18 changed files with 186 additions and 66 deletions

View File

@ -8,7 +8,7 @@ import com.nisemoe.generated.Public
import com.nisemoe.generated.keys.BEATMAPS_PKEY import com.nisemoe.generated.keys.BEATMAPS_PKEY
import com.nisemoe.generated.tables.records.BeatmapsRecord import com.nisemoe.generated.tables.records.BeatmapsRecord
import java.time.LocalDateTime import java.time.OffsetDateTime
import java.util.function.Function import java.util.function.Function
import org.jooq.Field import org.jooq.Field
@ -105,7 +105,7 @@ open class Beatmaps(
/** /**
* The column <code>public.beatmaps.sys_last_update</code>. * The column <code>public.beatmaps.sys_last_update</code>.
*/ */
val SYS_LAST_UPDATE: TableField<BeatmapsRecord, LocalDateTime?> = createField(DSL.name("sys_last_update"), SQLDataType.LOCALDATETIME(6), this, "") val SYS_LAST_UPDATE: TableField<BeatmapsRecord, OffsetDateTime?> = createField(DSL.name("sys_last_update"), SQLDataType.TIMESTAMPWITHTIMEZONE(6).defaultValue(DSL.field(DSL.raw("CURRENT_TIMESTAMP"), SQLDataType.TIMESTAMPWITHTIMEZONE)), this, "")
private constructor(alias: Name, aliased: Table<BeatmapsRecord>?): this(alias, null, null, aliased, null) private constructor(alias: Name, aliased: Table<BeatmapsRecord>?): this(alias, null, null, aliased, null)
private constructor(alias: Name, aliased: Table<BeatmapsRecord>?, parameters: Array<Field<*>?>?): this(alias, null, null, aliased, parameters) private constructor(alias: Name, aliased: Table<BeatmapsRecord>?, parameters: Array<Field<*>?>?): this(alias, null, null, aliased, parameters)
@ -150,16 +150,16 @@ open class Beatmaps(
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Row9 type methods // Row9 type methods
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
override fun fieldsRow(): Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> = super.fieldsRow() as Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> override fun fieldsRow(): Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?> = super.fieldsRow() as Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?>
/** /**
* Convenience mapping calling {@link SelectField#convertFrom(Function)}. * Convenience mapping calling {@link SelectField#convertFrom(Function)}.
*/ */
fun <U> mapping(from: (Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?) -> U): SelectField<U> = convertFrom(Records.mapping(from)) fun <U> mapping(from: (Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?) -> U): SelectField<U> = convertFrom(Records.mapping(from))
/** /**
* Convenience mapping calling {@link SelectField#convertFrom(Class, * Convenience mapping calling {@link SelectField#convertFrom(Class,
* Function)}. * Function)}.
*/ */
fun <U> mapping(toType: Class<U>, from: (Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from)) fun <U> mapping(toType: Class<U>, from: (Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from))
} }

View File

@ -9,6 +9,7 @@ import com.nisemoe.generated.keys.UPDATE_USER_QUEUE_PKEY
import com.nisemoe.generated.tables.records.UpdateUserQueueRecord import com.nisemoe.generated.tables.records.UpdateUserQueueRecord
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.util.function.Function import java.util.function.Function
import org.jooq.Field import org.jooq.Field
@ -86,7 +87,7 @@ open class UpdateUserQueue(
/** /**
* The column <code>public.update_user_queue.processed_at</code>. * The column <code>public.update_user_queue.processed_at</code>.
*/ */
val PROCESSED_AT: TableField<UpdateUserQueueRecord, LocalDateTime?> = createField(DSL.name("processed_at"), SQLDataType.LOCALDATETIME(6), this, "") val PROCESSED_AT: TableField<UpdateUserQueueRecord, OffsetDateTime?> = createField(DSL.name("processed_at"), SQLDataType.TIMESTAMPWITHTIMEZONE(6).defaultValue(DSL.field(DSL.raw("CURRENT_TIMESTAMP"), SQLDataType.TIMESTAMPWITHTIMEZONE)), this, "")
/** /**
* The column <code>public.update_user_queue.progress_current</code>. * The column <code>public.update_user_queue.progress_current</code>.
@ -142,16 +143,16 @@ open class UpdateUserQueue(
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Row7 type methods // Row7 type methods
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
override fun fieldsRow(): Row7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> = super.fieldsRow() as Row7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> override fun fieldsRow(): Row7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?> = super.fieldsRow() as Row7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?>
/** /**
* Convenience mapping calling {@link SelectField#convertFrom(Function)}. * Convenience mapping calling {@link SelectField#convertFrom(Function)}.
*/ */
fun <U> mapping(from: (Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?) -> U): SelectField<U> = convertFrom(Records.mapping(from)) fun <U> mapping(from: (Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?) -> U): SelectField<U> = convertFrom(Records.mapping(from))
/** /**
* Convenience mapping calling {@link SelectField#convertFrom(Class, * Convenience mapping calling {@link SelectField#convertFrom(Class,
* Function)}. * Function)}.
*/ */
fun <U> mapping(toType: Class<U>, from: (Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from)) fun <U> mapping(toType: Class<U>, from: (Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from))
} }

View File

@ -9,6 +9,7 @@ import com.nisemoe.generated.keys.USERS_PKEY
import com.nisemoe.generated.tables.records.UsersRecord import com.nisemoe.generated.tables.records.UsersRecord
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.util.function.Function import java.util.function.Function
import org.jooq.Field import org.jooq.Field
@ -140,7 +141,7 @@ open class Users(
/** /**
* The column <code>public.users.sys_last_update</code>. * The column <code>public.users.sys_last_update</code>.
*/ */
val SYS_LAST_UPDATE: TableField<UsersRecord, LocalDateTime?> = createField(DSL.name("sys_last_update"), SQLDataType.LOCALDATETIME(6), this, "") val SYS_LAST_UPDATE: TableField<UsersRecord, OffsetDateTime?> = createField(DSL.name("sys_last_update"), SQLDataType.TIMESTAMPWITHTIMEZONE(6).defaultValue(DSL.field(DSL.raw("CURRENT_TIMESTAMP"), SQLDataType.TIMESTAMPWITHTIMEZONE)), this, "")
/** /**
* The column <code>public.users.is_admin</code>. * The column <code>public.users.is_admin</code>.
@ -190,16 +191,16 @@ open class Users(
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Row17 type methods // Row17 type methods
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
override fun fieldsRow(): Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> = super.fieldsRow() as Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> override fun fieldsRow(): Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?> = super.fieldsRow() as Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?>
/** /**
* Convenience mapping calling {@link SelectField#convertFrom(Function)}. * Convenience mapping calling {@link SelectField#convertFrom(Function)}.
*/ */
fun <U> mapping(from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?) -> U): SelectField<U> = convertFrom(Records.mapping(from)) fun <U> mapping(from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?) -> U): SelectField<U> = convertFrom(Records.mapping(from))
/** /**
* Convenience mapping calling {@link SelectField#convertFrom(Class, * Convenience mapping calling {@link SelectField#convertFrom(Class,
* Function)}. * Function)}.
*/ */
fun <U> mapping(toType: Class<U>, from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from)) fun <U> mapping(toType: Class<U>, from: (Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?) -> U): SelectField<U> = convertFrom(toType, Records.mapping(from))
} }

View File

@ -6,7 +6,7 @@ package com.nisemoe.generated.tables.records
import com.nisemoe.generated.tables.Beatmaps import com.nisemoe.generated.tables.Beatmaps
import java.time.LocalDateTime import java.time.OffsetDateTime
import org.jooq.Field import org.jooq.Field
import org.jooq.Record1 import org.jooq.Record1
@ -19,7 +19,7 @@ import org.jooq.impl.UpdatableRecordImpl
* This class is generated by jOOQ. * This class is generated by jOOQ.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRecord>(Beatmaps.BEATMAPS), Record9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> { open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRecord>(Beatmaps.BEATMAPS), Record9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?> {
open var beatmapId: Int? open var beatmapId: Int?
set(value): Unit = set(0, value) set(value): Unit = set(0, value)
@ -53,9 +53,9 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
set(value): Unit = set(7, value) set(value): Unit = set(7, value)
get(): String? = get(7) as String? get(): String? = get(7) as String?
open var sysLastUpdate: LocalDateTime? open var sysLastUpdate: OffsetDateTime?
set(value): Unit = set(8, value) set(value): Unit = set(8, value)
get(): LocalDateTime? = get(8) as LocalDateTime? get(): OffsetDateTime? = get(8) as OffsetDateTime?
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Primary key information // Primary key information
@ -67,8 +67,8 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
// Record9 type implementation // Record9 type implementation
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
override fun fieldsRow(): Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> = super.fieldsRow() as Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> override fun fieldsRow(): Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?> = super.fieldsRow() as Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?>
override fun valuesRow(): Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> = super.valuesRow() as Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, LocalDateTime?> override fun valuesRow(): Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?> = super.valuesRow() as Row9<Int?, String?, Int?, String?, String?, Double?, String?, String?, OffsetDateTime?>
override fun field1(): Field<Int?> = Beatmaps.BEATMAPS.BEATMAP_ID override fun field1(): Field<Int?> = Beatmaps.BEATMAPS.BEATMAP_ID
override fun field2(): Field<String?> = Beatmaps.BEATMAPS.ARTIST override fun field2(): Field<String?> = Beatmaps.BEATMAPS.ARTIST
override fun field3(): Field<Int?> = Beatmaps.BEATMAPS.BEATMAPSET_ID override fun field3(): Field<Int?> = Beatmaps.BEATMAPS.BEATMAPSET_ID
@ -77,7 +77,7 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
override fun field6(): Field<Double?> = Beatmaps.BEATMAPS.STAR_RATING override fun field6(): Field<Double?> = Beatmaps.BEATMAPS.STAR_RATING
override fun field7(): Field<String?> = Beatmaps.BEATMAPS.TITLE override fun field7(): Field<String?> = Beatmaps.BEATMAPS.TITLE
override fun field8(): Field<String?> = Beatmaps.BEATMAPS.VERSION override fun field8(): Field<String?> = Beatmaps.BEATMAPS.VERSION
override fun field9(): Field<LocalDateTime?> = Beatmaps.BEATMAPS.SYS_LAST_UPDATE override fun field9(): Field<OffsetDateTime?> = Beatmaps.BEATMAPS.SYS_LAST_UPDATE
override fun component1(): Int? = beatmapId override fun component1(): Int? = beatmapId
override fun component2(): String? = artist override fun component2(): String? = artist
override fun component3(): Int? = beatmapsetId override fun component3(): Int? = beatmapsetId
@ -86,7 +86,7 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
override fun component6(): Double? = starRating override fun component6(): Double? = starRating
override fun component7(): String? = title override fun component7(): String? = title
override fun component8(): String? = version override fun component8(): String? = version
override fun component9(): LocalDateTime? = sysLastUpdate override fun component9(): OffsetDateTime? = sysLastUpdate
override fun value1(): Int? = beatmapId override fun value1(): Int? = beatmapId
override fun value2(): String? = artist override fun value2(): String? = artist
override fun value3(): Int? = beatmapsetId override fun value3(): Int? = beatmapsetId
@ -95,7 +95,7 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
override fun value6(): Double? = starRating override fun value6(): Double? = starRating
override fun value7(): String? = title override fun value7(): String? = title
override fun value8(): String? = version override fun value8(): String? = version
override fun value9(): LocalDateTime? = sysLastUpdate override fun value9(): OffsetDateTime? = sysLastUpdate
override fun value1(value: Int?): BeatmapsRecord { override fun value1(value: Int?): BeatmapsRecord {
set(0, value) set(0, value)
@ -137,12 +137,12 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
return this return this
} }
override fun value9(value: LocalDateTime?): BeatmapsRecord { override fun value9(value: OffsetDateTime?): BeatmapsRecord {
set(8, value) set(8, value)
return this return this
} }
override fun values(value1: Int?, value2: String?, value3: Int?, value4: String?, value5: String?, value6: Double?, value7: String?, value8: String?, value9: LocalDateTime?): BeatmapsRecord { override fun values(value1: Int?, value2: String?, value3: Int?, value4: String?, value5: String?, value6: Double?, value7: String?, value8: String?, value9: OffsetDateTime?): BeatmapsRecord {
this.value1(value1) this.value1(value1)
this.value2(value2) this.value2(value2)
this.value3(value3) this.value3(value3)
@ -158,7 +158,7 @@ open class BeatmapsRecord private constructor() : UpdatableRecordImpl<BeatmapsRe
/** /**
* Create a detached, initialised BeatmapsRecord * Create a detached, initialised BeatmapsRecord
*/ */
constructor(beatmapId: Int? = null, artist: String? = null, beatmapsetId: Int? = null, creator: String? = null, source: String? = null, starRating: Double? = null, title: String? = null, version: String? = null, sysLastUpdate: LocalDateTime? = null): this() { constructor(beatmapId: Int? = null, artist: String? = null, beatmapsetId: Int? = null, creator: String? = null, source: String? = null, starRating: Double? = null, title: String? = null, version: String? = null, sysLastUpdate: OffsetDateTime? = null): this() {
this.beatmapId = beatmapId this.beatmapId = beatmapId
this.artist = artist this.artist = artist
this.beatmapsetId = beatmapsetId this.beatmapsetId = beatmapsetId

View File

@ -7,6 +7,7 @@ package com.nisemoe.generated.tables.records
import com.nisemoe.generated.tables.UpdateUserQueue import com.nisemoe.generated.tables.UpdateUserQueue
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime
import org.jooq.Field import org.jooq.Field
import org.jooq.Record1 import org.jooq.Record1
@ -19,7 +20,7 @@ import org.jooq.impl.UpdatableRecordImpl
* This class is generated by jOOQ. * This class is generated by jOOQ.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<UpdateUserQueueRecord>(UpdateUserQueue.UPDATE_USER_QUEUE), Record7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> { open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<UpdateUserQueueRecord>(UpdateUserQueue.UPDATE_USER_QUEUE), Record7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?> {
open var id: Int? open var id: Int?
set(value): Unit = set(0, value) set(value): Unit = set(0, value)
@ -37,9 +38,9 @@ open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<Upd
set(value): Unit = set(3, value) set(value): Unit = set(3, value)
get(): LocalDateTime? = get(3) as LocalDateTime? get(): LocalDateTime? = get(3) as LocalDateTime?
open var processedAt: LocalDateTime? open var processedAt: OffsetDateTime?
set(value): Unit = set(4, value) set(value): Unit = set(4, value)
get(): LocalDateTime? = get(4) as LocalDateTime? get(): OffsetDateTime? = get(4) as OffsetDateTime?
open var progressCurrent: Int? open var progressCurrent: Int?
set(value): Unit = set(5, value) set(value): Unit = set(5, value)
@ -59,27 +60,27 @@ open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<Upd
// Record7 type implementation // Record7 type implementation
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
override fun fieldsRow(): Row7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> = super.fieldsRow() as Row7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> override fun fieldsRow(): Row7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?> = super.fieldsRow() as Row7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?>
override fun valuesRow(): Row7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> = super.valuesRow() as Row7<Int?, Long?, Boolean?, LocalDateTime?, LocalDateTime?, Int?, Int?> override fun valuesRow(): Row7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?> = super.valuesRow() as Row7<Int?, Long?, Boolean?, LocalDateTime?, OffsetDateTime?, Int?, Int?>
override fun field1(): Field<Int?> = UpdateUserQueue.UPDATE_USER_QUEUE.ID override fun field1(): Field<Int?> = UpdateUserQueue.UPDATE_USER_QUEUE.ID
override fun field2(): Field<Long?> = UpdateUserQueue.UPDATE_USER_QUEUE.USER_ID override fun field2(): Field<Long?> = UpdateUserQueue.UPDATE_USER_QUEUE.USER_ID
override fun field3(): Field<Boolean?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROCESSED override fun field3(): Field<Boolean?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROCESSED
override fun field4(): Field<LocalDateTime?> = UpdateUserQueue.UPDATE_USER_QUEUE.CREATED_AT override fun field4(): Field<LocalDateTime?> = UpdateUserQueue.UPDATE_USER_QUEUE.CREATED_AT
override fun field5(): Field<LocalDateTime?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROCESSED_AT override fun field5(): Field<OffsetDateTime?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROCESSED_AT
override fun field6(): Field<Int?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROGRESS_CURRENT override fun field6(): Field<Int?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROGRESS_CURRENT
override fun field7(): Field<Int?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROGRESS_TOTAL override fun field7(): Field<Int?> = UpdateUserQueue.UPDATE_USER_QUEUE.PROGRESS_TOTAL
override fun component1(): Int? = id override fun component1(): Int? = id
override fun component2(): Long = userId override fun component2(): Long = userId
override fun component3(): Boolean? = processed override fun component3(): Boolean? = processed
override fun component4(): LocalDateTime? = createdAt override fun component4(): LocalDateTime? = createdAt
override fun component5(): LocalDateTime? = processedAt override fun component5(): OffsetDateTime? = processedAt
override fun component6(): Int? = progressCurrent override fun component6(): Int? = progressCurrent
override fun component7(): Int? = progressTotal override fun component7(): Int? = progressTotal
override fun value1(): Int? = id override fun value1(): Int? = id
override fun value2(): Long = userId override fun value2(): Long = userId
override fun value3(): Boolean? = processed override fun value3(): Boolean? = processed
override fun value4(): LocalDateTime? = createdAt override fun value4(): LocalDateTime? = createdAt
override fun value5(): LocalDateTime? = processedAt override fun value5(): OffsetDateTime? = processedAt
override fun value6(): Int? = progressCurrent override fun value6(): Int? = progressCurrent
override fun value7(): Int? = progressTotal override fun value7(): Int? = progressTotal
@ -103,7 +104,7 @@ open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<Upd
return this return this
} }
override fun value5(value: LocalDateTime?): UpdateUserQueueRecord { override fun value5(value: OffsetDateTime?): UpdateUserQueueRecord {
set(4, value) set(4, value)
return this return this
} }
@ -118,7 +119,7 @@ open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<Upd
return this return this
} }
override fun values(value1: Int?, value2: Long?, value3: Boolean?, value4: LocalDateTime?, value5: LocalDateTime?, value6: Int?, value7: Int?): UpdateUserQueueRecord { override fun values(value1: Int?, value2: Long?, value3: Boolean?, value4: LocalDateTime?, value5: OffsetDateTime?, value6: Int?, value7: Int?): UpdateUserQueueRecord {
this.value1(value1) this.value1(value1)
this.value2(value2) this.value2(value2)
this.value3(value3) this.value3(value3)
@ -132,7 +133,7 @@ open class UpdateUserQueueRecord private constructor() : UpdatableRecordImpl<Upd
/** /**
* Create a detached, initialised UpdateUserQueueRecord * Create a detached, initialised UpdateUserQueueRecord
*/ */
constructor(id: Int? = null, userId: Long, processed: Boolean? = null, createdAt: LocalDateTime? = null, processedAt: LocalDateTime? = null, progressCurrent: Int? = null, progressTotal: Int? = null): this() { constructor(id: Int? = null, userId: Long, processed: Boolean? = null, createdAt: LocalDateTime? = null, processedAt: OffsetDateTime? = null, progressCurrent: Int? = null, progressTotal: Int? = null): this() {
this.id = id this.id = id
this.userId = userId this.userId = userId
this.processed = processed this.processed = processed

View File

@ -7,6 +7,7 @@ package com.nisemoe.generated.tables.records
import com.nisemoe.generated.tables.Users import com.nisemoe.generated.tables.Users
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime
import org.jooq.Field import org.jooq.Field
import org.jooq.Record1 import org.jooq.Record1
@ -19,7 +20,7 @@ import org.jooq.impl.UpdatableRecordImpl
* This class is generated by jOOQ. * This class is generated by jOOQ.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(Users.USERS), Record17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> { open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(Users.USERS), Record17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?> {
open var userId: Long? open var userId: Long?
set(value): Unit = set(0, value) set(value): Unit = set(0, value)
@ -81,9 +82,9 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
set(value): Unit = set(14, value) set(value): Unit = set(14, value)
get(): Long? = get(14) as Long? get(): Long? = get(14) as Long?
open var sysLastUpdate: LocalDateTime? open var sysLastUpdate: OffsetDateTime?
set(value): Unit = set(15, value) set(value): Unit = set(15, value)
get(): LocalDateTime? = get(15) as LocalDateTime? get(): OffsetDateTime? = get(15) as OffsetDateTime?
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME")
@set:JvmName("setIsAdmin") @set:JvmName("setIsAdmin")
@ -101,8 +102,8 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
// Record17 type implementation // Record17 type implementation
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
override fun fieldsRow(): Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> = super.fieldsRow() as Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> override fun fieldsRow(): Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?> = super.fieldsRow() as Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?>
override fun valuesRow(): Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> = super.valuesRow() as Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, LocalDateTime?, Boolean?> override fun valuesRow(): Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?> = super.valuesRow() as Row17<Long?, String?, LocalDateTime?, String?, Long?, Long?, Double?, Double?, Long?, Long?, Long?, Long?, Long?, Long?, Long?, OffsetDateTime?, Boolean?>
override fun field1(): Field<Long?> = Users.USERS.USER_ID override fun field1(): Field<Long?> = Users.USERS.USER_ID
override fun field2(): Field<String?> = Users.USERS.USERNAME override fun field2(): Field<String?> = Users.USERS.USERNAME
override fun field3(): Field<LocalDateTime?> = Users.USERS.JOIN_DATE override fun field3(): Field<LocalDateTime?> = Users.USERS.JOIN_DATE
@ -118,7 +119,7 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
override fun field13(): Field<Long?> = Users.USERS.COUNT_100 override fun field13(): Field<Long?> = Users.USERS.COUNT_100
override fun field14(): Field<Long?> = Users.USERS.COUNT_300 override fun field14(): Field<Long?> = Users.USERS.COUNT_300
override fun field15(): Field<Long?> = Users.USERS.COUNT_50 override fun field15(): Field<Long?> = Users.USERS.COUNT_50
override fun field16(): Field<LocalDateTime?> = Users.USERS.SYS_LAST_UPDATE override fun field16(): Field<OffsetDateTime?> = Users.USERS.SYS_LAST_UPDATE
override fun field17(): Field<Boolean?> = Users.USERS.IS_ADMIN override fun field17(): Field<Boolean?> = Users.USERS.IS_ADMIN
override fun component1(): Long? = userId override fun component1(): Long? = userId
override fun component2(): String? = username override fun component2(): String? = username
@ -135,7 +136,7 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
override fun component13(): Long? = count_100 override fun component13(): Long? = count_100
override fun component14(): Long? = count_300 override fun component14(): Long? = count_300
override fun component15(): Long? = count_50 override fun component15(): Long? = count_50
override fun component16(): LocalDateTime? = sysLastUpdate override fun component16(): OffsetDateTime? = sysLastUpdate
override fun component17(): Boolean? = isAdmin override fun component17(): Boolean? = isAdmin
override fun value1(): Long? = userId override fun value1(): Long? = userId
override fun value2(): String? = username override fun value2(): String? = username
@ -152,7 +153,7 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
override fun value13(): Long? = count_100 override fun value13(): Long? = count_100
override fun value14(): Long? = count_300 override fun value14(): Long? = count_300
override fun value15(): Long? = count_50 override fun value15(): Long? = count_50
override fun value16(): LocalDateTime? = sysLastUpdate override fun value16(): OffsetDateTime? = sysLastUpdate
override fun value17(): Boolean? = isAdmin override fun value17(): Boolean? = isAdmin
override fun value1(value: Long?): UsersRecord { override fun value1(value: Long?): UsersRecord {
@ -230,7 +231,7 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
return this return this
} }
override fun value16(value: LocalDateTime?): UsersRecord { override fun value16(value: OffsetDateTime?): UsersRecord {
set(15, value) set(15, value)
return this return this
} }
@ -240,7 +241,7 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
return this return this
} }
override fun values(value1: Long?, value2: String?, value3: LocalDateTime?, value4: String?, value5: Long?, value6: Long?, value7: Double?, value8: Double?, value9: Long?, value10: Long?, value11: Long?, value12: Long?, value13: Long?, value14: Long?, value15: Long?, value16: LocalDateTime?, value17: Boolean?): UsersRecord { override fun values(value1: Long?, value2: String?, value3: LocalDateTime?, value4: String?, value5: Long?, value6: Long?, value7: Double?, value8: Double?, value9: Long?, value10: Long?, value11: Long?, value12: Long?, value13: Long?, value14: Long?, value15: Long?, value16: OffsetDateTime?, value17: Boolean?): UsersRecord {
this.value1(value1) this.value1(value1)
this.value2(value2) this.value2(value2)
this.value3(value3) this.value3(value3)
@ -264,7 +265,7 @@ open class UsersRecord private constructor() : UpdatableRecordImpl<UsersRecord>(
/** /**
* Create a detached, initialised UsersRecord * Create a detached, initialised UsersRecord
*/ */
constructor(userId: Long? = null, username: String? = null, joinDate: LocalDateTime? = null, country: String? = null, countryRank: Long? = null, rank: Long? = null, ppRaw: Double? = null, accuracy: Double? = null, playcount: Long? = null, totalScore: Long? = null, rankedScore: Long? = null, secondsPlayed: Long? = null, count_100: Long? = null, count_300: Long? = null, count_50: Long? = null, sysLastUpdate: LocalDateTime? = null, isAdmin: Boolean? = null): this() { constructor(userId: Long? = null, username: String? = null, joinDate: LocalDateTime? = null, country: String? = null, countryRank: Long? = null, rank: Long? = null, ppRaw: Double? = null, accuracy: Double? = null, playcount: Long? = null, totalScore: Long? = null, rankedScore: Long? = null, secondsPlayed: Long? = null, count_100: Long? = null, count_300: Long? = null, count_50: Long? = null, sysLastUpdate: OffsetDateTime? = null, isAdmin: Boolean? = null): this() {
this.userId = userId this.userId = userId
this.username = username this.username = username
this.joinDate = joinDate this.joinDate = joinDate

View File

@ -1,11 +1,12 @@
package com.nisemoe.nise package com.nisemoe.nise
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.time.LocalDateTime import java.time.OffsetDateTime
data class UserQueueDetails( data class UserQueueDetails(
val isProcessing: Boolean, val isProcessing: Boolean,
val lastCompletedUpdate: LocalDateTime?, val lastCompletedUpdate: OffsetDateTime?,
val canUpdate: Boolean,
val progressCurrent: Int?, val progressCurrent: Int?,
val progressTotal: Int?, val progressTotal: Int?,

View File

@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
@RestController @RestController
class UserDetailsController( class UserDetailsController(
@ -23,6 +25,8 @@ 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,
@ -37,6 +41,10 @@ class UserDetailsController(
@PostMapping("user-queue") @PostMapping("user-queue")
fun addUserToQueue(@RequestBody request: UserQueueRequest): ResponseEntity<Unit> { fun addUserToQueue(@RequestBody request: UserQueueRequest): ResponseEntity<Unit> {
val userQueueDetails = this.userQueueService.getUserQueueDetails(request.userId)
if(!userQueueDetails.canUpdate)
return ResponseEntity.badRequest().build()
val inserted = this.userQueueService.insertUser(request.userId) val inserted = this.userQueueService.insertUser(request.userId)
return if(inserted) return if(inserted)
ResponseEntity.ok().build() ResponseEntity.ok().build()

View File

@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.ZoneOffset
@Service @Service
class UserService( class UserService(
@ -97,7 +98,7 @@ class UserService(
.set(USERS.COUNTRY, apiUser.country?.code) .set(USERS.COUNTRY, apiUser.country?.code)
.set(USERS.COUNTRY_RANK, apiUser.statistics?.country_rank) .set(USERS.COUNTRY_RANK, apiUser.statistics?.country_rank)
.set(USERS.PLAYCOUNT, apiUser.statistics?.play_count) .set(USERS.PLAYCOUNT, apiUser.statistics?.play_count)
.set(USERS.SYS_LAST_UPDATE, LocalDateTime.now()) .set(USERS.SYS_LAST_UPDATE, OffsetDateTime.now(ZoneOffset.UTC))
.where(USERS.USER_ID.eq(apiUser.id)) .where(USERS.USER_ID.eq(apiUser.id))
.execute() .execute()
@ -124,7 +125,7 @@ class UserService(
.set(USERS.COUNTRY, apiUser.country?.code) .set(USERS.COUNTRY, apiUser.country?.code)
.set(USERS.COUNTRY_RANK, apiUser.statistics?.country_rank) .set(USERS.COUNTRY_RANK, apiUser.statistics?.country_rank)
.set(USERS.PLAYCOUNT, apiUser.statistics?.play_count) .set(USERS.PLAYCOUNT, apiUser.statistics?.play_count)
.set(USERS.SYS_LAST_UPDATE, LocalDateTime.now()) .set(USERS.SYS_LAST_UPDATE, OffsetDateTime.now(ZoneOffset.UTC))
.onDuplicateKeyIgnore() .onDuplicateKeyIgnore()
.execute() .execute()

View File

@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.ZoneOffset
@Service @Service
@RestController @RestController
@ -159,7 +160,7 @@ class ImportScores(
Thread.sleep(this.sleepTimeInMs) Thread.sleep(this.sleepTimeInMs)
if(topUserScores != null) { if(topUserScores != null) {
val userExists = dslContext.fetchExists(USERS, USERS.USER_ID.eq(userId), USERS.SYS_LAST_UPDATE.greaterOrEqual(LocalDateTime.now().minusDays(UPDATE_USER_EVERY_DAYS))) val userExists = dslContext.fetchExists(USERS, USERS.USER_ID.eq(userId), USERS.SYS_LAST_UPDATE.greaterOrEqual(OffsetDateTime.now(ZoneOffset.UTC).minusDays(UPDATE_USER_EVERY_DAYS)))
if(!userExists) { if(!userExists) {
val apiUser = this.osuApi.getUserProfile(userId = userId.toString(), mode = "osu", key = "id") val apiUser = this.osuApi.getUserProfile(userId = userId.toString(), mode = "osu", key = "id")
if(apiUser != null) { if(apiUser != null) {
@ -178,7 +179,7 @@ class ImportScores(
.and(UPDATE_USER_QUEUE.PROCESSED.isTrue) .and(UPDATE_USER_QUEUE.PROCESSED.isTrue)
.orderBy(UPDATE_USER_QUEUE.PROCESSED_AT.desc()) .orderBy(UPDATE_USER_QUEUE.PROCESSED_AT.desc())
.limit(1) .limit(1)
.fetchOneInto(LocalDateTime::class.java) .fetchOneInto(OffsetDateTime::class.java)
for(topScore in topUserScores) { for(topScore in topUserScores) {
if(topScore.beatmap != null && topScore.beatmapset != null) { if(topScore.beatmap != null && topScore.beatmapset != null) {
@ -209,6 +210,7 @@ class ImportScores(
val currentQueueDetails = UserQueueDetails( val currentQueueDetails = UserQueueDetails(
isProcessing = true, isProcessing = true,
lastCompletedUpdate = lastCompletedUpdate, lastCompletedUpdate = lastCompletedUpdate,
canUpdate = false,
progressCurrent = current, progressCurrent = current,
progressTotal = topUserScores.size progressTotal = topUserScores.size
) )
@ -310,8 +312,8 @@ class ImportScores(
if(beatmapExists) { if(beatmapExists) {
val threeDaysAgo = LocalDateTime val threeDaysAgo = OffsetDateTime
.now() .now(ZoneOffset.UTC)
.minusDays(3) .minusDays(3)
if(dslContext.fetchExists(BEATMAPS, BEATMAPS.BEATMAP_ID.eq(beatmap.id).and(BEATMAPS.SYS_LAST_UPDATE.greaterOrEqual(threeDaysAgo)))) { if(dslContext.fetchExists(BEATMAPS, BEATMAPS.BEATMAP_ID.eq(beatmap.id).and(BEATMAPS.SYS_LAST_UPDATE.greaterOrEqual(threeDaysAgo)))) {
@ -360,7 +362,7 @@ class ImportScores(
this.statistics.currentScore++ this.statistics.currentScore++
this.logger.debug("Processing score: ${this.statistics.currentScore}/${beatmapScores.scores.size}") this.logger.debug("Processing score: ${this.statistics.currentScore}/${beatmapScores.scores.size}")
val userExists = dslContext.fetchExists(USERS, USERS.USER_ID.eq(score.user_id), USERS.SYS_LAST_UPDATE.greaterOrEqual(LocalDateTime.now().minusDays(UPDATE_USER_EVERY_DAYS))) val userExists = dslContext.fetchExists(USERS, USERS.USER_ID.eq(score.user_id), USERS.SYS_LAST_UPDATE.greaterOrEqual(OffsetDateTime.now(ZoneOffset.UTC).minusDays(UPDATE_USER_EVERY_DAYS)))
if(!userExists) { if(!userExists) {
this.userToUpdateBucket.add(score.user_id) this.userToUpdateBucket.add(score.user_id)
@ -396,7 +398,7 @@ class ImportScores(
checkReplaySimilarity(beatmap.id) checkReplaySimilarity(beatmap.id)
dslContext.update(BEATMAPS) dslContext.update(BEATMAPS)
.set(BEATMAPS.SYS_LAST_UPDATE, LocalDateTime.now()) .set(BEATMAPS.SYS_LAST_UPDATE, OffsetDateTime.now(ZoneOffset.UTC))
.where(BEATMAPS.BEATMAP_ID.eq(beatmap.id)) .where(BEATMAPS.BEATMAP_ID.eq(beatmap.id))
.execute() .execute()
} }

View File

@ -2,11 +2,14 @@ package com.nisemoe.nise.service
import com.nisemoe.generated.tables.records.UpdateUserQueueRecord import com.nisemoe.generated.tables.records.UpdateUserQueueRecord
import com.nisemoe.generated.tables.references.UPDATE_USER_QUEUE import com.nisemoe.generated.tables.references.UPDATE_USER_QUEUE
import com.nisemoe.generated.tables.references.USERS
import com.nisemoe.nise.UserQueueDetails import com.nisemoe.nise.UserQueueDetails
import org.jooq.DSLContext import org.jooq.DSLContext
import org.springframework.http.ResponseEntity
import org.springframework.messaging.simp.SimpMessagingTemplate import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.LocalDateTime import java.time.OffsetDateTime
import java.time.ZoneOffset
@Service @Service
class UpdateUserQueueService( class UpdateUserQueueService(
@ -14,19 +17,53 @@ class UpdateUserQueueService(
private val messagingTemplate: SimpMessagingTemplate private val messagingTemplate: SimpMessagingTemplate
) { ) {
private val USER_UPDATE_INTERVAL_HOURS = 4
/**
* Retrieves the user queue details for the given user ID.
*
* @param userId The osu!id of the user.
*/
fun getUserQueueDetails(userId: Long): UserQueueDetails { fun getUserQueueDetails(userId: Long): UserQueueDetails {
val isProcessing = dslContext.fetchExists( val isProcessing = dslContext.fetchExists(
UPDATE_USER_QUEUE, UPDATE_USER_QUEUE,
UPDATE_USER_QUEUE.USER_ID.eq(userId).and(UPDATE_USER_QUEUE.PROCESSED.isFalse) UPDATE_USER_QUEUE.USER_ID.eq(userId).and(UPDATE_USER_QUEUE.PROCESSED.isFalse)
) )
val lastCompletedUpdate = dslContext.select(UPDATE_USER_QUEUE.PROCESSED_AT) val lastCompletedUpdateQueue = dslContext.select(UPDATE_USER_QUEUE.PROCESSED_AT)
.from(UPDATE_USER_QUEUE) .from(UPDATE_USER_QUEUE)
.where(UPDATE_USER_QUEUE.USER_ID.eq(userId)) .where(UPDATE_USER_QUEUE.USER_ID.eq(userId))
.and(UPDATE_USER_QUEUE.PROCESSED.isTrue) .and(UPDATE_USER_QUEUE.PROCESSED.isTrue)
.orderBy(UPDATE_USER_QUEUE.PROCESSED_AT.desc()) .orderBy(UPDATE_USER_QUEUE.PROCESSED_AT.desc())
.limit(1) .limit(1)
.fetchOneInto(LocalDateTime::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
if(lastCompletedUpdate != null) {
val now = OffsetDateTime.now(ZoneOffset.UTC)
val hoursSinceLastUpdate = now.hour - lastCompletedUpdate.hour
if(hoursSinceLastUpdate < USER_UPDATE_INTERVAL_HOURS)
canUpdate = false
}
val currentProgress = dslContext.select( val currentProgress = dslContext.select(
UPDATE_USER_QUEUE.PROGRESS_CURRENT, UPDATE_USER_QUEUE.PROGRESS_CURRENT,
@ -42,12 +79,12 @@ class UpdateUserQueueService(
return UserQueueDetails( return UserQueueDetails(
isProcessing, isProcessing,
lastCompletedUpdate, lastCompletedUpdate,
canUpdate,
currentProgress?.progressCurrent, currentProgress?.progressCurrent,
currentProgress?.progressTotal currentProgress?.progressTotal
) )
} }
fun getQueue(): List<Long> { fun getQueue(): List<Long> {
return dslContext.select(UPDATE_USER_QUEUE.USER_ID) return dslContext.select(UPDATE_USER_QUEUE.USER_ID)
.from(UPDATE_USER_QUEUE) .from(UPDATE_USER_QUEUE)
@ -78,7 +115,7 @@ class UpdateUserQueueService(
fun setUserAsProcessed(userId: Long) { fun setUserAsProcessed(userId: Long) {
dslContext.update(UPDATE_USER_QUEUE) dslContext.update(UPDATE_USER_QUEUE)
.set(UPDATE_USER_QUEUE.PROCESSED, true) .set(UPDATE_USER_QUEUE.PROCESSED, true)
.set(UPDATE_USER_QUEUE.PROCESSED_AT, LocalDateTime.now()) .set(UPDATE_USER_QUEUE.PROCESSED_AT, OffsetDateTime.now(ZoneOffset.UTC))
.where(UPDATE_USER_QUEUE.USER_ID.eq(userId)) .where(UPDATE_USER_QUEUE.USER_ID.eq(userId))
.and(UPDATE_USER_QUEUE.PROCESSED.isFalse) .and(UPDATE_USER_QUEUE.PROCESSED.isFalse)
.execute() .execute()

View File

@ -0,0 +1,14 @@
ALTER TABLE public.update_user_queue
ALTER COLUMN processed_at TYPE timestamptz;
ALTER TABLE public.users
ALTER COLUMN sys_last_update TYPE timestamptz,
ALTER COLUMN sys_last_update SET DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE public.beatmaps
ALTER COLUMN sys_last_update TYPE timestamptz,
ALTER COLUMN sys_last_update SET DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE public.scores
ALTER COLUMN added_at TYPE timestamptz,
ALTER COLUMN added_at SET DEFAULT CURRENT_TIMESTAMP;

View File

@ -19,6 +19,7 @@
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@stomp/rx-stomp": "^2.0.0", "@stomp/rx-stomp": "^2.0.0",
"chart.js": "^4.4.1", "chart.js": "^4.4.1",
"date-fns": "^3.3.1",
"lz-string": "^1.5.0", "lz-string": "^1.5.0",
"ng2-charts": "^5.0.4", "ng2-charts": "^5.0.4",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
@ -5452,6 +5453,15 @@
"integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
"dev": true "dev": true
}, },
"node_modules/date-fns": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz",
"integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/date-format": { "node_modules/date-format": {
"version": "4.0.14", "version": "4.0.14",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",

View File

@ -21,6 +21,7 @@
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@stomp/rx-stomp": "^2.0.0", "@stomp/rx-stomp": "^2.0.0",
"chart.js": "^4.4.1", "chart.js": "^4.4.1",
"date-fns": "^3.3.1",
"lz-string": "^1.5.0", "lz-string": "^1.5.0",
"ng2-charts": "^5.0.4", "ng2-charts": "^5.0.4",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",

View File

@ -15,6 +15,7 @@ export interface UserDetails {
export interface UserQueueDetails { export interface UserQueueDetails {
isProcessing: boolean; isProcessing: boolean;
lastCompletedUpdate: string | null; lastCompletedUpdate: string | null;
canUpdate: boolean;
progressCurrent: number | null; progressCurrent: number | null;
progressTotal: number | null; progressTotal: number | null;

View File

@ -41,10 +41,16 @@
</ul> </ul>
<ng-container *ngIf="!this.userInfo.queue_details.isProcessing; else updateProgress"> <ng-container *ngIf="!this.userInfo.queue_details.isProcessing; else updateProgress">
<ng-container *ngIf="this.userInfo.queue_details.canUpdate">
<a (click)="this.addUserToQueue()" class="btn btn-success pointer" target="_blank"> <a (click)="this.addUserToQueue()" class="btn btn-success pointer" target="_blank">
update user scores now! update user scores now!
</a> <span style="margin-left: 4px">|</span> </a>
last update: {{ this.userInfo.queue_details.lastCompletedUpdate ? this.userInfo.queue_details.lastCompletedUpdate : 'never'}} </ng-container>
<ng-container *ngIf="!this.userInfo.queue_details.canUpdate">
<span class="btn-info">can't force update now</span>
</ng-container>
<span style="margin-left: 4px">|</span>
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">

View File

@ -11,6 +11,7 @@ import {Title} from "@angular/platform-browser";
import {RxStompService} from "../../corelib/stomp/stomp.service"; import {RxStompService} from "../../corelib/stomp/stomp.service";
import {Message} from "@stomp/stompjs/esm6"; import {Message} from "@stomp/stompjs/esm6";
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component"; import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
import {differenceInDays, differenceInHours} from "date-fns/fp";
interface UserInfo { interface UserInfo {
user_details: UserDetails; user_details: UserDetails;
@ -37,6 +38,8 @@ 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;
@ -94,6 +97,27 @@ export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {
this.liveUserSub?.unsubscribe(); this.liveUserSub?.unsubscribe();
} }
calculateTimeAgo(dateStr: string): string {
const inputDate = new Date(dateStr);
const now = new Date();
if (isNaN(inputDate.getTime())) {
return "???";
}
const difference = Math.abs(differenceInHours(now, inputDate)); // Use absolute value
if (difference < 1) {
return "recently";
} else if (difference < 24) {
return `${difference}h ago`;
} else {
const days = differenceInDays(now, inputDate);
const hours = difference % 24;
return `${days}d ${hours}h ago`;
}
}
addUserToQueue(): void { addUserToQueue(): void {
const body = { const body = {
userId: this.userInfo?.user_details.user_id userId: this.userInfo?.user_details.user_id

View File

@ -151,6 +151,17 @@ a.btn-success:hover {
border: 1px dotted #2af171; border: 1px dotted #2af171;
} }
.btn-info {
padding: 2px;
color: #3498db;
border: 1px dotted #3498db;
}
.btn-info:hover {
color: #3498db;
border: 1px dotted #3498db;
}
.btn-warning { .btn-warning {
padding: 2px; padding: 2px;
color: #f1c40f; color: #f1c40f;