#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

"""server.py: Main executable file."""

import Cookie
import re
import subprocess
import uuid

import bottle as app
import bcrypt
from beaker.middleware import SessionMiddleware
from mako.template import Template
from mako.lookup import TemplateLookup

import config
import models
from modules import function
from modules.form import Form
from modules.thumbnail import Thumbnail
from modules.upload import Upload
from modules.logger import Logger

# Global variables (for all the templates):
meta = dict(
    site_name=config.SITE_NAME,
    site_uri=config.SITE_URI,
    site_tagline=config.SITE_TAGLINE,
    license_url=config.LICENSE_URL,
    favicon=config.FAVICON,
    css=config.CSS,
    js=config.JS
)

# Global variables (for all the routes):
form = Form()
log = Logger().log()
templates = TemplateLookup(directories=[config.PAGES])
session = SessionMiddleware(app.app(), {
    'session.data_dir': config.SESSION,
    'session.type': config.SESSION_TYPE,
    'session.auto': config.SESSION_AUTO
})
cookie = Cookie.SimpleCookie()
cookie['id'], cookie['token'], auth_id = '', '', ''

# Global functions (for all the routes)
def authenticate(func):
    """TODO: Function docstring."""

    def logged_in(*args, **kwargs):
        if 'id' in cookie and 'token' in cookie:
            cookie_id = cookie['id'].value
            cookie_token = cookie['token'].value

            if all(cookie.values()):
                results = models.db.read('Tokens', ['*'], [
                    ('key', '=', cookie_id),
                    ('value', '=', cookie_token)
                ])

                if results:
                    app.request.session['auth_id'] = results[0]['user_id']
                    return func(*args, **kwargs)

        if 'auth_id' in app.request.session and app.request.session['auth_id']:
            return func(*args, **kwargs)

        app.redirect('/login')

    return logged_in

@app.hook('before_request')
def global_session():
    results = models.db.read('Users', ['id'])

    if not results and '.' not in app.request.fullpath:
        if not '/setup' in app.request.fullpath:
            app.redirect('/setup')

    app.request.session = app.request.environ.get('beaker.session')

