Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 59 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
[![Develop branch](https://github.com/openbeta/climbing-grades/actions/workflows/nodejs.yml/badge.svg?branch=develop)](https://github.com/OpenBeta/climbing-grades/actions/workflows/nodejs.yml?query=develop) [![License](https://img.shields.io/github/license/openbeta/climbing-grades?style=flat-square)](./LICENSE)
[![Develop branch](https://github.com/openbeta/climbing-grades/actions/workflows/nodejs.yml/badge.svg?branch=develop)](https://github.com/OpenBeta/climbing-grades/actions/workflows/nodejs.yml?query=develop) [![License](https://img.shields.io/github/license/openbeta/climbing-grades?style=flat-square)](./LICENSE)

<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-)

<!-- ALL-CONTRIBUTORS-BADGE:END -->

# What is this?

Javascript utilities for working with rock climbing grades.
Typescript utilities for working with rock climbing grades.

### Supported systems

**Sport & Traditional climbing**

- [x] Yosemite Decimal System
- [x] French
- [x] UIAA
- [x] Ewbanks
- [x] Saxon
- [x] Brazilian
- [x] Norwegian

**Bouldering**

- [x] Vermin (V-scale)
- [x] Fontainebleau

**Aid**

- [x] A# & C#
- [ ] Aid with mandatory free climbing (5.8 A0, etc)
- [ ] Aid with mandatory free climbing (5.8 A0, etc)

**Ice**

- [x] Winter Ice (WI#)
- [x] Alpine Ice (AI#)

Expand All @@ -33,102 +42,102 @@ Javascript utilities for working with rock climbing grades.
Code contributions are most welcome!

### Questions?
Join us on [Discord](https://discord.gg/fY9DbRav8h).

---
Join us on [Discord](https://discord.gg/fY9DbRav8h).

### How to use the library
## How to use the library

#### Install the package
### Install the package

Using NPM

```
npm install @openbeta/sandbag
```

Using Yarn

```
yarn add @openbeta/sandbag
```

#### Sample Usage
### Sample Usage

- Convert Grades to Scores
#### Convert Grades to Scores

```javascript
import { French, YosemiteDecimal } from '@openbeta/sandbag'
```typescript
import { French, YosemiteDecimal } from "@openbeta/sandbag";

const score = French.getScore('8a') // Output [ 84, 85 ]
const score = French.getScore("8a"); // Output [ 84, 85 ]

// Support slash grade
const slashGradeScore=French.getScore('7c+/8a') // Output [ 83, 84 ]
const slashGradeScore = French.getScore("7c+/8a"); // Output [ 83, 84 ]

// Accept +/- modifier
const plusGrade= YosemiteDecimal.getScore('5.12+') // Output [ 79, 80 ]
const plusGrade = YosemiteDecimal.getScore("5.12+"); // Output [ 79, 80 ]
```

- Convert Scores to Grades
#### Convert Scores to Grades

```javascript
import { Font } from '@openbeta/sandbag'
```typescript
import { Font } from "@openbeta/sandbag";

// Single score provided
Font.getGrade(80) // Output '7c'

// Support a range of scores
Font.getGrade([79,81]) // Output'7b+/7c'
Font.getGrade(80); // Output '7c'

// Support a range of scores
Font.getGrade([79, 81]); // Output'7b+/7c'
```

- Validate Grading Scales
``` javascript
import { VScale , Font }from '@openbeta/sandbag'
#### Validate Grading Scales

console.log('Is 6A a V Scale?',VScale.isType('6A')) // Output false
console.log('Is 6A a Font Scale?',Font.isType('6A')) // Output true
```typescript
import { VScale, Font } from "@openbeta/sandbag";

console.log("Is 6A a V Scale?", VScale.isType("6A")); // Output false
console.log("Is 6A a Font Scale?", Font.isType("6A")); // Output true
```

- Convert Grades Across Scales
#### Convert Grades Across Scales

``` javascript
import {convertGrade , GradeScales }from '@openbeta/sandbag'
```typescript
import { convertGrade, GradeScales } from "@openbeta/sandbag";

const ydsInFrench=convertGrade('5.11a',GradeScales.YDS,GradeScales.FRENCH) // Output '6b+/6c'
const ydsInFrench = convertGrade("5.11a", GradeScales.YDS, GradeScales.FRENCH); // Output '6b+/6c'

const fontInVScale=convertGrade('6a',GradeScales.FONT,GradeScales.VSCALE) //OutPut 'V3'
const fontInVScale = convertGrade("6a", GradeScales.FONT, GradeScales.VSCALE); //OutPut 'V3'

// Conversions across different disciplines are not allowed
const sportToBoulder=convertGrade('5.11a',GradeScales.YDS,GradeScales.VSCALE)
const sportToBoulder = convertGrade(
"5.11a",
GradeScales.YDS,
GradeScales.VSCALE,
);
// Output: Scale: Yosemite Decimal System doesn't support converting to Scale: V Scale
// ''
```

- Get Gradeband
#### Get Gradeband

```javascript
import { Ewbank } from '@openbeta/sandbag'

Ewbank.getGradeBand('10') // Output: 'beginner'
Ewbank.getGradeBand('30') // Output: 'expert'
Ewbank.getGradeBand('6a') // Output: Unexpected grade format: 6a for grade scale Ewbank 'unknown'
```typescript
import { Ewbank } from "@openbeta/sandbag";

Ewbank.getGradeBand("10"); // Output: 'beginner'
Ewbank.getGradeBand("30"); // Output: 'expert'
Ewbank.getGradeBand("6a"); // Output: Unexpected grade format: 6a for grade scale Ewbank 'unknown'
```
- Compare Grades

```javascript

import { French, YosemiteDecimal } from '@openbeta/sandbag'
#### Compare Grades

const harder = French.getScore('8a') // Output: [ 84, 85 ]
const easier = YosemiteDecimal.getScore('5.13a') // Output: [ 82, 83 ]
```typescript
import { French, YosemiteDecimal } from "@openbeta/sandbag";

console.log('Is 8a harder than 5.13a?',harder > easier) // Output: true
const harder = French.getScore("8a"); // Output: [ 84, 85 ]
const easier = YosemiteDecimal.getScore("5.13a"); // Output: [ 82, 83 ]

console.log("Is 8a harder than 5.13a?", harder > easier); // Output: true
```



See [unit tests](./src/__tests__) for more examples.

### Development (TBD)
Expand All @@ -153,9 +162,11 @@ file:///Users/<userName>/sandbag/coverage/lcov-report/index.html
```

#### How to publish a new release to NPM

Submit a PR with commit message `[npm publish]`

### Project Maintainers

- [Nathan Musoke](https://github.com/musoke)
- [Viet Nguyen](https://github.com/vnugent)

Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
"name": "@openbeta/sandbag",
"version": "0.0.55",
"description": "Rock climbing grades and conversions",
"repository": "https://github.com/OpenBeta/sandbag.git",
"repository": {
"type": "git",
"url": "git+https://github.com/OpenBeta/sandbag.git"
},
"module": "./dist/sandbag.esm.js",
"main": "./dist/index.js",
"exports": {
Expand Down
3 changes: 3 additions & 0 deletions src/GradeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export const convertGrade = (
return toScale.getGrade(toScore)
}

/**
* @deprecated use VScale.isType instead
*/
export const isVScale = (grade: string): boolean => {
const scale = scales[GradeScales.VSCALE]
if (scale == null) {
Expand Down
7 changes: 7 additions & 0 deletions src/GradeScale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import { GradeBandTypes } from './GradeBands'
export type Tuple = [number, number]

export default interface GradeScale {
/** Return true if the passed string is a valid grade for this scale */
isType: (grade: string) => boolean
/** Convert a grade to a difficult score or score range */
getScore: (grade: string) => number | Tuple
/** Convert a difficulty score or score range to a grade */
getGrade: (score: number | Tuple) => string
/** Return the difficulty band for this grade, e.g. "beginner" */
getGradeBand: (grade: string) => GradeBandTypes
/** Human-readable name of this rating scale */
displayName: string
/** Slug name of this rating scale */
name: GradeScalesTypes
offset: number
conversionGroup: ConversionGroupsTypes
/** List of all grades in this scale */
grades: string[]
}

Expand Down
20 changes: 19 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ const BRITISH_ADJ_ARRAY = [
'E11'
] as const

/**
* @deprecated grades can now be accessed as a property of each GradeScale
*/
const YDS_ARRAY = [
'5.0',
'5.1',
Expand Down Expand Up @@ -117,6 +120,9 @@ const YDS_ARRAY = [
'5.15d'
]

/**
* @deprecated grades can now be accessed as a property of each GradeScale
*/
const FRENCH_ARRAY = [
'1a',
'1b',
Expand Down Expand Up @@ -162,6 +168,9 @@ const FRENCH_ARRAY = [
'9c+'
]

/**
* @deprecated grades can now be accessed as a property of each GradeScale
*/
const UIAA_ARRAY = [
'1',
'2',
Expand Down Expand Up @@ -192,6 +201,9 @@ const UIAA_ARRAY = [
'12'
]

/**
* @deprecated grades can now be accessed as a property of each GradeScale
*/
const EWBANK_ARRAY = [
'1',
'2',
Expand Down Expand Up @@ -235,6 +247,9 @@ const EWBANK_ARRAY = [
'40'
]

/**
* @deprecated grades can now be accessed as a property of each GradeScale
*/
const SAXON_ARRAY = [
'1',
'2',
Expand All @@ -261,6 +276,9 @@ const SAXON_ARRAY = [
'12b'
]

/**
* @deprecated grades can now be accessed as a property of each GradeScale
*/
const NORWAY_ARRAY = [
'1-',
'1',
Expand Down Expand Up @@ -301,7 +319,7 @@ const NORWAY_ARRAY = [
]

/**
* @deprecated - grades can now be accessed as a property of each GradeScale
* @deprecated grades can now be accessed as a property of each GradeScale
*/
export const freeClimbing = {
clean: {
Expand Down
46 changes: 21 additions & 25 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
{
"compilerOptions": {
"types": ["node", "jest"],
"allowJs": true,
"skipLibCheck": true,
"target": "ES6",
"module": "ESNext",
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "Node",
"rootDir": "./src",
"outDir": "build",
"sourceMap": true,
"strictNullChecks": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"declaration": true
},
"ts-node":{
"esm":true,
"experimentalSpecifierResolution": "node",

"types": ["node", "jest"],
"allowJs": true,
"skipLibCheck": true,
"target": "ES6",
"module": "ESNext",
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "Node",
"rootDir": "./src",
"outDir": "build",
"sourceMap": true,
"strictNullChecks": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"declaration": true
},
"include": [
".ts",
"src/**/*.ts"
],
}
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
},
"include": [".ts", "src/**/*.ts"]
}
Loading