コード例 #1
0
 def is_left_service():
     if (co.find_first_xpath(element,
                             "child::svg[contains(@class, '--serveHome')]")
             is not None):
         return True
     if (co.find_first_xpath(element,
                             "child::svg[contains(@class, '--serveAway')]")
             is not None):
         return False
コード例 #2
0
    def ingame_score():
        def get_xpath(is_left):
            return (f"child::div[contains(@class,'event__part--"
                    f"{'home' if is_left else 'away'}') and "
                    f"contains(@class,'event__part--6')]")

        fst_el = co.find_first_xpath(element, get_xpath(is_left=True))
        snd_el = co.find_first_xpath(element, get_xpath(is_left=False))
        if fst_el is not None and snd_el is not None:
            result = fst_el.text, snd_el.text
            if result != ("", ""):
                return result
コード例 #3
0
def _make_tourinfo(element, start_idx, skip_levels):
    def is_skip_key():
        return ("DOUBLES" in evt_title_text or "EXHIBITION" in evt_title_text
                or "Next Gen Finals" in evt_title_text
                or "Laver Cup" in evt_title_text)

    def is_skip_obj(tourinfo_fs: TourInfoFlashscore):
        return (tourinfo_fs.level in skip_levels[tourinfo_fs.sex]
                or tourinfo_fs.sex == SKIP_SEX or tourinfo_fs.teams)

    def split_before_after_text(text, part):
        if part in text:
            before_including = co.strip_after_find(text, part)
            after_not_including = text[text.index(part) + len(part):]
            return before_including, after_not_including

    def split_sex_doubles_text(text):
        # now ':' is not in text_content() and we need split text by key words:
        result = split_before_after_text(text, "SINGLES")
        if result is None:
            result = split_before_after_text(text, "TEAMS - WOMEN")
            if result is None:
                result = split_before_after_text(text, "TEAMS - MEN")
        return result

    if "event__header" not in element.get("class"):
        log.error(
            "class != event__header got class: '{}' start_idx {}\n{}".format(
                element.get("class"), start_idx,
                etree.tostring(element, encoding="utf8")))
        raise Exception(
            "class != event__header got class: '{}' start_idx {}".format(
                element.get("class"), start_idx))

    evt_title_el = co.find_first_xpath(
        element, "child::div[contains(@class,'event__title ')]")
    if evt_title_el is None:
        raise co.TennisParseError(
            "not parsed sex_doubles el FS TourInfo from\n{}".format(
                etree.tostring(element, encoding="utf8")))
    evt_title_text = evt_title_el.text_content().strip()
    if not evt_title_text:
        raise co.TennisParseError("empty sex_doubles txt FS TourInfo")

    if is_skip_key():
        raise LiveEventToskipError()
    obj = tourinfo_cache.get(evt_title_text)
    if obj is not None:
        return obj

    evt_title_parts = split_sex_doubles_text(evt_title_text)
    if evt_title_parts is None:
        raise co.TennisParseError("fail split sex_doubles: '{}'\n{}".format(
            evt_title_text, etree.tostring(element, encoding="utf8")))
    obj = TourInfoFlashscore(evt_title_parts[0], evt_title_parts[1])
    if is_skip_obj(obj):
        tourinfo_cache.put_skip(evt_title_text)
    else:
        tourinfo_cache.put(evt_title_text, obj)
        return obj
コード例 #4
0
 def is_right_serve():
     xpath_txt = (
         "child::div[starts-with(@class, 'away___')]/"
         "div[starts-with(@class, 'participantServe___')]/"
         "div[@title='Serving player']"
     )
     return co.find_first_xpath(entry_elem0, xpath_txt) is not None
コード例 #5
0
 def is_final_result_only():
     # score (not point by point) will be after match finish.
     # after finish we can not determinate that is was state FRO.
     el = co.find_first_xpath(
         element,
         "child::div[@class='event__time']/div[@title='Final result only.']"
     )
     return el is not None
コード例 #6
0
 def fill_inmatch():
     xpath_txt = "child::div[starts-with(@class, 'wrapper___')]"
     inmatch_el = co.find_first_xpath(entry_elem, xpath_txt)
     if inmatch_el is not None:
         txt = inmatch_el.text_content().strip()
         if txt:
             fst, snd = txt.split("-")
             if fst and snd:
                 result.inmatch = int(fst), int(snd)
コード例 #7
0
 def get_time():
     time_el = co.find_first_xpath(element,
                                   "child::div[@class='event__time']")
     if time_el is not None:
         time_txt = time_el.text
         if time_txt:
             hour_txt, minute_txt = time_txt.split(":")
             return datetime.time(hour=int(hour_txt),
                                  minute=int(minute_txt),
                                  second=0)
コード例 #8
0
def _make_current_date(root_elem):
    i_el = co.find_first_xpath(root_elem,
                               "//div[@class='icon icon--calendar']")
    if i_el is not None:
        date_txt = i_el.tail
        if date_txt is not None:
            date_txt = date_txt.strip()
            if len(date_txt) >= 5:
                day_txt, month_txt = date_txt[:5].split(r"/")
                return datetime.date(
                    year=datetime.date.today().year,
                    month=int(month_txt),
                    day=int(day_txt),
                )