@app.route('/setup', method=['GET', 'POST'])
def setup():
    results = models.db.read('Users', ['*'])

    if results:
        app.redirect('/')

    tpl = '{base}/core/setup.html'.format(base=config.PAGES)
    meta = globals()['meta']
    field = dict(
        email=form.field('email'),
        password=form.field('password'),
        confirm_password=form.field('confirm_password')
    )
    submit = dict(register=form.field('register'))
    result = dict(successes=[], errors=[])
    db_update = {}

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = '{title} information'.format(
        title=meta['title']
    )

    if submit['register']:
        field['email'] = field['email'].lower()

        if not all(field.values()):
            result['errors'].append('All fields are required')

        if not re.search(r'^(\w+(-|.)\w+)@(\w+(-|.)\w+)(\.\w+){1,2}$',
                         field['email'], re.IGNORECASE):
            result['errors'].append('Invalid e-mail format')
        else:
            db_update['email'] = field['email']

        if field['password']:
            if not len(field['password']) >= 6:
                result['errors'].append('Password is too short: must be '
                                        'atleast 6 characters')

            if not field['confirm_password']:
                result['errors'].append('Confirm password is required')
            elif field['password'] !=  field['confirm_password']:
                result['errors'].append('Passwords didn\'t match')
        elif field['confirm_password'] and not field['password']:
            result['errors'].append('Password is required')

        if not result['errors']:
            if field['password'] and field['confirm_password']:
                db_update['password'] = bcrypt.hashpw(field['password'],
                                                      bcrypt.gensalt())

    if not result['errors'] and db_update:
        db_update.update(
            token=uuid.uuid4().hex,
            verified=True
        )

        models.db.insert('Users', db_update)

        results = models.db.read('Users', ['id'], [
            ('email', '=', field['email'])
        ])

        if results:
            user_id = dict(user_id=results[0]['id'])
            tables = [
                'Profiles',
                'Addresses',
                'Educations',
                'Experiences',
                'Skills',
                'Affiliations',
                'Publications'
            ]

            for table in tables:
                models.db.insert(table, user_id)

            result['successes'].append('Registration successful')

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = dict(auth_id=auth_id)
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/')
def index():
    id_ = models.db.read('Users', ['id'])[0]['id']
    tpl = '{base}/index.html'.format(base=config.PAGES)
    meta = globals()['meta']
    user_info = models.db.read_all(
        'Users',
        [
            'Profiles',
            'Addresses'
        ],
        dict(pk='id', pk_value=id_, fk='user_id')
    )[0]
    result = dict(successes=[], errors=[])
    misc = {}

    for table in ['Educations', 'Experiences', 'Skills', 'Affiliations',
                  'Publications']:
        user_info[table.lower()] = models.db.read(table, ['*'], [
            ('user_id', '=', id_)
        ])

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = 'home'
    meta['slugs'] = ['home']
    meta['title'] = meta['site_name']
    meta['document_title'] = '{site_name} - {site_tagline}'.format(
        site_name=meta['site_name'],
        site_tagline=meta['site_tagline']
    )
    meta['description'] = config.SITE_DESCRIPTION
    user_info['photo'] = user_info['photo'] or config.PHOTO
    user_info['nickname'] = user_info['nickname'] \
                            or user_info['email'].split('@')[0].title()
    misc['photo_info'] = function.image_info(user_info['photo'])
    misc['photo_info']['alt'] = 'Photo of {nickname}'.format(
        nickname=user_info['nickname']
    )

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/login', method=['GET', 'POST'])
def login():
    if 'auth_id' in app.request.session and app.request.session['auth_id']:
        app.redirect('/resume')

    tpl = '{base}/core/login.html'.format(base=config.PAGES)
    meta = globals()['meta']
    field = dict(
        email=form.field('email'),
        password=form.field('password'),
        keep_me_logged=form.field('keep_logged_in')
    )
    submit = dict(login=form.field('login'))
    result = dict(successes=[], errors=[])
    db_update = {}
    misc = {}

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'Log in to use access the {site_name}'.format(
        title=meta['title'].lower(),
        site_name=meta['site_name']
    )

    if submit['login']:
        field['email'] = field['email'].lower()

        if not (field['email'] or field['password']):
            result['errors'].append('E-mail and Password are required')
        else:
            results = models.db.read('Users', ['id', 'email', 'password'], [
                ('email', '=', field['email'])
            ])
            password = None

            if results:
                user_id = results[0]['id']
                password = str(results[0]['password'])

            if not results or bcrypt.hashpw(field['password'],
                                            password) != password:
                result['errors'].append('Invalid E-mail and/or Password')
            else:
                # Session:
                app.request.session['auth_id'] = user_id

                if field['keep_me_logged']:
                    # Cookie:
                    cookie['id'] = uuid.uuid4().hex
                    cookie['token'] = bcrypt.hashpw(
                        '{id}{email}{cookie_id}'.format(
                            id=user_id,
                            email=field['email'],
                            cookie_id=cookie['id']
                        ), bcrypt.gensalt()
                    )
                    auth_id = user_id

                    # Add cookie morsel objects:
                    for key in ['id', 'token']:
                        cookie[key]['max-age'] = config.COOKIE_MAX_AGE
                        cookie[key].update(
                            dict(
                                path=config.COOKIE,
                                domain=meta['site_uri'],
                                expires=config.COOKIE_EXPIRES,
                                httponly=config.COOKIE_HTTP_ONLY,
                                comment=config.COOKIE_COMMENT,
                                secure=config.COOKIE_SECURE,
                                version=config.COOKIE_VERSION
                            )
                        )

                    db_update.update(
                        key=cookie['id'].value,
                        value=cookie['token'].value,
                        user_id=user_id
                    )
                    models.db.insert('Tokens', db_update)

                app.redirect('/resume')

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/resume<:re:/?>')
def edit():
    app.redirect('/resume/profile')

