<commit_before>from flask_login import login_required, current_user
from flask import render_template, request, redirect, url_for, abort

from ...main import main
from ... import data_api_client, flask_featureflags
from dmutils.apiclient import APIError, HTTPError
from dmutils.content_loader import YAMLLoader, ContentBuilder
from dmutils.presenters import Presenters

yaml_loader = YAMLLoader()
existing_service_options = [
    "app/existing_service_manifest.yml",
    "app/content/g6/",
    yaml_loader
]
new_service_options = [
    "app/new_service_manifest.yml",
    "app/content/g6/",
    yaml_loader
]
presenters = Presenters()

@main.route('/services')
@login_required
def list_services():
    template_data = main.config['BASE_TEMPLATE_DATA']
    suppliers_services = data_api_client.find_services(
        supplier_id=current_user.supplier_id
    )

    return render_template(
        "services/list_services.html",
        services=suppliers_services["services"],
        updated_service_id=request.args.get('updated_service_id'),
        updated_service_name=request.args.get('updated_service_name'),
        updated_service_status=request.args.get('updated_service_status'),
        **template_data), 200

@main.route('/services/<string:service_id>', methods=['GET'])
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def edit_service(service_id):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    return _update_service_status(service)

# Might have to change the route if we're generalizing this to update
@main.route('/services/<string:service_id>', methods=['POST'])
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def update_service_status(service_id):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    if not _is_service_modifiable(service):
        return _update_service_status(
            service,
            "Sorry, but this service isn't modifiable."
        )

    # Value should be either public or private
    status = request.form.get('status', '').lower()

    translate_frontend_to_api = {
        'public': 'published',
        'private': 'enabled'
    }

    if status in translate_frontend_to_api.keys():
        status = translate_frontend_to_api[status]
    else:
        return _update_service_status(
            service,
            "Sorry, but '{}' is not a valid status.".format(status)
        )

    try:
        updated_service = data_api_client.update_service_status(
            service.get('id'), status,
            current_user.email_address, "Status changed to '{0}'".format(
                status))

    except APIError:

        return _update_service_status(
            service,
            "Sorry, there's been a problem updating the status."
        )

    updated_service = updated_service.get("services")
    return redirect(
        url_for(".list_services",
                updated_service_id=updated_service.get("id"),
                updated_service_name=updated_service.get("serviceName"),
                updated_service_status=updated_service.get("status")
                )
    )

