Skip to content

Instantly share code, notes, and snippets.

@doeixd
Created March 14, 2024 13:11
Show Gist options
  • Save doeixd/ab3d8179e838d0c144892e2cd777990b to your computer and use it in GitHub Desktop.
Save doeixd/ab3d8179e838d0c144892e2cd777990b to your computer and use it in GitHub Desktop.
DCI TS
import { A } from "solid-start";
import Counter from "~/components/Counter";
import capitalize from "lodash.capitalize"
import { match, P } from "ts-pattern";
import { Err, Ok, Result, Option, Some, None } from "ts-results"
import type { Appearances, SubcaptionName, CaptionOverallScore, SubcaptionInitials, CaptionName, DciSeason, Competition } from "~/utils/api_types";
import { Convert } from "~/utils/api_types";
// import type Result from "ts-results"
export default function Home() {
return (
<main class="text-center mx-auto text-gray-700 p-4">
<h1 class="max-6-xs text-6xl text-sky-700 font-thin uppercase my-16">
Hello world!
</h1>
</main>
);
}
type year = `20`
type tens_year_digits = `0` | `1` | `2`
type digits = `0` | `1` | `2` | `3` | `4` | `5` | `6` | `7` | `8` | `9`
type season_year = `${year}${tens_year_digits}${digits}`
type final_season_year = Exclude<season_year, '2000' | '2001' | '2002' | '2003' | '2004' | '2024' | '2025' | '2026' | '2027' | '2028' | '2029'>
type classes = `World` | `Open`
type seasons = classes | `Both`
class DCI_API {
base_url: "https://api.dci.org/v1";
cache: Map<string, string>
constructor() {
this.base_url = 'https://api.dci.org/v1'
this.cache = new Map();
this.get_season();
// @ts-expect-error
if (!window?.dci_api) window.dci_api = this
}
async get<T = unknown>(path: string, searchParams = {}, {fetchOptions, parser}: {fetchOptions?: RequestInit | undefined, parser?: Fn<T>}): Promise<Result<T, Error>> {
let ret_val;
let key = path + JSON.stringify(searchParams)
const url = `${this.base_url}${path}`
const params = (Object.entries(searchParams).reduce((acc, cur) => {
let [param_name, param_value] = cur
param_name = param_name.replace('className', 'class')
param_value =
/world|open/i.test(param_value as string)
? capitalize((param_value as string).replace(/world|open/i, '$&+Class'))
: param_value
acc.append(param_name, `${param_value}`)
return acc
}, new URLSearchParams())).toString()
if (this.cache.has(key)) {
let val = this.cache.get(key) as T
return Ok(val)
} else {
return await _fetch(`${url}${params && `?${params}`}`, fetchOptions, parser)
}
}
get_season(className: seasons = 'Both', year: final_season_year = new Date().getFullYear().toString() as final_season_year) {
const season_url = `${this.base_url}/performances/corps-results`
const inner_get_season =
async (season: seasons = className, season_year: final_season_year = year) => {
return await this.get<DciSeason>(season_url, { className: season, season: season_year }, { parser: Convert.toDciSeason })
}
let exp: AsyncFn<DciSeason> = async function() {
return match(className)
.with('Both', async () => {
let response = await Promise.all([inner_get_season('World'), inner_get_season('Open')])
let transformed_response = response.map((o) => {
let val = o.expect('Unable to fetch season')
let ret = Object.entries(val)
return ret
})
let season = Object.fromEntries(transformed_response) as DciSeason
return Promise.resolve(season)
})
.otherwise(async () => {
let result = (await inner_get_season()).expect('Unable to fetch season')
return Promise.resolve(result)
})
}
return aiife<DciSeason>(exp)
}
}
class Season {
className: seasons;
year: final_season_year;
data: any;
cache: Map<string, any>;
constructor(className: seasons = 'Both', year: final_season_year = new Date().getFullYear().toString() as final_season_year, data: any) {
this.className = className;
this.year = year;
this.data = data;
this.cache = new Map<string, any>();
}
get_latest_scores(useCache = true) {
let result = [];
if (this.cache.has('latest_scores') && useCache) { return this.cache.get('latest_scores') }
for (let corps of Object.keys(this.data)) {
for (let comp of this.data[corps]) {
}
}
return this.cache.get('latest_scores')
}
get_rankings() {
}
}
interface Judge {
first_name: string;
last_name: string;
}
interface AllData {
data: Season,
}
interface Caption {
name: string;
initials: string;
}
interface SubcaptionScore {
}
interface JudgeScoreEntry {
judge: Judge,
number: number;
score: CaptionScore;
subcaption_score: CaptionScore;
}
interface ScoreRecord {
active: boolean;
isOtherType: boolean;
competitionGuid: string;
subtotalRank: number;
subtotalScore: number;
rank: number;
totalScore: number;
orgGroupIdentifier: string;
round: string
divisionName: string;
categories: [];
}
interface CaptionScore {
caption: string | Caption;
score: string;
rank: number;
}
interface Corps {
name: string;
id: string;
}
class URLBuilder {
base_url: string;
paths: object;
searchParams: object;
constructor(base_url: string, paths: object, searchParams: object) {
this.base_url = base_url;
this.paths = paths;
this.searchParams = searchParams;
return new Proxy(this, {
})
}
// URLBuilder('dci.api').seasons.addParams({})
addParam() { }
toString() { }
}
/// season = api.get_season('world', '2023')
/// season.get
// api.corps('Phantom Regiment').get_season();
// api.corps('Phantom Regiment').get_competitions();
// api.corps('Phantom Regiment').get_season().get_rankings();
// api.get_season().competitions()
// api.season('World').rankings('Visual')
// api.season('World').get_rankings('Visual')
// api.competitions('asdfasdf').ranking()
async function _fetch<ReturnedType = unknown>(path: RequestInfo | URL, options?: RequestInit, parser?: Fn<ReturnedType>) {
return async_try<ReturnedType>(async () => await fetch(path, options)
.then(i => {
if (parser) return parser(i.text()) as ReturnedType
return i.json() as ReturnedType
})
)
}
async function _make_fetcher<ReturnedType = unknown>(path: RequestInfo | URL, options?: RequestInit) {
type ReturnedParamObj = {
searchParams?: string | URLSearchParams
body?: unknown
}
return async (args: ReturnedParamObj) => {
let searchParams = args?.searchParams?.toString?.() || ''
let body = args?.body && JSON.stringify(args?.body)
let new_path = path + (searchParams && (`?` + searchParams))
let new_options = Object.assign({}, options, body && { body })
return await _fetch(new_path, new_options)
}
}
type CallbackParams<T> = T extends (...args: infer P) => any ? P : never;
type CallbackReturnType<T> = T extends (...args: any) => infer R ? R : never;
type AsyncCallbackReturnType<T> = Awaited<T extends (...args: any) => infer R ? R : never>;
type AsyncFn<Returns = Promise<unknown>> =
(...args: any[]) => Promise<Returns>;
type Fn<Returns = unknown> =
(...args: any[]) => Returns;
async function async_try<ReturnsOk, ReturnsError = Error>(fn: AsyncFn<ReturnsOk>): Promise<Result<AsyncCallbackReturnType<typeof fn>, ReturnsError>> {
try {
return Ok(await fn())
} catch (err) {
return Err(err as ReturnsError);
}
}
function _try<ReturnsOk, ReturnsError = Error>(fn: Fn<ReturnsOk>): Result<CallbackReturnType<typeof fn>, ReturnsError> {
try {
return Ok(fn())
} catch (err) {
return Err(err as ReturnsError);
}
}
const astr = async (): Promise<'hello'> => 'hello'
const str = async (): Promise<'hello'> => 'hello'
const test = await async_try(astr)
const test2 = await async_try(str)
function o<T>(value: T): Option<T> {
if (value) return Some(value)
return None
}
let test0 = o('happy')
const api = new DCI_API();
let res = api.get_season()
let env: any;
const get_cache = async <Return, KeyType = string>(key: KeyType) => {
const data = await env.dci.get(key, { type: "json" })
return data as Return
}
const set_cache = async (key: string, data: string | object) => {
if (typeof data === 'object' && data !== null) data = JSON.stringify(data)
if (typeof data === 'string' || (data as any) instanceof String) await env.dci.put(key, data)
return await get_cache(key)
}
async function save ({type, key, value}: {type: string, key: string, value: unknown}, metadata?: unknown) {
await env.dci.put(key, value, {
metadata: { type, metadata }
})
}
async function list (prefix:string, limit?: number, cursor?: any) {
return await env.dci.list({prefix, limit, cursor})
}
const Relation = {
create: async function (
{from_key, to_key, from_name, to_name}: {from_key:string, to_key:string, from_name:string, to_name:string, from_type?: string, to_type?: string},
data = {from: from_key, to: to_key, from_name, to_name, from_type: from_key.split('::')[0], to_type: to_key.split('::')[0]},
metadata = {from: from_key, to: to_key, from_name, to_name}
) {
const from = `relation::from:${from_key}->to:${to_key}|using:${from_name}`
const to = `relation::to:${from_key}->from:${to_key}|using:${to_name}`
await save({type: "relation", key: from, value: data}, metadata)
await save({type: "relation", key: to, value: data}, metadata)
const from_type = `relationType::from:${data.from_type}->to:${data.to_type}|using:${from_name}|from_key:${from_key}->to_key:${to_key}`
const to_type = `relationType::to:${data.to_type}<-from:${data.from_type}|using:${to_name}|to_key:${to_key}<-from_key:${from_key}`
await save({type: "relationType", key: from_type, value: data}, data)
await save({type: "relationType", key: to_type, value: data}, data)
const arrow_from_name = `relationArrow::${from_name}:from_type:${data.from_type}->to_type:${data.to_type}|from_key:${from_key}->to_key:${to_key}`
const arrow_to_name = `relationArrow::${to_name}:to_type:${data.to_type}<-from_type:${data.from_type}|to_key:${to_key}<-from_key:${from_key}`
await save({type: "relationArrow", key: arrow_from_name, value: data}, metadata)
await save({type: "relationArrow", key: arrow_to_name, value: data}, metadata)
},
find: async function (args: { from_type?: string, to_type?: string, from_key?: string, to_key?: string, using?: string, where?: Fn } ) {
const call = (data: any) => {
if (args?.where) {
return args.where(data)
}
return data
}
if (args?.from_key) {
const type_from_key = args.from_key.split('::')[0]
if (args?.to_key) {
if (args?.using) {
return call(await get_cache(`relation::from:${args.from_key}->to:${args.to_key}|using:${args.using}`))
}
return call(await list(`relation::from:${args.from_key}->to:${args.to_key}`))
}
if (args?.to_type) {
if (args?.using) {
return call(await list(`relationArrow::${args.using}:from_type:${type_from_key}->to_type:${args.to_type}|from_key:${args.from_key}`))
}
return call(await list(`relation::from:${args.from_key}->to:${args.to_type}`))
}
}
if(args?.from_type) {
if (args?.to_key) {
const type_from_key = args.to_key.split('::')[0]
if (args?.using) {
return call(await list(`relationType::to:${args.to_type}<-from:${args.from_type}|using:${args.using}|to_key:${type_from_key}`))
}
return call(await list(`relation::to:${args.to_key}->from:${args.from_type}`))
}
if (args?.to_type) {
if (args?.using) {
return call(await list(`relationArrow::${args.using}:from_type:${args.from_type}->to_type:${args.to_type}`))
}
return call(await list(`relationType::from:${args.from_type}->to:${args.to_type}`))
}
}
}
}
function makeType(type: string, fn = (i: unknown) => i) {
let prefix = type
return {
save: async function (key: string | string[], value: object) {
key = [key].flat()
let made_key = [prefix, ...key].join('::')
await save({ type, key: made_key, value })
return [made_key, fn(value)]
},
get: async function (key: string | string[], as = 'json') {
key = [key].flat()
let made_key = [prefix, ...key].join('::')
return fn(await env.dci.get(made_key, { type: as }))
},
list: async function ({ suffix, limit, cursor }: { suffix: string; limit?: number; cursor: any }) {
return await env.dci.list({ prefix: [prefix, suffix].join('::'), limit, cursor })
},
}
}
class KVType<SaveType = object> {
type: string
prefix: string
initial_key: string[] | string | undefined;
key: string | undefined;
constructor(name: string, key?: string | string[]) {
key &&= [key].flat()
key &&= [name + ':', ...key].join(':')
this.prefix = name
this.type = name
this.initial_key = key
this.key = key
}
async save<R = any>(key: string | string[], value: SaveType, fn = (i: unknown) => i): Promise<any> {
let data;
if (!value && this.key) {
data = key
key = this.key
} else {
data = value
}
let made_key = _key(key,this.prefix)
await save({ type: this.type, key: made_key, value: data})
return [made_key, fn(data)]
}
async get(key: string | string[], as = 'json', fn = (i: unknown) => i) {
key = [key].flat()
let made_key = [this.prefix, ...key].join('::')
return fn(await env.dci.get(made_key, { type: as }))
}
async list ({ suffix, limit, cursor }: { suffix: string; limit?: number; cursor: any }) {
return await env.dci.list({ prefix: [this.prefix, suffix].join('::'), limit, cursor })
}
async relate (args: {using: string; to_type?: string; to_key?: string, to_name?: string, from_key?: string}) {
if (this?.key) {
if (args?.to_key) {
await Relation.create({
from_key: this.key,
to_key: args.to_key,
from_name: args.using,
to_name: args?.to_name || args.using
})
}
}
if(args?.to_key && args?.from_key && this?.prefix) {
await Relation.create({
from_key: _key(args.from_key, this?.prefix),
to_key: args?.to_key,
from_name: args.using,
to_name: args?.to_name || args.using
})
}
}
}
Relation.find({
from_key: "Corps:SCV",
to_type: "Competition",
using: "Performed-in",
where: (key) => key == "SCV"
})
function _key(key: string | string[], prefix?: string): string {
key = [key].flat();
key.join(':')
prefix = prefix ? prefix + '::' : ''
return prefix + key
}
// class Corps {
// name: string;
// id: string;
// constructor(corps_obj) {
// this.name = corps_obj.groupName
// this.id = corps_obj.orgGroupIdentifier
// save({ type: 'Corps', name, id })
// }
// }
interface Show extends Competition {
id: string,
year: string,
}
let SeasonKV = new KVType<{id: string}>('Season')
let CorpsKV = new KVType<Corps>('Corps')
let AppearanceKV = new KVType<Appearances>('Appearance')
let ShowKV = new KVType<Show>('Show')
let CaptionKV = new KVType<{name: CaptionName}>('Caption')
let SubcaptionKV = new KVType<{name: SubcaptionName, initials: Lowercase<SubcaptionInitials> }>('Caption')
interface CorpsOverallScore {
total: number;
subtotal: number;
rank: number;
subtotal_rank: number;
corps: string;
corpsID: string;
show: string;
}
let CorpsOverallScoreKV = new KVType<CorpsOverallScore>('ShowOverallScore')
interface CaptionScoreKV extends CaptionScore {
corps: string;
show: string;
season: string;
}
let CaptionScoreKV = new KVType<CaptionScoreKV>('CaptionScore')
interface JudgesScoreKV extends CaptionScore {
corps: string
show: string;
season: string;
judge: string;
judge_number: number;
breakdown: SubcaptionOverallScoreLowercase[];
caption: CaptionName;
subcaption: SubcaptionName;
}
let JudgesScoreKV = new KVType<JudgesScoreKV>('JudgesScore')
let JudgeKV = new KVType<{
first_name: string,
last_name: string,
full_name: string,
}>('Judge')
interface SubcaptionOverallScoreLowercase extends Omit<CaptionOverallScore, 'initials'> {
initials: Lowercase<SubcaptionInitials>;
}
interface SubcaptionScoreBreakdown extends Omit<Caption, 'initials'> {
initials: Lowercase<SubcaptionInitials> | undefined;
corps: string;
judge_name?: string;
judge_number: number;
show: string;
subcaption: SubcaptionName;
season: string;
score: string;
rank: number;
}
let SubcaptionScoreBreakdownKV = new KVType<SubcaptionScoreBreakdown>('SubcaptionScoreBreakdown')
let Show = makeType('Show')
async function ingest() {
const season = api.get_season()
const recaps = {}
for (let corps_name of Object.keys(season)) {
const corps_season = season[corps_name]
const [corps_key] = await CorpsKV.save(corps_name, {name: corps_name, id: corps_season[0].orgGroupIdentifier})
for (let appearance of corps_season) {
const season_year = appearance.competition.seasonName
const [season_key] = await SeasonKV.save(season_year, {id: season_year})
const [appearance_key] = await AppearanceKV.save([season_year, appearance.competitionGUID, appearance.orgGroupIdentifier], appearance)
await Relation.create({
from_key: season_key,
to_key: appearance_key,
from_name: 'had_appearance',
to_name: 'apart_of_season'
})
const [show_key] = await ShowKV.save([appearance.competitionGUID, season_year], { id: appearance.competitionGUID, year: season_year, ...appearance.competition })
await Relation.create({from_key: show_key, to_key: appearance_key, from_name: 'apart_of_appearance', to_name: 'apart_of_show'})
await Relation.create({from_key: show_key, to_key: season_key, from_name: 'apart_of_season', to_name: 'apart_of_season'})
await Relation.create({
from_key: corps_key,
to_key: show_key,
from_name: 'appeared_at',
to_name: 'appeared_at'
})
const [corps_overall_score_key] = await CorpsOverallScoreKV.save([appearance.orgGroupIdentifier, appearance.competitionGUID], {
total: appearance.totalScore,
subtotal: appearance.subtotalScore,
rank: appearance.rank,
subtotal_rank: appearance.subtotalRank,
corps: corps_name,
corpsID: appearance.orgGroupIdentifier,
show: appearance.competitionGUID
})
await Relation.create({
from_key: corps_key,
to_key: corps_overall_score_key,
from_name: 'received_score',
to_name: 'received_score'
})
for (let caption_score of appearance.categories) {
const [caption_key] = await CaptionKV.save(caption_score.name, {name: caption_score.name})
const [caption_score_key] = await CaptionScoreKV.save([corps_name, appearance.competitionGUID, caption_score.name, season_year], {
caption: caption_score.name,
score: caption_score.score,
rank: caption_score.rank,
corps: corps_name,
show: appearance.competitionGUID,
season: season_year,
})
await Relation.create({from_key: caption_key, to_key: caption_score_key, from_name: 'score', to_name: 'belongs_to_caption'})
await Relation.create({from_key: caption_score_key, to_key: corps_key, from_name: 'belongs_to', to_name: 'received_score'})
if (caption_score.captions) {
for (let judges_score of caption_score.captions) {
let judge = (judges_score.judgeFirstName || 'unknown') +' ' + (judges_score.judgeLastName || 'unknown')
const [judge_key] = await JudgeKV.save(judge, {
full_name: judge,
first_name: judges_score.judgeFirstName || 'unknown',
last_name: judges_score.judgeLastName || 'unknown',
})
const [subcaption_key] = await SubcaptionKV.save(judges_score.name, {name: judges_score.name, initials: judges_score.initials.toLowerCase() as Lowercase<SubcaptionInitials>})
await Relation.create({
from_key: show_key,
to_key: judge_key,
from_name: 'judged_at',
to_name: 'judged_at'
})
await Relation.create({
from_key: judge_key,
to_key: corps_key,
from_name: 'judged',
to_name: 'was_judged_by',
})
await Relation.create({
from_key: judge_key,
to_key: corps_key,
from_name: 'judged',
to_name: 'was_judged_by',
})
await Relation.create({
from_key: judge_key,
to_key: subcaption_key,
from_name: 'judged_subcaption',
to_name: 'judged_subcaption',
})
const breakdown_keys: string[] = []
const breakdown: SubcaptionOverallScoreLowercase[] = [];
for (let subcaption_score_breakdown of judges_score.subcaptions) {
let build: unknown = {...subcaption_score_breakdown}
Object.defineProperty(build, 'initials', subcaption_score_breakdown?.initials?.toLowerCase?.() as Lowercase<SubcaptionInitials> ?? '')
build = build as SubcaptionOverallScoreLowercase
breakdown.push(build as SubcaptionOverallScoreLowercase)
const [subcaption_score_breakdown_key] = await SubcaptionScoreBreakdownKV.save(
[corps_name, appearance.competitionGUID, caption_score.name, judges_score.initials, subcaption_score_breakdown.initials ?? 'unknown' , season_year ],
{
initials: subcaption_score_breakdown?.initials ? (subcaption_score_breakdown.initials?.toLowerCase() as Lowercase<SubcaptionInitials>) : undefined,
corps: corps_name,
judge_name: judge,
judge_number: judges_score.judge,
show: appearance.competitionGUID,
subcaption: judges_score.name,
season: season_year,
score: subcaption_score_breakdown.score,
rank: subcaption_score_breakdown.rank,
name: subcaption_score_breakdown.name
}
)
breakdown_keys.push(subcaption_score_breakdown_key)
}
const [judges_score_key] = await JudgesScoreKV.save([corps_name, appearance.competitionGUID, caption_score.name, judges_score.initials, judge, season_year], {
judge,
judge_number: judges_score.judge,
score: judges_score.score,
caption: caption_score.name,
subcaption: judges_score.name,
corps: corps_name,
season: season_year,
breakdown,
rank: judges_score.rank,
show: appearance.competitionGUID,
})
await Relation.create({
from_key: judges_score_key,
to_key: corps_key,
from_name: 'for_corps',
to_name: 'received_score',
})
await Relation.create({
from_key: judges_score_key,
to_key: caption_score_key,
from_name: 'contributed_to',
to_name: 'contributed_to',
})
await Relation.create({
from_key: caption_score_key,
to_key: corps_overall_score_key,
from_name: 'contributed_to',
to_name: 'contributed_to',
})
for (let breakdown_key of breakdown_keys) {
await Relation.create({
from_key: breakdown_key,
to_key: judges_score_key,
from_name: 'apart_of_judges_breakdown',
to_name: 'apart_of_breakdown',
})
await Relation.create({
from_key: breakdown_key,
to_key: subcaption_key,
from_name: 'breakdown_for',
to_name: 'breakdown_for',
})
}
}
}
}
// let corps_score = {
// show: 'adfasfd'
// corps: 'SCV',
// date: ''
// total: 100,
// subtotal: 100,
// captions: {
// visual: {
// total: 55,
// subcaptions: {
// },
// }
// }
// judges: {
// },
// }
}
}
}
function aiife<ReturnedType>(fn: AsyncFn<ReturnedType>) {
let result;
((async function (){
result = await fn()
})())
return result as ReturnedType
}
function destr<OutputType>(obj: object, ...keys:any[]) {
return Object.entries(obj).reduce((acc, [key, value]) => {
// @ts-expect-error
if (keys.includes(key)) acc[key] = value
return acc
}, {}) as OutputType
}
async function wait(ms, fn) {
return new Promise((resolve)=>{
setTimeout(() => resolve(fn().then(l => l)), ms)
})
}
async function retry (fn, {sleep = 150, totalTimes = 20, times = totalTimes}) {
console.log({times})
if (times > 0) {
try {
result = await fn()
} catch (e) {
return await wait(sleep || 150, async () => await retry(fn, {times: times - 1, sleep}))
}
}
}
async function toError() {
throw 'hello'
}
async function main() {
await retry(toError)
}
main().then(l => l)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment