[+] Import (TODO)

pull/23/head
Azalea 2024-03-20 12:33:33 -04:00
parent aaf7e1e3e5
commit 313dd681de
7 changed files with 57 additions and 33 deletions

View File

@ -2,6 +2,7 @@ package ext
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
@ -19,6 +20,7 @@ import org.apache.tika.Tika
import org.apache.tika.mime.MimeTypes
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
@ -37,6 +39,7 @@ import kotlin.reflect.jvm.jvmErasure
typealias RP = RequestParam
typealias RB = RequestBody
typealias RH = RequestHeader
typealias PV = PathVariable
typealias API = RequestMapping
typealias Str = String
typealias Bool = Boolean
@ -83,7 +86,7 @@ fun Str.isValidEmail(): Bool = emailRegex.matches(this)
val ACCEPTABLE_FALSE = setOf("0", "false", "no", "off", "False", "None", "null")
val ACCEPTABLE_TRUE = setOf("1", "true", "yes", "on", "True")
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST")
val jackson = ObjectMapper().apply {
val JACKSON = ObjectMapper().apply {
findAndRegisterModules()
registerModule(SimpleModule().addDeserializer(Boolean::class.java, object : JsonDeserializer<Boolean>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext) = when(parser.text) {
@ -103,7 +106,10 @@ val jackson = ObjectMapper().apply {
override fun deserialize(parser: JsonParser, context: DeserializationContext) =
parser.text.asDateTime() ?: (400 - "Invalid date time value ${parser.text}")
}))
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
inline fun <reified T> Str.parseJson() = JACKSON.readValue(this, T::class.java)
fun <T> T.toJson() = JACKSON.writeValueAsString(this)
@OptIn(ExperimentalSerializationApi::class)
val JSON = Json {
ignoreUnknownKeys = true
@ -144,10 +150,12 @@ catch (e: Exception) { null } }
fun Long.toHex(len: Int = 16): Str = "0x${this.toString(len).padStart(len, '0').uppercase()}"
fun Map<String, Any>.toUrl() = entries.joinToString("&") { (k, v) -> "$k=$v" }
// Map
// Collections
operator fun <K, V> Map<K, V>.plus(map: Map<K, V>) =
(if (this is MutableMap) this else toMutableMap()).apply { putAll(map) }
operator fun <K, V> MutableMap<K, V>.plusAssign(map: Map<K, V>) { putAll(map) }
fun <T> MutableList<T>.popAll(list: List<T>) = list.also { removeAll(it) }
fun <T> MutableList<T>.popAll(vararg items: T) = popAll(items.toList())
// Strings
operator fun Str.get(range: IntRange) = substring(range.first, (range.last + 1).coerceAtMost(length))

View File

@ -40,14 +40,6 @@ class SettingsApi(
userRepo.save(u.apply { gameOptions = it })
}
// Check field type
// val type = field.returnType
// val newValue = when (type.classifier) {
// String::class -> value
// Int::class -> value.toInt()
// Boolean::class -> value.toBoolean()
// else -> (400 - "Invalid field type $type")
// }
// field.set(options, newValue)
field.setCast(options, value)
goRepo.save(options)
}

View File

@ -1,6 +1,6 @@
package icu.samnyan.aqua.net.games
import ext.jackson
import ext.JACKSON
import ext.splitLines
import java.lang.reflect.Field
import kotlin.reflect.KClass
@ -54,7 +54,7 @@ abstract class ImportController<T: Any>(
lists[tb.name]?.add(obj) ?: field.set(data, obj)
}
return ImportResult(errors, warnings, jackson.writeValueAsString(data))
return ImportResult(errors, warnings, JACKSON.writeValueAsString(data))
}
companion object
@ -69,7 +69,7 @@ abstract class ImportController<T: Any>(
// Process Nones
dict = dict.filterValues { it != "None" }
return jackson.convertValue(dict, type.java)
return JACKSON.convertValue(dict, type.java)
}
}
}

View File