@main.route(
    '/services/<string:service_id>/edit/<string:section>',
    methods=['GET']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def edit_section(service_id, section):

    service = data_api_client.get_service(service_id)['services']

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    return render_template(
        "services/edit_section.html",
        section=content.get_section(section),
        service_data=service,
        service_id=service_id,
        post_to=".update_section",
        return_to=".edit_service",
        **main.config['BASE_TEMPLATE_DATA']
    )

@main.route(
    '/services/<string:service_id>/edit/<string:section>',
    methods=['POST']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def update_section(service_id, section):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    posted_data = dict(
        list(request.form.items()) + list(request.files.items())
    )

    posted_data.pop('csrf_token', None)

    # Turn responses which have multiple parts into lists
    for key in request.form:
        item_as_list = request.form.getlist(key)
        list_types = ['list', 'checkboxes', 'pricing']
        if (
            key != 'csrf_token' and
            content.get_question(key)['type'] in list_types
        ):
            posted_data[key] = item_as_list

    if posted_data:
        try:
            data_api_client.update_service(
                service_id,
                posted_data,
                "user",
                "supplier app")
        except HTTPError as e:
            return render_template(
                "services/edit_section.html",
                section=content.get_section(section),
                service_data=service_data,
                service_id=service_id,
                error=e.message,
                **main.config['BASE_TEMPLATE_DATA']
            )

    return redirect(url_for(".edit_service", service_id=service_id))

@main.route('/submission/services/<string:service_id>', methods=['GET'])
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def view_service_submission(service_id):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*new_service_options)
    content.filter(service)

    return render_template(
        "services/service_submission.html",
        service_id=service_id,
        service_data=presenters.present_all(service, content),
        sections=content.sections,
        **main.config['BASE_TEMPLATE_DATA']), 200

@main.route(
    '/submission/services/<string:service_id>/edit/<string:section>',
    methods=['GET']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def edit_service_submission(service_id, section):

    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*new_service_options)
    content.filter(service)

    return render_template(
        "services/edit_section.html",
        section=content.get_section(section),
        service_data=service,
        service_id=service_id,
        post_to=".update_section_submission",
        return_to=".view_service_submission",
        **main.config['BASE_TEMPLATE_DATA']
    )

@main.route(
    '/submission/services/<string:service_id>/edit/<string:section>',
    methods=['POST']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def update_section_submission(service_id, section):

    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*new_service_options)
    content.filter(service)

    if "success":
        return redirect(
            url_for(".view_service_submission", service_id=service_id)
        )
    else:
        return render_template(
            "services/edit_section.html",
            section=content.get_section_filtered_by(section, service),
            service_data=service_data,
            service_id=service_id,
            error=e.message,
            **main.config['BASE_TEMPLATE_DATA']
        )

def _is_service_associated_with_supplier(service):

    return service.get('supplierId') == current_user.supplier_id

def _is_service_modifiable(service):

    return service.get('status') != 'disabled'

def _update_service_status(service, error_message=None):

    template_data = main.config['BASE_TEMPLATE_DATA']
    status_code = 400 if error_message else 200

    question = {
        'question': 'Choose service status',
        'hint': 'Private services don\'t appear in search results '
                'and don\'t have a URL',
        'name': 'status',
        'type': 'radio',
        'inline': True,
        'options': [
            {
                'checked': service['status'] == 'published',
                'label': 'Public'
            },
            {
                'checked': service['status'] == 'enabled',
                'label': 'Private'
            }
        ]
    }

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    return render_template(
        "services/service.html",
        service_id=service.get('id'),
        service_data=presenters.present_all(service, content),
        sections=content.sections,
        error=error_message,
        **dict(question, **template_data)), status_code
<commit_msg>New content builder for status updates<commit_after>from flask_login import login_required, current_user
from flask import render_template, request, redirect, url_for, abort

from ...main import main
from ... import data_api_client, flask_featureflags
from dmutils.apiclient import APIError, HTTPError
from dmutils.content_loader import YAMLLoader, ContentBuilder
from dmutils.presenters import Presenters

yaml_loader = YAMLLoader()
existing_service_options = [
    "app/existing_service_manifest.yml",
    "app/content/g6/",
    yaml_loader
]
new_service_options = [
    "app/new_service_manifest.yml",
    "app/content/g6/",
    yaml_loader
]
presenters = Presenters()

@main.route('/services')
@login_required
def list_services():
    template_data = main.config['BASE_TEMPLATE_DATA']
    suppliers_services = data_api_client.find_services(
        supplier_id=current_user.supplier_id
    )

    return render_template(
        "services/list_services.html",
        services=suppliers_services["services"],
        updated_service_id=request.args.get('updated_service_id'),
        updated_service_name=request.args.get('updated_service_name'),
        updated_service_status=request.args.get('updated_service_status'),
        **template_data), 200

@main.route('/services/<string:service_id>', methods=['GET'])
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def edit_service(service_id):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    return _update_service_status(service)

# Might have to change the route if we're generalizing this to update
@main.route('/services/<string:service_id>', methods=['POST'])
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def update_service_status(service_id):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    if not _is_service_modifiable(service):
        return _update_service_status(
            service,
            "Sorry, but this service isn't modifiable."
        )

    # Value should be either public or private
    status = request.form.get('status', '').lower()

    translate_frontend_to_api = {
        'public': 'published',
        'private': 'enabled'
    }

    if status in translate_frontend_to_api.keys():
        status = translate_frontend_to_api[status]
    else:
        return _update_service_status(
            service,
            "Sorry, but '{}' is not a valid status.".format(status)
        )

    try:
        updated_service = data_api_client.update_service_status(
            service.get('id'), status,
            current_user.email_address, "Status changed to '{0}'".format(
                status))

    except APIError:

        return _update_service_status(
            service,
            "Sorry, there's been a problem updating the status."
        )

    updated_service = updated_service.get("services")
    return redirect(
        url_for(".list_services",
                updated_service_id=updated_service.get("id"),
                updated_service_name=updated_service.get("serviceName"),
                updated_service_status=updated_service.get("status")
                )
    )

@main.route(
    '/services/<string:service_id>/edit/<string:section>',
    methods=['GET']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def edit_section(service_id, section):

    service = data_api_client.get_service(service_id)['services']

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    return render_template(
        "services/edit_section.html",
        section=content.get_section(section),
        service_data=service,
        service_id=service_id,
        post_to=".update_section",
        return_to=".edit_service",
        **main.config['BASE_TEMPLATE_DATA']
    )

@main.route(
    '/services/<string:service_id>/edit/<string:section>',
    methods=['POST']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def update_section(service_id, section):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    posted_data = dict(
        list(request.form.items()) + list(request.files.items())
    )

    posted_data.pop('csrf_token', None)

    # Turn responses which have multiple parts into lists
    for key in request.form:
        item_as_list = request.form.getlist(key)
        list_types = ['list', 'checkboxes', 'pricing']
        if (
            key != 'csrf_token' and
            content.get_question(key)['type'] in list_types
        ):
            posted_data[key] = item_as_list

    if posted_data:
        try:
            data_api_client.update_service(
                service_id,
                posted_data,
                "user",
                "supplier app")
        except HTTPError as e:
            return render_template(
                "services/edit_section.html",
                section=content.get_section(section),
                service_data=service_data,
                service_id=service_id,
                error=e.message,
                **main.config['BASE_TEMPLATE_DATA']
            )

    return redirect(url_for(".edit_service", service_id=service_id))

@main.route('/submission/services/<string:service_id>', methods=['GET'])
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def view_service_submission(service_id):
    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*new_service_options)
    content.filter(service)

    return render_template(
        "services/service_submission.html",
        service_id=service_id,
        service_data=presenters.present_all(service, content),
        sections=content.sections,
        **main.config['BASE_TEMPLATE_DATA']), 200

@main.route(
    '/submission/services/<string:service_id>/edit/<string:section>',
    methods=['GET']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def edit_service_submission(service_id, section):

    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*new_service_options)
    content.filter(service)

    return render_template(
        "services/edit_section.html",
        section=content.get_section(section),
        service_data=service,
        service_id=service_id,
        post_to=".update_section_submission",
        return_to=".view_service_submission",
        **main.config['BASE_TEMPLATE_DATA']
    )

@main.route(
    '/submission/services/<string:service_id>/edit/<string:section>',
    methods=['POST']
)
@login_required
@flask_featureflags.is_active_feature('EDIT_SERVICE_PAGE')
def update_section_submission(service_id, section):

    service = data_api_client.get_service(service_id).get('services')

    if not _is_service_associated_with_supplier(service):
        abort(404)

    content = ContentBuilder(*new_service_options)
    content.filter(service)

    if "success":
        return redirect(
            url_for(".view_service_submission", service_id=service_id)
        )
    else:
        return render_template(
            "services/edit_section.html",
            section=content.get_section_filtered_by(section, service),
            service_data=service_data,
            service_id=service_id,
            error=e.message,
            **main.config['BASE_TEMPLATE_DATA']
        )

def _is_service_associated_with_supplier(service):

    return service.get('supplierId') == current_user.supplier_id

def _is_service_modifiable(service):

    return service.get('status') != 'disabled'

def _update_service_status(service, error_message=None):

    template_data = main.config['BASE_TEMPLATE_DATA']
    status_code = 400 if error_message else 200

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    question = {
        'question': 'Choose service status',
        'hint': 'Private services don\'t appear in search results '
                'and don\'t have a URL',
        'name': 'status',
        'type': 'radio',
        'inline': True,
        'options': [
            {
                'checked': service['status'] == 'published',
                'label': 'Public'
            },
            {
                'checked': service['status'] == 'enabled',
                'label': 'Private'
            }
        ]
    }

    content = ContentBuilder(*existing_service_options)
    content.filter(service)

    return render_template(
        "services/service.html",
        service_id=service.get('id'),
        service_data=presenters.present_all(service, content),
        sections=content.sections,
        error=error_message,
        **dict(question, **template_data)), status_code
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Helper functions used in views.
"""

import csv
import urllib2

import time
import threading
import locale

from lxml import etree
from json import dumps
from functools import wraps
from datetime import datetime

from flask import Response

from presence_analyzer.main import app

import logging
log = logging.getLogger(__name__)  # pylint: disable-msg=C0103

CACHE = {}
TIMESTAMPS = {}

def jsonify(function):
    """
    Creates a response with the JSON representation of wrapped function result.
    """
    @wraps(function)
    def inner(*args, **kwargs):
        return Response(dumps(function(*args, **kwargs)),
                        mimetype='application/json')
    return inner

def memoize(key, validity_peroid):
    """
    Memorizing decorator. Returning cached data
    if its validity period is not expired
    """
    def _decorating_wrapper(function):
        @wraps(function)
        def _caching_wrapper(*args, **kwargs):
            cache_key = key
            now = time.time()

            if TIMESTAMPS.get(cache_key, now) > now:
                return CACHE[cache_key]

            result = function(*args, **kwargs)
            CACHE[cache_key] = result
            TIMESTAMPS[cache_key] = now + validity_peroid
            return result
        return _caching_wrapper
    return _decorating_wrapper

def locker(function):
    """
    Global thread locking decorator.
    """
    function.__lock__ = threading.Lock()

    @wraps(function)
    def _lock_wrapper(*args, **kwargs):
        with function.__lock__:
            result = function(*args, **kwargs)
        return result
    return _lock_wrapper

@locker
@memoize('get_data', 600)
def get_data():
    """
    Extracts presence data from CSV file and groups it by user_id.

    It creates structure like this:
    data = {
        'user_id': {
            datetime.date(2013, 10, 1): {
                'start': datetime.time(9, 0, 0),
                'end': datetime.time(17, 30, 0),
            },
            datetime.date(2013, 10, 2): {
                'start': datetime.time(8, 30, 0),
                'end': datetime.time(16, 45, 0),
            },
        }
    }
    """
    data = {}
    with open(app.config['DATA_CSV'], 'r') as csvfile:
        presence_reader = csv.reader(csvfile, delimiter=',')
        for i, row in enumerate(presence_reader):
            if len(row) != 4:
                # ignore header and footer lines
                continue

            try:
                user_id = int(row[0])
                date = datetime.strptime(row[1], '%Y-%m-%d').date()
                start = datetime.strptime(row[2], '%H:%M:%S').time()
                end = datetime.strptime(row[3], '%H:%M:%S').time()
            except (ValueError, TypeError):
                log.debug('Problem with line %d: ', i, exc_info=True)

            data.setdefault(user_id, {})[date] = {'start': start, 'end': end}

    return data

def get_data_from_xml():
    """
    Extracts personal data and server adress from XML file
    """
    data = {}
    with open(app.config['DATA_XML'], 'r') as xmlfile:
        locale.setlocale(locale.LC_ALL, 'pl_PL.UTF-8')
        tree = etree.parse(xmlfile)
        root = tree.getroot()
        config = root[0]
        server = {
            u'host': unicode(config.findtext('host')),
            u'protocol': unicode(config.findtext('protocol')),
        }
        data['server'] = "%(protocol)s://%(host)s" % server
        users = root[1]
        data['users'] = [
            {
                u'id': int(user.attrib['id']),
                u'name': unicode(user.findtext('name')),
                u'avatar': unicode(user.findtext('avatar'))
            }
            for user in sorted(
                users,
                key=lambda element: unicode(element.findtext('name')),
                cmp=locale.strcoll,
            )
        ]

        return data

def get_xml():
    """
    Download xml user data file from server and
    save it  in runtime/data directory.
    """
    response = urllib2.urlopen(app.config['XML_URL'])
    with open(app.config['DATA_XML'], 'wb') as xmlfile:
        while True:
            chunk = response.read(16 * 1024)
            if not chunk:
                break
            xmlfile.write(chunk)

def group_by_weekday(items):
    """
    Groups presence entries by weekday.
    """
    result = {i: [] for i in range(7)}
    for date in items:
        start = items[date]['start']
        end = items[date]['end']
        result[date.weekday()].append(interval(start, end))
    return result

def group_by_weekday_start_end(items):
    """
    Groups presence entries by weekday start end.
    """
    result = {
        i: {
            'start': [],
            'end': [],
        }
        for i in range(7)
    }
    for date in items:
        start = items[date]['start']
        end = items[date]['end']
        result[date.weekday()]['start'].append(
            seconds_since_midnight(start)
        )
        result[date.weekday()]['end'].append(
            seconds_since_midnight(end)
        )
    return result

def seconds_since_midnight(time):
    """
    Calculates amount of seconds since midnight.
    """
    return time.hour * 3600 + time.minute * 60 + time.second

def interval(start, end):
    """
    Calculates inverval in seconds between two datetime.time objects.
    """
    return seconds_since_midnight(end) - seconds_since_midnight(start)

def mean(items):
    """
    Calculates arithmetic mean. Returns zero for empty lists.
    """
    return float(sum(items)) / len(items) if len(items) > 0 else 0
<commit_msg>sorting dropdown list by name ascending<commit_after># -*- coding: utf-8 -*-
"""
Helper functions used in views.
"""

import csv
import urllib2

import time
import threading
import locale

from lxml import etree
from json import dumps
from functools import wraps
from datetime import datetime

from flask import Response

from presence_analyzer.main import app

import logging
log = logging.getLogger(__name__)  # pylint: disable-msg=C0103

CACHE = {}
TIMESTAMPS = {}

def jsonify(function):
    """
    Creates a response with the JSON representation of wrapped function result.
    """
    @wraps(function)
    def inner(*args, **kwargs):
        return Response(dumps(function(*args, **kwargs)),
                        mimetype='application/json')
    return inner

def memoize(key, validity_peroid):
    """
    Memorizing decorator. Returning cached data
    if its validity period is not expired
    """
    def _decorating_wrapper(function):
        @wraps(function)
        def _caching_wrapper(*args, **kwargs):
            cache_key = key
            now = time.time()

            if TIMESTAMPS.get(cache_key, now) > now:
                return CACHE[cache_key]

            result = function(*args, **kwargs)
            CACHE[cache_key] = result
            TIMESTAMPS[cache_key] = now + validity_peroid
            return result
        return _caching_wrapper
    return _decorating_wrapper

def locker(function):
    """
    Global thread locking decorator.
    """
    function.__lock__ = threading.Lock()

    @wraps(function)
    def _lock_wrapper(*args, **kwargs):
        with function.__lock__:
            result = function(*args, **kwargs)
        return result
    return _lock_wrapper

@locker
@memoize('get_data', 600)
def get_data():
    """
    Extracts presence data from CSV file and groups it by user_id.

    It creates structure like this:
    data = {
        'user_id': {
            datetime.date(2013, 10, 1): {
                'start': datetime.time(9, 0, 0),
                'end': datetime.time(17, 30, 0),
            },
            datetime.date(2013, 10, 2): {
                'start': datetime.time(8, 30, 0),
                'end': datetime.time(16, 45, 0),
            },
        }
    }
    """
    data = {}
    with open(app.config['DATA_CSV'], 'r') as csvfile:
        presence_reader = csv.reader(csvfile, delimiter=',')
        for i, row in enumerate(presence_reader):
            if len(row) != 4:
                # ignore header and footer lines
                continue

            try:
                user_id = int(row[0])
                date = datetime.strptime(row[1], '%Y-%m-%d').date()
                start = datetime.strptime(row[2], '%H:%M:%S').time()
                end = datetime.strptime(row[3], '%H:%M:%S').time()
            except (ValueError, TypeError):
                log.debug('Problem with line %d: ', i, exc_info=True)

            data.setdefault(user_id, {})[date] = {'start': start, 'end': end}

    return data

def get_data_from_xml():
    """
    Extracts personal data and server adress from XML file
    """
    data = {}
    with open(app.config['DATA_XML'], 'r') as xmlfile:
        locale.setlocale(locale.LC_ALL, 'pl_PL.UTF-8')
        tree = etree.parse(xmlfile)
        root = tree.getroot()
        config = root[0]
        server = {
            u'host': unicode(config.findtext('host')),
            u'protocol': unicode(config.findtext('protocol')),
        }
        data['server'] = "%(protocol)s://%(host)s" % server
        users = root[1]
        data['users'] = [
            {
                u'id': int(user.attrib['id']),
                u'name': unicode(user.findtext('name')),
                u'avatar': unicode(user.findtext('avatar'))
            }
            for user in sorted(
                users,
                key=lambda element: unicode(element.findtext('name')),
                cmp=locale.strcoll,
            )
        ]
        locale.setlocale(locale.LC_ALL, (None, None))

        return data

def get_xml():
    """
    Download xml user data file from server and
    save it  in runtime/data directory.
    """
    response = urllib2.urlopen(app.config['XML_URL'])
    with open(app.config['DATA_XML'], 'wb') as xmlfile:
        while True:
            chunk = response.read(16 * 1024)
            if not chunk:
                break
            xmlfile.write(chunk)

def group_by_weekday(items):
    """
    Groups presence entries by weekday.
    """
    result = {i: [] for i in range(7)}
    for date in items:
        start = items[date]['start']
        end = items[date]['end']
        result[date.weekday()].append(interval(start, end))
    return result

def group_by_weekday_start_end(items):
    """
    Groups presence entries by weekday start end.
    """
    result = {
        i: {
            'start': [],
            'end': [],
        }
        for i in range(7)
    }
    for date in items:
        start = items[date]['start']
        end = items[date]['end']
        result[date.weekday()]['start'].append(
            seconds_since_midnight(start)
        )
        result[date.weekday()]['end'].append(
            seconds_since_midnight(end)
        )
    return result

def seconds_since_midnight(time):
    """
    Calculates amount of seconds since midnight.
    """
    return time.hour * 3600 + time.minute * 60 + time.second

def interval(start, end):
    """
    Calculates inverval in seconds between two datetime.time objects.
    """
    return seconds_since_midnight(end) - seconds_since_midnight(start)

def mean(items):
    """
    Calculates arithmetic mean. Returns zero for empty lists.
    """
    return float(sum(items)) / len(items) if len(items) > 0 else 0
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Defines views.
"""

# pylint: disable=import-error, no-name-in-module
import calendar
from flask import redirect
from flask.ext.mako import render_template, exceptions
from lxml import etree

from presence_analyzer.main import app
from presence_analyzer.utils import (
    jsonify,
    get_data,
    mean,
    group_by_weekday,
    group_start_end_times_by_weekday,
    median
)

import logging
log = logging.getLogger(__name__)  # pylint: disable=invalid-name

@app.errorhandler(404)
def page_not_found(error):
    """
    Shows "Error 404 page not found" message.
    """
    return render_template('error.html', error=error), 404

@app.route('/')
def mainpage():
    """
    Redirects to front page.
    """
    return redirect('/presence_weekday')

@app.route('/<chosen_template>')
def page_to_display(chosen_template):
    """
    Shows page with chosen option.
    """
    options = [
        ['presence_weekday', 'Presence by weekday'],
        ['mean_time_weekday', 'Presence mean time'],
        ['presence_start_end', 'Presence start-end'],
        ['median_weekday', 'Presence median time']
    ]
    try:
        return render_template(chosen_template+'.html', options=options)
    except exceptions.TemplateLookupException:
        return render_template('error.html', error='Page not found.'), 404

@app.route('/api/v1/users', methods=['GET'])
@jsonify
def users_view():
    """
    Users listing for dropdown.
    """
    data = get_data()
    return [
        {'user_id': i, 'name': 'User {0}'.format(str(i))}
        for i in data.keys()
    ]

@app.route('/api/v1/users_data', methods=['GET'])
@jsonify
def users_view_data():
    """
    Users listing for dropdown.
    """
    with open(app.config['USERS_XML_LOCAL_FILE'], 'r') as f_xml:
        tree = etree.parse(f_xml)    # pylint: disable=no-member
    data_server = tree.find('server')
    url_prefix = '{0}://{1}:{2}'.format(
        data_server.find('protocol').text,
        data_server.find('host').text,
        data_server.find('port').text
    )
    result = [
        {
            'user_id': person.get('id'),
            'name': person.findtext('name'),
            'avatar': '{0}{1}'.format(url_prefix, person.findtext('avatar')),
        }
        for person in tree.findall('./users/user')
    ]
    return result

@app.route('/api/v1/mean_time_weekday/<int:user_id>', methods=['GET'])
@jsonify
def mean_time_weekday_view(user_id):
    """
    Returns mean presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    weekdays = group_by_weekday(data[user_id])
    result = [
        (calendar.day_abbr[weekday], mean(intervals))
        for weekday, intervals in enumerate(weekdays)
    ]
    return result

@app.route('/api/v1/presence_weekday/<int:user_id>', methods=['GET'])
@jsonify
def presence_weekday_view(user_id):
    """
    Returns total presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    weekdays = group_by_weekday(data[user_id])
    result = [
        (calendar.day_abbr[weekday], sum(intervals))
        for weekday, intervals in enumerate(weekdays)
    ]

    result.insert(0, ('Weekday', 'Presence (s)'))
    return result

@app.route('/api/v1/presence_start_end/<int:user_id>', methods=['GET'])
@jsonify
def presence_start_end_view(user_id):
    """
    Returns mean start time and mean end time.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    week = group_start_end_times_by_weekday(data[user_id])

    result = []
    for day in week:
        starts = mean(week[day]['start'])
        ends = mean(week[day]['end'])
        result.append([calendar.day_abbr[day], starts, ends])

    return result

@app.route('/api/v1/median_weekday/<int:user_id>', methods=['GET'])
@jsonify
def median_weekday_view(user_id):
    """
    Returns mean presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    weekdays = group_by_weekday(data[user_id])
    result = [
        (calendar.day_abbr[weekday], median(intervals))
        for weekday, intervals in enumerate(weekdays)
    ]
    return result
<commit_msg>[bug_5833]  Sorting persons list fixed (polish characters).<commit_after># -*- coding: utf-8 -*-
"""
Defines views.
"""

# pylint: disable=import-error, no-name-in-module
import calendar
from flask import redirect
from flask.ext.mako import render_template, exceptions
from lxml import etree
import locale

from presence_analyzer.main import app
from presence_analyzer.utils import (
    jsonify,
    get_data,
    mean,
    group_by_weekday,
    group_start_end_times_by_weekday,
    median
)

import logging
log = logging.getLogger(__name__)  # pylint: disable=invalid-name

@app.errorhandler(404)
def page_not_found(error):
    """
    Shows "Error 404 page not found" message.
    """
    return render_template('error.html', error=error), 404

@app.route('/')
def mainpage():
    """
    Redirects to front page.
    """
    return redirect('/presence_weekday')

@app.route('/<chosen_template>')
def page_to_display(chosen_template):
    """
    Shows page with chosen option.
    """
    options = [
        ['presence_weekday', 'Presence by weekday'],
        ['mean_time_weekday', 'Presence mean time'],
        ['presence_start_end', 'Presence start-end'],
        ['median_weekday', 'Presence median time']
    ]
    try:
        return render_template(chosen_template+'.html', options=options)
    except exceptions.TemplateLookupException:
        return render_template('error.html', error='Page not found.'), 404

@app.route('/api/v1/users', methods=['GET'])
@jsonify
def users_view():
    """
    Users listing for dropdown.
    """
    data = get_data()
    return [
        {'user_id': i, 'name': 'User {0}'.format(str(i))}
        for i in data.keys()
    ]

@app.route('/api/v1/users_data', methods=['GET'])
@jsonify
def users_view_data():
    """
    Users listing for dropdown.
    """
    with open(app.config['USERS_XML_LOCAL_FILE'], 'r') as f_xml:
        tree = etree.parse(f_xml)    # pylint: disable=no-member
    data_server = tree.find('server')
    url_prefix = '{0}://{1}:{2}'.format(
        data_server.find('protocol').text,
        data_server.find('host').text,
        data_server.find('port').text
    )
    locale.setlocale(locale.LC_COLLATE, ('pl', 'utf-8'))
    not_sorted_list = [
        {
            'user_id': person.get('id'),
            'name': person.findtext('name'),
            'avatar': '{0}{1}'.format(url_prefix, person.findtext('avatar')),
        }
        for person in tree.findall('./users/user')
    ]
    result = sorted(
        not_sorted_list,
        key=lambda person: person['name'],
        cmp=locale.strcoll
    )
    return result

@app.route('/api/v1/mean_time_weekday/<int:user_id>', methods=['GET'])
@jsonify
def mean_time_weekday_view(user_id):
    """
    Returns mean presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    weekdays = group_by_weekday(data[user_id])
    result = [
        (calendar.day_abbr[weekday], mean(intervals))
        for weekday, intervals in enumerate(weekdays)
    ]
    return result

@app.route('/api/v1/presence_weekday/<int:user_id>', methods=['GET'])
@jsonify
def presence_weekday_view(user_id):
    """
    Returns total presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    weekdays = group_by_weekday(data[user_id])
    result = [
        (calendar.day_abbr[weekday], sum(intervals))
        for weekday, intervals in enumerate(weekdays)
    ]

    result.insert(0, ('Weekday', 'Presence (s)'))
    return result

@app.route('/api/v1/presence_start_end/<int:user_id>', methods=['GET'])
@jsonify
def presence_start_end_view(user_id):
    """
    Returns mean start time and mean end time.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    week = group_start_end_times_by_weekday(data[user_id])

    result = []
    for day in week:
        starts = mean(week[day]['start'])
        ends = mean(week[day]['end'])
        result.append([calendar.day_abbr[day], starts, ends])

    return result

@app.route('/api/v1/median_weekday/<int:user_id>', methods=['GET'])
@jsonify
def median_weekday_view(user_id):
    """
    Returns mean presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return 'no_data'

    weekdays = group_by_weekday(data[user_id])
    result = [
        (calendar.day_abbr[weekday], median(intervals))
        for weekday, intervals in enumerate(weekdays)
    ]
    return result
<|endoftext|>


<commit_before>#!/usr/bin/python

from random import randint
from Tkinter import *

taille=70 #la taille de la matrice
k=1 #On aura 1/(k+1) cellules en vie au depart
largeurCellule=8
temps=150 #intervalle de temps entre chaque rafraichissement

def mat(): #On genere la matrice
	matrice=[]
	for i in range(taille):
		ligne=[]
		for j in range(taille):
			ligne.append(randint(0,k)==0) #On tire au hasard entre 0 et k. Si ca vaut 0 c'est True, sinon c'est False
		matrice.append(ligne)
	return matrice

def evolution(m): #On evolue a la generation suivante
	matrice=[]
	for i in range(taille):
		ligne=[]
		for j in range(taille):
			etat=m[i][j] #etat de la cellule dans l'ex-matrice m
			nb=m[(i-1)%taille][(j-1)%taille]+m[(i-1)%taille][j]+m[(i-1)%taille][(j+1)%taille]+m[i][(j-1)%taille]+m[i][(j+1)%taille]+m[(i+1)%taille][(j-1)%taille]+m[(i+1)%taille][j]+m[(i+1)%taille][(j+1)%taille] #nb est le nombre de cellules voisines en vie
			if etat and nb in (2,3): k=True #les regles du jeu
			elif not etat and nb==3: k=True
			else: k=False
			ligne.append(k)
		matrice.append(ligne)
	return matrice

class fenetre(): #la fenetre avec le resultat
	def __init__(main):
		main.root=Tk()
		main.pas=1

		main.menu=Frame(main.root) #le menu
		main.btnNouveau=Button(main.menu,text="Nouvelle simulation",command=main.nouveau)
		main.btnPP=Button(main.menu,text="Lecture / Pause",command=main.pp)
		main.btnQuitter=Button(main.menu,text="Quitter",command=main.root.quit)

		main.can=Canvas(main.root, width=largeurCellule*taille, height=largeurCellule*taille, bg="#444334") #On cree la fenetre

		main.btnNouveau.pack(side="left") #on pack le tout
		main.btnPP.pack(side="left")
		main.btnQuitter.pack()
		main.menu.pack()
		main.can.pack()

		main.matrice=mat() #On cree la matrice
		main.rendu() #On demande le dessin des rectangles
		main.root.after(temps,main.animation)
		main.root.mainloop()

	def nouveau(main):
		main.matrice=mat() #On cree une matrice toute neuve

	def pp(main): #la fonction Play/Pause
		if main.pas==1:
			main.pas=0
		else:
			main.pas=1

	def animation(main): #on raffraichi l'apercu tous les pas
		for x in range(main.pas):
			main.matrice=evolution(main.matrice)
		main.rendu()
		main.root.after(temps,main.animation)

	def rendu(main): #On dessine les rectangles
		main.can.delete(ALL)
		x,y=0,0
		for i in main.matrice:
			for j in i:
				if j: #Si la cellule est a True, on cree un rectangle
					main.can.create_rectangle(x,y,x+largeurCellule,y+largeurCellule,fill="#CF9130")
				x+=largeurCellule
			y+=largeurCellule
			x=0

fenetre()
<commit_msg>update<commit_after>#!/usr/bin/python

from random import randint
from Tkinter import *

taille=70 #la taille de la matrice
k=1 #On aura 1/(k+1) cellules en vie au depart
largeurCellule=8
temps=150 #intervalle de temps entre chaque rafraichissement

def mat(): #On genere la matrice
	matrice=[]
	for i in range(taille):
		ligne=[]
		for j in range(taille):
			ligne.append(randint(0,k)==0) #On tire au hasard entre 0 et k. Si ca vaut 0 c'est True, sinon c'est False
		matrice.append(ligne)
	return matrice

def evolution(m): #On evolue a la generation suivante
	matrice=[]
	for i in range(taille):
		ligne=[]
		for j in range(taille):
			etat=m[i][j] #etat de la cellule dans l'ex-matrice m
			nb=m[(i-1)%taille][(j-1)%taille]+m[(i-1)%taille][j]+m[(i-1)%taille][(j+1)%taille]+m[i][(j-1)%taille]+m[i][(j+1)%taille]+m[(i+1)%taille][(j-1)%taille]+m[(i+1)%taille][j]+m[(i+1)%taille][(j+1)%taille] #nb est le nombre de cellules voisines en vie
			if etat and nb in (2,3): k=True #les regles du jeu
			elif not etat and nb==3: k=True
			else: k=False
			ligne.append(k)
		matrice.append(ligne)
	return matrice

class fenetre(): #la fenetre avec le resultat
	def __init__(main):
		main.root=Tk()
		main.pas=1

		main.menu=Frame(main.root) #le menu
		main.btnNouveau=Button(main.menu,text="Nouvelle simulation",command=main.nouveau)
		main.btnPP=Button(main.menu,text="Lecture / Pause",command=main.pp)
		main.btnQuitter=Button(main.menu,text="Quitter",command=main.root.quit)

		main.can=Canvas(main.root, width=largeurCellule*taille, height=largeurCellule*taille, bg="#444334") #On cree la fenetre

		main.btnNouveau.pack(side="left") #on pack le tout
		main.btnPP.pack(side="left")
		main.btnQuitter.pack()
		main.menu.pack()
		main.can.pack()

		main.matrice=mat() #On cree la matrice
		main.rendu() #On demande le dessin des rectangles
		main.root.after(temps,main.animation)
		main.root.mainloop()

	def nouveau(main):
		main.matrice=mat() #On cree une matrice toute neuve

	def pp(main): #la fonction Play/Pause
		if main.pas==1:
			main.pas=0
		else:
			main.pas=1

	def animation(main): #on raffraichi l'apercu tous les pas
		for x in range(main.pas):
			main.matrice=evolution(main.matrice)
		main.rendu()
		main.root.after(temps,main.animation)

	def rendu(main): #On dessine les rectangles
		main.can.delete(ALL)
		x,y=0,0
		for i in main.matrice:
			for j in i:
				if j: #Si la cellule est a True, on cree un rectangle
					main.can.create_rectangle(x,y,x+largeurCellule,y+largeurCellule,fill="#CF9130")
				x+=largeurCellule
			y+=largeurCellule
			x=0

fenetre()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Defines views.
"""

import calendar, datetime
from flask import redirect

from presence_analyzer.main import app
from presence_analyzer.utils import jsonify, get_data, mean, group_by_weekday

import logging
log = logging.getLogger(__name__)  # pylint: disable-msg=C0103

@app.route('/')
def mainpage():
    """
    Redirects to front page.
    """
    return redirect('/static/presence_weekday.html')

@app.route('/api/v1/users', methods=['GET'])
@jsonify
def users_view():
    """
    Users listing for dropdown.
    """
    data = get_data()
    return [{'user_id': i, 'name': 'User {0}'.format(str(i))}
            for i in data.keys()]

@app.route('/api/v1/mean_time_weekday/<int:user_id>', methods=['GET'])
@jsonify
def mean_time_weekday_view(user_id):
    """
    Returns mean presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return []

    weekdays = group_by_weekday(data[user_id])
    result = [(calendar.day_abbr[weekday], mean(intervals))
              for weekday, intervals in weekdays.items()]

    return result

@app.route('/api/v1/presence_weekday/<int:user_id>', methods=['GET'])
@jsonify
def presence_weekday_view(user_id):
    """
    Returns total presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return []

    weekdays = group_by_weekday(data[user_id])
    result = [(calendar.day_abbr[weekday], sum(intervals))
              for weekday, intervals in weekdays.items()]

    result.insert(0, ('Weekday', 'Presence (s)'))
    return result

@app.route('/api/v1/presence_start_end/<int:user_id>', methods=['GET'])
@jsonify
def presence_start_end(user_id):
    """
    Return average presence time of given user
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return []

    total_start = []
    start_counter = 0

    for date in data[user_id]:
        total_start.append(str(data[user_id][date]['start']))

    totalSecs = 0
    for tm in total_start:
        timeParts = [int(s) for s in tm.split(':')]
        totalSecs += (timeParts[0] * 60 + timeParts[1]) * 60 + timeParts[2]
        start_counter += 1
    totalSecs, sec = divmod(totalSecs, 60)
    hr, min = divmod(totalSecs, 60)
    total = "%d:%02d:%02d" % (hr, min, sec)

    return total, start_counter
<commit_msg>Views update<commit_after># -*- coding: utf-8 -*-
"""
Defines views.
"""

import calendar, datetime
from flask import redirect

from presence_analyzer.main import app
from presence_analyzer.utils import jsonify, get_data, mean, group_by_weekday

import logging
log = logging.getLogger(__name__)  # pylint: disable-msg=C0103

@app.route('/')
def mainpage():
    """
    Redirects to front page.
    """
    return redirect('/static/presence_weekday.html')

@app.route('/api/v1/users', methods=['GET'])
@jsonify
def users_view():
    """
    Users listing for dropdown.
    """
    data = get_data()
    return [{'user_id': i, 'name': 'User {0}'.format(str(i))}
            for i in data.keys()]

@app.route('/api/v1/mean_time_weekday/<int:user_id>', methods=['GET'])
@jsonify
def mean_time_weekday_view(user_id):
    """
    Returns mean presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return []

    weekdays = group_by_weekday(data[user_id])
    result = [(calendar.day_abbr[weekday], mean(intervals))
              for weekday, intervals in weekdays.items()]

    return result

@app.route('/api/v1/presence_weekday/<int:user_id>', methods=['GET'])
@jsonify
def presence_weekday_view(user_id):
    """
    Returns total presence time of given user grouped by weekday.
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return []

    weekdays = group_by_weekday(data[user_id])
    result = [(calendar.day_abbr[weekday], sum(intervals))
              for weekday, intervals in weekdays.items()]

    result.insert(0, ('Weekday', 'Presence (s)'))
    return result

@app.route('/api/v1/presence_start_end/<int:user_id>', methods=['GET'])
@jsonify
def presence_start_end(user_id):
    """
    Return average presence time of given user
    """
    data = get_data()
    if user_id not in data:
        log.debug('User %s not found!', user_id)
        return []

    total_start, total_end = [], []
    start_counter , end_counter= 0, 0

    for date in data[user_id]:
        total_start.append(str(data[user_id][date]['start']))
        total_end.append(str(data[user_id][date]['end']))

    total_start_sec = 0
    for tm in total_start:
        time_part = [int(s) for s in tm.split(':')]
        total_start_sec += (time_part[0] * 60 + time_part[1]) * 60 + time_part[2]
        start_counter += 1
    total_start_sec, sec = divmod(total_start_sec, 60)
    hr, min = divmod(total_start_sec, 60)
    start_sum = "%d:%02d:%02d" % (hr, min, sec)

    start_sum_hour = start_sum.split(':')[0]
    start_sum_min = start_sum.split(':')[1]
    start_sum_sec = start_sum.split(':')[2]

    total_end_sec = 0
    for tm in total_end:
        time_part = [int(s) for s in tm.split(':')]
        total_end_sec += (time_part[0] * 60 + time_part[1]) * 60 + time_part[2]
        end_counter += 1
    total_end_sec, sec = divmod(total_end_sec, 60)
    hr, min = divmod(total_end_sec, 60)
    end_sum = "%d:%02d:%02d" % (hr, min, sec)

    end_sum_hour = end_sum.split(':')[0]
    end_sum_min = end_sum.split(':')[1]
    end_sum_sec = end_sum.split(':')[2]

    return type(end_sum_hour)
<|endoftext|>


<commit_before>from kivy.uix.screenmanager import Screen
from kivy.properties import ListProperty

from kivy.adapters.dictadapter import DictAdapter
from kivy.uix.listview import ListItemButton, ListItemLabel, \
        CompositeListItem, ListView
from kivy.uix.gridlayout import GridLayout

import popup as p_m
import audio as a_m

from defines import *

class GamesScreen(Screen):
    def __init__(self, *args, **kwargs):
        super(GamesScreen, self).__init__(*args, **kwargs)

        self.clear_selected()

    def games_view(self):
        return self.ids.games_view

    def on_pre_enter(self):
        self.games_view().refresh()
        self.clear_selected()

    def clear_selected(self):
        self.selected_gid = None

    def set_selected_gid(self, gid):
        self.selected_gid = gid

    def load_game(self):
        if not self.selected_gid:
            # Ignore it
            return
        game = self.gm.get_game(self.selected_gid)
        self.clear_selected()
        self.app.start_game(game, self.size)

    def edit_game(self):
        if not self.selected_gid:
            # Ignore it
            return

        game = self.gm.get_game(self.selected_gid)
        self.clear_selected()
        self.app.edit_game(game)

    def delete_game(self):
        if not self.selected_gid:
            # Ignore it
            return

        msg_str = "Delete game %s?" % self.selected_gid
        p_m.ConfirmPopup.create_and_open(message=msg_str,
            action=self.delete_confirmed,
            size_hint=(.8, .2))

    def delete_confirmed(self):
        self.gm.delete_game(self.selected_gid)
        self.games_view().refresh()
        self.clear_selected()

def game_data(game):
    data = {}
    data['id'] = str(game.game_id)
    data['black'] = game.get_player_name(BLACK)

    # TODO: This is a hack.
    try:
        data['white'] = game.get_player_name(WHITE)
    except:
        data['white'] = "Corrupted"

    data['date'] = str(game.get_date())
    #data['status_colour'] = [0,0,0,.5] #game.get_won_by()
    wb = game.get_won_by()
    if wb == BLACK:
        data['deselected_color'] = [0,0,0,.5] #game.get_won_by()
        data['selected_color'] = [0,0,0,.5] #game.get_won_by()
    data['height'] = 50
    data['size'] = str(game.size())
    data['rules'] = str(game.rules.get_type_name())
    data['is_selected'] = False
    # TODO: Winner
    return data

class MyCompositeListItem(CompositeListItem):
    pass

class GamesView(GridLayout):

    def __init__(self, **kwargs):
        kwargs['cols'] = 2
        super(GamesView, self).__init__(**kwargs)

        '''
        TODO:
        We have a collection of unfinished games, and another of all games
        We want to display them in several orders:
        Black Player Name (A or Z first)
        White Player Name (A or Z first)
        Date (latest as default, or earliest)
        Board size
        Rules type
        '''

    def on_enter(self):
        self.fill_er_up()

    def refresh(self):
        try:
            w = self.view
            self.remove_widget(w)
        except:
            pass
        self.fill_er_up()

    def changed_selection(self, da, *args, **kwargs):
        try:
            # TODO: Fix this incredibly ugly hack
            try:
                # For some reason this is not opening a selected game if it
                # was selected through the other row for the same game
                gid_str = da.selection[0].parent.children[-1].text
                print "Selected: GID %s" % (gid_str)
                self.parent.parent.set_selected_gid(int(gid_str))
            except AttributeError, e:
                # It is selected already, load the game.
                self.parent.parent.load_game()
        except IndexError:
            print "Removed"

    def fill_er_up(self, unused=None):
        dc = 'deselected_color'
        sc = 'selected_color'
        args_converter = \
            lambda row_index, rec: \
                {'size_hint_y': None,
                 'height': 25,
                 #'height': rec['height'],
                 #'background_color': rec['status_colour'],
                 'cls_dicts': [
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['id']}}, #, dc: rec[dc]}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['black']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['white']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['date']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['size']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['rules']}},
                       ]}

        # TODO: Another hack - parent.parent
        gm = self.parent.parent.gm
        games = gm.get_all_unfinished()

        # TODO: The 'if' is a hack
        games_dict = { g.game_id: game_data(g) for g in games if g }

        self.item_strings = ["{0}".format(g_id) for g_id in games_dict.iterkeys() ]

        dict_adapter = DictAdapter(data=games_dict,
                                   args_converter=args_converter,
                                   selection_mode='single',
                                   allow_empty_selection=False, # Not working?
                                   cls=MyCompositeListItem)

        dict_adapter.bind(
            on_selection_change=self.changed_selection)

        self.adapter = dict_adapter

        # Use the adapter in our ListView:
        list_view = ListView(adapter=dict_adapter)

        self.add_widget(list_view)
        self.view = list_view

