T0nyX1ang/WHU-ScoreChecker

View on GitHub
scorechecker/net/analyze.py

Summary

Maintainability
A
0 mins
Test Coverage
"""
Analyze the page to extract important infomation.

Almost all of the functions are based on BeautifulSoup.
Although lxml library is not explicitly imported, it's needed.
Information extracted is: captcha_id, login_id, csrf_token, score_table.
"""

import bs4


def get_captcha_id(content):
    """
    Get the captcha ID from HTML page.

    The captcha ID is extracted by filtering non-displaying image tags.
    """
    print('Extracting captcha ID ...')
    b = bs4.BeautifulSoup(content, features='lxml')
    styles = b.style.get_text()
    # As the captcha image is exclusive, the result is ONLY one.
    search_string = [
        '#captcha-img1', '#captcha-img2', '#captcha-img3', '#captcha-img4',
        '#captcha-img'
    ]
    result = [b.select(val) for val in search_string if val not in styles]
    if len(result) != 1:
        raise ValueError('Multiple captcha images found, please retry.')
    captcha_id = result[0][0]['src']
    print('Captcha ID is set to:', captcha_id)
    return captcha_id


def get_login_id(content):
    """
    Get the login ID from HTML page.

    The login ID is extracted from [POST] form hyperlink.
    """
    print('Extracting login ID ...')
    b = bs4.BeautifulSoup(content, features='lxml')
    login_box = b.find(id='loginBox')
    login_id = login_box.form['action']
    print('Login ID is set to:', login_id)
    return login_id


def get_csrf_token(content):
    """
    Get the CSRF token from HTML page.

    The CSRF token is extracted when the user logs in successfully.
    """
    print('Extracting CSRF token ...')
    b = bs4.BeautifulSoup(content, features='lxml')
    csrf_prefetch = str(b.find(id='system'))
    start = csrf_prefetch.find('csrftoken=')
    if start == -1:
        raise ValueError('Unable to find a CSRF token in this page.')
    csrf_token = csrf_prefetch[start + 10:start + 46]
    print('CSRF token is set to:', csrf_token)
    return csrf_token


def _convert_to_float(score):
    """
    Convert a string ('score') to float.

    If the string is empty, return None.
    If the string is float-like, return its float.
    """
    if len(score) == 0:
        return None
    else:
        return float(score)


def get_score_table(content):
    """
    Get the score table from HTML page.

    The score table is a refined version of the raw score table.
    It contains a set of tuples, which makes it easy to calculate further.
    The items are: course_name (str), course_type (str), credit (float),
                   course_academy (str), study_type (str), year (int),
                   semester (int) and score (float).
    """
    print('Fetching score table ...')
    b = bs4.BeautifulSoup(content, features='lxml')
    score_table = set()
    raw_score_table = [
        item.get_text().replace('\n', '').replace('\r', '').replace(
            ' ', '').replace('\t', '') for item in b.find_all('td')
    ]
    if len(raw_score_table) % 12:  # score table is parsed wrong
        raise ValueError('The score table has missing or redundant items.')
    total_course = len(raw_score_table) // 12
    for i in range(0, total_course):
        score_table.add(
            (raw_score_table[i * 12], raw_score_table[i * 12 + 1],
             _convert_to_float(raw_score_table[i * 12 + 4]),
             raw_score_table[i * 12 + 6], raw_score_table[i * 12 + 7],
             int(raw_score_table[i * 12 + 8]),
             int(raw_score_table[i * 12 + 9]),
             _convert_to_float(raw_score_table[i * 12 + 10])))
    print('Score table has been fetched successfully ...')
    return score_table