@ -1,13 +1,14 @@
package icu.samnyan.aqua.net.games.chu3
import ext.API
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external.Chu3DataExport
import icu.samnyan.aqua.net.games.ImportClass
import icu.samnyan.aqua.net.games.ImportController
import icu.samnyan.aqua.sega.chusan.model.userdata.*
import kotlin.io.path.Path
import kotlin.io.path.readText
import org.springframework.web.bind.annotation.RestController
@RestController
@API("api/v2/game/chu3")
class Chu3Import : ImportController<Chu3DataExport>(
exportFields = Chu3DataExport::class.java.declaredFields.associateBy {
var name = it.name
@ -34,8 +35,4 @@ class Chu3Import : ImportController<Chu3DataExport>(
) {
override fun createEmpty() = Chu3DataExport("SDEZ", UserData(), UserGameOption(), ArrayList(), ArrayList(),
ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList())
}
fun main() {
Chu3Import().importArtemisSql(Path("C:\\Users\\Azalea\\Downloads\\all_inserts (2).sql").readText())
}

View File

@ -1,10 +1,14 @@
package icu.samnyan.aqua.net.games.mai2
import ext.API
import icu.samnyan.aqua.api.model.resp.sega.maimai2.external.Maimai2DataExport
import icu.samnyan.aqua.net.games.ImportClass
import icu.samnyan.aqua.net.games.ImportController
import icu.samnyan.aqua.sega.maimai2.model.userdata.*
import org.springframework.web.bind.annotation.RestController
@RestController
@API("api/v2/game/mai2")
class Mai2Import : ImportController<Maimai2DataExport>(
exportFields = Maimai2DataExport::class.java.declaredFields.associateBy {
it.name.replace("List", "").lowercase()

View File

@ -2,6 +2,7 @@ package icu.samnyan.aqua.net.games.mai2
import ext.*
import icu.samnyan.aqua.api.model.resp.sega.maimai2.external.Maimai2DataExport
import icu.samnyan.aqua.net.db.AquaNetUser
import icu.samnyan.aqua.net.db.AquaUserServices
import icu.samnyan.aqua.net.games.*
import icu.samnyan.aqua.net.utils.*
@ -9,7 +10,10 @@ import icu.samnyan.aqua.sega.maimai2.model.*
import icu.samnyan.aqua.sega.maimai2.model.userdata.UserDetail
import org.springframework.web.bind.annotation.RestController
import java.lang.reflect.Field
import java.time.LocalDateTime
import java.util.*
import kotlin.io.path.Path
import kotlin.io.path.writeText
import kotlin.reflect.full.declaredMembers
@RestController
@ -19,7 +23,8 @@ class Maimai2(
override val playlogRepo: Mai2UserPlaylogRepo,
override val userDataRepo: Mai2UserDataRepo,
val userGeneralDataRepository: Mai2UserGeneralDataRepo,
val repos: Mai2Repos
val repos: Mai2Repos,
val netProps: AquaNetProps
): GameApiController<UserDetail>("mai2", UserDetail::class) {
override suspend fun trend(@RP username: Str): List<TrendOut> = us.cardByName(username) { card ->
findTrend(playlogRepo.findByUserCardExtId(card.extId)
@ -49,23 +54,40 @@ class Maimai2(
// Use reflection to get all properties in Mai2Repos with matching names in Maimai2DataExport
val exportFields: Map<Field, UserLinked<*>> = listOf(*Maimai2DataExport::class.java.declaredFields)
.associateWith { Mai2Repos::class.declaredMembers
.filter { f -> f.name !in setOf("gameId", "userData") }
.filter { f -> f returns UserLinked::class }
.firstOrNull { f -> f.name == it.name || f.name == it.name.replace("List", "") }
?.call(repos) as UserLinked<*>? ?: error("No matching field found for ${it.name}")
}
fun export(u: AquaNetUser) = Maimai2DataExport().apply {
gameId = "SDEZ"
userData = repos.userData.findByCard(u.ghostCard) ?: (404 - "User not found")
exportFields.forEach { (f, u) ->
if (f.name == "gameId" || f.name == "userData") return@forEach
f.set(this, if (f.type == List::class.java) u.findByUser(userData)
else u.findSingleByUser(userData).orElse(null))
}
}
@API("export")
fun exportAllUserData(@RP token: Str) = us.jwt.auth(token) { u ->
try {
Maimai2DataExport().apply {
gameId = "SDEZ"
userData = repos.userData.findByCard(u.ghostCard) ?: (404 - "User not found")
exportFields.forEach { (f, u) ->
if (f.name == "gameId" || f.name == "userData") return@forEach
f.set(this, if (f.type == List::class.java) u.findByUser(userData)
else u.findSingleByUser(userData).orElse(null))
}
}
} catch (e: Exception) { 500 - "Error during data export. Reason: ${e.message}" }
fun exportAllUserData(@RP token: Str) = us.jwt.auth(token) { u -> export(u) }
@API("import")
fun importUserData(@RP token: Str, @RP json: Str) = us.jwt.auth(token) { u ->
val export = json.parseJson<Maimai2DataExport>()
if (!export.gameId.equals("SDEZ", true)) 400 - "Invalid game ID"
// Check existing data
if (repos.userData.findByCard(u.ghostCard) != null) {
// Store a backup of the old data
val fl = "mai2-backup-${u.auId}-${LocalDateTime.now().isoDateTime()}.json"
(Path(netProps.importBackupPath) / fl).writeText(export(u).toJson())
// Delete the old data
TODO()
}
TODO()
}
}

View File

@ -7,4 +7,5 @@ import org.springframework.context.annotation.Configuration
@ConfigurationProperties(prefix = "aqua-net")
class AquaNetProps {
var linkCardLimit: Int = 10
var importBackupPath = "data/import-backups"
}