nhlscrapi.scrapr.eventsummrep — nhlscrapi documentation

# Source code for nhlscrapi.scrapr.eventsummrep

    from itertools import chain

    from nhlscrapi._tools import (
        to_int,
    #    re_comp_num_pos_name,
        exclude_from as ex_junk
    )
    from nhlscrapi.scrapr.reportloader import ReportLoader

    def _rem(txt):
        return ex_junk(txt, containing=['\n','\r'])

    def _zip_top_bot(txt_lst):
        cols = len(txt_lst)/2
        return [ (k,v) for k, v in zip(txt_lst[:cols],txt_lst[cols:]) ]

    class EventSummRep(ReportLoader):
        """Retrieve and load event summary report from nhl.com"""

        def __init__(self, game_key):
            super(EventSummRep, self).__init__(game_key, 'event_summary')

            self.shots = { }
            """
            The shot summary by strength at the aggregate and detail level

            :returns: dict of the form

            .. code:: python

                {
                    'home/away': {
                        'agg': { 'ev/pp/sh/tot': { 'g': goals, 's': shots } },
                        'det': { '5v5/5v4/5v3/et c': { 'g': goals, 's': shots } }
                    }
                }
            """

            self.face_offs = { }
            """
            Face off summary by strength.

            :returns: dict of the form

            .. code:: python

                {
                    'home/away': {
                        'ev/pp/sh/tot': { 'won': goals, 'total': shots } },
                    }
                }
            """

            self.by_team = { }
            """
            By player team summary.

            :returns: dict of the form

            .. code:: python

                {
                    'home/away': {
                        'num': {
                            'pos': pos,
                            'name': { 'first': first, 'last': last },
                            'g/a/p/pm/pn/pim/shifts/s/ab/ms/ht/gv/tk/bs': value
                            'fo': { 'won': won, 'tot': total },
                            'toi': {
                                'tot/avg/pp/sh/ev': { 'min': mins, 'sec': secs }
                            }
                        }
                    }
                }
            """

            # define place holders for different parts of the report
            # each one corresponds to a bit of data that can be parsed individually
            self.__aw_top, self.__aw_bot, self.__h_top, self.__h_bot = [None]*4  # shot summ
            self.__away_fo, self.__home_fo = [None]*2  # face off summ

        def parse(self):
            """
            Retreive and parse Event Summary report for the given :py:class:`nhlscrapi.games.game.GameKey`

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                return super(EventSummRep, self).parse() \
                    .parse_away_shots() \
                    .parse_home_shots() \
                    .parse_away_fo() \
                    .parse_home_fo() \
                    .parse_away_by_player() \
                    .parse_home_by_player()
            except:
                return None

        def __get_tr_by_name(self, name):
            xp = ".//td[contains(./text(),'{}')]/../following-sibling::tr".format(name)
            return self.html_doc().xpath(xp)[0]

        def __set_shot_tables(self):
            if self.__aw_top is None:
                shot_tr = self.__get_tr_by_name('SHOTS SUMM')
                _ , self.__aw_top, self.__aw_bot, self.__home_top, self.__home_bot = shot_tr.xpath('.//table')

        def __parse_shot_tables(self, top_table, bot_table):
            def key_g_s(rows_lst):
                res = { }
                for k, v in _zip_top_bot(_rem(rows_lst)):
                    g, s = v.split('-') if '-' in v else ('0', '0')
                    res[k.lower()] = { 'g': to_int(g, 0), 's': to_int(s, 0) }
                return res

            return {
                'agg': key_g_s(top_table.xpath('.//text()')),
                'det': key_g_s(bot_table.xpath('.//text()'))
            }

        def __set_fo_tables(self):
            if self.__away_fo is None:
                fo_tr = self.__get_tr_by_name('FACE-OFF')
                _, self.__away_fo, self.__home_fo = fo_tr.xpath('.//table')

        def __parse_fo_table(self, table):
            def str_w_t(rows_lst):
                res = { }
                for k, v in _zip_top_bot(_rem(rows_lst)):
                    ct, _ = v.split('/') if '/' in  v else ('0-0','0')
                    w, t = ct.split('-') if '-' in v else ('0', '0')
                    res[k.lower()] = { 'won': to_int(w, 0), 'total': to_int(t, 0) }
                return res

            return str_w_t(table.xpath('.//text()'))

        def __set_team_tables(self):
            if self.__away is None:
                team_tr = self.__get_tr_by_name('TEAM')
                _, self.__away, self.__home = team_tr.xpath('.//table')

        def __parse_team_tables(self, table):
            pass

        def parse_home_shots(self):
            """
            Parse shot info for home team.

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                self.__set_shot_tables()
                self.shots['home'] = self.__parse_shot_tables(
                    self.__home_top,
                    self.__home_bot
                )
                return self
            except:
                return None

        def parse_away_shots(self):
            """
            Parse shot info for away team.

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                self.__set_shot_tables()
                self.shots['away'] = self.__parse_shot_tables(
                    self.__aw_top,
                    self.__aw_bot
                )
                return self
            except:
                return None

        def parse_home_fo(self):
            """
            Parse face-off info for home team.

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                self.__set_fo_tables()
                self.face_offs['home'] = self.__parse_fo_table(self.__home_fo)
                return self
            except:
                return None

        def parse_away_fo(self):
            """
            Parse face-off info for away team.

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                self.__set_fo_tables()
                self.face_offs['away'] = self.__parse_fo_table(self.__away_fo)
                return self
            except:
                return None

        def __read_line(self, tr):
            rec = _rem(tr.xpath('.//text()'))
            if len(rec) == 25:
                # player info
                num = to_int(rec[0],0)
                if not num:
                    return { }

                dat = { }
                dat['pos'] = rec[1]

                last, first = rec[2].split(',')
                dat['name'] = { 'first': first, 'last': last }

                dat['shifts'] = to_int(rec[10],0)

                cols = chain(
                    [ (i+3,v) for i,v in enumerate(['g','a','p','pm','pn','pim']) ],
                    [ (i+15,v) for i,v in enumerate(['s','ab','ms','ht','gv','tk','bs']) ]
                )
                for i, col in cols:
                    dat[col] = to_int(rec[i],0)

                dat['toi'] = { }
                for k, v in [ (9,'tot'), (11,'avg'), (12,'pp'), (13,'sh'), (14,'ev') ]:
                    if ':' in rec[k]:
                        mins, secs = rec[k].split(':')
                        dat['toi'][v] = { 'min': to_int(mins,0), 'sec': to_int(secs,0) }
                    else:
                        dat['toi'][v] = { 'min': 0, 'sec': 0 }

                w, l = to_int(rec[22],0), to_int(rec[23],0)
                dat['fo'] = { 'won': w, 'total': w+l }

                return num, dat
            else:
                return -1, { }

        def __parse_players(self):
            bp_tr = self.__get_tr_by_name('TEAM SUMM')
            all_bp = bp_tr.xpath(".//tr[contains(@class,'Color')]")

            t = 'away'
            self.by_team = { 'home': { }, 'away': { } }
            for abp in all_bp:
                num, p = self.__read_line(abp)
                if num == -1:
                    t = 'home'
                else:
                    self.by_team[t][num] = p

        def parse_home_by_player(self):
            """
            Parse by player info for home team.

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                if not self.by_team:
                    self.__parse_players()

                return self
            except:
                return None

        def parse_away_by_player(self):
            """
            Parse by player info for away team.

            :returns: ``self`` on success, ``None`` otherwise
            """
            try:
                if not self.by_team is None:
                    self.__parse_players()

                return self
            except:
                return None