Skip to content

Commit

Permalink
refactor: infer GradeScale type
Browse files Browse the repository at this point in the history
  • Loading branch information
Ugzuzg committed Sep 9, 2023
1 parent d5c4170 commit d2002c4
Show file tree
Hide file tree
Showing 16 changed files with 74 additions and 48 deletions.
16 changes: 12 additions & 4 deletions src/GradeParser.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import GradeScale, { GradeScales, GradeScalesTypes, Tuple } from './GradeScale'
import { GradeScales, GradeScalesTypes, Tuple } from './GradeScale'
import { scales } from './scales'

/**
*
* @param gradeScaleType grade scale type
* @returns grade scale of provided grade scale name
*/
export const getScale = (gradeScaleType: GradeScalesTypes): GradeScale | null => {
export const getScale = <T extends GradeScalesTypes>(gradeScaleType: T): (typeof scales)[T] => {
const scale = scales[gradeScaleType]
if (scale === null) {
if (scale == null) {
console.warn(`Scale: ${gradeScaleType} isn't currently supported`)
}

Check warning on line 13 in src/GradeParser.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
return scale
Expand Down Expand Up @@ -53,7 +53,7 @@ export const convertGrade = (
): string => {
const fromScale = getScale(fromGradeScaleType)
const toScale = getScale(toGradeScaleType)
if (fromScale === null || toScale === null) {
if (fromScale == null || toScale == null) {
return ''
}

Check warning on line 58 in src/GradeParser.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
const checkScaleToConvert: boolean = fromScale.allowableConversionType.includes(toGradeScaleType)
Expand All @@ -67,6 +67,14 @@ export const convertGrade = (
return toScale.getGrade(toScore)
}

export const inferScale = (grade: string): GradeScalesTypes | null => {
const matchedScales = Object.values(scales)
.map(scale => [scale.name, scale.isType(grade)] as const)
.filter(v => v[1])
if (matchedScales.length === 1) return matchedScales[0][0]
return null
}

export const isVScale = (grade: string): boolean => {
const scale = scales[GradeScales.VSCALE]
if (scale == null) {
Expand Down
13 changes: 10 additions & 3 deletions src/GradeScale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@ import { GradeBandTypes } from './GradeBands'

export type Tuple = [number, number]

export default interface GradeScale {
export default interface GradeScale<
Name extends GradeScalesTypes = GradeScalesTypes,
Offset extends number = number,
> {
isType: (grade: string) => boolean
getScore: (grade: string) => number | Tuple
getGrade: (score: number | Tuple) => string
getGradeBand: (grade: string) => GradeBandTypes
displayName: string
name: GradeScalesTypes
offset: number
name: Name
offset: Offset
allowableConversionType: GradeScalesTypes[]
}

export const infer = <Name extends GradeScalesTypes, Offset extends number>(
gradeScale: GradeScale<Name, Offset>
): GradeScale<Name, Offset> => gradeScale

export const GradeScales = {
AI: 'ai',
AID: 'aid',
Expand Down
14 changes: 13 additions & 1 deletion src/__tests__/GradeParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getScoreForSort, convertGrade, getScale } from '../GradeParser'
import { getScoreForSort, convertGrade, getScale, inferScale } from '../GradeParser'
import { GradeScales } from '../GradeScale'
import { VScale, Font, YosemiteDecimal, French, Saxon, AI, WI } from '../scales'

Expand Down Expand Up @@ -322,4 +322,16 @@ describe('Grade Scales', () => {
)
})
})

describe('inferScale', () => {
test.each([
[GradeScales.YDS, '5.10'],
[null, '6a'],
[null, 'abcdef'],
[null, 'abcdef'],
[GradeScales.VSCALE, 'V7']
])('detects %s scale for %s', (scale, grade) => {
expect(inferScale(grade)).toEqual(scale)
})
})
})
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getScale,
getScore,
getScoreForSort,
inferScale,
isVScale,
convertGrade
} from './GradeParser'
Expand Down Expand Up @@ -259,7 +260,7 @@ const NORWAY_ARRAY = [
'12-',
'12',
'12+'
]
] as const

const CLASS_ARRAY = ['Class 1', 'Class 2', 'Class 3', 'Class 4', 'Class 5']

Expand Down Expand Up @@ -294,6 +295,7 @@ export {
getScoreForSort,
isVScale,
getScale,
inferScale,

Check warning on line 298 in src/index.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
GradeScales,
GradeScalesTypes,
GradeBands,
Expand Down
6 changes: 3 additions & 3 deletions src/scales/ai.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, infer, Tuple } from '../GradeScale'
import ice_table from '../data/ice.json'
import { IceGrade } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -9,7 +9,7 @@ const aiGradeRegex = /^(AI)([1-2]|[3-9]\+?|1[0-3]\+?)$/

const isAI = (grade: string): RegExpMatchArray | null => grade.match(aiGradeRegex)

const AIScale: GradeScale = {
const AIScale = infer({
displayName: 'AI Grade',
name: GradeScales.AI,
offset: 1000,
Expand Down Expand Up @@ -42,7 +42,7 @@ const AIScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isAI(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/aid.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, infer, Tuple } from '../GradeScale'
import aid_table from '../data/aid.json'
import { AidGrade } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -8,7 +8,7 @@ import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
const aidGradeRegex = /^([AC])([0-5]|[2-4]\+)$/i
const isAid = (grade: string): RegExpMatchArray | null => grade.match(aidGradeRegex)

const AidScale: GradeScale = {
const AidScale = infer({
displayName: 'Aid Grade',
name: GradeScales.AID,
offset: 1000,
Expand Down Expand Up @@ -41,7 +41,7 @@ const AidScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isAid(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/ewbank.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, getRoundedScoreTuple, Tuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, getRoundedScoreTuple, Tuple, infer } from '../GradeScale'
import routes from '../data/routes.json'
import { Route } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -11,7 +11,7 @@ import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
const ewbankGradeRegex = /^(([1-9])|([1-3][0-9])|(40)){1}(?:(\/)(([1-9])|([1-3][0-9])|(40)))?$/i
const isEwbank = (grade: string): RegExpMatchArray | null => grade.match(ewbankGradeRegex)

const EwbankScale: GradeScale = {
const EwbankScale = infer({
displayName: 'Ewbank Grade',
name: GradeScales.EWBANK,
offset: 1000,
Expand Down Expand Up @@ -45,7 +45,7 @@ const EwbankScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isEwbank(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/font.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import boulder from '../data/boulder.json'
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple, infer } from '../GradeScale'

import { Boulder } from '.'
import { boulderScoreToBand, GradeBandTypes } from '../GradeBands'
Expand All @@ -10,7 +10,7 @@ const fontGradeRegex = /^([1-9][a-c][+]?){1}(?:(\/)([1-9][a-c][+]?))?$/i
// i.e. 6b+/5a => 6b+/6c
const isFont = (grade: string): RegExpMatchArray | null => grade.match(fontGradeRegex)

const FontScale: GradeScale = {
const FontScale = infer({
displayName: 'Fontainebleau',
name: GradeScales.FONT,
offset: 1000,
Expand Down Expand Up @@ -43,7 +43,7 @@ const FontScale: GradeScale = {
const score = getScore(grade)
return boulderScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isFont(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/french.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple, infer } from '../GradeScale'
import routes from '../data/routes.json'
import { Route } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -9,7 +9,7 @@ const frenchGradeRegex = /^([1-9][a-c][+]?){1}(?:(\/)([1-9][a-c][+]?))?$/i
// i.e. 6b+/5a => 6b+/6c
const isFrench = (grade: string): RegExpMatchArray | null => grade.match(frenchGradeRegex)

const FrenchScale: GradeScale = {
const FrenchScale = infer({
displayName: 'French Scale',
name: GradeScales.FRENCH,
offset: 1000,
Expand Down Expand Up @@ -42,7 +42,7 @@ const FrenchScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isFrench(grade)
Expand Down
9 changes: 3 additions & 6 deletions src/scales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import AI from './ai'
import Aid from './aid'
import WI from './wi'
import UIAA from './uiaa'
import GradeScale, { GradeScales } from '../GradeScale'
import { GradeScales } from '../GradeScale'
export { Aid, VScale, Font, YosemiteDecimal, French, Saxon, UIAA, Ewbank, AI, WI, Norwegian }

export interface Boulder {
Expand Down Expand Up @@ -39,10 +39,7 @@ export interface AidGrade {
aid: string
}

export const scales: Record<
typeof GradeScales[keyof typeof GradeScales],
GradeScale | null
> = {
export const scales = {
[GradeScales.VSCALE]: VScale,
[GradeScales.YDS]: YosemiteDecimal,
[GradeScales.FONT]: Font,
Expand All @@ -54,4 +51,4 @@ GradeScale | null
[GradeScales.AI]: AI,
[GradeScales.WI]: WI,
[GradeScales.AID]: Aid
}
} as const
6 changes: 3 additions & 3 deletions src/scales/norwegian.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple, infer } from '../GradeScale'
import routes from '../data/routes.json'
import { Route } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -9,7 +9,7 @@ import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
const norwegianGradeRegex = /^((?:[1-9]|1[0-1])[+-]?)((?:\/)(?:[1-9]|1[0-1])[+-]?)?$/i
const isNorwegian = (grade: string): RegExpMatchArray | null => grade.match(norwegianGradeRegex)

const Norwegian: GradeScale = {
const Norwegian = infer({
displayName: 'Norwegian Scale',
name: GradeScales.NORWEGIAN,
offset: 1000,
Expand Down Expand Up @@ -42,7 +42,7 @@ const Norwegian: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isNorwegian(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/saxon.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple, infer } from '../GradeScale'
import routes from '../data/routes.json'
import { Route } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -10,7 +10,7 @@ const saxonGradeRegex = /^((([7-9]|1[0-3])([a-c]))|([1-6]))$/i
// (Roman numerals, e. g. "V", are used - this is not supported)
const isSaxon = (grade: string): RegExpMatchArray | null => grade.match(saxonGradeRegex)

const SaxonScale: GradeScale = {
const SaxonScale = infer({
displayName: 'Saxon Scale',
name: GradeScales.SAXON,
offset: 1000,
Expand Down Expand Up @@ -43,7 +43,7 @@ const SaxonScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isSaxon(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/uiaa.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple, infer } from '../GradeScale'
import routes from '../data/routes.json'
import { Route } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -10,7 +10,7 @@ const isUIAA = (grade: string): RegExpMatchArray | null => grade.match(uiaaGrade
// Uses Arabic numerals with +/- signs, e.g. "7", "7-" (easier), or "7+" (harder)
// (Sometimes roman numerals, e. g. "VII", are used - this is not supported)

const UIAAScale: GradeScale = {
const UIAAScale = infer({
displayName: 'UIAA Scale',
name: GradeScales.UIAA,
offset: 2000,
Expand Down Expand Up @@ -43,7 +43,7 @@ const UIAAScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isUIAA(grade)
Expand Down
6 changes: 3 additions & 3 deletions src/scales/v.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, Tuple, getRoundedScoreTuple, infer } from '../GradeScale'
import boulder from '../data/boulder.json'
import { Boulder } from '.'
import { boulderScoreToBand, GradeBandTypes } from '../GradeBands'

const vGradeRegex = /^(V[0-9]{1,2}|VB(?![0-9]))([/+])?([/-])?([0-9]{1,2})?$/i

const VScale: GradeScale = {
const VScale = infer({
displayName: 'V Scale',
name: GradeScales.VSCALE,
offset: 1000,
Expand Down Expand Up @@ -40,7 +40,7 @@ const VScale: GradeScale = {
const score = getScore(grade)
return boulderScoreToBand(getAvgScore(score))
}
}
})

export default VScale

Expand Down
6 changes: 3 additions & 3 deletions src/scales/wi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import GradeScale, { findScoreRange, getAvgScore, GradeScales, Tuple } from '../GradeScale'
import { findScoreRange, getAvgScore, GradeScales, infer, Tuple } from '../GradeScale'
import ice_table from '../data/ice.json'
import { IceGrade } from '.'
import { GradeBandTypes, routeScoreToBand } from '../GradeBands'
Expand All @@ -9,7 +9,7 @@ const wiGradeRegex = /^(WI)([1-2]|[3-9]\+?|1[0-3]\+?)$/

const isWI = (grade: string): RegExpMatchArray | null => grade.match(wiGradeRegex)

const WIScale: GradeScale = {
const WIScale = infer({
displayName: 'WI Grade',
name: GradeScales.WI,
offset: 1000,
Expand Down Expand Up @@ -42,7 +42,7 @@ const WIScale: GradeScale = {
const score = getScore(grade)
return routeScoreToBand(getAvgScore(score))
}
}
})

const getScore = (grade: string): number | Tuple => {
const parse = isWI(grade)
Expand Down
Loading

0 comments on commit d2002c4

Please sign in to comment.