@app.route('/account<:re:(|/verify)/?>', method=['GET', 'POST'])
@authenticate
def account():
    id_ = auth_id or app.request.session['auth_id']
    tpl = '{base}/edit.html'.format(base=config.PAGES)
    user = models.db.read('Users', ['email', 'token'], [('id', '=', id_)])[0]
    meta = globals()['meta']
    field = dict(
        email=form.field('email'),
        new_password=form.field('new_password'),
        confirm_password=form.field('confirm_password')
    )
    param = dict(
        token=form.param('token'),
        old_email=form.param('old_email'),
        new_email=form.param('new_email')
    )
    submit = dict(update=form.field('update'))
    result = dict(successes=[], errors=[])
    db_update = {}
    misc = {}

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'Edit {title} information'.format(
        title=meta['title'].lower()
    )
    field['email'] = field['email'].lower() or user['email']
    misc['tpl'] ='/edit/account.html'

    if submit['update']:
        if not re.search(r'^(\w+(-|.)\w+)@(\w+(-|.)\w+)(\.\w+){1,2}$',
                         field['email'], re.IGNORECASE):
            result['errors'].append('Invalid e-mail format')

        if field['new_password']:
            if not len(field['new_password']) >= 6:
                result['errors'].append('New Password is too short: must be '
                                        'atleast 6 characters')

            if not field['confirm_password']:
                result['errors'].append('Confirm password is required')
            elif field['new_password'] !=  field['confirm_password']:
                result['errors'].append('Passwords didn\'t match')
        elif field['confirm_password'] and not field['new_password']:
            result['errors'].append('New Password is required')

        if not result['errors']:
            if field['new_password'] and field['confirm_password']:
                db_update['password'] = bcrypt.hashpw(field['new_password'],
                                                      bcrypt.gensalt())
                field['new_password'] = ''
                field['confirm_password'] = ''
                result['successes'].append('Password change successful')

            results = models.db.read('Users', ['id'], [
                ('email', '=', field['email'])
            ])

            if results:
              if id_ !=  results[0]['id']:
                  result['errors'].append(
                      '{email} is already used'.format(email=field['email'])
                  )
            else:
                # Create a verification url:
                misc['token'] = user['token']
                misc['old_email'] = user['email']
                misc['new_email'] = field['email']
                misc['verify_url'] = (
                    '{base}/account/verify?old_email={old_email}&'
                    'new_email={new_email}&token={token}'.format(
                        base=meta['site_uri'],
                        old_email=misc['old_email'],
                        new_email=misc['new_email'],
                        token=misc['token']
                    )
                )

                # Send an e-mail verification:
                misc['sender'] = dict(
                    name=meta['site_name'],
                    email=config.SMTP_EMAIL
                )
                misc['recipients'] = [
                    dict(
                        name=field['email'],
                        email=field['email']
                    )
                ]
                misc['subject'] = 'Change E-mail Verification'
                file_ = open('{base}/change_email.txt'.format(
                    base=config.EMAILS), 'rb'
                )
                misc['message'] = file_.read().format(
                    email=field['email'],
                    verify_url=misc['verify_url'],
                    site_name=meta['site_name']
                )

                file_.close()

                send_mail = function.send_mail(
                    misc['sender'],
                    misc['recipients'],
                    misc['subject'],
                    misc['message']
                )

                if (send_mail):
                    result['successes'].append(
                        'A verification url has been sent to '
                        '{email}'.format(email=field['email'])
                    )
                else:
                    log.warning('Failed to send verification url to '
                                '{email}. Please check the SMTP settings in '
                                'config.py file'.format(email=field['email']))

    if not submit['update'] and '/verify' in app.request.fullpath:
        meta['title'] = app.request.fullpath.strip('/').split('/')[0].title()
        results = models.db.read('Users', ['id'], [
            ('email', '=', param['old_email']),
            ('token', '=', param['token'])
        ])

        if not results:
            result['errors'].append('Invalid e-mail verification')
        else:
            existing_users = models.db.read('Users', ['id'], [
                ('email', '=', param['new_email'])
            ])

            if existing_users:
                result['errors'].append('E-mail is already in use')
            else:
                db_update.update(
                    email=param['new_email'],
                    token=uuid.uuid4().hex
                )
                result['successes'].append('Change e-mail successful')

    if not result['errors'] and db_update:
        models.db.update('Users', db_update, [('id', '=', id_)])

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/resume/profile<:re:/?>', method=['GET', 'POST'])
@authenticate
def profile():
    id_ = auth_id or app.request.session['auth_id']
    tpl = '{base}/edit.html'.format(base=config.PAGES)
    user = models.db.read('Users', ['*'], [('id', '=', id_)])[0]
    profile = models.db.read('Profiles', ['*'], [('user_id', '=', id_)])[0]
    meta = globals()['meta']
    file_ = dict(photo=form.file('photo'))
    field = function.set_field(profile, ['id', 'photo', 'user_id'])
    submit = dict(upload=form.field('upload'), update=form.field('update'))
    result = dict(successes=[], errors=[])
    db_update = {}
    fieldset = {}
    misc = {}

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'Edit {title} information'.format(
        title=meta['title'].lower()
    )
    field['alt_email'] = field['alt_email'] or user['email']
    field['nickname'] = field['nickname'] \
                        or user['email'].split('@')[0].title()
    profile['photo'] = profile['photo'] or config.PHOTO
    misc['tpl'] = '/edit/{slug}.html'.format(slug=meta['slug'])
    misc['photo_info'] = function.image_info(profile['photo'])
    misc['photo_info']['alt'] = 'Photo of {nickname}'.format(
        nickname=field['nickname']
    )

    if submit['upload'] and not file_['photo']:
        result['errors'].append('There was no image to upload')

    if any(submit.values()) and file_['photo']:
        upload_file = Upload()

        if (upload_file.image(file_['photo'])):
            thumbnail = Thumbnail()

            if (thumbnail.create(upload_file.info['path'])):
                misc['photo_info'].update(thumbnail.info)
                db_update['photo'] = misc['photo_info']['path']
            else:
                result['errors'].extend(thumbnail.errors)
        else:
            result['errors'].extend(upload_file.errors)

    if submit['update']:
        if field['alt_email'] \
           and not re.search(r'^(\w+(-|.)\w+)@(\w+(-|.)\w+)(\.\w+){1,2}$',
                             field['alt_email'], re.IGNORECASE):
            result['errors'].append('Invalid e-mail format')

        if field['uri'] \
           and not re.search(r'^(http[s]?://)?(www\.)?(\w+(-|.)?\w+)'
                             '(\.\w+){1,2}\/?$', field['uri'], re.IGNORECASE):
            result['errors'].append('Invalid URI format')

    if any(submit.values()):
        field['alt_email'] = field['alt_email'].lower()
        field['uri'] = field['uri'].lower()

        db_update.update(field)

        if not result['errors'] and db_update:
            models.db.update('Profiles', db_update, [('user_id', '=', id_)])
            result['successes'].append('{title} successfully updated'.format(
                title=meta['title'])
            )

    # Update field with ordered key-value pairs:
    fieldset.update(
        function.set_fieldset(field, [
            'alt_email',
            'given_name',
            'additional_name',
            'family_name',
            'nickname',
            'uri',
            'tel',
            'summary'
        ])
    )

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    # Remove the following dict since it might contain heavy object upon upload
    del dict_locals['file_']

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/resume/address<:re:/?>', method=['GET', 'POST'])
@authenticate
def address():
    id_ = auth_id or app.request.session['auth_id']
    tpl = '{base}/edit.html'.format(base=config.PAGES)
    address = models.db.read('Addresses', ['*'], [('id', '=', id_)])[0]
    meta = globals()['meta']
    field = function.set_field(address)
    submit = dict(update=form.field('update'))
    result = dict(successes=[], errors=[])
    db_update = {}
    fieldset = {}
    misc = {}

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'Edit {title} information'.format(
        title=meta['title'].lower()
    )
    misc['tpl'] = '/edit/{slug}.html'.format(slug=meta['slug'])

    if submit['update']:
        db_update.update(field)

        if not result['errors'] and db_update:
            models.db.update('Addresses', db_update, [('user_id', '=', id_)])
            result['successes'].append('{title} successfully updated'.format(
                title=meta['title'])
            )

    # Update field with ordered key-value pairs:
    fieldset.update(
        function.set_fieldset(field, [
            'street',
            'locality',
            'region',
            'postal_code',
            'country'
        ])
    )

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/resume/<path:re:(education|experience)><:re:/?>',
           method=['GET', 'POST'])