class MyListItemButton(ListItemButton):
    """ Add click sound """
    def on_touch_down(self, touch):
        # TODO: Check Scroll
        a_m.instance.click()
        super(MyListItemButton, self).on_touch_down(touch)

<commit_msg>Speed up by not making click sound once for each button on the screen!<commit_after>from kivy.uix.screenmanager import Screen
from kivy.properties import ListProperty

from kivy.adapters.dictadapter import DictAdapter
from kivy.uix.listview import ListItemButton, ListItemLabel, \
        CompositeListItem, ListView
from kivy.uix.gridlayout import GridLayout

import popup as p_m
import audio as a_m

from defines import *

class GamesScreen(Screen):
    def __init__(self, *args, **kwargs):
        super(GamesScreen, self).__init__(*args, **kwargs)

        self.clear_selected()

    def games_view(self):
        return self.ids.games_view

    def on_pre_enter(self):
        self.games_view().refresh()
        self.clear_selected()

    def clear_selected(self):
        self.selected_gid = None

    def set_selected_gid(self, gid):
        self.selected_gid = gid

    def load_game(self):
        if not self.selected_gid:
            # Ignore it
            return
        game = self.gm.get_game(self.selected_gid)
        self.clear_selected()
        self.app.start_game(game, self.size)

    def edit_game(self):
        if not self.selected_gid:
            # Ignore it
            return

        game = self.gm.get_game(self.selected_gid)
        self.clear_selected()
        self.app.edit_game(game)

    def delete_game(self):
        if not self.selected_gid:
            # Ignore it
            return

        msg_str = "Delete game %s?" % self.selected_gid
        p_m.ConfirmPopup.create_and_open(message=msg_str,
            action=self.delete_confirmed,
            size_hint=(.8, .2))

    def delete_confirmed(self):
        self.gm.delete_game(self.selected_gid)
        self.games_view().refresh()
        self.clear_selected()

