<commit_before>#!/usr/bin/env python
# Copyright 2013 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Frontend for the API server."""

import functools
import logging

# Local libraries
import flask
from flask import Flask, abort, g, redirect, render_template, request, url_for
from flask.ext.login import (
    current_user, fresh_login_required, login_fresh, login_required)
from flask.ext.wtf import Form
import sqlalchemy

# Local modules
from . import app
from . import db
from . import login
from dpxdt.server import auth
from dpxdt.server import forms
from dpxdt.server import models


def _render_template_with_defaults(template_path, **context):
    """Renders the template with some extra context"""
    context.setdefault('current_user', current_user)
    return render_template(template_path, **context)


@app.route('/')
def homepage():
    """Renders the homepage."""
    build_list = (
        models.Build.query
        .filter_by(public=True)
        .order_by(models.Build.created.desc())
        .limit(1000)
        .all())

    if current_user.is_authenticated():
        if not login_fresh():
            logging.debug('User needs a fresh token')
            abort(login.needs_refresh())

        auth.claim_invitations(current_user)

        # List builds you own first, followed by public ones.
        # TODO: Cache this list
        db.session.add(current_user)
        build_list = (
            current_user.builds
            .order_by(models.Build.created.desc())
            .limit(1000)
            .all()) + build_list

    return _render_template_with_defaults(
        'home.html',
        build_list=build_list)


@app.route('/new', methods=['GET', 'POST'])
@fresh_login_required
def new_build():
    """Page for crediting or editing a build."""
    form = forms.BuildForm()
    if form.validate_on_submit():
        build = models.Build()
        form.populate_obj(build)
        build.owners.append(current_user)
        db.session.add(build)
        db.session.commit()

        logging.info('Created build via UI: build_id=%r, name=%r',
                     build.id, build.name)
        return redirect(url_for('view_build', id=build.id))

    return _render_template_with_defaults(
        'new_build.html',
        build_form=form)


@app.route('/build')
@auth.build_access_required
def view_build():
    """Page for viewing all releases in a build."""
    build = g.build
    page_size = 20
    offset = request.args.get('offset', 0, type=int)
    candidate_list = (
        models.Release.query
        .filter_by(build_id=build.id)
        .order_by(models.Release.created.desc())
        .offset(offset)
        .limit(page_size + 1)
        .all())

    has_next_page = len(candidate_list) > page_size
    if has_next_page:
        candidate_list = candidate_list[:-1]

    # Collate by release name, order releases by latest creation. Init stats.
    release_dict = {}
    created_dict = {}
    run_stats_dict = {}
    for candidate in candidate_list:
        release_list = release_dict.setdefault(candidate.name, [])
        release_list.append(candidate)

        max_created = created_dict.get(candidate.name, candidate.created)

        created_dict[candidate.name] = max(candidate.created, max_created)

        run_stats_dict[candidate.id] = dict(
            runs_total=0,
            runs_complete=0,
            runs_successful=0,
            runs_failed=0,
            runs_baseline=0)

    # Sort each release by candidate number descending
    for release_list in release_dict.itervalues():
        release_list.sort(key=lambda x: x.number, reverse=True)

    # Sort all releases by created time descending
    release_age_list = [
        (value, key) for key, value in created_dict.iteritems()]
    release_age_list.sort(reverse=True)
    release_name_list = [key for _, key in release_age_list]

    # Extract run metadata about each release
    stats_counts = (
        db.session.query(
            models.Run.release_id,
            models.Run.status,
            sqlalchemy.func.count(models.Run.id))
        .join(models.Release)
        .filter(models.Release.id.in_(run_stats_dict.keys()))
        .group_by(models.Run.status, models.Run.release_id)
        .all())

    for candidate_id, status, count in stats_counts:
        stats_dict = run_stats_dict[candidate_id]

        if status in (models.Run.DIFF_APPROVED, models.Run.DIFF_NOT_FOUND):
            stats_dict['runs_successful'] += count
            stats_dict['runs_complete'] += count
            stats_dict['runs_total'] += count
        elif status == models.Run.DIFF_FOUND:
            stats_dict['runs_failed'] += count
            stats_dict['runs_complete'] += count
            stats_dict['runs_total'] += count
        elif status == models.Run.NO_DIFF_NEEDED:
            stats_dict['runs_baseline'] += count

    return _render_template_with_defaults(
        'view_build.html',
        build=build,
        release_name_list=release_name_list,
        release_dict=release_dict,
        run_stats_dict=run_stats_dict,
        has_next_page=has_next_page,
        current_offset=offset,
        next_offset=offset + page_size,
        last_offset=max(0, offset -  page_size))


def classify_runs(run_list):
    """Returns (total, complete, succesful, failed) for the given Runs."""
    total, successful, failed, baseline = 0, 0, 0, 0
    for run in run_list:
        if run.status in (models.Run.DIFF_APPROVED, models.Run.DIFF_NOT_FOUND):
            successful += 1
            total += 1
        elif run.status == models.Run.DIFF_FOUND:
            failed += 1<commit_msg>Yet another template fix (need a test)<commit_after>#!/usr/bin/env python
# Copyright 2013 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Frontend for the API server."""

import functools
import logging