@authenticate
def education_experience(path):
    id_ = auth_id or app.request.session['auth_id']
    tpl = '{base}/edit.html'.format(base=config.PAGES)
    table = '{path}s'.format(path=path.title())
    table_rows = models.db.read(table, ['*'], [('user_id', '=', id_)])
    meta = globals()['meta']
    submit = dict(
        add=form.field('add'),
        remove=form.field('remove'),
        update=form.field('update')
    )
    result = dict(successes=[], errors=[])
    db_update = {}
    fieldsets = []
    misc = {}
    form_id = form.field('id')

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'Edit {title} information'.format(
        title=meta['title'].lower()
    )
    misc['tpl'] = '/edit/generic_education_experience.html'

    if submit['add']:
        db_update.update(user_id=id_)
        models.db.insert(table, db_update)
        result['successes'].append('New form successfully added')
    elif submit['remove']:
        if len(table_rows) == 1:
            result['errors'].append('Cannot remove last available form')
        else:
            models.db.remove(table, [
                ('id', '=', form_id),
                ('user_id', '=', id_)
            ])
            result['successes'].append('Form successfully removed')
    elif submit['update']:
        table_rows = models.db.read(table, ['*'], [
            ('id', '=', form_id),
            ('user_id', '=', id_)
        ])

        if table_rows:
            pattern = r'\d{4}-\d{2}-\d{2}'

            for key in ['start', 'end']:
                field = str(form.field(key))

                if key == 'end':
                    pattern = r'({pattern}|present)'.format(pattern=pattern)

                if field and not re.search(pattern, field, re.IGNORECASE):
                    error_message = 'Invalid {title} date: should be YYYY-MM-DD'

                    if key == 'end':
                        error_message += ' or present'

                    result['errors'].append(
                        error_message.format(title=key.title())
                    )

            if form.field('uri') \
               and not re.search(r'^(http[s]?://)?(www\.)?(\w+(-|.)?\w+)'
                                 '(\.\w+){1,2}\/?$', form.field('uri'),
                                 re.IGNORECASE):
                result['errors'].append('Invalid URI format')

            if not result['errors']:
                field = function.set_field(table_rows[0])
                field['uri'] = field['uri'].lower()

                db_update.update(field)
                models.db.update(table, db_update, [
                    ('id', '=', form_id),
                    ('user_id', '=', id_)
                ])
                result['successes'].append(
                    '{title} successfully updated'.format(title=meta['title'])
                )

    # Update fields with ordered key-value pairs:
    if path == 'education':
        ordered_keys = [
            'id',
            'degree',
            'major',
            'school',
            'uri',
            'start',
            'end'
        ]
    elif path == 'experience':
        ordered_keys = [
            'id',
            'position',
            'description',
            'company',
            'uri',
            'start',
            'end'
        ]

    for table_row in table_rows:
        fieldsets.append(
            function.set_fieldset(table_row, ordered_keys)
        )

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['fieldsets'] = fieldsets
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/resume/<path:re:(skill|affiliation|publication)><:re:/?>',
           method=['GET', 'POST'])