def game_data(game):
    data = {}
    data['id'] = str(game.game_id)
    data['black'] = game.get_player_name(BLACK)

    # TODO: This is a hack.
    try:
        data['white'] = game.get_player_name(WHITE)
    except:
        data['white'] = "Corrupted"

    data['date'] = str(game.get_date())
    #data['status_colour'] = [0,0,0,.5] #game.get_won_by()
    wb = game.get_won_by()
    if wb == BLACK:
        data['deselected_color'] = [0,0,0,.5] #game.get_won_by()
        data['selected_color'] = [0,0,0,.5] #game.get_won_by()
    data['height'] = 50
    data['size'] = str(game.size())
    data['rules'] = str(game.rules.get_type_name())
    data['is_selected'] = False
    # TODO: Winner
    return data

class MyCompositeListItem(CompositeListItem):
    pass

class GamesView(GridLayout):

    def __init__(self, **kwargs):
        kwargs['cols'] = 2
        super(GamesView, self).__init__(**kwargs)

        '''
        TODO:
        We have a collection of unfinished games, and another of all games
        We want to display them in several orders:
        Black Player Name (A or Z first)
        White Player Name (A or Z first)
        Date (latest as default, or earliest)
        Board size
        Rules type
        '''

    def on_enter(self):
        self.fill_er_up()

    def refresh(self):
        try:
            w = self.view
            self.remove_widget(w)
        except:
            pass
        self.fill_er_up()

    def changed_selection(self, da, *args, **kwargs):
        try:
            # TODO: Fix this incredibly ugly hack
            try:
                # For some reason this is not opening a selected game if it
                # was selected through the other row for the same game
                gid_str = da.selection[0].parent.children[-1].text
                print "Selected: GID %s" % (gid_str)
                self.parent.parent.set_selected_gid(int(gid_str))
            except AttributeError, e:
                # It is selected already, load the game.
                self.parent.parent.load_game()
        except IndexError:
            print "Removed"

    def fill_er_up(self, unused=None):
        dc = 'deselected_color'
        sc = 'selected_color'
        args_converter = \
            lambda row_index, rec: \
                {'size_hint_y': None,
                 'height': 25,
                 #'height': rec['height'],
                 #'background_color': rec['status_colour'],
                 'cls_dicts': [
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['id']}}, #, dc: rec[dc]}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['black']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['white']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['date']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['size']}},
                       {'cls': MyListItemButton,
                           'kwargs': {'text': rec['rules']}},
                       ]}

        # TODO: Another hack - parent.parent
        gm = self.parent.parent.gm
        games = gm.get_all_unfinished()

        # TODO: The 'if' is a hack
        games_dict = { g.game_id: game_data(g) for g in games if g }

        self.item_strings = ["{0}".format(g_id) for g_id in games_dict.iterkeys() ]

        dict_adapter = DictAdapter(data=games_dict,
                                   args_converter=args_converter,
                                   selection_mode='single',
                                   allow_empty_selection=False, # Not working?
                                   cls=MyCompositeListItem)

        dict_adapter.bind(
            on_selection_change=self.changed_selection)

        self.adapter = dict_adapter

        # Use the adapter in our ListView:
        list_view = ListView(adapter=dict_adapter)

        self.add_widget(list_view)
        self.view = list_view

