def build_discrepancy_reports(
        precinct: str, precinct_agreed_df: pd.DataFrame,
        precinct_disagreed_df: pd.DataFrame,
        precinct_marks_df: pd.DataFrame) -> dominate.document:
    version = utils.show_version()
    doc = dominate.document(title='Audit Engine version: ' + version)
    report_head(doc)
    with doc:
        with div(cls='container'):
            report_headline(version)
            div(a('< Back', href='#', onclick='window.history.back()'))
            h5(precinct)
            for contest in precinct_marks_df['contest'].unique().tolist():
                contest_disagreed_df = precinct_disagreed_df.loc[
                    precinct_disagreed_df['contest'] == contest]
                h6(contest)
                mount_discrepancy_table(contest, contest_disagreed_df,
                                        precinct_marks_df)
    return doc
def get_contest_row(contest, cmpcvr_details, style_df):
    cmpcvr_options = cmpcvr_details.get('options')
    contest_row = div(h6(u(contest)), cls='my-2')
    contest_df = style_df.loc[style_df['contest'] == contest]
    options = [
        o for o in contest_df['option'].tolist()
        if '#contest vote_for' not in o
    ]
    for option in options:
        option_row = div(div(option, cls='col'), cls='row')
        if option in cmpcvr_options:
            if cmpcvr_options[option].get('selected'):
                option_row.attributes['class'] += ' font-weight-bold'
            if cmpcvr_options[option].get('PMV'):
                option_row += div(f"PMV: {cmpcvr_options[option].get('PMV')}",
                                  cls='col')
        contest_row += option_row
    return contest_row
Exemple #3
0
    def get_option_details(option) -> div:
        """Return 'div' element with info about 'option' instance.
        :param option: Option instance from which 'div' tag should
            be build.
        """
        coordinates = f'{option.coordinates[0]}px, {option.coordinates[1]}px'
        option_attributes = [
            ('name from OCR', option.name),
            ('name from fuzzy matching', option.fuzzy_name),
            ('Position (left, top)', coordinates),
        ]
        title = option.fuzzy_name or option.name
        option_container = div(id=title, cls='ml-1 mb-2')
        option_container.add(h6(title, cls='ml-2 mb-0'))
        option_div = div(cls='col pl-4')
        for option_key, option_value in option_attributes:
            option_div.add(
                StyleSummary.get_details_row(option_key, str(option_value)))
        option_container.add(option_div)

        return option_container
Exemple #4
0
 def visit_Text(self, node):
     if not self._in_dropdown:
         return tags.span(node.text, _class='navbar-text')
     return tags.h6(node.text, _class='dropdown-header')
Exemple #5
0
 def visit_Text(self, node):
     if not self._in_dropdown:
         return tags.span(node.text, _class='navbar-text')
     return tags.h6(node.text, _class='dropdown-header')
