import { BoxrecCommonTablesColumnsClass } from "../../../boxrec-common-tables/boxrec-common-tables-columns.class" ; import { BoxrecTitles } from "../../../boxrec-common-tables/boxrec-common.constants" ; import { OutputGetter , OutputInterface } from "../../../decorators/output.decorator" ; import { convertFractionsToNumber , parseHeight , trimRemoveLineBreaks } from "../../../helpers" ; import { BoxrecBasic , BoxrecJudge , Record , Stance , WinLossDraw } from "../../boxrec.constants" ; import { WeightDivision } from "../../champions/boxrec.champions.constants" ; import { BoxingBoutOutcome , BoxingBoutOutcomeKeys } from "../boxrec.event.constants" ; import { BoxrecPageEvent } from "../boxrec.page.event" ; import { BoutPageBoutOutcome , BoutPageLast6 , BoutPageOutcome , BoxrecEventBoutOutput } from "./boxrec.event.bout.constants" ; /**
 * Parse a BoxRec bout page
 * Note: because BoxRec is using inline styles for a lot of things, can't guarantee perfect results.  Report issues
 * <pre>http://boxrec.com/en/event/726555/2037455</pre>
 */ @ OutputGetter ( [ "bouts" , "commission" , "date" , "division" , "doctors" , "firstBoxer" , "firstBoxerAge" , "firstBoxerHeight" , "firstBoxerKOs" , "firstBoxerLast6" , "firstBoxerPointsAfter" , "firstBoxerPointsBefore" , "firstBoxerRanking" , "firstBoxerReach" , "firstBoxerRecord" , "firstBoxerStance" , "id" , "inspector" , "judges" , "location" , "matchmakers" , "media" , "numberOfBouts" , "numberOfRounds" , "outcome" , "promoters" , "rating" , "referee" , "secondBoxer" , "secondBoxerAge" , "secondBoxerHeight" , "secondBoxerKOs" , "secondBoxerLast6" , "secondBoxerPointsAfter" , "secondBoxerPointsBefore" , "secondBoxerRanking" , "secondBoxerReach" , "secondBoxerRecord" , "secondBoxerStance" , "television" , "titles" , ] ) export class BoxrecPageEventBout extends BoxrecPageEvent implements OutputInterface { output : BoxrecEventBoutOutput ; private static parseOutcome ( outcomeStr ) { const trimmedOutcomeStr = trimRemoveLineBreaks ( outcomeStr ) ; const matches = trimmedOutcomeStr . match ( /^(\w+)\s(\w+)$/ ) ; const outcomeObj = { outcome : null , outcomeByWayOf : null , } ; if ( matches ) { const firstMatch = matches [ 1 ] ; const secondMatch = matches [ 2 ] ; const values = BoxingBoutOutcome ; if ( firstMatch . length > 1 ) { outcomeObj . outcomeByWayOf = values [ firstMatch ] as BoxingBoutOutcome ; outcomeObj . outcome = BoxrecCommonTablesColumnsClass . parseOutcome ( secondMatch ) ; } else { outcomeObj . outcomeByWayOf = values [ secondMatch ] as BoxingBoutOutcome ; outcomeObj . outcome = BoxrecCommonTablesColumnsClass . parseOutcome ( firstMatch ) ; } } return outcomeObj ; } get date ( ) : string | null { let date = this . $ ( ".page h2:nth-child(1) a" ) . text ( ) ; if ( date ) { date = trimRemoveLineBreaks ( date ) ; return new Date ( date ) . toISOString ( ) . slice ( 0 , 10 ) ; } return date ; } get division ( ) : WeightDivision | null { return BoxrecCommonTablesColumnsClass . parseDivision ( this . parseDivision ( "division" ) ) ; } get firstBoxer ( ) : BoxrecBasic { const boxer = this . parseBoxers ( "first" ) ; return BoxrecCommonTablesColumnsClass . parseNameAndId ( boxer ) ; } get firstBoxerAge ( ) : number | null { return this . parseBoxerAge ( 1 ) ; } get firstBoxerHeight ( ) : number [ ] | null { return this . parseBoxerHeight ( 1 ) ; } get firstBoxerKOs ( ) : number { return this . getKOs ( 1 ) ; } get firstBoxerLast6 ( ) : BoutPageLast6 [ ] { return this . parseLast6Object ( true ) ; } get firstBoxerPointsAfter ( ) : number | null { return this . parsePoints ( false , 1 ) ; } get firstBoxerPointsBefore ( ) : number | null { return this . parsePoints ( true , 1 ) ; } get firstBoxerRanking ( ) : number | null { return this . parseRankingData ( 1 ) ; } get firstBoxerReach ( ) : number [ ] | null { return this . parseBoxerReach ( 1 ) ; } get firstBoxerRecord ( ) : Record { return this . getRecord ( 1 ) ; } get firstBoxerStance ( ) : Stance | null { return this . parseBoxerStance ( 1 ) ; } get judges ( ) : BoxrecJudge [ ] { const judges = [ ] ; const judgesArr = this . parseJudges ( ) ; judgesArr . forEach ( item => { const judgeEl = this . $ ( `<tr>${ item }</tr>` ) ; const judgeObj = { id : null , name : null , scorecard : [ ] , } ; const firstRating = judgeEl . find ( "td:nth-child(1)" ) . text ( ) ; const nameAndId = judgeEl . find ( "td:nth-child(2)" ) . html ( ) ; const secondRating = judgeEl . find ( "td:nth-child(3)" ) . text ( ) ; if ( firstRating ) { judgeObj . scorecard [ 0 ] = parseInt ( firstRating , 10 ) ; } if ( nameAndId ) { const { id , name } = BoxrecCommonTablesColumnsClass . parseNameAndId ( nameAndId ) ; judgeObj . id = id ; judgeObj . name = name ; } if ( secondRating ) { judgeObj . scorecard [ 1 ] = parseInt ( secondRating , 10 ) ; } judges . push ( judgeObj ) ; } ) ; return judges ; } returns { number | null } get numberOfRounds ( ) : number | null { const numberOfRounds = this . parseDivision ( "numberOfRounds" ) ; if ( numberOfRounds ) { return parseInt ( numberOfRounds , 10 ) ; } return null ; } get outcome ( ) : BoutPageBoutOutcome { const outcome = { boxer : { id : null , name : null , } , outcome : null , outcomeByWayOf : null } ; const objRow = this . getBoxersSideObjRow ( ) ; const textWon = objRow [ 0 ] . find ( ".textWon" ) ; const textWonSecond = objRow [ 1 ] . find ( ".textWon" ) ; const textDraw = objRow [ 0 ] . find ( ".textDrawn" ) ; if ( textWon . length === 1 || textWonSecond . length === 1 ) { let boxerStr = "" ; let outcomeString ; if ( textWon . length === 1 ) { boxerStr = this . $ . html ( objRow [ 0 ] . find ( ".personLink" ) [ 0 ] ) ; outcomeString = textWon [ 0 ] . children [ 0 ] . data ; } else { boxerStr = this . $ . html ( objRow [ 0 ] . find ( ".personLink" ) [ 1 ] ) ; outcomeString = textWonSecond [ 0 ] . children [ 0 ] . data ; } if ( outcomeString ) { const outcomeResult = this . parseOutcomeOfBout ( outcomeString ) ; outcome . outcome = outcomeResult . outcome ; outcome . outcomeByWayOf = outcomeResult . outcomeByWayOf ; } const boxer = BoxrecCommonTablesColumnsClass . parseNameAndId ( boxerStr ) ; outcome . boxer . id = boxer . id ; outcome . boxer . name = boxer . name ; } else if ( textDraw . length === 2 ) { outcome . outcome = WinLossDraw . draw ; const firstBoxerDrawText = textDraw [ 0 ] . children [ 0 ] . data ; if ( firstBoxerDrawText ) { const outcomeResult = this . parseOutcomeOfBout ( firstBoxerDrawText ) ; outcome . outcomeByWayOf = outcomeResult . outcomeByWayOf ; } } return outcome ; } get rating ( ) : number | null { let starRating = this . $ ( ".starRating" ) . parent ( ) . html ( ) ; if ( starRating ) { starRating = `<div>${ starRating }</div>` ; return BoxrecCommonTablesColumnsClass . parseRating ( starRating ) ; } return null ; } get referee ( ) : BoxrecBasic { const refereeLink = this . $ . html ( this . $ ( ".responseLessDataTable a:nth-child(1)" ) . get ( 0 ) ) ; return BoxrecCommonTablesColumnsClass . parseReferee ( refereeLink ) ; } get secondBoxer ( ) : BoxrecBasic { const boxer = this . parseBoxers ( "second" ) ; return BoxrecCommonTablesColumnsClass . parseNameAndId ( boxer ) ; } get secondBoxerAge ( ) : number | null { return this . parseBoxerAge ( 3 ) ; } get secondBoxerHeight ( ) : number [ ] | null { return this . parseBoxerHeight ( 3 ) ; } get secondBoxerKOs ( ) : number { return this . getKOs ( 3 ) ; } get secondBoxerLast6 ( ) : BoutPageLast6 [ ] { return this . parseLast6Object ( false ) ; } get secondBoxerPointsAfter ( ) : number | null { return this . parsePoints ( false , 3 ) ; } get secondBoxerPointsBefore ( ) : number | null { return this . parsePoints ( true , 3 ) ; } get secondBoxerRanking ( ) : number | null { return this . parseRankingData ( 3 ) ; } get secondBoxerReach ( ) : number [ ] | null { return this . parseBoxerReach ( 3 ) ; } get secondBoxerRecord ( ) : Record { return this . getRecord ( 3 ) ; } get secondBoxerStance ( ) : Stance | null { return this . parseBoxerStance ( 3 ) ; } get titles ( ) : BoxrecTitles [ ] { const titles = this . parseTitles ( ) ; return BoxrecCommonTablesColumnsClass . parseTitles ( titles || "" ) ; } protected getPeopleTable ( ) { return this . $ ( "h1" ) . parent ( ) . find ( "table" ) . last ( ) . find ( "tbody tr" ) ; } param { string } textToFind returns { Cheerio } private findColumnByText ( textToFind = "" ) { const filteredCheerio = this . $ ( "td b" ) . filter ( ( index , elem ) => { return this . $ ( elem ) . text ( ) . trim ( ) === textToFind ; } ) ; if ( filteredCheerio . length > 1 ) { throw new Error ( "found two columns with the same name, please report this" ) ; } return filteredCheerio ; } private getBoxersSideObjRow ( ) { const boxers = [ ] ; this . $ ( "table .personLink" ) . each ( ( i , elem ) => { const href = this . $ ( elem ) . attr ( "href" ) ; if ( href . includes ( "boxer" ) && boxers . length < 2 ) { boxers . push ( this . $ ( elem ) . parent ( ) . parent ( ) . parent ( ) ) ; } } ) ; return boxers ; } private getKOs ( tableColumn ) { const knockouts = this . parseMiddleRowByText ( "KOs" , tableColumn ) as string ; return parseInt ( knockouts , 10 ) ; } private getRecord ( tableColumn ) { const won = this . parseMiddleRowByText ( "won" , tableColumn ) as string ; const lost = this . parseMiddleRowByText ( "lost" , tableColumn ) as string ; const drawn = this . parseMiddleRowByText ( "drawn" , tableColumn ) as string ; return { draw : parseInt ( drawn , 10 ) , loss : parseInt ( lost , 10 ) , win : parseInt ( won , 10 ) , } ; } private parseBoxerAge ( tableColumn ) { const text = this . parseMiddleRowByText ( "age" , tableColumn ) as string ; if ( text ) { return parseInt ( text , 10 ) ; } return null ; } private parseBoxerHeight ( tableColumn ) { let text = this . parseMiddleRowByText ( "height" , tableColumn ) as string ; text = trimRemoveLineBreaks ( text ) ; return parseHeight ( text ) ; } private parseBoxerReach ( tableColumn ) { let text = this . parseMiddleRowByText ( "reach" , tableColumn ) as string ; text = trimRemoveLineBreaks ( text ) ; let reach = null ; if ( text ) { const regex = /^(\d{2})(¼|½|¾)?(?:″|\&\#x2033\;)\s(?:\/|\&\#xA0\;\s)\s(?:\/|\&\#xA0\;)?(\d{3})cm$/ ; const reachMatch = text . match ( regex ) ; if ( reachMatch ) { const [ , inches , fraction , centimeters ] = reachMatch ; let parsedInches = parseInt ( inches , 10 ) ; if ( fraction ) { parsedInches += convertFractionsToNumber ( fraction ) ; } reach = [ parsedInches , parseInt ( centimeters , 10 ) , ] ; } } return reach ; } param { number } tableColumn returns { Stance | null } private parseBoxerStance ( tableColumn ) { const text = this . parseMiddleRowByText ( "stance" , tableColumn ) as string ; if ( text ) { return trimRemoveLineBreaks ( text ) as Stance ; } return null ; } private parseBoxers ( boxer ) { const name = [ ] ; const alreadyAdded = [ ] ; this . $ ( "table a" ) . each ( ( i , elem ) => { const href = this . $ ( elem ) . attr ( "href" ) ; if ( name . length < 2 && href . includes ( "boxer" ) && ! alreadyAdded . find ( item => item === href ) ) { alreadyAdded . push ( href ) ; name . push ( this . $ . html ( this . $ ( elem ) ) ) ; } } ) ; if ( boxer === "first" && name [ 0 ] ) { return name [ 0 ] ; } else if ( boxer === "second" && name [ 1 ] ) { return name [ 1 ] ; } return "" ; } private parseDivision ( type ) { const h2 = this . $ ( "h2" ) . get ( 1 ) ; if ( h2 ) { const division = h2 . children [ 0 ] . data ; if ( division ) { const divisionMatches = division . match ( /^(\w+).*\,\s?(\d{1,2})?/ ) ; if ( ! divisionMatches ) { return "" ; } if ( type === "division" && divisionMatches [ 1 ] ) { return divisionMatches [ 1 ] ; } if ( type === "numberOfRounds" && divisionMatches [ 2 ] ) { return divisionMatches [ 2 ] ; } } } return "" ; } private parseJudges ( ) { const links = this . $ ( ".responseLessDataTable a" ) ; const judges = [ ] ; links . each ( ( i , elem ) => { const href = this . $ ( elem ) . attr ( "href" ) ; if ( href . includes ( "judge" ) ) { const judgeString = this . $ . html ( this . $ ( elem ) . parent ( ) . parent ( ) ) ; judges . push ( judgeString ) ; } } ) ; return judges ; } private parseLast6 ( boxer ) { let ranking = this . parseMiddleRowByText ( "last 6" ) as Cheerio ; const boxerArr = [ ] ; for ( let i = 0 ; i < 6 ; i ++ ) { ranking = ranking . next ( ) ; const tableFirst = ranking . find ( "td:nth-child(1) table" ) ; const tableSecond = ranking . find ( "td:nth-child(3) table" ) ; if ( tableFirst && boxer === "first" ) { const firstBoxerTable = this . $ . html ( tableFirst ) ; boxerArr . push ( firstBoxerTable ) ; } if ( tableSecond && boxer === "second" ) { const secondBoxerTable = this . $ . html ( tableSecond ) ; boxerArr . push ( secondBoxerTable ) ; } } return boxerArr ; } private parseLast6Object ( isFirstBoxer ) { const parsedBoxerLast6 = [ ] ; const last6 = this . parseLast6 ( isFirstBoxer ? "first" : "second" ) ; for ( const last of last6 ) { const obj = { date : null , id : null , name : null , outcome : null , outcomeByWayOf : null , } ; const table = this . $ ( last ) ; let idName = { id : null , name : null , } ; let outcomeStr = null ; if ( isFirstBoxer ) { const idNameStr = this . $ . html ( table . find ( "td:nth-child(1) a" ) ) ; idName = BoxrecCommonTablesColumnsClass . parseNameAndId ( idNameStr ) ; } else { outcomeStr = table . find ( "td:nth-child(1) span" ) . html ( ) ; } const dateStr = this . $ . html ( table . find ( "td:nth-child(2) a" ) ) ; const date = BoxrecCommonTablesColumnsClass . parseNameAndId ( dateStr ) . name ; if ( isFirstBoxer ) { outcomeStr = table . find ( "td:nth-child(3) span" ) . html ( ) ; } else { const idNameStr = this . $ . html ( table . find ( "td:nth-child(3) a" ) ) ; idName = BoxrecCommonTablesColumnsClass . parseNameAndId ( idNameStr ) ; } if ( outcomeStr ) { const { outcome , outcomeByWayOf } = BoxrecPageEventBout . parseOutcome ( outcomeStr ) ; obj . outcome = outcome ; obj . outcomeByWayOf = outcomeByWayOf ; } obj . id = idName . id ; obj . name = idName . name ; if ( date ) { obj . date = date ; } parsedBoxerLast6 . push ( obj ) ; } return parsedBoxerLast6 ; } param { string } textToFind param { number } tableColumn returns { Cheerio | string } private parseMiddleRowByText ( textToFind = "" , tableColumn = - 1 ) { const filteredRow = this . findColumnByText ( textToFind ) . parent ( ) . parent ( ) ; if ( tableColumn >= 0 ) { return filteredRow . find ( `td:nth-child(${ tableColumn })` ) . text ( ) ; } return filteredRow ; } private parseOutcomeOfBout ( outcomeText ) { const splitDrawText = outcomeText . split ( " " ) ; const outcomeKeys = Object . keys ( BoxingBoutOutcome ) ; const foundKey = outcomeKeys . find ( item => item === splitDrawText [ 1 ] ) ; if ( foundKey ) { const outcomeByWayOf = BoxingBoutOutcome [ foundKey as BoxingBoutOutcomeKeys ] ; return { outcome : WinLossDraw . win , outcomeByWayOf , } ; } return { outcome : null , outcomeByWayOf : null , } ; } private parsePoints ( beforePoints = true , tableColumn ) { const strToSearch = beforePoints ? "before fight" : "after fight" ; const points = this . parseMiddleRowByText ( strToSearch , tableColumn ) as string ; const pointsParsed = points . match ( /[\d\,]+/ ) ; if ( pointsParsed ) { pointsParsed [ 0 ] = pointsParsed [ 0 ] . replace ( "," , "" ) ; return parseInt ( pointsParsed [ 0 ] , 10 ) ; } return null ; } private parseRanking ( ) { const ranking = this . parseMiddleRowByText ( "ranking" ) as Cheerio ; return this . $ . html ( ranking . next ( ) ) ; } private parseRankingData ( columnNumber = 1 ) { const ranking = this . $ ( this . parseRanking ( ) ) ; const td = ranking . find ( `td:nth-child(${ columnNumber })` ) ; if ( td . text ( ) ) { return parseInt ( td . text ( ) , 10 ) ; } return null ; } private parseTitles ( ) { return this . $ ( ".titleColor" ) . html ( ) ; } }