class MyListItemButton(ListItemButton):
    """ Add click sound """
    def on_touch_down(self, touch):
        # TODO: Check Scroll
        if super(MyListItemButton, self).on_touch_down(touch):
            a_m.instance.click()

<|endoftext|>


<commit_before>"""Virial isotherm model."""

import warnings

import matplotlib.pyplot as plt
import numpy
import scipy.optimize as opt

from ..utilities.exceptions import CalculationError
from .base_model import IsothermBaseModel

class Virial(IsothermBaseModel):
    r"""
    A virial isotherm model with 3 factors.

    .. math::

        p = n \exp{(-\ln{K_H} + An + Bn^2 + Cn^3)}

    Notes
    -----
    A virial isotherm model attempts to fit the measured data to a factorized
    exponent relationship between loading and pressure.

    .. math::

        p = n \exp{(K_1n^0 + K_2n^1 + K_3n^2 + K_4n^3 + ... + K_i n^{i-1})}

    It has been applied with success to describe the behaviour of standard as
    well as supercritical isotherms. The factors are usually empirical,
    although some relationship with physical can be determined:
    the first constant is related to the Henry constant at zero loading, while
    the second constant is a measure of the interaction strength with the surface.

    .. math::

        K_1 = -\ln{K_{H,0}}

    In practice, besides the first constant, only 2-3 factors are used.

    """

    # Model parameters
    name = 'Virial'
    calculates = 'pressure'
    param_names = ["K", "A", "B", "C"]
    param_bounds = {
        "K": [0, numpy.inf],
        "A": [-numpy.inf, numpy.inf],
        "B": [-numpy.inf, numpy.inf],
        "C": [-numpy.inf, numpy.inf],
    }

    def loading(self, pressure):
        """
        Calculate loading at specified pressure.

        Careful!
        For the Virial model, the loading has to
        be computed numerically.

        Parameters
        ----------
        pressure : float
            The pressure at which to calculate the loading.

        Returns
        -------
        float
            Loading at specified pressure.
        """
        def fun(x):
            return (self.pressure(x) - pressure)**2

        opt_res = opt.minimize(fun, pressure, method='Nelder-Mead')

        if not opt_res.success:
            raise CalculationError("""
            Root finding for failed. Error: \n\t{}
            """.format(opt_res.message))

        return opt_res.x

    def pressure(self, loading):
        """
        Calculate pressure at specified loading.

        The Virial model calculates the pressure directly.

        Parameters
        ----------
        loading : float
            The loading at which to calculate the pressure.

        Returns
        -------
        float
            Pressure at specified loading.
        """
        return loading * numpy.exp(-numpy.log(self.params['K']) + self.params['A'] * loading
                                   + self.params['B'] * loading**2 + self.params['C'] * loading**3)

    def spreading_pressure(self, pressure):
        r"""
        Calculate spreading pressure at specified gas pressure.

        Function that calculates spreading pressure by solving the
        following integral at each point i.

        .. math::

            \pi = \int_{0}^{p_i} \frac{n_i(p_i)}{p_i} dp_i

        The integral for the Virial model cannot be solved analytically
        and must be calculated numerically.

        Parameters
        ----------
        pressure : float
            The pressure at which to calculate the spreading pressure.

        Returns
        -------
        float
            Spreading pressure at specified pressure.
        """
        raise NotImplementedError

    def default_guess(self, pressure, loading):
        """
        Return initial guess for fitting.

        Parameters
        ----------
        pressure : ndarray
            Pressure data.
        loading : ndarray
            Loading data.

        Returns
        -------
        dict
            Dictionary of initial guesses for the parameters.
        """
        saturation_loading, langmuir_k = super().default_guess(pressure, loading)

        guess = {"K": saturation_loading * langmuir_k,
                 "A": 0, "B": 0, "C": 0}

        for param in guess:
            if guess[param] < self.param_bounds[param][0]:
                guess[param] = self.param_bounds[param][0]
            if guess[param] > self.param_bounds[param][1]:
                guess[param] = self.param_bounds[param][1]

        return guess

    def fit(self, pressure, loading, param_guess, optimization_params=None, verbose=False):
        """
        Fit model to data using nonlinear optimization with least squares loss function.

        Resulting parameters are assigned to self.

        Parameters
        ----------
        pressure : ndarray
            The pressures of each point.
        loading : ndarray
            The loading for each point.
        optimization_params : dict
            Custom parameters to pass to SciPy.optimize.least_squares.
        verbose : bool, optional
            Prints out extra information about steps taken.
        """
        if verbose:
            print("Attempting to model using {}".format(self.name))

        # parameter names (cannot rely on order in Dict)
        param_names = [param for param in self.params]
        guess = numpy.array([param_guess[param] for param in param_names])
        bounds = [[self.param_bounds[param][0] for param in param_names],
                  [self.param_bounds[param][1] for param in param_names]]

        # remove invalid values in function
        zero_values = ~numpy.logical_and(pressure > 0, loading > 0)
        if any(zero_values):
            warnings.warn('Removed points which are equal to 0.')
            pressure = pressure[~zero_values]
            loading = loading[~zero_values]

        # define fitting function as polynomial transformed input
        ln_p_over_n = numpy.log(numpy.divide(pressure, loading))

        # add point
        add_point = False
        if optimization_params:
            add_point = optimization_params.pop('add_point', None)
        fractional_loading = loading / max(loading)
        if len(fractional_loading[fractional_loading < 0.5]) < 3:
            if not add_point:
                raise CalculationError(
                    """
                    The isotherm recorded has very few points below 0.5
                    fractional loading. If a virial model fit is attempted
                    the resulting polynomial will likely be unstable in the
                    low loading region.

                    You can pass ``add_point=True`` in ``optimization_params``
                    to attempt to add a point in the low pressure region or
                    record better isotherms.
                    """
                )
            ln_p_over_n = numpy.hstack([ln_p_over_n[0], ln_p_over_n])
            loading = numpy.hstack([1e-1, loading])

        def fit_func(x, l, ln_p_over_n):
            for i, _ in enumerate(param_names):
                self.params[param_names[i]] = x[i]
            return self.params['C'] * l**3 + self.params['B'] * l**2 \
                + self.params['A'] * l - numpy.log(self.params['K']) - ln_p_over_n

        kwargs = dict(
            bounds=bounds,                      # supply the bounds of the parameters
            # loss='huber',                     # use a loss function against outliers
            # f_scale=0.1,                      # scale of outliers
        )
        if optimization_params:
            kwargs.update(optimization_params)

        # minimize RSS
        opt_res = opt.least_squares(
            fit_func, guess,                    # provide the fit function and initial guess
            args=(loading, ln_p_over_n),        # supply the extra arguments to the fit function
            **kwargs
        )
        if not opt_res.success:
            raise CalculationError(
                "\n\tMinimization of RSS for {0} isotherm fitting failed with error:"
                "\n\t\t{1}"
                "\n\tTry a different starting point in the nonlinear optimization"
                "\n\tby passing a dictionary of parameter guesses, param_guess, to the constructor."
                "\n\tDefault starting guess for parameters:"
                "\n\t{2}".format(self.name, opt_res.message, param_guess))

        # assign params
        for index, _ in enumerate(param_names):
            self.params[param_names[index]] = opt_res.x[index]

        self.rmse = numpy.sqrt(numpy.sum((opt_res.fun)**2) / len(loading))

        if verbose:
            print("Model {0} success, RMSE is {1:.3f}".format(self.name, self.rmse))