@authenticate
def skill_affiliation_publication(path):
    id_ = auth_id or app.request.session['auth_id']
    tpl = '{base}/edit.html'.format(base=config.PAGES)
    table = '{path}s'.format(path=path.title())
    table_rows = models.db.read(table, ['*'], [('user_id', '=', id_)])
    meta = globals()['meta']
    submit = dict(
        add=form.field('add'),
        remove=form.field('remove'),
        update=form.field('update')
    )
    result = dict(successes=[], errors=[])
    db_update = {}
    fieldsets = []
    misc = {}
    form_id = form.field('id')

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = meta['path'].split('/')[-1]
    meta['slugs'] = [path for path in meta['path'].split('/') if path]
    meta['title'] = meta['slug'].title()
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'Edit {title} information'.format(
        title=meta['title'].lower()
    )
    misc['tpl'] = '/edit/generic_skill_affiliation_publication.html'

    if submit['add']:
        db_update.update(user_id=id_)
        models.db.insert(table, db_update)
        result['successes'].append('New form successfully added')
    elif submit['remove']:
        if len(table_rows) == 1:
            result['errors'].append('Cannot remove last available form')
        else:
            models.db.remove(table, [
                ('id', '=', form_id),
                ('user_id', '=', id_)
            ])
            result['successes'].append('Form successfully removed')
    elif submit['update']:
        table_rows = models.db.read(table, ['*'], [
            ('id', '=', form_id),
            ('user_id', '=', id_)
        ])

        if table_rows:
            field = function.set_field(table_rows[0])

            if 'slug' in field.keys():
                field['name'] = field['name'].lower()
                field['slug'] = field['name']

            for key in ['uri', 'url']:
                if key in field.keys():
                    field[key] = field[key].lower()

                    if field[key] \
                       and not re.search(r'^(http[s]?://)?(www\.)?(\w+(-|.)?\w+)'
                                         '(\.\w+){1,2}(\/\w+(-|_)?\w+)*\/?$',
                                         field[key], re.IGNORECASE):
                        result['errors'].append('Invalid URL format')

            if not result['errors']:
                db_update.update(field)
                models.db.update(table, db_update, [
                    ('id', '=', form_id),
                    ('user_id', '=', id_)
                ])
                result['successes'].append(
                    '{title} successfully updated'.format(title=meta['title'])
                )

    # Update fields with ordered key-value pairs:
    if path == 'skill':
        ordered_keys = [
            'id',
            'name',
            'url'
        ]
    elif path == 'affiliation':
        ordered_keys = [
            'id',
            'name',
            'uri'
        ]
    elif path == 'publication':
        ordered_keys = [
            'id',
            'title',
            'uri'
        ]

    for table_row in table_rows:
        fieldsets.append(
            function.set_fieldset(table_row, ordered_keys)
        )

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['fieldsets'] = fieldsets
    dict_locals['session'] = app.request.session
    dict_locals['cookie'] = cookie

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/logout<:re:/?>')
def logout():
    if 'auth_id' in app.request.session and app.request.session['auth_id']:
        app.request.session['auth_id'] = ''

    for key in ['id', 'token']:
        if key in cookie:
            cookie[key] = ''

    app.redirect('/')

