def login_required(template_file):
    """
    Require user to be logged in and pass necessary data for navigation bar.
    """
    def decorator(handler):
        @aiohttp_login.login_required
        @template(template_file)
        @wraps(handler)
        async def inner_decorator(request):
            result = await handler(request)
            characters = request.app['db'].characters
            invalid_characters = await characters.find(
                {'user._id': request['user']['_id']}).to_list(length=100)
            for character in invalid_characters:
                await characters.update_one(
                    {'_id': character['_id']},
                    {
                        '$set': {'user_id': request['user']['_id']},
                        '$unset': {'user': True}})
            result['characters'] = await characters.find(
                {'user_id': request['user']['_id']}).to_list(length=100)
            for character in result['characters']:
                calculate_stats(character)
            campaigns = request.app['db'].campaigns
            result['campaigns'] = await campaigns.find(
                {'user_id': request['user']['_id']}).to_list(length=100)
            return result
        return inner_decorator
    return decorator def format_errors(errors):
    return "\n".join(["""
<div class="alert alert-danger alert-dismissable fade in">
    <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
    {}
</div>
""".format(message) for message in errors]) ABILITIES = [
    'strength',
    'dexterity',
    'constitution',
    'intelligence',
    'wisdom',
    'charisma',
    'perception'] RACES = {race['name']: race for race in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/races.yaml'),
    Loader=Loader) if race is not None} SKILLS = {skill['name'].lower(): skill for skill in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/skills.yaml'),
    Loader=Loader) if skill is not None} SPELLS = {spell['name'].lower(): spell for spell in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/spells.yaml'),
    Loader=Loader) if spell is not None} PRAYERS = {prayer['name'].lower(): prayer for prayer in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/prayers.yaml'),
    Loader=Loader) if prayer is not None} PRAYER_SPHERES = {PRAYERS[prayer]['sphere'] for prayer in PRAYERS} POWERS = {power['name'].lower(): power for power in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/powers.yaml'),
    Loader=Loader) if power is not None} CLASSES = {class_['name'].lower(): class_ for class_ in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/classes.yaml'),
    Loader=Loader) if class_ is not None} COINS = OrderedDict([
    ('oros', 1),
    ('dies', 24),
    ('semanis', 24 * 6),
    ('mensis', 24 * 6 * 5),
    ('annum', 24 * 6 * 5 * 12)]) WEAPONS = {weapon['name'].lower(): weapon for weapon in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/weapons.yaml'),
    Loader=Loader) if weapon is not None} WEAPON_CATEGORIES = {WEAPONS[weapon]['weapon_category'] for weapon in WEAPONS} WEAPON_SIZES = {size for weapon in WEAPONS for size in WEAPONS[weapon]['size']} WEAPON_AGES = {
    age for weapon in WEAPONS \
    for size in WEAPONS[weapon]['size'] \
    for age in WEAPONS[weapon]['size'][size]['time_period']} ARMOUR = {armour['name'].lower(): armour for armour in load_all(
    resource_stream(Requirement.parse('dnd'), 'dnd/config/armour.yaml'),
    Loader=Loader) if armour is not None} ARMOUR_AGES = {age for armour in ARMOUR for age in ARMOUR[armour]['time_period']} def convert_coins(coins):
    """
    Convert oros into higher coins, or a dictionary of higher coins into oros.
    """
    if isinstance(coins, int):
        result = {coin: 0 for coin in COINS}
        for coin in reversed(COINS):
            rest = coins % COINS[coin]
            if rest != coins:
                result[coin] = int((coins - rest) / COINS[coin])
                coins = rest
        return result
    elif isinstance(coins, dict):
        oros = 0
        for coin in coins:
            oros += COINS[coin] * coins[coin]
        return int(oros) def calculate_stats(character):
    """Calculate and set characters statistics."""
    _character_level(character)
    _character_classes(character)
    _character_race(character)
    _character_abilities(character)
    _character_powers(character)
    _character_skills(character)
    _character_hit_points(character)
    _character_background(character)
    _character_spells(character)
    _character_prayers(character)
    _character_money(character)
    _character_inventory(character)
    _character_equipment(character)
import time
import datetime
import re
from inspect import iscoroutinefunction
from bson import ObjectId
from aiohttp_login.decorators import restricted_api
from aiohttp.web import json_response
from markupsafe import escape
from aiohttp_jinja2 import get_env
from dnd.decorators import login_required
from dnd.common import format_errors
from dnd.character import (
    ABILITIES,
    RACES,
    SKILLS,
    SPELLS,
    PRAYERS,
    PRAYER_SPHERES,
    POWERS,
    CLASSES,
    COINS,
    WEAPONS,
    WEAPON_CATEGORIES,
    WEAPON_SIZES,
    WEAPON_AGES,
    ARMOUR,
    ARMOUR_AGES,
    convert_coins,
    calculate_stats)
"""Character page."""

async def get_character(request):
    """Fetch character from database."""
    errors = []
    editing_privileges = False
    characters = request.app['db'].characters
    character = await characters.find_one(
        {'_id': ObjectId(request.match_info['id'])})
    if character is None:
        errors.append('character {} does not exist'.format(
            request.match_info['id']))
    else:
        if request['user']['_id'] == character['user_id']:
            editing_privileges = True

        calculate_stats(character)