# Local libraries
import flask
from flask import Flask, abort, g, redirect, render_template, request, url_for
from flask.ext.login import (
    current_user, fresh_login_required, login_fresh, login_required)
from flask.ext.wtf import Form
import sqlalchemy

# Local modules
from . import app
from . import db
from . import login
from dpxdt.server import auth
from dpxdt.server import forms
from dpxdt.server import models


def _render_template_with_defaults(template_path, **context):
    """Renders the template with some extra context"""
    context.setdefault('current_user', current_user)
    return render_template(template_path, **context)


@app.route('/')
def homepage():
    """Renders the homepage."""
    build_list = (
        models.Build.query
        .filter_by(public=True)
        .order_by(models.Build.created.desc())
        .limit(1000)
        .all())

    if current_user.is_authenticated():
        if not login_fresh():
            logging.debug('User needs a fresh token')
            abort(login.needs_refresh())

        auth.claim_invitations(current_user)

        # List builds you own first, followed by public ones.
        # TODO: Cache this list
        db.session.add(current_user)
        build_list = (
            current_user.builds
            .order_by(models.Build.created.desc())
            .limit(1000)
            .all()) + build_list

    return _render_template_with_defaults(
        'home.html',
        build_list=build_list)


@app.route('/new', methods=['GET', 'POST'])
@fresh_login_required
def new_build():
    """Page for crediting or editing a build."""
    form = forms.BuildForm()
    if form.validate_on_submit():
        build = models.Build()
        form.populate_obj(build)
        build.owners.append(current_user)
        db.session.add(build)
        db.session.commit()

        logging.info('Created build via UI: build_id=%r, name=%r',
                     build.id, build.name)
        return redirect(url_for('view_build', id=build.id))

    return _render_template_with_defaults(
        'new_build.html',
        build_form=form)


@app.route('/build')
@auth.build_access_required
def view_build():
    """Page for viewing all releases in a build."""
    build = g.build
    page_size = 20
    offset = request.args.get('offset', 0, type=int)
    candidate_list = (
        models.Release.query
        .filter_by(build_id=build.id)
        .order_by(models.Release.created.desc())
        .offset(offset)
        .limit(page_size + 1)
        .all())

    has_next_page = len(candidate_list) > page_size
    if has_next_page:
        candidate_list = candidate_list[:-1]

    # Collate by release name, order releases by latest creation. Init stats.
    release_dict = {}
    created_dict = {}
    run_stats_dict = {}
    for candidate in candidate_list:
        release_list = release_dict.setdefault(candidate.name, [])
        release_list.append(candidate)

        max_created = created_dict.get(candidate.name, candidate.created)

        created_dict[candidate.name] = max(candidate.created, max_created)

        run_stats_dict[candidate.id] = dict(
            runs_total=0,
            runs_complete=0,
            runs_successful=0,
            runs_failed=0,
            runs_baseline=0)

    # Sort each release by candidate number descending
    for release_list in release_dict.itervalues():
        release_list.sort(key=lambda x: x.number, reverse=True)

    # Sort all releases by created time descending
    release_age_list = [
        (value, key) for key, value in created_dict.iteritems()]
    release_age_list.sort(reverse=True)
    release_name_list = [key for _, key in release_age_list]

    # Extract run metadata about each release
    stats_counts = (
        db.session.query(
            models.Run.release_id,
            models.Run.status,
            sqlalchemy.func.count(models.Run.id))
        .join(models.Release)
        .filter(models.Release.id.in_(run_stats_dict.keys()))
        .group_by(models.Run.status, models.Run.release_id)
        .all())

    for candidate_id, status, count in stats_counts:
        stats_dict = run_stats_dict[candidate_id]

        if status in (models.Run.DIFF_APPROVED, models.Run.DIFF_NOT_FOUND):
            stats_dict['runs_successful'] += count
            stats_dict['runs_complete'] += count
            stats_dict['runs_total'] += count
        elif status == models.Run.DIFF_FOUND:
            stats_dict['runs_failed'] += count
            stats_dict['runs_complete'] += count
            stats_dict['runs_total'] += count
        elif status == models.Run.NO_DIFF_NEEDED:
            stats_dict['runs_baseline'] += count
        elif status == models.Run.NEEDS_DIFF:
            stats_dict['runs_total'] += count

    return _render_template_with_defaults(
        'view_build.html',
        build=build,
        release_name_list=release_name_list,
        release_dict=release_dict,
        run_stats_dict=run_stats_dict,
        has_next_page=has_next_page,
        current_offset=offset,
        next_offset=offset + page_size,
        last_offset=max(0, offset -  page_size))


def classify_runs(run_list):
    """Returns (total, complete, succesful, failed) for the given Runs."""
    total, successful, failed, baseline = 0, 0, 0, 0
    for run in run_list:
        if run.status in (models.Run.DIFF_APPROVED, models.Run.DIFF_NOT_FOUND):
            successful += 1
            total += 1
        elif run.status == models.Run.DIFF_FOUND:
            failed += 1
{'fraction_non_alphanumeric': 0.10109090909090909, 'fraction_numerical': 0.005494949494949495, 'mean_word_length': 2.7939914163090127, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '84444', 'n_tokens_mistral': 3759, 'n_tokens_neox': 3654, 'n_words': 908}