@app.error(404)
@app.error(500)
def page_not_found(error):
    tpl = '{base}/404.html'.format(base=config.PAGES)
    meta = globals()['meta']
    result = dict(successes=[], errors=[])

    # Default values:
    meta['path'] = app.request.fullpath
    meta['slug'] = '404'
    meta['slugs'] = [
        path for path in meta['path'].split('/') if path
    ]
    meta['slugs'].insert(0, meta['slug'])
    meta['title'] = 'Page not found'
    meta['document_title'] = '{title} - {site_name}'.format(
        site_name=meta['site_name'],
        title=meta['title']
    )
    meta['description'] = 'The page does not exist'

    # Filter non-dict values:
    dict_locals = function.filter_non_dict_values(locals())
    dict_locals['session'] = {}

    log.info(function.console_log('dict_locals', dict_locals))
    return Template(filename=tpl, lookup=templates).render(**dict_locals)

@app.route('/<path:re:((img|css|js)/?|)><file_>')
def static_file(path, file_):
    if '.' not in file_:
        app.abort(404)

    return app.static_file(
        file_,
        root='{base}/{path}'.format(base=config.ASSETS, path=path)
    )

if __name__ ==  '__main__':
    try:
        # The pyclean command doesn't seem to work on others Linux distros
        # (e.g. CentOS)
        subprocess.call(['pyclean', '.']) # remove all .pyc and .pyo files
    except:
        pass

    app.run(
        server=config.SERVER,
        host=config.HOST,
        port=config.PORT,
        debug=config.DEBUG,
        app=session
    )