def create_html_string(COUNTERS, BALLOTLISTS, DISAGREED_INFO_DICT):
    """Creates a HTML string for generating the summary file.
    
    Accesses the following:
        COUNTERS['ballots_processed']
        COUNTERS['styles_detected']
        COUNTERS['matched_ballots']
        COUNTERS['non_matched_ballots']
        COUNTERS['blank_ballots']
        list of ballot OVERVOTED_BALLOTS
        list of ballot DISAGREED_BALLOTS
            accesses ballot pdf files per precinct and ballot_id
        DISAGREE_INFO_DICT is keyed by ballot_id which provides dict of contests providing error information
                f"{config_dict['RESOURCES_PATH']}{config_dict['DISAGREEMENTS_PATHFRAG']}{ballot.ballotdict['precinct']}/{ballot.ballotdict['ballot_id']}.pdf")

        list STYLES
            style.style_num
            style.number
            style.build_from_count
        files style_summary = glob.glob(f"{config_dict['RESOURCES_PATH']}{config_dict['STYLES_PATHFRAG']}{style.code}.html")[0]
        list VOTES_RESULTS   (results for each contest)
            result_contest['contest_name']
            result_contest['selections']
            result_contest['vote_for']
            result_contest['question']
            result_contest['total_ballots']
            result_contest['total_votes']
            result_contest['undervote']
            result_contest['overvote']
    """
    script_abs_path = os.path.abspath('assets/copy_to_clipboard.js')
    version = utils.show_version()
    doc = dominate.document(title='Audit Engine version: ' + version)
    with doc.head:
        link(
            rel='stylesheet',
            href=
            'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css',
            integrity=
            "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T",
            crossorigin="anonymous",
        )
        script(type='text/javascript', src=script_abs_path)

    with doc:
        with div(cls='container'):
            with div(cls='jumbotron'):
                h1('Audit Engine: {version} - vote records summary'.format(
                    version=version))
                build_time = datetime.datetime.now(datetime.timezone.utc)
                p(f'Summary built at: {build_time.strftime("%Y-%m-%d %H:%M:%S")}',
                  cls='lead')
            with table(cls='table table-striped'):
                with tbody():
                    with tr():
                        th('Number of ballots processed')
                        td(COUNTERS['ballots_processed'])
                    with tr():
                        th('Number of different ballot types')
                        td(COUNTERS['styles_detected'])
                    with tr():
                        th('Number of ballots matching the CVR results')
                        td(COUNTERS['matched_ballots'])
                    with tr():
                        th('Number of ballots not matching the CVR results')
                        td(COUNTERS['non_matched_ballots'])
                    with tr():
                        th('Number of completely blank ballots')
                        td(COUNTERS['blank_ballots'])
                    with tr():
                        th('Number of overvotes')
                        td(COUNTERS['overvoted_ballots'])
                    with tr():
                        th('Number of disagreements')
                        td(COUNTERS['disagreed_ballots'])
            with div(cls='my-4'):
                h2('Styles')
            with table(cls='table table-striped'):
                with thead():
                    with tr():
                        th('Style code', scope="col")
                        th('Style number', scope="col")
                        th('Based on number of ballots', scope="col")
                        th('Built at', scope="col")
                with tbody():
                    for style in STYLES:
                        with tr():
                            utc_time = datetime.datetime.utcfromtimestamp(
                                style.timestamp)
                            style_summary = glob.glob(
                                f"{config_dict['RESOURCES_PATH']}{config_dict['STYLES_PATHFRAG']}{style.code}.html"
                            )[0]
                            td(
                                a(style.style_num,
                                  href=os.path.realpath(style_summary),
                                  target="_blank"))
                            td(style.number)
                            td(style.build_from_count)
                            td(f'{utc_time.strftime("%Y-%m-%d %H:%M:%S")}')
            # Tables with contests results:
            with div(cls='my-4'):
                h2('Contests results')
            for result_contest in VOTES_RESULTS:
                contest_name = result_contest['contest_name']
                selections = result_contest['selections']
                vote_for = result_contest['vote_for']
                question = result_contest['question']
                with div(cls='my-4'):
                    h5(f'Contest results "{contest_name}" (vote for {vote_for}):'
                       )
                    if question:
                        h6(f'Question "{question}"')
                with table(cls='table table-striped'):
                    with thead():
                        with tr():
                            th('#', scope="col")
                            th('Candidate', scope="col")
                            th('Votes', scope="col")
                            th('%', scope="col")
                    with tbody():
                        for index, candidate_name in enumerate(
                                sort_option_names(selections.keys())):
                            try:
                                total_votes = result_contest['total_votes']
                                percent = round(
                                    (selections[candidate_name] / total_votes)
                                    * 100, 2)
                            except ZeroDivisionError:
                                percent = 0.0
                            with tr():
                                th(index + 1, scope="row")
                                td(candidate_name)
                                td(candidate_name)
                                td(f'{percent}%')
                with table(cls='table table-striped'):
                    with tbody():
                        with tr():
                            th('Total number of ballots')
                            td(result_contest['total_ballots'])
                        with tr():
                            th('Number of votes')
                            td(result_contest['total_votes'])
                        with tr():
                            th('Number of undervotes')
                            td(result_contest['undervote'])
                        with tr():
                            th('Number of overvotes')
                            td(result_contest['overvote'])
            # Table with overvotes:
            with div(cls='my-4'):
                h2('Ballots with overvotes:')
            with table(cls='table table-striped'):
                with thead():
                    with tr():
                        th('#', scope="col")
                        th('Precinct / Contest name', scope="col")
                        th('Ballot file / Ballot and CVR status', scope="col")
                        th('Overvotes / Contest validation status',
                           scope="col")
                with tbody():
                    dirpath = DB.dirpath_from_dirname('overvotes')
                    for index, ballot_id in enumerate(
                            BALLOTLISTS['overvoted_ballots']):
                        filepathlist = glob.glob(
                            f"{dirpath}**/{ballot_id}i.pdf", recursive=True)
                        if not filepathlist: continue
                        filepath = filepathlist[0]
                        with tr():
                            th(index + 1, scope="row")
                            td('')
                            with td():
                                ballot_image_filepath = os.path.abspath(
                                    filepath)
                                a(ballot_id,
                                  href=ballot_image_filepath,
                                  target="_blank")
                            td('')


