idz: Add SqlTimeAttackRepository

This commit is contained in:
Tau
2019-11-06 17:28:30 -05:00
parent 561f06eccb
commit ceaeebc0b4
7 changed files with 115 additions and 35 deletions
+90
View File
@@ -0,0 +1,90 @@
import { Client } from "pg";
import * as sql from "sql-bricks";
import { _findProfile } from "./_util";
import { ExtId } from "../model/base";
import { Profile } from "../model/profile";
import { TimeAttackScore } from "../model/timeAttack";
import { TimeAttackRepository } from "../repo";
import { generateId } from "../../db";
export class SqlTimeAttackRepository implements TimeAttackRepository {
constructor(private readonly _conn: Client) {}
async loadAll(extId: ExtId<Profile>): Promise<TimeAttackScore[]> {
const loadSql = sql
.select("ta.*")
.from("idz.time_attack ta")
.join("idz.profile p", { "ta.profile_id": "p.id" })
.where("p.ext_id", extId)
.toParams();
const { rows } = await this._conn.query(loadSql);
return rows.map(row => ({
routeNo: row.route_no,
timestamp: new Date(row.timestamp),
flags: row.flags,
totalTime: row.total_time,
sectionTimes: row.section_times,
grade: row.grade,
}));
}
async save(extId: ExtId<Profile>, score: TimeAttackScore): Promise<void> {
const profileId = await _findProfile(this._conn, extId);
const logSql = sql
.insert("idz.ta_result", {
id: generateId(),
profile_id: profileId,
route_no: score.routeNo,
total_time: score.totalTime,
section_times: score.sectionTimes,
flags: score.flags,
grade: score.grade,
})
.toParams();
await this._conn.query(logSql);
const existSql = sql
.select("ta.total_time")
.from("idz.ta_best ta")
.where("ta.profile_id", profileId)
.where("ta.route_no", score.routeNo)
.toParams();
const { rows } = await this._conn.query(existSql);
const row = rows[0];
if (row === undefined) {
const insertSql = sql
.insert("idz.ta_best", {
id: generateId(),
profile_id: profileId,
route_no: score.routeNo,
total_time: score.totalTime,
section_times: score.sectionTimes,
flags: score.flags,
grade: score.grade,
})
.toParams();
await this._conn.query(insertSql);
} else if (row.total_time > score.totalTime) {
const updateSql = sql
.update("idz.ta_best", {
total_time: score.totalTime,
section_times: score.sectionTimes,
flags: score.flags,
grade: score.grade,
})
.where("profile_id", profileId)
.where("route_no", score.routeNo)
.toParams();
await this._conn.query(updateSql);
}
}
}
+8 -8
View File
@@ -1,5 +1,5 @@
import { RequestCode } from "./_defs";
import { ExtId } from "../model/base";
import { ExtId, RouteNo } from "../model/base";
import { Profile } from "../model/profile";
import { SaveTimeAttackRequest } from "../request/saveTimeAttack";
@@ -12,14 +12,14 @@ export function saveTimeAttack(buf: Buffer): SaveTimeAttackRequest {
profileId: buf.readUInt32LE(0x0004) as ExtId<Profile>,
dayNight: buf.readUInt8(0x0054) & 1,
payload: {
courseId: buf.readUInt8(0x0054) >> 1,
timestamp: new Date(buf.readUInt32LE(0x0058) * 1000).toISOString(), //hck
routeNo: (buf.readUInt8(0x0054) >> 1) as RouteNo,
timestamp: new Date(buf.readUInt32LE(0x0058) * 1000),
flags: buf.readUInt8(0x005c),
totalMsec: buf.readUInt32LE(0x0018),
stageMsec: [
buf.readUInt32LE(0x0024),
buf.readUInt32LE(0x0028),
buf.readUInt32LE(0x002c),
totalTime: buf.readUInt32LE(0x0018) / 1000,
sectionTimes: [
buf.readUInt32LE(0x0024) / 1000,
buf.readUInt32LE(0x0028) / 1000,
buf.readUInt32LE(0x002c) / 1000,
],
grade: buf.readUInt8(0x0062),
},
+9 -9
View File
@@ -17,23 +17,23 @@ export function loadProfile2(res: LoadProfileResponse2) {
buf.fill(4, 0x0680, 0x06a0);
for (const score of res.timeAttack) {
const { courseId } = score;
const { routeNo } = score;
buf.writeUInt32LE(
(new Date(score.timestamp).getTime() / 1000) | 0, // Date ctor hack
0x00e4 + courseId * 4
0x00e4 + routeNo * 4
);
buf.writeUInt16LE(0, 0x0560 + 2 * courseId); // ???
buf.writeUInt16LE(0xffff, 0x0164 + 2 * courseId); // National rank
buf.writeUInt32LE(score.totalMsec, 0x04e0 + 4 * courseId);
buf.writeUInt8(score.flags, 0x05a0 + courseId);
buf.writeUInt8(score.grade, 0x0680 + courseId);
buf.writeUInt16LE(0, 0x0560 + 2 * routeNo); // ???
buf.writeUInt16LE(0xffff, 0x0164 + 2 * routeNo); // National rank
buf.writeUInt32LE((score.totalTime * 1000) | 0, 0x04e0 + 4 * routeNo);
buf.writeUInt8(score.flags, 0x05a0 + routeNo);
buf.writeUInt8(score.grade, 0x0680 + routeNo);
for (let i = 0; i < 3; i++) {
buf.writeUInt16LE(
score.stageMsec[i] >> 2,
0x05c0 + 6 * courseId + 2 * i
(score.sectionTimes[i] * 1000) >> 2,
0x05c0 + 6 * routeNo + 2 * i
);
}
}
+1 -9
View File
@@ -6,15 +6,7 @@ export async function saveTimeAttack(
w: Repositories,
req: SaveTimeAttackRequest
): Promise<SaveTimeAttackResponse> {
if (req.payload.totalMsec > 0) {
const existing = await w
.timeAttack()
.load(req.profileId, req.payload.courseId);
if (existing === undefined || existing.totalMsec > req.payload.totalMsec) {
await w.timeAttack().save(req.profileId, req.payload);
}
}
await w.timeAttack().save(req.profileId, req.payload);
return {
type: "save_time_attack_res",
+1
View File
@@ -3,4 +3,5 @@
export type BackgroundCode = number & { __brand: "backgroundCode" };
export type ExtId<T> = number & { __extId: T };
export type RouteNo = number & { __brand: "routeNo" };
export type TitleCode = number & { __brand: "titleCode" };
+6 -4
View File
@@ -1,8 +1,10 @@
import { RouteNo } from "./base";
export interface TimeAttackScore {
courseId: number;
timestamp: string; // hack
routeNo: RouteNo;
timestamp: Date;
flags: number;
totalMsec: number;
stageMsec: number[];
totalTime: number;
sectionTimes: number[];
grade: number;
}
-5
View File
@@ -64,11 +64,6 @@ export interface TimeAttackRepository {
profileId: Model.ExtId<Model.Profile>
): Promise<Model.TimeAttackScore[]>;
load(
profileId: Model.ExtId<Model.Profile>,
courseId: number
): Promise<Model.TimeAttackScore | undefined>;
save(
profileId: Model.ExtId<Model.Profile>,
score: Model.TimeAttackScore