コード例 #9
0
    def fill_details():
        status_el = co.find_first_xpath(
            entry_elem, "child::div[starts-with(@class, 'status___')]"
        )
        if status_el is not None:
            detstatus_el = co.find_first_xpath(
                status_el, "child::span[starts-with(@class, 'detailStatus___')]"
            )
            if detstatus_el is not None:
                text = detstatus_el.text
                if text:
                    if "Finished" in text:
                        result.finished = True
                    elif "Set " in text:
                        result.setnum = int(text.strip()[4])

            scr_el = co.find_first_xpath(
                status_el, "child::span[starts-with(@class, 'detailScoreServe___')]"
            )
            if scr_el is not None:
                text = scr_el.text
                if text and ":" in text:
                    fst, snd = text.strip().split(":")
                    result.inset = int(fst), int(snd)
コード例 #10
0
 def get_name_cou(is_left):
     inclass_txt = "event__participant--{}".format(
         "home" if is_left else "away")
     xpath_txt = "child::div[contains(@class, '{}')]".format(inclass_txt)
     try:
         plr_el = co.find_first_xpath(element, xpath_txt)
         if plr_el is None:
             raise co.TennisParseError("nofound plr_el in \n{}".format(
                 etree.tostring(element, encoding="utf8")))
         txt = co.to_ascii(plr_el.text).strip()
         bracket_idx = txt.index("(")
         name = txt[0:bracket_idx].strip()
         cou = txt[bracket_idx + 1:bracket_idx + 4].strip().upper()
         return name, cou
     except ValueError:
         # maybe it is team's result div without '(cou)':
         tourinfo_cache.edit_skip(live_event.tour_info)
コード例 #11
0
def build_root(url, fsdriver, verbose: bool = False):
    wdriver.load_url(fsdriver.drv, url)
    fsdriver.implicitly_wait(3)
    page = fsdriver.page()
    if page is None:
        if verbose:
            print(f"not fetched summary page in {url}")
        return None
    parser = lxml.html.HTMLParser(encoding="utf8")
    tree = lxml.html.document_fromstring(page, parser)  # lxml.html.HtmlElement
    if tree is None:
        if verbose:
            print(f"fail build root url:{url}")
        return None
    content_el = co.find_first_xpath(tree, '//*[@id="detail"]')
    if content_el is None:
        if verbose:
            print(f"fail tennis/detail url:{url}")
        return None
    return content_el
コード例 #12
0
    def current_status(
    ) -> Tuple[Optional[MatchStatus], Optional[MatchSubStatus]]:
        status, sub_status = None, None
        cls_text = element.get("class")
        if "event__match--scheduled" in cls_text:
            status = MatchStatus.scheduled
        elif "event__match--live" in cls_text:
            status = MatchStatus.live
        stage_el = co.find_first_xpath(
            element, "child::div[contains(@class,'event__stage')]")
        if stage_el is not None:
            text = stage_el.text_content().strip().lower()
            if "finished" in text:
                status = MatchStatus.finished

            if "retired" in text or "walkover" in text:
                sub_status = MatchSubStatus.retired
            elif "interrupted" in text:
                sub_status = MatchSubStatus.interrupted
            elif "cancelled" in text:
                sub_status = MatchSubStatus.cancelled
        return status, sub_status
コード例 #13
0
 def is_live_active():
     qtxt = 'descendant::div[@class="tabs__tab selected" and text() = "Live odds"]'
     return co.find_first_xpath(root, qtxt) is not None
コード例 #14
0
 def is_serve(serve_elem):
     elem = co.find_first_xpath(
         serve_elem, "child::div[contains(@title, 'Serving player')]"
     )
     return elem is not None
コード例 #15
0
 def is_lost_serve(lostserve_elem):
     span_elem = co.find_first_xpath(
         lostserve_elem, "child::span[contains(text(), 'LOST SERVE')]"
     )
     return span_elem is not None
