Standards domain models shuffling and renaming
This commit is contained in:
@@ -27,16 +27,16 @@ export async function up(db: Kysely<any>): Promise<void> {
|
|||||||
.nullsNotDistinct()
|
.nullsNotDistinct()
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
// TABLE: CALCULATOR_CONFIGS
|
// TABLE: STANDARDS_CONFIGS
|
||||||
await db.schema.createTable("calculator_configs")
|
await db.schema.createTable("standards_configs")
|
||||||
.$call(addDefaultColumns)
|
.$call(addDefaultColumns)
|
||||||
.addColumn("standards_config", "jsonb", (cb) => cb.notNull())
|
.addColumn("parameters", "jsonb", (cb) => cb.notNull())
|
||||||
.addColumn("is_selected", "boolean", (cb) => cb.notNull().defaultTo(false))
|
.addColumn("is_selected", "boolean", (cb) => cb.notNull().defaultTo(false))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
// INDEX: ONE_SELECTED_CALCULATOR_CONFIG
|
// INDEX: ONE_SELECTED_STANDARDS_CONFIG
|
||||||
await db.schema.createIndex("idx_one_selected_calculator_config")
|
await db.schema.createIndex("idx_one_selected_standards_config")
|
||||||
.on("calculator_configs")
|
.on("standards_configs")
|
||||||
.column("is_selected")
|
.column("is_selected")
|
||||||
.unique()
|
.unique()
|
||||||
.where("is_selected", "=", true)
|
.where("is_selected", "=", true)
|
||||||
@@ -50,9 +50,9 @@ export async function down(db: Kysely<any>): Promise<void> {
|
|||||||
// INDEX: ONE_ACTIVE_PRODUCT_SYNC
|
// INDEX: ONE_ACTIVE_PRODUCT_SYNC
|
||||||
await db.schema.dropIndex("idx_one_active_product_sync").ifExists().execute();
|
await db.schema.dropIndex("idx_one_active_product_sync").ifExists().execute();
|
||||||
|
|
||||||
// TABLE: CALCULATOR_CONFIGS
|
// TABLE: STANDARDS_CONFIGS
|
||||||
await db.schema.dropTable("calculator_configs").ifExists().execute()
|
await db.schema.dropTable("standards_configs").ifExists().execute()
|
||||||
|
|
||||||
// INDEX: ONE_SELECTED_CALCULATOR_CONFIG
|
// INDEX: ONE_SELECTED_STANDARDS_CONFIG
|
||||||
await db.schema.dropIndex("idx_one_selected_calculator_config").ifExists().execute();
|
await db.schema.dropIndex("idx_one_selected_standards_config").ifExists().execute();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { CommerceService } from "./services/commerce";
|
|||||||
import cluster from "node:cluster";
|
import cluster from "node:cluster";
|
||||||
import { randomUUIDv7 } from "bun";
|
import { randomUUIDv7 } from "bun";
|
||||||
import { CalculatorService } from "./services/calculator";
|
import { CalculatorService } from "./services/calculator";
|
||||||
import { StandardsConfigSchema } from "@blade-and-brawn/calculator";
|
import { StandardsParametersSchema } from "@blade-and-brawn/calculator";
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
// -----------
|
// -----------
|
||||||
@@ -117,23 +117,23 @@ export const app = new Elysia()
|
|||||||
return { sessionId: token.sessionId.toString() };
|
return { sessionId: token.sessionId.toString() };
|
||||||
})
|
})
|
||||||
.put("/config/:id", async ({ params: { id }, body: { standardsConfig } }) => {
|
.put("/config/:id", async ({ params: { id }, body: { standardsConfig } }) => {
|
||||||
await calculatorService.setConfig(id, standardsConfig);
|
await calculatorService.standards.setConfig(id, standardsConfig);
|
||||||
}, {
|
}, {
|
||||||
params: t.Object({ id: t.String() }),
|
params: t.Object({ id: t.String() }),
|
||||||
body: t.Object({ standardsConfig: StandardsConfigSchema })
|
body: t.Object({ standardsConfig: StandardsParametersSchema })
|
||||||
})
|
})
|
||||||
.post("/config", async ({ body: { standardsConfig } }) => {
|
.post("/config", async ({ body: { standardsConfig } }) => {
|
||||||
await calculatorService.createConfig(standardsConfig);
|
await calculatorService.standards.createConfig(standardsConfig);
|
||||||
}, {
|
}, {
|
||||||
body: t.Object({ standardsConfig: StandardsConfigSchema })
|
body: t.Object({ standardsConfig: StandardsParametersSchema })
|
||||||
})
|
})
|
||||||
.post("/config/:id/select", async ({ params: { id } }) => {
|
.post("/config/:id/select", async ({ params: { id } }) => {
|
||||||
await calculatorService.selectConfig(id);
|
await calculatorService.standards.selectConfig(id);
|
||||||
}, {
|
}, {
|
||||||
params: t.Object({ id: t.String() })
|
params: t.Object({ id: t.String() })
|
||||||
})
|
})
|
||||||
.get("/config", async () => {
|
.get("/config", async () => {
|
||||||
return await calculatorService.getSelectedConfig();
|
return await calculatorService.standards.getSelectedConfig();
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,39 @@
|
|||||||
import { LevelCalculator, Standards, DEFAULT_STANDARDS_DATA, type StandardsConfig, StandardsConfigSchema } from "@blade-and-brawn/calculator";
|
import { LevelCalculator, Standards, DEFAULT_STANDARDS_DATA, type StandardsParameters, StandardsParametersSchema } from "@blade-and-brawn/calculator";
|
||||||
import type { ActivityPerformance, Player } from "@blade-and-brawn/domain";
|
import type { ActivityPerformance, Player } from "@blade-and-brawn/domain";
|
||||||
import { db } from "../database/db";
|
import { db } from "../database/db";
|
||||||
import { Value } from "@sinclair/typebox/value";
|
import { Value } from "@sinclair/typebox/value";
|
||||||
import { log } from "../util";
|
import { log } from "../util";
|
||||||
|
|
||||||
export class CalculatorService {
|
let calculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATA));
|
||||||
private static readonly CONFIG_REFRESH_INTERVAL_MS = 30_000;
|
|
||||||
|
|
||||||
|
export class CalculatorService {
|
||||||
|
standards: StandardsSubService = new StandardsSubService();
|
||||||
|
|
||||||
|
calculate(player: Player, activityPerformances: ActivityPerformance[]) {
|
||||||
|
this.standards.refreshConfig({ onlyIfStale: true }).catch((err) => log.error({ err }, "failed to refresh standards config"));
|
||||||
|
return calculator.calculate(player, activityPerformances);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandardsSubService {
|
||||||
|
private static readonly CONFIG_REFRESH_INTERVAL_MS = 30_000;
|
||||||
private cachedStandardsConfigData: string | null = null;
|
private cachedStandardsConfigData: string | null = null;
|
||||||
private lastRefreshedAt = 0;
|
private lastRefreshedAt = 0;
|
||||||
|
|
||||||
private calculator: LevelCalculator;
|
async getSelectedConfig(opt: { skipCache?: boolean } = {}): Promise<StandardsParameters> {
|
||||||
|
if (!opt.skipCache) return calculator.standards.params;
|
||||||
constructor() {
|
|
||||||
this.calculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATA));
|
|
||||||
}
|
|
||||||
|
|
||||||
calculate(player: Player, activityPerformances: ActivityPerformance[]) {
|
|
||||||
this.refreshConfig({ onlyIfStale: true }).catch((err) => log.error({ err }, "failed to refresh calculator config"));
|
|
||||||
return this.calculator.calculate(player, activityPerformances);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSelectedConfig(opt: { skipCache?: boolean } = {}): Promise<StandardsConfig> {
|
|
||||||
if (!opt.skipCache) return this.calculator.standards.cfg;
|
|
||||||
|
|
||||||
await this.refreshConfig();
|
await this.refreshConfig();
|
||||||
return this.calculator.standards.cfg;
|
return calculator.standards.params;
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectConfig(id: string) {
|
async selectConfig(id: string) {
|
||||||
await db.transaction().execute(async (trx) => {
|
await db.transaction().execute(async (trx) => {
|
||||||
await trx.updateTable("calculator_configs")
|
await trx.updateTable("standards_configs")
|
||||||
.set({ is_selected: false })
|
.set({ is_selected: false })
|
||||||
.where("is_selected", "=", true)
|
.where("is_selected", "=", true)
|
||||||
.execute();
|
.execute();
|
||||||
const result = await trx.updateTable("calculator_configs")
|
const result = await trx.updateTable("standards_configs")
|
||||||
.set({ is_selected: true })
|
.set({ is_selected: true })
|
||||||
.where("id", "=", id)
|
.where("id", "=", id)
|
||||||
.execute();
|
.execute();
|
||||||
@@ -42,37 +41,37 @@ export class CalculatorService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setConfig(id: string, standardsConfig: StandardsConfig) {
|
async setConfig(id: string, parameters: StandardsParameters) {
|
||||||
const result = await db.updateTable("calculator_configs")
|
const result = await db.updateTable("standards_configs")
|
||||||
.set({ standards_config: standardsConfig })
|
.set({ parameters })
|
||||||
.where("id", "=", id)
|
.where("id", "=", id)
|
||||||
.execute();
|
.execute();
|
||||||
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
if (result[0]?.numUpdatedRows === 0n) throw new Error(`No config with id "${id}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createConfig(standardsConfig: StandardsConfig) {
|
async createConfig(parameters: StandardsParameters) {
|
||||||
await db.insertInto("calculator_configs")
|
await db.insertInto("standards_configs")
|
||||||
.values({ standards_config: standardsConfig })
|
.values({ parameters })
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshConfig(opt: { onlyIfStale?: boolean } = {}) {
|
async refreshConfig(opt: { onlyIfStale?: boolean } = {}) {
|
||||||
if (opt.onlyIfStale)
|
if (opt.onlyIfStale)
|
||||||
if (Date.now() - this.lastRefreshedAt < CalculatorService.CONFIG_REFRESH_INTERVAL_MS) return;
|
if (Date.now() - this.lastRefreshedAt < StandardsSubService.CONFIG_REFRESH_INTERVAL_MS) return;
|
||||||
|
|
||||||
this.lastRefreshedAt = Date.now();
|
this.lastRefreshedAt = Date.now();
|
||||||
|
|
||||||
const result = await db.selectFrom("calculator_configs")
|
const { parameters } = await db.selectFrom("standards_configs")
|
||||||
.select(["standards_config"])
|
.select(["parameters"])
|
||||||
.where("is_selected", "=", true)
|
.where("is_selected", "=", true)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.executeTakeFirstOrThrow({ errorConstructor: () => new Error("No calculator config selected") });
|
.executeTakeFirstOrThrow({ errorConstructor: () => new Error("No standards config selected") });
|
||||||
Value.Assert(StandardsConfigSchema, result.standards_config);
|
Value.Assert(StandardsParametersSchema, parameters);
|
||||||
|
|
||||||
const standardsConfigData = JSON.stringify(result.standards_config);
|
const standardsConfigData = JSON.stringify(parameters);
|
||||||
if (standardsConfigData === this.cachedStandardsConfigData) return;
|
if (standardsConfigData === this.cachedStandardsConfigData) return;
|
||||||
|
|
||||||
this.calculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATA, result.standards_config));
|
calculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATA, parameters));
|
||||||
this.cachedStandardsConfigData = standardsConfigData;
|
this.cachedStandardsConfigData = standardsConfigData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {
|
import {
|
||||||
Standards,
|
Standards,
|
||||||
type Standard,
|
type Standard,
|
||||||
type StandardsConfig,
|
type StandardsParameters,
|
||||||
} from "@blade-and-brawn/calculator";
|
} from "@blade-and-brawn/calculator";
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
selected: {
|
selected: {
|
||||||
activity: Activity;
|
activity: Activity;
|
||||||
metrics: Metrics;
|
metrics: Metrics;
|
||||||
cfg: StandardsConfig;
|
params: StandardsParameters;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<p class="text-xs label">
|
<p class="text-xs label">
|
||||||
{#if selected.cfg.activity[selected.activity].enableGeneration && allStandards
|
{#if selected.params.activity[selected.activity].enableGeneration && allStandards
|
||||||
.byActivity(selected.activity)
|
.byActivity(selected.activity)
|
||||||
.getMetadata().generators.length}
|
.getMetadata().generators.length}
|
||||||
(Generated data: {allStandards
|
(Generated data: {allStandards
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
? parseFloat(kgToLb(standard.metrics.weight).toFixed(2))
|
? parseFloat(kgToLb(standard.metrics.weight).toFixed(2))
|
||||||
: "None"}</th
|
: "None"}</th
|
||||||
>
|
>
|
||||||
{#each range(selected.cfg.global.maxLevel).map((i) => ++i) as lvl}
|
{#each range(selected.params.global.maxLevel).map((i) => ++i) as lvl}
|
||||||
<td>{getLvlValue(activity, standard, lvl)}</td>
|
<td>{getLvlValue(activity, standard, lvl)}</td>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Body weight</th>
|
<th>Body weight</th>
|
||||||
{#each range(selected.cfg.global.maxLevel).map((i) => ++i) as lvl}
|
{#each range(selected.params.global.maxLevel).map((i) => ++i) as lvl}
|
||||||
<td>{lvl}</td>
|
<td>{lvl}</td>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {
|
import {
|
||||||
DEFAULT_STANDARDS_DATA,
|
DEFAULT_STANDARDS_DATA,
|
||||||
Standards,
|
Standards,
|
||||||
type StandardsConfig,
|
type StandardsParameters,
|
||||||
} from "@blade-and-brawn/calculator";
|
} from "@blade-and-brawn/calculator";
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
age: null as number | null,
|
age: null as number | null,
|
||||||
weight: null as number | null, // lb
|
weight: null as number | null, // lb
|
||||||
},
|
},
|
||||||
cfg: structuredClone(defaultStandards.cfg) as StandardsConfig,
|
params: structuredClone(defaultStandards.params) as StandardsParameters,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selected = $derived({
|
const selected = $derived({
|
||||||
@@ -34,11 +34,11 @@
|
|||||||
age: input.metrics.age ?? 0,
|
age: input.metrics.age ?? 0,
|
||||||
weight: lbToKg(input.metrics.weight ?? 0),
|
weight: lbToKg(input.metrics.weight ?? 0),
|
||||||
} as Metrics,
|
} as Metrics,
|
||||||
cfg: input.cfg,
|
params: input.params,
|
||||||
});
|
});
|
||||||
|
|
||||||
const allStandards = $derived(
|
const allStandards = $derived(
|
||||||
new Standards(DEFAULT_STANDARDS_DATA, selected.cfg),
|
new Standards(DEFAULT_STANDARDS_DATA, selected.params),
|
||||||
);
|
);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
min="1"
|
min="1"
|
||||||
max="100"
|
max="100"
|
||||||
placeholder="5"
|
placeholder="5"
|
||||||
bind:value={input.cfg.global.maxLevel}
|
bind:value={input.params.global.maxLevel}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={
|
bind:checked={
|
||||||
input.cfg.activity[selected.activity]
|
input.params.activity[selected.activity]
|
||||||
.enableGeneration
|
.enableGeneration
|
||||||
}
|
}
|
||||||
class="toggle toggle-lg m-auto"
|
class="toggle toggle-lg m-auto"
|
||||||
@@ -118,10 +118,10 @@
|
|||||||
step=".05"
|
step=".05"
|
||||||
placeholder=".1"
|
placeholder=".1"
|
||||||
bind:value={
|
bind:value={
|
||||||
input.cfg.activity[selected.activity]
|
input.params.activity[selected.activity]
|
||||||
.weightModifier
|
.weightModifier
|
||||||
}
|
}
|
||||||
disabled={!selected.cfg.activity[selected.activity]
|
disabled={!selected.params.activity[selected.activity]
|
||||||
.enableGeneration}
|
.enableGeneration}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
<!-- inputSelected.cfg.activity[selected.activity] -->
|
<!-- inputSelected.cfg.activity[selected.activity] -->
|
||||||
<!-- .weightSkew -->
|
<!-- .weightSkew -->
|
||||||
<!-- } -->
|
<!-- } -->
|
||||||
<!-- disabled={!selected.cfg.activity[selected.activity] -->
|
<!-- disabled={!selected.params.activity[selected.activity] -->
|
||||||
<!-- .enableGeneration} -->
|
<!-- .enableGeneration} -->
|
||||||
<!-- /> -->
|
<!-- /> -->
|
||||||
<!-- </label> -->
|
<!-- </label> -->
|
||||||
@@ -150,10 +150,10 @@
|
|||||||
max="1"
|
max="1"
|
||||||
step=".05"
|
step=".05"
|
||||||
bind:value={
|
bind:value={
|
||||||
input.cfg.activity[selected.activity]
|
input.params.activity[selected.activity]
|
||||||
.difficultyModifier
|
.difficultyModifier
|
||||||
}
|
}
|
||||||
disabled={!selected.cfg.activity[selected.activity]
|
disabled={!selected.params.activity[selected.activity]
|
||||||
.enableGeneration}
|
.enableGeneration}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -166,10 +166,10 @@
|
|||||||
max="1"
|
max="1"
|
||||||
step=".05"
|
step=".05"
|
||||||
bind:value={
|
bind:value={
|
||||||
input.cfg.activity[selected.activity]
|
input.params.activity[selected.activity]
|
||||||
.ageModifier
|
.ageModifier
|
||||||
}
|
}
|
||||||
disabled={!selected.cfg.activity[selected.activity]
|
disabled={!selected.params.activity[selected.activity]
|
||||||
.enableGeneration}
|
.enableGeneration}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -182,9 +182,9 @@
|
|||||||
max="100"
|
max="100"
|
||||||
step="1"
|
step="1"
|
||||||
bind:value={
|
bind:value={
|
||||||
input.cfg.activity[selected.activity].peakAge
|
input.params.activity[selected.activity].peakAge
|
||||||
}
|
}
|
||||||
disabled={!selected.cfg.activity[selected.activity]
|
disabled={!selected.params.activity[selected.activity]
|
||||||
.enableGeneration}
|
.enableGeneration}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -202,10 +202,10 @@
|
|||||||
max="10"
|
max="10"
|
||||||
step="1"
|
step="1"
|
||||||
bind:value={
|
bind:value={
|
||||||
input.cfg.activity[selected.activity]
|
input.params.activity[selected.activity]
|
||||||
.stretch.lower
|
.stretch.lower
|
||||||
}
|
}
|
||||||
disabled={!selected.cfg.activity[
|
disabled={!selected.params.activity[
|
||||||
selected.activity
|
selected.activity
|
||||||
].enableGeneration}
|
].enableGeneration}
|
||||||
/>
|
/>
|
||||||
@@ -219,10 +219,10 @@
|
|||||||
max="10"
|
max="10"
|
||||||
step="1"
|
step="1"
|
||||||
bind:value={
|
bind:value={
|
||||||
input.cfg.activity[selected.activity]
|
input.params.activity[selected.activity]
|
||||||
.stretch.upper
|
.stretch.upper
|
||||||
}
|
}
|
||||||
disabled={!selected.cfg.activity[
|
disabled={!selected.params.activity[
|
||||||
selected.activity
|
selected.activity
|
||||||
].enableGeneration}
|
].enableGeneration}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Gender, Metrics, StandardUnit } from "@blade-and-brawn/domain";
|
import type { Gender, Metrics, StandardUnit } from "@blade-and-brawn/domain";
|
||||||
import avgWeightDataJson from "./data/avg-weights.json" assert { type: "json" };
|
import avgWeightDataJson from "./data/avg-weights.json" with { type: "json" };
|
||||||
|
|
||||||
export const getAvgWeight = (gender: Gender, age: number) => {
|
export const getAvgWeight = (gender: Gender, age: number) => {
|
||||||
type AvgWeightData = {
|
type AvgWeightData = {
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export class LevelCalculator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const StandardsActivityConfigSchema = Type.Object({
|
const StandardsActivityParametersSchema = Type.Object({
|
||||||
weightModifier: Type.Number(),
|
weightModifier: Type.Number(),
|
||||||
weightSkew: Type.Number(),
|
weightSkew: Type.Number(),
|
||||||
ageModifier: Type.Number(),
|
ageModifier: Type.Number(),
|
||||||
@@ -226,22 +226,22 @@ const StandardsActivityConfigSchema = Type.Object({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const StandardsConfigSchema = Type.Object({
|
export const StandardsParametersSchema = Type.Object({
|
||||||
global: Type.Object({
|
global: Type.Object({
|
||||||
maxLevel: Type.Number(),
|
maxLevel: Type.Number(),
|
||||||
}),
|
}),
|
||||||
activity: Type.Record(Type.Enum(Activity), StandardsActivityConfigSchema),
|
activity: Type.Record(Type.Enum(Activity), StandardsActivityParametersSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type StandardsConfig = Static<typeof StandardsConfigSchema>;
|
export type StandardsParameters = Static<typeof StandardsParametersSchema>;
|
||||||
|
|
||||||
export class Standards {
|
export class Standards {
|
||||||
readonly cfg: StandardsConfig;
|
readonly params: StandardsParameters;
|
||||||
private standardsData: StandardsData;
|
private standardsData: StandardsData;
|
||||||
|
|
||||||
constructor(standardsData: StandardsData, cfg?: Partial<StandardsConfig>) {
|
constructor(standardsData: StandardsData, params?: Partial<StandardsParameters>) {
|
||||||
Value.Assert(StandardsConfigSchema, defaultConfig);
|
Value.Assert(StandardsParametersSchema, defaultConfig);
|
||||||
this.cfg = Object.assign({}, defaultConfig, cfg);
|
this.params = Object.assign({}, defaultConfig, params);
|
||||||
|
|
||||||
// prepare data
|
// prepare data
|
||||||
this.standardsData = structuredClone(standardsData);
|
this.standardsData = structuredClone(standardsData);
|
||||||
@@ -254,7 +254,7 @@ export class Standards {
|
|||||||
return (i: number) => A * Math.exp(-B * i) + C;
|
return (i: number) => A * Math.exp(-B * i) + C;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.cfg.activity[activity].enableGeneration) {
|
if (!this.params.activity[activity].enableGeneration) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,13 +314,13 @@ export class Standards {
|
|||||||
const newLevels: Levels = {};
|
const newLevels: Levels = {};
|
||||||
const newLowerLevelCount = Math.max(
|
const newLowerLevelCount = Math.max(
|
||||||
Math.round(
|
Math.round(
|
||||||
this.cfg.activity[activity].stretch.lower,
|
this.params.activity[activity].stretch.lower,
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
const newUpperLevelCount = Math.max(
|
const newUpperLevelCount = Math.max(
|
||||||
Math.round(
|
Math.round(
|
||||||
this.cfg.activity[activity].stretch.upper,
|
this.params.activity[activity].stretch.upper,
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
@@ -362,7 +362,7 @@ export class Standards {
|
|||||||
let i = 1;
|
let i = 1;
|
||||||
while (
|
while (
|
||||||
Object.keys(standard.levels).length <
|
Object.keys(standard.levels).length <
|
||||||
this.cfg.global.maxLevel
|
this.params.global.maxLevel
|
||||||
) {
|
) {
|
||||||
standard.levels = this.expandLevels(standard.levels, i++);
|
standard.levels = this.expandLevels(standard.levels, i++);
|
||||||
}
|
}
|
||||||
@@ -370,11 +370,11 @@ export class Standards {
|
|||||||
// compress
|
// compress
|
||||||
if (
|
if (
|
||||||
Object.keys(standard.levels).length >
|
Object.keys(standard.levels).length >
|
||||||
this.cfg.global.maxLevel
|
this.params.global.maxLevel
|
||||||
) {
|
) {
|
||||||
standard.levels = this.compressLevels(
|
standard.levels = this.compressLevels(
|
||||||
standard.levels,
|
standard.levels,
|
||||||
this.cfg.global.maxLevel,
|
this.params.global.maxLevel,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,12 +382,12 @@ export class Standards {
|
|||||||
for (const level in standard.levels) {
|
for (const level in standard.levels) {
|
||||||
standard.levels[level] =
|
standard.levels[level] =
|
||||||
standard.levels[level] *
|
standard.levels[level] *
|
||||||
(1 + this.cfg.activity[activity].difficultyModifier);
|
(1 + this.params.activity[activity].difficultyModifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate data
|
// generate data
|
||||||
const allGenerators = this.cfg.activity[activity].enableGeneration
|
const allGenerators = this.params.activity[activity].enableGeneration
|
||||||
? this.standardsData[activity].metadata.generators.sort(
|
? this.standardsData[activity].metadata.generators.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
metricPriority(a.metric) - metricPriority(b.metric),
|
metricPriority(a.metric) - metricPriority(b.metric),
|
||||||
@@ -397,7 +397,7 @@ export class Standards {
|
|||||||
for (const generator of allGenerators) {
|
for (const generator of allGenerators) {
|
||||||
if (generator.metric === "age") {
|
if (generator.metric === "age") {
|
||||||
for (const gender of Object.values(Gender)) {
|
for (const gender of Object.values(Gender)) {
|
||||||
const peakAge = this.cfg.activity[activity].peakAge;
|
const peakAge = this.params.activity[activity].peakAge;
|
||||||
const ageStep = 10;
|
const ageStep = 10;
|
||||||
|
|
||||||
const minAge = 0;
|
const minAge = 0;
|
||||||
@@ -427,7 +427,7 @@ export class Standards {
|
|||||||
if (base == null) continue;
|
if (base == null) continue;
|
||||||
|
|
||||||
const ageModifier =
|
const ageModifier =
|
||||||
this.cfg.activity[activity].ageModifier;
|
this.params.activity[activity].ageModifier;
|
||||||
|
|
||||||
const leftSpan = peakAge - minAge;
|
const leftSpan = peakAge - minAge;
|
||||||
const rightSpan = maxAge - peakAge;
|
const rightSpan = maxAge - peakAge;
|
||||||
@@ -515,7 +515,7 @@ export class Standards {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const scalingExponent =
|
const scalingExponent =
|
||||||
this.cfg.activity[activity]
|
this.params.activity[activity]
|
||||||
.weightModifier;
|
.weightModifier;
|
||||||
const coefficient =
|
const coefficient =
|
||||||
currWeight / referenceWeight;
|
currWeight / referenceWeight;
|
||||||
|
|||||||
Reference in New Issue
Block a user