<commit_msg>undo virial change<commit_after>"""Virial isotherm model."""

import warnings

import matplotlib.pyplot as plt
import numpy
import scipy.optimize as opt

from ..utilities.exceptions import CalculationError
from .base_model import IsothermBaseModel

class Virial(IsothermBaseModel):
    r"""
    A virial isotherm model with 3 factors.

    .. math::

        p = n \exp{(-\ln{K_H} + An + Bn^2 + Cn^3)}

    Notes
    -----
    A virial isotherm model attempts to fit the measured data to a factorized
    exponent relationship between loading and pressure.

    .. math::

        p = n \exp{(K_1n^0 + K_2n^1 + K_3n^2 + K_4n^3 + ... + K_i n^{i-1})}

    It has been applied with success to describe the behaviour of standard as
    well as supercritical isotherms. The factors are usually empirical,
    although some relationship with physical can be determined:
    the first constant is related to the Henry constant at zero loading, while
    the second constant is a measure of the interaction strength with the surface.

    .. math::

        K_1 = -\ln{K_{H,0}}

    In practice, besides the first constant, only 2-3 factors are used.

    """

    # Model parameters
    name = 'Virial'
    calculates = 'pressure'
    param_names = ["K", "A", "B", "C"]
    param_bounds = {
        "K": [0, numpy.inf],
        "A": [-numpy.inf, numpy.inf],
        "B": [-numpy.inf, numpy.inf],
        "C": [-numpy.inf, numpy.inf],
    }

    def loading(self, pressure):
        """
        Calculate loading at specified pressure.

        Careful!
        For the Virial model, the loading has to
        be computed numerically.

        Parameters
        ----------
        pressure : float
            The pressure at which to calculate the loading.

        Returns
        -------
        float
            Loading at specified pressure.
        """
        def fun(x):
            return (self.pressure(x) - pressure)**2

        opt_res = opt.minimize(fun, pressure, method='Nelder-Mead')

        if not opt_res.success:
            raise CalculationError("""
            Root finding for failed. Error: \n\t{}
            """.format(opt_res.message))

        return opt_res.x

    def pressure(self, loading):
        """
        Calculate pressure at specified loading.

        The Virial model calculates the pressure directly.

        Parameters
        ----------
        loading : float
            The loading at which to calculate the pressure.

        Returns
        -------
        float
            Pressure at specified loading.
        """
        return loading * numpy.exp(-numpy.log(self.params['K']) + self.params['A'] * loading
                                   + self.params['B'] * loading**2 + self.params['C'] * loading**3)

    def spreading_pressure(self, pressure):
        r"""
        Calculate spreading pressure at specified gas pressure.

        Function that calculates spreading pressure by solving the
        following integral at each point i.

        .. math::

            \pi = \int_{0}^{p_i} \frac{n_i(p_i)}{p_i} dp_i

        The integral for the Virial model cannot be solved analytically
        and must be calculated numerically.

        Parameters
        ----------
        pressure : float
            The pressure at which to calculate the spreading pressure.

        Returns
        -------
        float
            Spreading pressure at specified pressure.
        """
        raise NotImplementedError

    def default_guess(self, pressure, loading):
        """
        Return initial guess for fitting.

        Parameters
        ----------
        pressure : ndarray
            Pressure data.
        loading : ndarray
            Loading data.

        Returns
        -------
        dict
            Dictionary of initial guesses for the parameters.
        """
        saturation_loading, langmuir_k = super().default_guess(pressure, loading)

        guess = {"K": saturation_loading * langmuir_k,
                 "A": 0, "B": 0, "C": 0}

        for param in guess:
            if guess[param] < self.param_bounds[param][0]:
                guess[param] = self.param_bounds[param][0]
            if guess[param] > self.param_bounds[param][1]:
                guess[param] = self.param_bounds[param][1]

        return guess

    def fit(self, pressure, loading, param_guess, optimization_params=None, verbose=False):
        """
        Fit model to data using nonlinear optimization with least squares loss function.

        Resulting parameters are assigned to self.

        Parameters
        ----------
        pressure : ndarray
            The pressures of each point.
        loading : ndarray
            The loading for each point.
        optimization_params : dict
            Custom parameters to pass to SciPy.optimize.least_squares.
        verbose : bool, optional
            Prints out extra information about steps taken.
        """
        if verbose:
            print("Attempting to model using {}".format(self.name))

        # parameter names (cannot rely on order in Dict)
        param_names = [param for param in self.params]
        guess = numpy.array([param_guess[param] for param in param_names])
        bounds = [[self.param_bounds[param][0] for param in param_names],
                  [self.param_bounds[param][1] for param in param_names]]

        # remove invalid values in function
        zero_values = ~numpy.logical_and(pressure > 0, loading > 0)
        if any(zero_values):
            warnings.warn('Removed points which are equal to 0.')
            pressure = pressure[~zero_values]
            loading = loading[~zero_values]

        # define fitting function as polynomial transformed input
        ln_p_over_n = numpy.log(numpy.divide(pressure, loading))

        # add point
        add_point = False
        added_point = False
        if optimization_params:
            add_point = optimization_params.pop('add_point', None)
        fractional_loading = loading / max(loading)
        if len(fractional_loading[fractional_loading < 0.5]) < 3:
            if not add_point:
                raise CalculationError(
                    """
                    The isotherm recorded has very few points below 0.5
                    fractional loading. If a virial model fit is attempted
                    the resulting polynomial will likely be unstable in the
                    low loading region.

                    You can pass ``add_point=True`` in ``optimization_params``
                    to attempt to add a point in the low pressure region or
                    record better isotherms.
                    """
                )
            added_point = True
            ln_p_over_n = numpy.hstack([ln_p_over_n[0], ln_p_over_n])
            loading = numpy.hstack([1e-1, loading])

        def fit_func(x, l, ln_p_over_n):
            for i, _ in enumerate(param_names):
                self.params[param_names[i]] = x[i]
            return self.params['C'] * l**3 + self.params['B'] * l**2 \
                + self.params['A'] * l - numpy.log(self.params['K']) - ln_p_over_n

        kwargs = dict(
            bounds=bounds,                      # supply the bounds of the parameters
            # loss='huber',                     # use a loss function against outliers
            # f_scale=0.1,                      # scale of outliers
        )
        if optimization_params:
            kwargs.update(optimization_params)

        # minimize RSS
        opt_res = opt.least_squares(
            fit_func, guess,                    # provide the fit function and initial guess
            args=(loading, ln_p_over_n),        # supply the extra arguments to the fit function
            **kwargs
        )
        if not opt_res.success:
            raise CalculationError(
                "\n\tMinimization of RSS for {0} isotherm fitting failed with error:"
                "\n\t\t{1}"
                "\n\tTry a different starting point in the nonlinear optimization"
                "\n\tby passing a dictionary of parameter guesses, param_guess, to the constructor."
                "\n\tDefault starting guess for parameters:"
                "\n\t{2}".format(self.name, opt_res.message, param_guess))

        # assign params
        for index, _ in enumerate(param_names):
            self.params[param_names[index]] = opt_res.x[index]

        self.rmse = numpy.sqrt(numpy.sum((opt_res.fun)**2) / len(loading))

        if verbose:
            print("Model {0} success, rmse is {1}".format(
                self.name, self.rmse))
            n_load = numpy.linspace(1e-2, numpy.amax(loading), 100)
            plt.plot(loading, ln_p_over_n, '.')
            plt.plot(n_load, numpy.log(numpy.divide(self.pressure(n_load), n_load)), '-')
            if added_point:
                plt.plot(1e-1, ln_p_over_n[0], '.r')
            plt.title("Virial fit")
            plt.xlabel("Loading")
            plt.ylabel("ln(p/n)")
            plt.show()
<|endoftext|>