コード例 #16
0
def parse_match_detailed_score(match, fsdriver, verbose: bool = False):
    """return True if ok, False if logged error. type(match) == LiveMatch"""
    if not match.href or match.score is None or match.score.retired:
        return
    sets_count = match.score.sets_count()
    best_of_five = match.best_of_five
    det_score = DetailedScore()
    try:
        for setnum in range(1, sets_count + 1):
            det_set_el = build_root(
                match.pointbypoint_href(setnum), fsdriver, verbose=verbose
            )
            fsdriver.implicitly_wait(random.randint(4, 7))
            entry_elem = co.find_first_xpath(
                det_set_el, "descendant::div[contains(@class,'matchHistoryWrapper')]"
            )
            if entry_elem is None:
                if verbose:
                    print(f"not found matchHistoryWrapper in set={setnum} {match}")
                return False

            set_scr = match.score[setnum - 1]
            is_decided_set = setnum == (5 if best_of_five else 3)
            tie_inf = get_tie_info(match, is_decided_set)
            is_tie = tie_inf.beg_scr is not None and set_scr in (
                (tie_inf.beg_scr[0] + 1, tie_inf.beg_scr[1]),
                (tie_inf.beg_scr[0], tie_inf.beg_scr[1] + 1),
            )
            mhistos = list(
                entry_elem.xpath("child::div[contains(@class, 'matchHistory')]")
            )
            fifteens = list(entry_elem.xpath("child::div[contains(@class, 'fifteen')]"))
            n_usial_games = sum(set_scr) - 1 if is_tie else sum(set_scr)
            for game_idx in range(min(n_usial_games, len(fifteens))):
                mhisto = mhistos[game_idx]
                fifteen = fifteens[game_idx]
                dg_res = _parse_out_result(mhisto)
                game_winside = dg_res.winner()
                if game_winside.is_left():
                    src_score = (dg_res.x - 1, dg_res.y)
                else:
                    src_score = (dg_res.x, dg_res.y - 1)
                point_scores_txt = _parse_point_scores(fifteen)
                nkey = _make_next_key(det_score, setnum, src_score)
                det_game = make_detailed_game(
                    dg_res.srv_side,
                    game_winside,
                    make_num_pairs(point_scores_txt),
                    tiebreak=False,
                    is_super_tie=False,
                )
                det_score[nkey] = copy.copy(det_game)

            if is_tie:
                mhisto = mhistos[n_usial_games]
                x_sup, y_sup = _parse_detgame_tiesup_score(mhisto)
                at_xy = (n_usial_games // 2, n_usial_games // 2)
                nkey = _make_next_key(det_score, setnum, (min(at_xy), min(at_xy)))
                det_game = _parse_tie_scores(
                    mhistos[n_usial_games + 1 :],
                    setnum,
                    (x_sup, y_sup),
                    tie_inf.is_super,
                )
                det_score[nkey] = copy.copy(det_game)
    except co.TennisScoreError as err:
        if verbose:
            print("{} in {}".format(err, match))
        return False
    if len(det_score) >= 12:
        match.detailed_score = det_score
        return True
コード例 #17
0
def parse_title_score(summary_href, fsdriver, verbose: bool = False) -> TitleScore:
    """return TitleScore or None."""

    def is_left_serve():
        xpath_txt = (
            "child::div[starts-with(@class, 'home___')]/"
            "div[starts-with(@class, 'participantServe___')]/"
            "div[@title='Serving player']"
        )
        return co.find_first_xpath(entry_elem0, xpath_txt) is not None

    def is_right_serve():
        xpath_txt = (
            "child::div[starts-with(@class, 'away___')]/"
            "div[starts-with(@class, 'participantServe___')]/"
            "div[@title='Serving player']"
        )
        return co.find_first_xpath(entry_elem0, xpath_txt) is not None

    def fill_inmatch():
        xpath_txt = "child::div[starts-with(@class, 'wrapper___')]"
        inmatch_el = co.find_first_xpath(entry_elem, xpath_txt)
        if inmatch_el is not None:
            txt = inmatch_el.text_content().strip()
            if txt:
                fst, snd = txt.split("-")
                if fst and snd:
                    result.inmatch = int(fst), int(snd)

    def fill_details():
        status_el = co.find_first_xpath(
            entry_elem, "child::div[starts-with(@class, 'status___')]"
        )
        if status_el is not None:
            detstatus_el = co.find_first_xpath(
                status_el, "child::span[starts-with(@class, 'detailStatus___')]"
            )
            if detstatus_el is not None:
                text = detstatus_el.text
                if text:
                    if "Finished" in text:
                        result.finished = True
                    elif "Set " in text:
                        result.setnum = int(text.strip()[4])

            scr_el = co.find_first_xpath(
                status_el, "child::span[starts-with(@class, 'detailScoreServe___')]"
            )
            if scr_el is not None:
                text = scr_el.text
                if text and ":" in text:
                    fst, snd = text.strip().split(":")
                    result.inset = int(fst), int(snd)

    result = TitleScore()
    root = build_root(summary_href, fsdriver, verbose=verbose)
    if root is None:
        if verbose:
            print(f"root build failed, title score in {summary_href}")
        return result
    entry_elem0 = co.find_first_xpath(
        root, "child::div[starts-with(@class, 'wrapper___')]"
    )
    if entry_elem0 is None:
        if verbose:
            print(f"not found entry0 title score in {summary_href}")
        return result
    if is_left_serve():
        result.is_left_srv = True
    elif is_right_serve():
        result.is_left_srv = False
    path = (
        "child::div[starts-with(@class, 'score___')]/"
        "div[starts-with(@class, 'matchInfo___')]"
    )
    entry_elem = co.find_first_xpath(entry_elem0, path)
    if entry_elem is None:
        if verbose:
            print(f"not found entry title score in {summary_href}")
        return result
    fill_inmatch()
    fill_details()
    return result