#                        overvotes_contests = list(
#                            filter(
#                                lambda x: (x.contest_ballot_status == STATUS_DICT['overvote']) or
#                                (x.contest_cvr_status == STATUS_DICT['overvote']), ballot.ballotdict['contests']))
#                        for contest in overvotes_contests:
#                            with tr():
#                                td()
#                                td(contest.contest_name)
#                                td(f"{contest.contest_ballot_status} / {contest.contest_cvr_status}")
#                                td(contest.contest_validation if contest.contest_validation is not None else '')
# Table with blank ballots:
            with div(cls='my-4'):
                h2('Blank Ballots:')
            with table(cls='table table-striped'):
                with thead():
                    with tr():
                        th('#', scope="col")
                        th('Precinct / Contest name', scope="col")
                        th('Ballot file / Ballot and CVR status', scope="col")
                        th('Overvotes / Contest validation status',
                           scope="col")
                with tbody():
                    dirpath = DB.dirpath_from_dirname('blank_ballots')
                    for index, ballot_id in enumerate(
                            BALLOTLISTS['blank_ballots']):
                        filepathlist = glob.glob(f"f{dirpath}{ballot_id}i.pdf",
                                                 recursive=True)
                        if not filepathlist:
                            continue
                        filepath = filepathlist[0]
                        with tr():
                            th(index + 1, scope="row")
                            td('')
                            with td():
                                ballot_image_filepath = os.path.abspath(
                                    filepath)
                                a(ballot_id,
                                  href=ballot_image_filepath,
                                  target="_blank")
                            td('')
            # Table with disagreements:
            with div(cls='my-4'):
                h2('Ballots with disagreements:')
            with table(cls='table table-striped'):
                with thead():
                    with tr():
                        th('#', scope="col")
                        th('Ballot file', scope="col")
                        th('Disagreement Details', scope="col")
                with tbody():
                    dirpath = DB.dirpath_from_dirname('disagreements')
                    for index, ballot_id in enumerate(
                            BALLOTLISTS['disagreed_ballots']):
                        filepathlist = glob.glob(
                            f"{dirpath}**/{ballot_id}i.pdf", recursive=True)
                        if not filepathlist: continue
                        filepath = filepathlist[0]
                        with tr():
                            th(index + 1, scope="row")
                            with td():
                                ballot_image_filepath = os.path.abspath(
                                    filepath)
                                a(ballot_id,
                                  href=ballot_image_filepath,
                                  target="_blank")
                            td(
                                raw(f"<pre>{DISAGREED_INFO_DICT[ballot_id]}</PRE>"
                                    ))
    return doc