From df38dc9e9fee0826e8f576685bb7acc3175631f8 Mon Sep 17 00:00:00 2001 From: Iacami Gevaerd Date: Fri, 10 Nov 2023 09:09:31 -0300 Subject: [PATCH] feat: add Brazilian grade context (closes #358) --- package.json | 2 +- src/GradeUtils.ts | 17 +++++ src/__tests__/gradeUtils.ts | 32 ++++++++ src/db/ClimbSchema.ts | 1 + src/db/ClimbTypes.ts | 1 + src/graphql/schema/Climb.gql | 3 +- src/model/__tests__/MutableClimbDataSource.ts | 75 +++++++++++++++++++ yarn.lock | 8 +- 8 files changed, 133 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e5439ff8..fbbfcbde 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@babel/runtime": "^7.17.2", "@google-cloud/storage": "^6.9.5", "@graphql-tools/schema": "^8.3.1", - "@openbeta/sandbag": "^0.0.48", + "@openbeta/sandbag": "^0.0.51", "@turf/area": "^6.5.0", "@turf/bbox": "^6.5.0", "@turf/bbox-polygon": "^6.5.0", diff --git a/src/GradeUtils.ts b/src/GradeUtils.ts index d869fdfa..42dab36c 100644 --- a/src/GradeUtils.ts +++ b/src/GradeUtils.ts @@ -15,6 +15,7 @@ export enum GradeContexts { ALSK = 'ALSK', /** Australia */ AU = 'AU', + /** Brazil */ BRZ = 'BRZ', FIN = 'FIN', FR = 'FR', @@ -96,6 +97,22 @@ export const gradeContextToGradeScales: Partial { actual = createGradeObject('5.9', sanitizeDisciplines({ bouldering: true }), context) expect(actual).toBeUndefined() }) + + it('creates grade object correctly in BRZ context', () => { + const context = gradeContextToGradeScales.BRZ + if (context == null) { fail('Bad grade context. Should not happen.') } + let actual = createGradeObject('V', sanitizeDisciplines({ sport: true }), context) + expect(actual).toEqual({ + brazilian_crux: 'V' + }) + actual = createGradeObject('A2', sanitizeDisciplines({ aid: true }), context) + expect(actual).toEqual({ + aid: 'A2' + }) + actual = createGradeObject('C1', sanitizeDisciplines({ aid: true }), context) + expect(actual).toEqual({ + aid: 'C1' + }) + actual = createGradeObject('V5', sanitizeDisciplines({ bouldering: true }), context) + expect(actual).toEqual({ + vscale: 'V5' + }) + actual = createGradeObject('WI6', sanitizeDisciplines({ ice: true }), context) + expect(actual).toEqual({ + wi: 'WI6' + }) + actual = createGradeObject('VIIb', sanitizeDisciplines({ deepwatersolo: true }), context) + expect(actual).toEqual({ + brazilian_crux: 'VIIb' + }) + // Invalid input + actual = createGradeObject('5.9', sanitizeDisciplines({ bouldering: true }), context) + expect(actual).toBeUndefined() + }) }) diff --git a/src/db/ClimbSchema.ts b/src/db/ClimbSchema.ts index af3b7199..29cab777 100644 --- a/src/db/ClimbSchema.ts +++ b/src/db/ClimbSchema.ts @@ -63,6 +63,7 @@ const GradeTypeSchema = new Schema({ vscale: Schema.Types.String, yds: { type: Schema.Types.String, required: false }, ewbank: { type: Schema.Types.String, required: false }, + brazilian_crux: { type: Schema.Types.String, required: false }, french: { type: Schema.Types.String, required: false }, font: { type: Schema.Types.String, required: false }, UIAA: { type: Schema.Types.String, required: false } diff --git a/src/db/ClimbTypes.ts b/src/db/ClimbTypes.ts index 4884fb81..40eb5083 100644 --- a/src/db/ClimbTypes.ts +++ b/src/db/ClimbTypes.ts @@ -103,6 +103,7 @@ export enum SafetyType { export interface IGradeType { yds?: string ewbank?: string + brazilian_crux?: string french?: string font?: string uiaa?: string diff --git a/src/graphql/schema/Climb.gql b/src/graphql/schema/Climb.gql index 453086bf..13372ae9 100644 --- a/src/graphql/schema/Climb.gql +++ b/src/graphql/schema/Climb.gql @@ -154,6 +154,7 @@ type GradeType { """ ewbank: String french: String + brazilian_crux: String """ Fontainebleau grading system, the most widely used grading system in Europe. Mostly used for bouldering. @@ -204,4 +205,4 @@ type Pitch { length: Int boltsCount: Int description: String -} \ No newline at end of file +} diff --git a/src/model/__tests__/MutableClimbDataSource.ts b/src/model/__tests__/MutableClimbDataSource.ts index c3665fa1..911ab721 100644 --- a/src/model/__tests__/MutableClimbDataSource.ts +++ b/src/model/__tests__/MutableClimbDataSource.ts @@ -380,6 +380,81 @@ describe('Climb CRUD', () => { } }) + it('handles Brazilian grade context correctly', async () => { + await areas.addCountry('bra') + + { + // A roped climbing area + const newClimbingArea = await areas.addArea(testUser, 'Climbing area in Brazil', null, 'bra') + if (newClimbingArea == null) fail('Expect new area to be created in Brazil') + + const newclimbs = [ + { ...newSportClimb1, grade: 'VIsup' }, // good sport grade + { ...newSportClimb2, grade: 'VIsup/VIIa', disciplines: { trad: true } }, // good trad and slash grade + { ...newSportClimb2, grade: '5.9' }, // bad BRZ context grade + { ...newIceRoute, grade: 'WI4+' }, // good WI BRZ context grade + { ...newAidRoute, grade: 'A0' } // good aid grade + ] + + const newIDs = await climbs.addOrUpdateClimbs( + testUser, + newClimbingArea.metadata.area_id, + newclimbs + ) + expect(newIDs).toHaveLength(newclimbs.length) + + const climb1 = await climbs.findOneClimbByMUUID(muid.from(newIDs[0])) + expect(climb1?.grades).toEqual({ brazilian_crux: 'VIsup' }) + expect(climb1?.type.sport).toBe(true) + expect(newSportClimb1?.boltsCount).toEqual(2) + + const climb2 = await climbs.findOneClimbByMUUID(muid.from(newIDs[1])) + expect(climb2?.grades).toEqual({ brazilian_crux: 'VIsup/VIIa' }) + expect(climb2?.type.sport).toBe(false) + expect(climb2?.type.trad).toBe(true) + + const climb3 = await climbs.findOneClimbByMUUID(muid.from(newIDs[2])) + expect(climb3?.grades).toEqual(undefined) + + const climb4 = await climbs.findOneClimbByMUUID(muid.from(newIDs[3])) + expect(climb4?.grades).toEqual({ wi: 'WI4+' }) + expect(climb4?.type.sport).toBe(false) + expect(climb4?.type.trad).toBe(false) + expect(climb4?.type.bouldering).toBe(false) + expect(climb4?.type.ice).toBe(true) + + const climb5 = await climbs.findOneClimbByMUUID(muid.from(newIDs[4])) + expect(climb5?.grades).toEqual({ aid: 'A0' }) + expect(climb5?.type.sport).toBe(false) + expect(climb5?.type.trad).toBe(false) + expect(climb5?.type.aid).toBe(true) + } + + { + // A bouldering area + const newBoulderingArea = await areas.addArea(testUser, 'Bouldering area 1', null, 'bra') + if (newBoulderingArea == null) fail('Expect new area to be created') + + const newIDs = await climbs.addOrUpdateClimbs( + testUser, + newBoulderingArea.metadata.area_id, + [{ ...newBoulderProblem1, grade: 'V3' }, // good grade + { ...newBoulderProblem2, grade: '23' }, // bad boulder grade + { ...newBoulderProblem2, grade: '7B' }]) // invalid grade (font grade for a BRZ context boulder problem) + + expect(newIDs).toHaveLength(3) + + const climb1 = await climbs.findOneClimbByMUUID(muid.from(newIDs[0])) + expect(climb1?.grades).toEqual({ vscale: 'V3' }) + + const climb2 = await climbs.findOneClimbByMUUID(muid.from(newIDs[1])) + expect(climb2?.grades).toEqual(undefined) + + const climb3 = await climbs.findOneClimbByMUUID(muid.from(newIDs[2])) + expect(climb3?.grades).toEqual(undefined) + } + }) + it('handles UIAA grades correctly', async () => { await areas.addCountry('deu') // Assuming Germany since UIAA is dominant grading system diff --git a/yarn.lock b/yarn.lock index 2f5485d8..ad2ed3d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1579,10 +1579,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@openbeta/sandbag@^0.0.48": - version "0.0.48" - resolved "https://registry.yarnpkg.com/@openbeta/sandbag/-/sandbag-0.0.48.tgz#d876ae47634c287be1f71858576a9f826f8ee270" - integrity sha512-QyTGG9Y+c+2TY+EbdUByAAr1u7qyqZ4H1ZM0TztetJU/a7TLjdMoR32yaRY8uSFvm3f1sP0zn0ffhlLiICDdlg== +"@openbeta/sandbag@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@openbeta/sandbag/-/sandbag-0.0.51.tgz#21ce618d2414dc0b8d4f31ef260ac2ebad5a43c8" + integrity sha512-qMVohgqRdFjXH8a3aSEZa6zemwSpak/HMttR/pqvclDIXqgPKzWvjFRA3o/YDGieI/19P4dtizLo91TKx0smGQ== "@panva/asn1.js@^1.0.0": version "1.0.0"