# coding: utf-8

import logging
import requests
import mimetypes
from io import BytesIO
from urllib.parse import urlparse
from datetime import datetime, timedelta
from collections import OrderedDict
from flask_babelex import gettext as _
from flask import (
    render_template,
    abort,
    current_app,
    request,
    session,
    redirect,
    jsonify,
    url_for,
    Response,
    send_from_directory,
    g,
    make_response,
)
from werkzeug.contrib.atom import AtomFeed
from urllib.parse import urljoin
from legendarium.formatter import descriptive_short_format

from . import main
from webapp import babel
from webapp import cache
from webapp import controllers
from webapp.choices import STUDY_AREAS
from webapp.utils import utils
from webapp.utils.caching import cache_key_with_lang, cache_key_with_lang_with_qs
from webapp import forms

from webapp.config.lang_names import display_original_lang_name

from opac_schema.v1.models import Journal, Issue, Article, Collection

from lxml import etree
from packtools import HTMLGenerator

logger = logging.getLogger(__name__)

JOURNAL_UNPUBLISH = _("O periódico está indisponível por motivo de: ")
ISSUE_UNPUBLISH = _("O número está indisponível por motivo de: ")
ARTICLE_UNPUBLISH = _("O artigo está indisponível por motivo de: ")

IAHX_LANGS = dict(
    p='pt',
    e='es',
    i='en',
)


def url_external(endpoint, **kwargs):
    url = url_for(endpoint, **kwargs)
    return urljoin(request.url_root, url)


class RetryableError(Exception):
    """Erro recuperável sem que seja necessário modificar o estado dos dados
    na parte cliente, e.g., timeouts, erros advindos de particionamento de rede
    etc.
    """


class NonRetryableError(Exception):
    """Erro do qual não pode ser recuperado sem modificar o estado dos dados
    na parte cliente, e.g., recurso solicitado não exite, URI inválida etc.
    """


def fetch_data(url: str, timeout: float = 2) -> bytes:
    try:
        response = requests.get(url, timeout=timeout)
    except (requests.ConnectionError, requests.Timeout) as exc:
        raise RetryableError(exc) from exc
    except (requests.InvalidSchema, requests.MissingSchema, requests.InvalidURL) as exc:
        raise NonRetryableError(exc) from exc
    else:
        try:
            response.raise_for_status()
        except requests.HTTPError as exc:
            if 400 <= exc.response.status_code < 500:
                raise NonRetryableError(exc) from exc
            elif 500 <= exc.response.status_code < 600:
                raise RetryableError(exc) from exc
            else:
                raise

    return response.content


@main.before_app_request
def add_collection_to_g():

    if not hasattr(g, 'collection'):
        try:
            collection = controllers.get_current_collection()
            setattr(g, 'collection', collection)
        except Exception:
            # discutir o que fazer aqui
            setattr(g, 'collection', {})


@main.after_request
def add_header(response):
    response.headers['x-content-type-options'] = 'nosniff'
    return response

@main.after_request
def add_language_code(response):
    language = session.get('lang', get_locale())
    response.set_cookie('language', language)
    return response


@main.before_app_request
def add_forms_to_g():
    setattr(g, 'email_share', forms.EmailShareForm())
    setattr(g, 'email_contact', forms.ContactForm())
    setattr(g, 'error', forms.ErrorForm())


@main.before_app_request
def add_scielo_org_config_to_g():
    language = session.get('lang', get_locale())
    scielo_org_links = {
        key: url[language]
        for key, url in current_app.config.get('SCIELO_ORG_URIS', {}).items()
    }
    setattr(g, 'scielo_org', scielo_org_links)


@babel.localeselector
def get_locale():
    langs = current_app.config.get('LANGUAGES')
    lang_from_headers = request.accept_languages.best_match(list(langs.keys()))

    if 'lang' not in list(session.keys()):
        session['lang'] = lang_from_headers

    if not lang_from_headers and not session['lang']:
        # Caso não seja possível detectar o idioma e não tenhamos a chave lang
        # no seção, fixamos o idioma padrão.
        session['lang'] = current_app.config.get('BABEL_DEFAULT_LOCALE')

    return session['lang']


@main.route('/set_locale/<string:lang_code>/')
def set_locale(lang_code):
    langs = current_app.config.get('LANGUAGES')

    if lang_code not in list(langs.keys()):
        abort(400, _('Código de idioma inválido'))

    referrer = request.referrer
    hash = request.args.get('hash')
    if hash:
        referrer += "#" + hash

    # salvar o lang code na sessão
    session['lang'] = lang_code
    return redirect(referrer)


def get_lang_from_session():
    """
    Tenta retornar o idioma da seção, caso não consiga retorna
    BABEL_DEFAULT_LOCALE.
    """
    try:
        return session['lang']
    except KeyError:
        return current_app.config.get('BABEL_DEFAULT_LOCALE')


@main.route('/')
@cache.cached(key_prefix=cache_key_with_lang)
def index():
    language = session.get('lang', get_locale())
    news = controllers.get_latest_news_by_lang(language)

    tweets = controllers.get_collection_tweets()
    press_releases = controllers.get_press_releases({'language': language})

    urls = {
        'downloads': '{0}/w/accesses?collection={1}'.format(
            current_app.config['METRICS_URL'],
            current_app.config['OPAC_COLLECTION']),
        'references': '{0}/w/publication/size?collection={1}'.format(
            current_app.config['METRICS_URL'],
            current_app.config['OPAC_COLLECTION']),
        'other': '{0}/?collection={1}'.format(
            current_app.config['METRICS_URL'],
            current_app.config['OPAC_COLLECTION'])
    }

    if (
        g.collection is not None
        and isinstance(g.collection, Collection)
        and g.collection.metrics is not None
        and current_app.config['USE_HOME_METRICS']
    ):
        g.collection.metrics.total_journal = Journal.objects.filter(
            is_public=True, current_status="current"
        ).count()
        g.collection.metrics.total_article = Article.objects.filter(
            is_public=True
        ).count()

    context = {
        'news': news,
        'urls': urls,
        'tweets': tweets,
        'press_releases': press_releases,
    }

    return render_template("collection/index.html", **context)


# ##################################Collection###################################


@main.route('/journals/alpha')
@cache.cached(key_prefix=cache_key_with_lang)
def collection_list():
    allowed_filters = ["current", "no-current", ""]
    query_filter = request.args.get("status", "")

    if not query_filter in allowed_filters:
        query_filter = ""

    journals_list = [
        controllers.get_journal_json_data(journal)
        for journal in controllers.get_journals(query_filter=query_filter)
    ]

    return render_template("collection/list_journal.html",
                           **{'journals_list': journals_list, 'query_filter': query_filter})


@main.route("/journals/thematic")
@cache.cached(key_prefix=cache_key_with_lang)
def collection_list_thematic():
    allowed_query_filters = ["current", "no-current", ""]
    allowed_thematic_filters = ["areas", "wos", "publisher"]
    thematic_table = {
        "areas": "study_areas",
        "wos": "subject_categories",
        "publisher": "publisher_name",
    }
    query_filter = request.args.get("status", "")
    title_query = request.args.get("query", "")
    thematic_filter = request.args.get("filter", "areas")

    if not query_filter in allowed_query_filters:
        query_filter = ""

    if not thematic_filter in allowed_thematic_filters:
        thematic_filter = "areas"

    lang = get_lang_from_session()[:2].lower()
    objects = controllers.get_journals_grouped_by(
        thematic_table[thematic_filter],
        title_query,
        query_filter=query_filter,
        lang=lang,
    )

    return render_template(
        "collection/list_thematic.html",
        **{"objects": objects, "query_filter": query_filter, "filter": thematic_filter}
    )


@main.route('/journals/feed/')
@cache.cached(key_prefix=cache_key_with_lang)
def collection_list_feed():
    language = session.get('lang', get_locale())
    collection = controllers.get_current_collection()

    title = 'SciELO - %s - %s' % (collection.name, _('Últimos periódicos inseridos na coleção'))
    subtitle = _('10 últimos periódicos inseridos na coleção %s' % collection.name)

    feed = AtomFeed(title,
                    subtitle=subtitle,
                    feed_url=request.url, url=request.url_root)

    journals = controllers.get_journals_paginated(
        title_query='', page=1, order_by='-created', per_page=10)

    if not journals.items:
        feed.add('Nenhum periódico encontrado',
                 url=request.url,
                 updated=datetime.now())

    for journal in journals.items:
        issues = controllers.get_issues_by_jid(journal.jid, is_public=True)
        last_issue = issues[0] if issues else None

        articles = []
        if last_issue:
            articles = controllers.get_articles_by_iid(last_issue.iid,
                                                       is_public=True)

        result_dict = OrderedDict()
        for article in articles:
            section = article.get_section_by_lang(language[:2])
            result_dict.setdefault(section, [])
            result_dict[section].append(article)

        context = {
            'journal': journal,
            'articles': result_dict,
            'language': language,
            'last_issue': last_issue
        }

        feed.add(journal.title,
                 render_template("collection/list_feed_content.html", **context),
                 content_type='html',
                 author=journal.publisher_name,
                 url=url_external('main.journal_detail', url_seg=journal.url_segment),
                 updated=journal.updated,
                 published=journal.created)

    return feed.get_response()


@main.route("/about/", methods=['GET'])
@main.route('/about/<string:slug_name>', methods=['GET'])
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def about_collection(slug_name=None):
    language = session.get('lang', get_locale())

    context = {}
    page = None
    if slug_name:
        # caso seja uma página
        page = controllers.get_page_by_slug_name(slug_name, language)
        if not page:
            abort(404, _('Página não encontrada'))
        context['page'] = page
    else:
        # caso não seja uma página é uma lista
        pages = controllers.get_pages_by_lang(language)
        context['pages'] = pages

    return render_template("collection/about.html", **context)


# ###################################Journal#####################################


@main.route('/scielo.php/')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def router_legacy():

    script_php = request.args.get('script', None)
    pid = request.args.get('pid', None)
    tlng = request.args.get('tlng', None)
    allowed_scripts = [
        'sci_serial', 'sci_issuetoc', 'sci_arttext', 'sci_abstract', 'sci_issues', 'sci_pdf'
    ]
    if (script_php is not None) and (script_php in allowed_scripts) and not pid:
        # se tem pelo menos um param: pid ou script_php
        abort(400, _(u'Requsição inválida ao tentar acessar o artigo com pid: %s' % pid))
    elif script_php and pid:

        if script_php == 'sci_serial':
            # pid = issn
            journal = controllers.get_journal_by_issn(pid)

            if not journal:
                abort(404, _('Periódico não encontrado'))

            if not journal.is_public:
                abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

            return redirect(url_for('main.journal_detail',
                            url_seg=journal.url_segment), code=301)

        elif script_php == 'sci_issuetoc':

            issue = controllers.get_issue_by_pid(pid)

            if not issue:
                abort(404, _('Número não encontrado'))

            if not issue.is_public:
                abort(404, ISSUE_UNPUBLISH + _(issue.unpublish_reason))

            if not issue.journal.is_public:
                abort(404, JOURNAL_UNPUBLISH + _(issue.journal.unpublish_reason))

            if issue.url_segment and "ahead" in issue.url_segment:
                return redirect(
                    url_for('main.aop_toc', url_seg=url_seg), code=301)

            return redirect(
                url_for(
                    "main.issue_toc",
                    url_seg=issue.journal.url_segment,
                    url_seg_issue=issue.url_segment),
                301
            )

        elif script_php == 'sci_arttext' or script_php == 'sci_abstract':
            article = controllers.get_article_by_pid_v2(pid)
            if not article:
                abort(404, _('Artigo não encontrado'))

            # 'abstract' or None (not False, porque False converterá a string 'False')
            part = (script_php == 'sci_abstract' and 'abstract') or None

            if tlng not in article.languages:
                tlng = article.original_language

            return redirect(url_for('main.article_detail_v3',
                                    url_seg=article.journal.url_segment,
                                    article_pid_v3=article.aid,
                                    part=part,
                                    lang=tlng),
                            code=301)

        elif script_php == 'sci_issues':

            journal = controllers.get_journal_by_issn(pid)

            if not journal:
                abort(404, _('Periódico não encontrado'))

            if not journal.is_public:
                abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

            return redirect(url_for('main.issue_grid',
                                    url_seg=journal.url_segment), 301)

        elif script_php == 'sci_pdf':
            # accesso ao pdf do artigo:
            article = controllers.get_article_by_pid_v2(pid)
            if not article:
                abort(404, _('Artigo não encontrado'))

            return redirect(
                url_for(
                    'main.article_detail_v3',
                    url_seg=article.journal.url_segment,
                    article_pid_v3=article.aid,
                    format='pdf',
                ),
                code=301
            )

        else:
            abort(400, _(u'Requsição inválida ao tentar acessar o artigo com pid: %s' % pid))

    else:
        return redirect('/')


@main.route('/<string:journal_seg>')
@main.route('/journal/<string:journal_seg>')
def journal_detail_legacy_url(journal_seg):
    return redirect(url_for('main.journal_detail',
                            url_seg=journal_seg), code=301)


@main.route('/j/<string:url_seg>/')
@cache.cached(key_prefix=cache_key_with_lang)
def journal_detail(url_seg):
    journal = controllers.get_journal_by_url_seg(url_seg)

    if not journal:
        abort(404, _('Periódico não encontrado'))

    if not journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

    utils.fix_journal_last_issue(journal)

    # todo: ajustar para que seja só noticias relacionadas ao periódico
    language = session.get('lang', get_locale())
    news = controllers.get_latest_news_by_lang(language)

    # Press releases
    press_releases = controllers.get_press_releases({
        'journal': journal,
        'language': language})

    # Lista de seções
    # Mantendo sempre o idioma inglês para as seções na página incial do periódico
    if journal.last_issue and journal.current_status == "current":
        sections = [section for section in journal.last_issue.sections if section.language == 'en']
        recent_articles = controllers.get_recent_articles_of_issue(journal.last_issue.iid, is_public=True)
    else:
        sections = []
        recent_articles = []

    latest_issue = journal.last_issue

    if latest_issue:
        latest_issue_legend = descriptive_short_format(
            title=journal.title, short_title=journal.short_title,
            pubdate=str(latest_issue.year), volume=latest_issue.volume, number=latest_issue.number,
            suppl=latest_issue.suppl_text, language=language[:2].lower())
    else:
        latest_issue_legend = ''

    journal_metrics = controllers.get_journal_metrics(journal)

    context = {
        'journal': journal,
        'press_releases': press_releases,
        'recent_articles': recent_articles,
        'journal_study_areas': [
            STUDY_AREAS.get(study_area.upper()) for study_area in journal.study_areas
        ],
        # o primiero item da lista é o último número.
        # condicional para verificar se issues contém itens
        'last_issue': latest_issue,
        'latest_issue_legend': latest_issue_legend,
        'sections': sections if sections else None,
        'news': news,
        'journal_metrics': journal_metrics
    }

    return render_template("journal/detail.html", **context)


@main.route('/journal/<string:url_seg>/feed/')
@cache.cached(key_prefix=cache_key_with_lang)
def journal_feed(url_seg):
    journal = controllers.get_journal_by_url_seg(url_seg)

    if not journal:
        abort(404, _('Periódico não encontrado'))

    if not journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

    issues = controllers.get_issues_by_jid(journal.jid, is_public=True)
    last_issue = issues[0] if issues else None
    articles = controllers.get_articles_by_iid(last_issue.iid, is_public=True)

    feed = AtomFeed(journal.title,
                    feed_url=request.url,
                    url=request.url_root,
                    subtitle=utils.get_label_issue(last_issue))

    feed_language = session.get('lang', get_locale())
    feed_language = feed_language[:2].lower()

    for article in articles:

        # ######### TODO: Revisar #########
        article_lang = feed_language
        if feed_language not in article.languages:
            article_lang = article.original_language

        feed.add(article.title or _('Artigo sem título'),
                 render_template("issue/feed_content.html", article=article),
                 content_type='html',
                 id=article.doi or article.pid,
                 author=article.authors,
                 url=url_external('main.article_detail_v3',
                                  url_seg=journal.url_segment,
                                  article_pid_v3=article.aid,
                                  lang=article_lang),
                 updated=journal.updated,
                 published=journal.created)

    return feed.get_response()


@main.route("/journal/<string:url_seg>/about/", methods=['GET'])
@cache.cached(key_prefix=cache_key_with_lang)
def about_journal(url_seg):
    language = session.get('lang', get_locale())

    journal = controllers.get_journal_by_url_seg(url_seg)

    if not journal:
        abort(404, _('Periódico não encontrado'))

    if not journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

    latest_issue = utils.fix_journal_last_issue(journal)

    if latest_issue:
        latest_issue_legend = descriptive_short_format(
            title=journal.title, short_title=journal.short_title,
            pubdate=str(latest_issue.year), volume=latest_issue.volume, number=latest_issue.number,
            suppl=latest_issue.suppl_text, language=language[:2].lower())
    else:
        latest_issue_legend = None

    page = controllers.get_page_by_journal_acron_lang(journal.acronym, language)

    context = {
        'journal': journal,
        'latest_issue_legend': latest_issue_legend,
        'last_issue': latest_issue,
        'journal_study_areas': [
            STUDY_AREAS.get(study_area.upper()) for study_area in journal.study_areas
        ],
    }

    if page:
        context['content'] = page.content
        if page.updated_at:
            context['page_updated_at'] = page.updated_at

    return render_template("journal/about.html", **context)


@main.route("/journals/search/alpha/ajax/", methods=['GET', ])
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def journals_search_alpha_ajax():

    if not request.is_xhr:
        abort(400, _('Requisição inválida. Deve ser por ajax'))

    query = request.args.get('query', '', type=str)
    query_filter = request.args.get('query_filter', '', type=str)
    page = request.args.get('page', 1, type=int)
    lang = get_lang_from_session()[:2].lower()

    response_data = controllers.get_alpha_list_from_paginated_journals(
                        title_query=query,
                        query_filter=query_filter,
                        page=page,
                        lang=lang)

    return jsonify(response_data)


@main.route("/journals/search/group/by/filter/ajax/", methods=['GET'])
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def journals_search_by_theme_ajax():

    if not request.is_xhr:
        abort(400, _('Requisição inválida. Deve ser por ajax'))

    query = request.args.get('query', '', type=str)
    query_filter = request.args.get('query_filter', '', type=str)
    filter = request.args.get('filter', 'areas', type=str)
    lang = get_lang_from_session()[:2].lower()

    if filter == 'areas':
        objects = controllers.get_journals_grouped_by('study_areas', query, query_filter=query_filter, lang=lang)
    elif filter == 'wos':
        objects = controllers.get_journals_grouped_by('subject_categories', query, query_filter=query_filter, lang=lang)
    elif filter == 'publisher':
        objects = controllers.get_journals_grouped_by('publisher_name', query, query_filter=query_filter, lang=lang)
    else:
        return jsonify({
            'error': 401,
            'message': _('Parámetro "filter" é inválido, deve ser "areas", "wos" ou "publisher".')
        })
    return jsonify(objects)


@main.route("/journals/download/<string:list_type>/<string:extension>/", methods=['GET', ])
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def download_journal_list(list_type, extension):
    if extension.lower() not in ['csv', 'xls']:
        abort(401, _('Parámetro "extension" é inválido, deve ser "csv" ou "xls".'))
    elif list_type.lower() not in ['alpha', 'areas', 'wos', 'publisher']:
        abort(401, _('Parámetro "list_type" é inválido, deve ser: "alpha", "areas", "wos" ou "publisher".'))
    else:
        if extension.lower() == 'xls':
            mimetype = 'application/vnd.ms-excel'
        else:
            mimetype = 'text/csv'
        query = request.args.get('query', '', type=str)
        data = controllers.get_journal_generator_for_csv(list_type=list_type,
                                                         title_query=query,
                                                         extension=extension.lower())
        timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        filename = 'journals_%s_%s.%s' % (list_type, timestamp, extension)
        response = Response(data, mimetype=mimetype)
        response.headers['Content-Disposition'] = 'attachment; filename=%s' % filename
        return response


@main.route("/<string:url_seg>/contact", methods=['POST'])
def contact(url_seg):

    if not request.is_xhr:
        abort(403, _('Requisição inválida, deve ser ajax.'))

    if utils.is_recaptcha_valid(request):

        form = forms.ContactForm(request.form)

        journal = controllers.get_journal_by_url_seg(url_seg)

        if not journal.enable_contact:
            abort(403, _('Periódico não permite envio de email.'))

        recipients = journal.editor_email

        if form.validate():
            sent, message = controllers.send_email_contact(recipients,
                                                           form.data['name'],
                                                           form.data['your_email'],
                                                           form.data['message'])

            return jsonify({'sent': sent, 'message': str(message),
                            'fields': [key for key in form.data.keys()]})

        else:
            return jsonify({'sent': False, 'message': form.errors,
                            'fields': [key for key in form.data.keys()]})

    else:
        abort(400, _('Requisição inválida, captcha inválido.'))


@main.route("/form_contact/<string:url_seg>/", methods=['GET'])
def form_contact(url_seg):
    journal = controllers.get_journal_by_url_seg(url_seg)
    if not journal:
        abort(404, _('Periódico não encontrado'))

    context = {
        'journal': journal
    }
    return render_template("journal/includes/contact_form.html", **context)


# ###################################Issue#######################################


@main.route('/grid/<string:url_seg>/')
def issue_grid_legacy(url_seg):
    return redirect(url_for('main.issue_grid', url_seg=url_seg), 301)


@main.route('/j/<string:url_seg>/grid')
@cache.cached(key_prefix=cache_key_with_lang)
def issue_grid(url_seg):
    journal = controllers.get_journal_by_url_seg(url_seg)

    if not journal:
        abort(404, _('Periódico não encontrado'))

    if not journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

    # idioma da sessão
    language = session.get('lang', get_locale())

    # A ordenação padrão da função ``get_issues_by_jid``: "-year", "-volume", "-order"
    issues_data = controllers.get_issues_for_grid_by_jid(journal.id, is_public=True)
    latest_issue = issues_data['last_issue']
    if latest_issue:
        latest_issue_legend = descriptive_short_format(
            title=journal.title, short_title=journal.short_title,
            pubdate=str(latest_issue.year), volume=latest_issue.volume, number=latest_issue.number,
            suppl=latest_issue.suppl_text, language=language[:2].lower())
    else:
        latest_issue_legend = None

    context = {
        'journal': journal,
        'last_issue': issues_data['last_issue'],
        'latest_issue_legend': latest_issue_legend,
        'volume_issue': issues_data['volume_issue'],
        'ahead': issues_data['ahead'],
        'result_dict': issues_data['ordered_for_grid'],
        'journal_study_areas': [
            STUDY_AREAS.get(study_area.upper()) for study_area in journal.study_areas
        ],
    }

    return render_template("issue/grid.html", **context)


@main.route('/toc/<string:url_seg>/<string:url_seg_issue>/')
def issue_toc_legacy(url_seg, url_seg_issue):
    if url_seg_issue and "ahead" in url_seg_issue:
        return redirect(url_for('main.aop_toc', url_seg=url_seg), code=301)

    return redirect(
        url_for('main.issue_toc',
                url_seg=url_seg,
                url_seg_issue=url_seg_issue),
        code=301)


@main.route('/j/<string:url_seg>/i/<string:url_seg_issue>/')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def issue_toc(url_seg, url_seg_issue):
    section_filter = None
    goto = request.args.get("goto", None, type=str)
    if goto not in ("previous", "next"):
        goto = None

    if goto in (None, "next") and "ahead" in url_seg_issue:
        # redireciona para `aop_toc`
        return redirect(url_for('main.aop_toc', url_seg=url_seg), code=301)

    # idioma da sessão
    language = session.get('lang', get_locale())

    if current_app.config["FILTER_SECTION_ENABLE"]:
        # seção dos documentos, se selecionada
        section_filter = request.args.get('section', '', type=str).upper()

    # obtém o issue
    issue = controllers.get_issue_by_url_seg(url_seg, url_seg_issue)
    if not issue:
        abort(404, _('Número não encontrado'))
    if not issue.is_public:
        abort(404, ISSUE_UNPUBLISH + _(issue.unpublish_reason))

    # obtém o journal
    journal = issue.journal
    if not journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

    # completa url_segment do last_issue
    utils.fix_journal_last_issue(journal)

    # goto_next_or_previous_issue (redireciona)
    goto_url = goto_next_or_previous_issue(
        issue, request.args.get('goto', None, type=str))
    if goto_url:
        return redirect(goto_url, code=301)

    # obtém os documentos
    articles = controllers.get_articles_by_iid(issue.iid, is_public=True)
    if articles:
        # obtém TODAS as seções dos documentos deste sumário
        sections = sorted({a.section.upper() for a in articles if a.section})
    else:
        # obtém as seções dos documentos deste sumário
        sections = []

    if current_app.config["FILTER_SECTION_ENABLE"] and section_filter != '':
        # obtém somente os documentos da seção selecionada
        articles = [a for a in articles if a.section.upper() == section_filter]

    # obtém PDF e TEXT de cada documento
    has_math_content = False
    for article in articles:
        article_text_languages = [doc['lang'] for doc in article.htmls]
        article_pdf_languages = [(doc['lang'], doc['url']) for doc in article.pdfs]
        setattr(article, "article_text_languages", article_text_languages)
        setattr(article, "article_pdf_languages", article_pdf_languages)
        if 'mml:' in article.title:
            has_math_content = True

    # obtém a legenda bibliográfica
    issue_bibliographic_strip = descriptive_short_format(
        title=journal.title, short_title=journal.short_title,
        pubdate=str(issue.year), volume=issue.volume, number=issue.number,
        suppl=issue.suppl_text, language=language[:2].lower())

    context = {
        'this_page_url': url_for(
                            'main.issue_toc',
                            url_seg=url_seg,
                            url_seg_issue=url_seg_issue),
        'has_math_content': has_math_content,
        'journal': journal,
        'issue': issue,
        'issue_bibliographic_strip': issue_bibliographic_strip,
        'articles': articles,
        'sections': sections,
        'section_filter': section_filter,
        'journal_study_areas': [
            STUDY_AREAS.get(study_area.upper()) for study_area in journal.study_areas
        ],
        'last_issue': journal.last_issue
    }
    return render_template("issue/toc.html", **context)


def goto_next_or_previous_issue(current_issue, goto_param):
    if goto_param not in ["next", "previous"]:
        return None

    all_issues = list(
        controllers.get_issues_by_jid(current_issue.journal.id, is_public=True))
    if goto_param == "next":
        selected_issue = utils.get_next_issue(all_issues, current_issue)
    elif goto_param == "previous":
        selected_issue = utils.get_prev_issue(all_issues, current_issue)
    if selected_issue in (None, current_issue):
        # nao precisa redirecionar
        return None
    try:
        url_seg_issue = selected_issue.url_segment
    except AttributeError:
        return None
    else:
        return url_for('main.issue_toc',
                       url_seg=selected_issue.journal.url_segment,
                       url_seg_issue=url_seg_issue)


def get_next_or_previous_issue(current_issue, goto_param):
    if goto_param not in ["next", "previous"]:
        return current_issue

    all_issues = list(
        controllers.get_issues_by_jid(current_issue.journal.id, is_public=True))
    if goto_param == "next":
        return utils.get_next_issue(all_issues, current_issue)
    return utils.get_prev_issue(all_issues, current_issue)


@main.route('/j/<string:url_seg>/aop')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def aop_toc(url_seg):

    section_filter = request.args.get('section', '', type=str).upper()

    aop_issues = controllers.get_aop_issues(url_seg) or []
    if not aop_issues:
        abort(404, _('Artigos ahead of print não encontrados'))

    goto = request.args.get("goto", None, type=str)
    if goto == "previous":
        url = goto_next_or_previous_issue(aop_issues[-1], goto)
        if url:
            redirect(url, code=301)

    journal = aop_issues[0].journal
    if not journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(journal.unpublish_reason))

    utils.fix_journal_last_issue(journal)

    articles = []
    for aop_issue in aop_issues:
        _articles = controllers.get_articles_by_iid(
            aop_issue.iid, is_public=True)
        if _articles:
            articles.extend(_articles)
    if not articles:
        abort(404, _('Artigos ahead of print não encontrados'))

    sections = sorted({a.section.upper() for a in articles if a.section})
    if section_filter != '':
        articles = [a for a in articles if a.section.upper() == section_filter]

    for article in articles:
        article_text_languages = [doc['lang'] for doc in article.htmls]
        article_pdf_languages = [(doc['lang'], doc['url']) for doc in article.pdfs]

        setattr(article, "article_text_languages", article_text_languages)
        setattr(article, "article_pdf_languages", article_pdf_languages)

    context = {
        'this_page_url': url_for("main.aop_toc", url_seg=url_seg),
        'journal': journal,
        'issue': aop_issues[0],
        'issue_bibliographic_strip': "ahead of print",
        'articles': articles,
        'sections': sections,
        'section_filter': section_filter,
        'journal_study_areas': [
            STUDY_AREAS.get(study_area.upper())
            for study_area in journal.study_areas
        ],
        # o primeiro item da lista é o último número.
        'last_issue': journal.last_issue
    }

    return render_template("issue/toc.html", **context)


@main.route('/feed/<string:url_seg>/<string:url_seg_issue>/')
@cache.cached(key_prefix=cache_key_with_lang)
def issue_feed(url_seg, url_seg_issue):
    issue = controllers.get_issue_by_url_seg(url_seg, url_seg_issue)

    if not issue:
        abort(404, _('Número não encontrado'))

    if not issue.is_public:
        abort(404, ISSUE_UNPUBLISH + _(issue.unpublish_reason))

    if not issue.journal.is_public:
        abort(404, JOURNAL_UNPUBLISH + _(issue.journal.unpublish_reason))

    journal = issue.journal
    articles = controllers.get_articles_by_iid(issue.iid, is_public=True)

    feed = AtomFeed(journal.title or "",
                    feed_url=request.url,
                    url=request.url_root,
                    subtitle=utils.get_label_issue(issue))

    feed_language = session.get('lang', get_locale())

    for article in articles:
        # ######### TODO: Revisar #########
        article_lang = feed_language
        if feed_language not in article.languages:
            article_lang = article.original_language

        feed.add(article.title or 'Unknow title',
                 render_template("issue/feed_content.html", article=article),
                 content_type='html',
                 author=article.authors,
                 id=article.doi or article.pid,
                 url=url_external('main.article_detail_v3',
                                  url_seg=journal.url_segment,
                                  article_pid_v3=article.aid,
                                  lang=article_lang),
                 updated=journal.updated,
                 published=journal.created)

    return feed.get_response()

# ##################################Article######################################


@main.route('/article/<regex("S\d{4}-\d{3}[0-9xX][0-2][0-9]{3}\d{4}\d{5}"):pid>/')
@cache.cached(key_prefix=cache_key_with_lang)
def article_detail_pid(pid):

    article = controllers.get_article_by_pid(pid)

    if not article:
        article = controllers.get_article_by_oap_pid(pid)

    if not article:
        abort(404, _('Artigo não encontrado'))

    return redirect(url_for('main.article_detail_v3',
                            url_seg=article.journal.acronym,
                            article_pid_v3=article.aid))


def render_html_from_xml(article, lang, gs_abstract=False):
    logger.debug("Get XML: %s", article.xml)

    if current_app.config["SSM_XML_URL_REWRITE"]:
        result = fetch_data(use_ssm_url(article.xml))
    else:
        result = fetch_data(article.xml)

    xml = etree.parse(BytesIO(result))

    generator = HTMLGenerator.parse(
        xml, valid_only=False, gs_abstract=gs_abstract, output_style="website")

    return generator.generate(lang), generator.languages


def render_html_from_html(article, lang):
    html_url = [html
                for html in article.htmls
                if html['lang'] == lang]

    try:
        html_url = html_url[0]['url']
    except IndexError:
        raise ValueError('Artigo não encontrado') from None

    result = fetch_data(use_ssm_url(html_url))

    html = result.decode('utf8')

    text_languages = [html['lang'] for html in article.htmls]

    return html, text_languages


def render_html_abstract(article, lang):
    abstract_text = ''
    for abstract in article.abstracts:
        if abstract['language'] == lang:
            abstract_text = abstract["text"]
            break
    return abstract_text, article.abstract_languages


def render_html(article, lang, gs_abstract=False):
    if article.xml:
        return render_html_from_xml(article, lang, gs_abstract)
    elif article.htmls:
        if gs_abstract:
            return render_html_abstract(article, lang)
        return render_html_from_html(article, lang)
    else:
        # TODO: Corrigir os teste que esperam ter o atributo ``htmls``
        # O ideal seria levantar um ValueError.
        return '', []


# TODO: Remover assim que o valor Article.xml estiver consistente na base de
# dados
def use_ssm_url(url):
    """Normaliza a string `url` de acordo com os valores das diretivas de
    configuração OPAC_SSM_SCHEME, OPAC_SSM_DOMAIN e OPAC_SSM_PORT.

    A normalização busca obter uma URL absoluta em função de uma relativa, ou
    uma absoluta em função de uma absoluta, mas com as partes *scheme* e
    *authority* trocadas pelas definidas nas diretivas citadas anteriormente.

    Este código deve ser removido assim que o valor de Article.xml estiver
    consistente, i.e., todos os registros possuirem apenas URLs absolutas.
    """
    if url.startswith("http"):
        parsed_url = urlparse(url)
        return current_app.config["SSM_BASE_URI"] + parsed_url.path
    else:
        return current_app.config["SSM_BASE_URI"] + url


@main.route('/article/<string:url_seg>/<string:url_seg_issue>/<string:url_seg_article>/')
@main.route('/article/<string:url_seg>/<string:url_seg_issue>/<string:url_seg_article>/<regex("(?:\w{2})"):lang_code>/')
@main.route('/article/<string:url_seg>/<string:url_seg_issue>/<regex("(.*)"):url_seg_article>/')
@main.route('/article/<string:url_seg>/<string:url_seg_issue>/<regex("(.*)"):url_seg_article>/<regex("(?:\w{2})"):lang_code>/')
@cache.cached(key_prefix=cache_key_with_lang)
def article_detail(url_seg, url_seg_issue, url_seg_article, lang_code=''):
    issue = controllers.get_issue_by_url_seg(url_seg, url_seg_issue)
    if not issue:
        abort(404, _('Issue não encontrado'))

    article = controllers.get_article_by_issue_article_seg(issue.iid, url_seg_article)
    if article is None:
        article = controllers.get_article_by_aop_url_segs(
            issue.journal, url_seg_issue, url_seg_article
        )
    if article is None:
        abort(404, _('Artigo não encontrado'))

    req_params = {
            "url_seg": article.journal.acronym,
            "article_pid_v3": article.aid,
    }
    if lang_code:
        req_params["lang"] = lang_code

    return redirect(url_for('main.article_detail_v3', **req_params))


@main.route('/j/<string:url_seg>/a/<string:article_pid_v3>/')
@main.route('/j/<string:url_seg>/a/<string:article_pid_v3>/<string:part>/')
@cache.cached(key_prefix=cache_key_with_lang)
def article_detail_v3(url_seg, article_pid_v3, part=None):
    qs_lang = request.args.get('lang', type=str) or None
    qs_goto = request.args.get('goto', type=str) or None
    qs_stop = request.args.get('stop', type=str) or None
    qs_format = request.args.get('format', 'html', type=str)


    gs_abstract = (part == "abstract")
    if part and not gs_abstract:
        abort(404,
              _("Não existe '{}'. No seu lugar use '{}'"
                ).format(part, 'abstract'))

    try:
        qs_lang, article = controllers.get_article(
            article_pid_v3, url_seg, qs_lang, gs_abstract, qs_goto)
        if qs_goto:
            return redirect(
                url_for(
                    'main.article_detail_v3',
                    url_seg=url_seg,
                    article_pid_v3=article.aid,
                    part=part,
                    format=qs_format,
                    lang=qs_lang,
                    stop=getattr(article, 'stop', None),
                ),
                code=301
            )
    except (controllers.PreviousOrNextArticleNotFoundError) as e:
        if gs_abstract:
            abort(404, _('Resumo inexistente'))
        abort(404, _('Artigo inexistente'))
    except (controllers.ArticleNotFoundError,
            controllers.ArticleJournalNotFoundError):
        abort(404, _('Artigo não encontrado'))
    except controllers.ArticleLangNotFoundError:
        return redirect(
            url_for(
                'main.article_detail_v3',
                url_seg=url_seg,
                article_pid_v3=article_pid_v3,
                format=qs_format,
            ),
            code=301
        )
    except controllers.ArticleAbstractNotFoundError:
        abort(404, _('Recurso não encontrado'))
    except controllers.ArticleIsNotPublishedError as e:
        abort(404, "{}{}".format(ARTICLE_UNPUBLISH, e))
    except controllers.IssueIsNotPublishedError as e:
        abort(404, "{}{}".format(ISSUE_UNPUBLISH, e))
    except controllers.JournalIsNotPublishedError as e:
        abort(404, "{}{}".format(JOURNAL_UNPUBLISH, e))
    except ValueError as e:
        abort(404, str(e))

    def _handle_html():

        citation_pdf_url = None
        for pdf_data in article.pdfs:
            if pdf_data.get("lang") == qs_lang:
                citation_pdf_url = url_for(
                          'main.article_detail_v3',
                          url_seg=article.journal.url_segment,
                          article_pid_v3=article_pid_v3,
                          lang=qs_lang,
                          format="pdf",
                       )
                break

        website = request.url
        if website:
            parsed_url = urlparse(request.url)
            if current_app.config["FORCE_USE_HTTPS_GOOGLE_TAGS"]:
                website = "{}://{}".format('https', parsed_url.netloc)
            else:
                website = "{}://{}".format(parsed_url.scheme, parsed_url.netloc)
        if citation_pdf_url:
            citation_pdf_url = "{}{}".format(website, citation_pdf_url)
        try:
            html, text_languages = render_html(article, qs_lang, gs_abstract)
        except (ValueError, NonRetryableError):
            abort(404, _('HTML do Artigo não encontrado ou indisponível'))
        except RetryableError:
            abort(500, _('Erro inesperado'))

        text_versions = sorted(
               [
                   (
                       lang,
                       display_original_lang_name(lang),
                       url_for(
                          'main.article_detail_v3',
                          url_seg=article.journal.url_segment,
                          article_pid_v3=article_pid_v3,
                          lang=lang
                       )
                   )
                   for lang in text_languages
               ]
           )
        citation_xml_url = "{}{}".format(
            website,
            url_for(
                'main.article_detail_v3',
                url_seg=article.journal.url_segment,
                article_pid_v3=article_pid_v3,
                format="xml",
                lang=article.original_language,
            )
        )
        context = {
            'next_article': qs_stop != 'next',
            'previous_article': qs_stop != 'previous',
            'article': article,
            'journal': article.journal,
            'issue': article.issue,
            'html': html,
            'citation_pdf_url': citation_pdf_url,
            'citation_xml_url': citation_xml_url,
            'article_lang': qs_lang,
            'text_versions': text_versions,
            'related_links': controllers.related_links(article),
            'gs_abstract': gs_abstract,
            'part': part,
        }
        return render_template("article/detail.html", **context)

    def _handle_pdf():
        if not article.pdfs:
            abort(404, _('PDF do Artigo não encontrado'))

        pdf_info = [pdf for pdf in article.pdfs if pdf['lang'] == qs_lang]
        if len(pdf_info) != 1:
            abort(404, _('PDF do Artigo não encontrado'))

        try:
            pdf_url = pdf_info[0]['url']
        except (IndexError, KeyError, ValueError, TypeError):
            abort(404, _('PDF do Artigo não encontrado'))

        if pdf_url:
            return get_pdf_content(pdf_url)
        raise abort(404, _('Recurso do Artigo não encontrado. Caminho inválido!'))

    def _handle_xml():
        if current_app.config["SSM_XML_URL_REWRITE"]:
            result = fetch_data(use_ssm_url(article.xml))
        else:
            result = fetch_data(article.xml)
        response = make_response(result)
        response.headers['Content-Type'] = 'application/xml'
        return response

    if 'html' == qs_format:
        return _handle_html()
    elif 'pdf' == qs_format:
        return _handle_pdf()
    elif 'xml' == qs_format:
        return _handle_xml()
    else:
        abort(400, _('Formato não suportado'))


@main.route('/readcube/epdf/')
@main.route('/readcube/epdf.php')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def article_epdf():
    doi = request.args.get('doi', None, type=str)
    pid = request.args.get('pid', None, type=str)
    pdf_path = request.args.get('pdf_path', None, type=str)
    lang = request.args.get('lang', None, type=str)

    if not all([doi, pid, pdf_path, lang]):
        abort(400, _('Parâmetros insuficientes para obter o EPDF do artigo'))
    else:
        context = {
            'doi': doi,
            'pid': pid,
            'pdf_path': pdf_path,
            'lang': lang,
        }
        return render_template("article/epdf.html", **context)


def get_pdf_content(url):
    logger.debug("Get PDF: %s", url)
    if current_app.config["SSM_ARTICLE_ASSETS_OR_RENDITIONS_URL_REWRITE"]:
        url = use_ssm_url(url)
    try:
        response = fetch_data(url)
    except NonRetryableError:
        abort(404, _('PDF não encontrado'))
    except RetryableError:
        abort(500, _('Erro inesperado'))
    else:
        mimetype, __ = mimetypes.guess_type(url)
        return Response(response, mimetype=mimetype)


@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def get_content_from_ssm(resource_ssm_media_path):
    resource_ssm_full_url = current_app.config['SSM_BASE_URI'] + resource_ssm_media_path

    url = resource_ssm_full_url.strip()
    mimetype, __ = mimetypes.guess_type(url)

    try:
        ssm_response = fetch_data(url)
    except NonRetryableError:
        abort(404, _('Recurso não encontrado'))
    except RetryableError:
        abort(500, _('Erro inesperado'))
    else:
        return Response(ssm_response, mimetype=mimetype)


@main.route('/media/assets/<regex("(.*)"):relative_media_path>')
@cache.cached(key_prefix=cache_key_with_lang)
def media_assets_proxy(relative_media_path):
    resource_ssm_path = '{ssm_media_path}{resource_path}'.format(
        ssm_media_path=current_app.config['SSM_MEDIA_PATH'],
        resource_path=relative_media_path)
    return get_content_from_ssm(resource_ssm_path)


@main.route('/article/ssm/content/raw/')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def article_ssm_content_raw():
    resource_ssm_path = request.args.get('resource_ssm_path', None)
    if not resource_ssm_path:
        raise abort(404, _('Recurso do Artigo não encontrado. Caminho inválido!'))
    else:
        return get_content_from_ssm(resource_ssm_path)


@main.route('/pdf/<string:url_seg>/<string:url_seg_issue>/<string:url_seg_article>')
@main.route('/pdf/<string:url_seg>/<string:url_seg_issue>/<string:url_seg_article>/<regex("(?:\w{2})"):lang_code>')
@main.route('/pdf/<string:url_seg>/<string:url_seg_issue>/<regex("(.*)"):url_seg_article>')
@main.route('/pdf/<string:url_seg>/<string:url_seg_issue>/<regex("(.*)"):url_seg_article>/<regex("(?:\w{2})"):lang_code>')
@cache.cached(key_prefix=cache_key_with_lang)
def article_detail_pdf(url_seg, url_seg_issue, url_seg_article, lang_code=''):
    """
    Padrões esperados:
        `/pdf/csc/2021.v26suppl1/2557-2558`
        `/pdf/csc/2021.v26suppl1/2557-2558/en`
    """
    if not lang_code and "." not in url_seg_issue:
        return router_legacy_pdf(url_seg, url_seg_issue, url_seg_article)

    issue = controllers.get_issue_by_url_seg(url_seg, url_seg_issue)
    if not issue:
        abort(404, _('Issue não encontrado'))

    article = controllers.get_article_by_issue_article_seg(issue.iid, url_seg_article)
    if not article:
        abort(404, _('Artigo não encontrado'))

    req_params = {
        'url_seg': article.journal.url_segment,
        'article_pid_v3': article.aid,
        'format': 'pdf',
    }
    if lang_code:
        req_params['lang'] = lang_code

    return redirect(url_for('main.article_detail_v3', **req_params), code=301)


@main.route('/pdf/<string:journal_acron>/<string:issue_info>/<string:pdf_filename>.pdf')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def router_legacy_pdf(journal_acron, issue_info, pdf_filename):
    pdf_filename = '%s.pdf' % pdf_filename

    journal = controllers.get_journal_by_url_seg(journal_acron)

    if not journal:
        abort(404, _('Este PDF não existe em http://www.scielo.br. Consulte http://search.scielo.org'))

    article = controllers.get_article_by_pdf_filename(
        journal_acron, issue_info, pdf_filename)

    if not article:
        abort(404, _('PDF do artigo não foi encontrado'))

    return redirect(
        url_for(
            'main.article_detail_v3',
            url_seg=article.journal.url_segment,
            article_pid_v3=article.aid,
            format='pdf',
            lang=article._pdf_lang,
        ),
        code=301
    )

@main.route('/cgi-bin/fbpe/<string:text_or_abstract>/')
@cache.cached(key_prefix=cache_key_with_lang_with_qs)
def router_legacy_article(text_or_abstract):
    pid = request.args.get('pid', None)
    lng = request.args.get('lng', None)
    if not (text_or_abstract in ['fbtext', 'fbabs'] and pid):
        # se tem pid
        abort(400, _('Requsição inválida ao tentar acessar o artigo com pid: %s' % pid))

    article = controllers.get_article_by_pid_v1(pid)
    if not article:
        abort(404, _('Artigo não encontrado'))

    return redirect(
        url_for(
            'main.article_detail_v3',
            url_seg=article.journal.url_segment,
            article_pid_v3=article.aid,
        ),
        code=301
    )


# ###############################E-mail share##################################


@main.route("/email_share_ajax/", methods=['POST'])
def email_share_ajax():

    if not request.is_xhr:
        abort(400, _('Requisição inválida.'))

    form = forms.EmailShareForm(request.form)

    if form.validate():
        recipients = [email.strip() for email in form.data['recipients'].split(';') if email.strip() != '']

        sent, message = controllers.send_email_share(form.data['your_email'],
                                                     recipients,
                                                     form.data['share_url'],
                                                     form.data['subject'],
                                                     form.data['comment'])

        return jsonify({'sent': sent, 'message': str(message),
                        'fields': [key for key in form.data.keys()]})

    else:
        return jsonify({'sent': False, 'message': form.errors,
                        'fields': [key for key in form.data.keys()]})


@main.route("/form_mail/", methods=['GET'])
def email_form():
    context = {'url': request.args.get('url')}
    return render_template("email/email_form.html", **context)


@main.route("/email_error_ajax/", methods=['POST'])
def email_error_ajax():

    if not request.is_xhr:
        abort(400, _('Requisição inválida.'))

    form = forms.ErrorForm(request.form)

    if form.validate():

        recipients = [email.strip() for email in current_app.config.get('EMAIL_ACCOUNTS_RECEIVE_ERRORS') if email.strip() != '']

        sent, message = controllers.send_email_error(form.data['name'],
                                                     form.data['your_email'],
                                                     recipients,
                                                     form.data['url'],
                                                     form.data['error_type'],
                                                     form.data['message'],
                                                     form.data['page_title'])

        return jsonify({'sent': sent, 'message': str(message),
                        'fields': [key for key in form.data.keys()]})

    else:
        return jsonify({'sent': False, 'message': form.errors,
                        'fields': [key for key in form.data.keys()]})


@main.route("/error_mail/", methods=['GET'])
def error_form():
    context = {'url': request.args.get('url')}
    return render_template("includes/error_form.html", **context)


# ###############################Others########################################


@main.route("/media/<path:filename>/", methods=['GET'])
@cache.cached(key_prefix=cache_key_with_lang)
def download_file_by_filename(filename):
    media_root = current_app.config['MEDIA_ROOT']
    return send_from_directory(media_root, filename)


@main.route("/img/scielo.gif", methods=['GET'])
def full_text_image():
    return send_from_directory('static', 'img/full_text_scielo_img.gif')


@main.route("/robots.txt", methods=['GET'])
def get_robots_txt_file():
    return send_from_directory('static', 'robots.txt')


@main.route("/revistas/<path:journal_seg>/<string:page>.htm", methods=['GET'])
def router_legacy_info_pages(journal_seg, page):
    """
    Essa view function realiza o redirecionamento das URLs antigas para as novas URLs.

    Mantém um dicionário como uma tabela relacionamento entre o nome das páginas que pode ser:

       Página      âncora

    [iaboutj.htm, eaboutj.htm, paboutj.htm] -> #about
    [iedboard.htm, eedboard.htm, pedboard.htm] -> #editors
    [iinstruc.htm einstruc.htm, pinstruc.htm]-> #instructions
    isubscrp.htm -> Sem âncora
    """

    page_anchor = {
        'iaboutj': '#about',
        'eaboutj': '#about',
        'paboutj': '#about',
        'eedboard': '#editors',
        'iedboard': '#editors',
        'pedboard': '#editors',
        'iinstruc': '#instructions',
        'pinstruc': '#instructions',
        'einstruc': '#instructions'
        }
    return redirect('%s%s' % (url_for('main.about_journal',
                                      url_seg=journal_seg), page_anchor.get(page, '')), code=301)


@main.route("/api/v1/counter_dict", methods=['GET'])
def router_counter_dicts():
    """
    Essa view function retorna um dicionário, em formato JSON, que mapeia PIDs a insumos
    necessários para o funcionamento das aplicações Matomo & COUNTER & SUSHI.
    """
    end_date = request.args.get('end_date', '', type=str)
    try:
        end_date = datetime.strptime(end_date, '%Y-%m-%d')
    except ValueError:
        end_date = datetime.now()
    begin_date = end_date - timedelta(days=30)

    page = request.args.get('page', type=int)
    if not page:
        page = 1

    limit = request.args.get('limit', type=int)
    if not limit or limit > 100 or limit < 0:
        limit = 100

    results = {'dictionary_date': end_date,
               'end_date': end_date.strftime('%Y-%m-%d %H-%M-%S'),
               'begin_date': begin_date.strftime('%Y-%m-%d %H-%M-%S'),
               'documents': {},
               'collection': current_app.config['OPAC_COLLECTION']}

    articles = controllers.get_articles_by_date_range(begin_date, end_date, page, limit)
    for a in articles.items:
        results['documents'].update(get_article_counter_data(a))

    results['total'] = articles.total
    results['pages'] = articles.pages
    results['limit'] = articles.per_page
    results['page'] = articles.page

    return jsonify(results)


def get_article_counter_data(article):
    return {
        article.aid: {
            "journal_acronym": article.journal.acronym,
            "pid": article.pid if article.pid else '',
            "aop_pid": article.aop_pid if article.aop_pid else '',
            "pid_v1": article.scielo_pids.get('v1', ''),
            "pid_v2": article.scielo_pids.get('v2', ''),
            "pid_v3": article.scielo_pids.get('v3', ''),
            "publication_date": article.publication_date,
            "default_language": article.original_language,
            "create": article.created,
            "update": article.updated
        }
    }


@main.route('/cgi-bin/wxis.exe/iah/')
def author_production():
    # http://www.scielo.br/cgi-bin/wxis.exe/iah/
    # ?IsisScript=iah/iah.xis&base=article%5Edlibrary&format=iso.pft&
    # lang=p&nextAction=lnk&
    # indexSearch=AU&exprSearch=MEIERHOFFER,+LILIAN+KOZSLOWSKI
    # ->
    # //search.scielo.org/?lang=pt&q=au:MEIERHOFFER,+LILIAN+KOZSLOWSKI

    search_url = current_app.config.get('URL_SEARCH')
    if not search_url:
        abort(404, "URL_SEARCH: {}".format(_('Página não encontrada')))

    qs_exprSearch = request.args.get('exprSearch', type=str) or ''
    qs_indexSearch = request.args.get('indexSearch', type=str) or ''
    qs_lang = request.args.get('lang', type=str) or ''

    _lang = IAHX_LANGS.get(qs_lang) or ''
    _lang = _lang and "lang={}".format(_lang)

    _expr = "{}{}".format(
        qs_indexSearch == "AU" and "au:" or '', qs_exprSearch)
    _expr = _expr and "q={}".format(_expr.replace(" ", "+"))

    _and = _lang and _expr and "&" or ''
    _question_mark = (_lang or _expr) and "?" or ""

    if search_url.startswith("//"):
        protocol = "https:"
    elif search_url.startswith("http"):
        protocol = ""
    else:
        protocol = "https://"

    url = "{}{}{}{}{}{}".format(
        protocol, search_url, _question_mark, _lang, _and, _expr)
    return redirect(url, code=301)

[(45, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (47, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (48, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (49, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (183, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (230, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (248, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (282, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (337, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (361, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (475, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (537, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (582, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (624, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (645, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (671, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (748, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (800, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (923, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (984, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1031, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1128, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1154, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1322, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1356, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1374, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1383, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1396, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1426, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1453, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (1547, 'webapp.cache.cached', 'cache.cached', False, 'from webapp import cache\n'), (59, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (60, 'urllib.parse.urljoin', 'urljoin', False, 'from urllib.parse import urljoin\n'), (140, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (156, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (162, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (168, 'flask.redirect', 'redirect', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (186, 'webapp.controllers.get_latest_news_by_lang', 'controllers.get_latest_news_by_lang', False, 'from webapp import controllers\n'), (188, 'webapp.controllers.get_collection_tweets', 'controllers.get_collection_tweets', False, 'from webapp import controllers\n'), (189, 'webapp.controllers.get_press_releases', 'controllers.get_press_releases', False, 'from webapp import controllers\n'), (223, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (233, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (243, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (257, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (258, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (259, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (268, 'webapp.controllers.get_journals_grouped_by', 'controllers.get_journals_grouped_by', False, 'from webapp import controllers\n'), (275, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (285, 'webapp.controllers.get_current_collection', 'controllers.get_current_collection', False, 'from webapp import controllers\n'), (288, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (290, 'werkzeug.contrib.atom.AtomFeed', 'AtomFeed', False, 'from werkzeug.contrib.atom import AtomFeed\n'), (294, 'webapp.controllers.get_journals_paginated', 'controllers.get_journals_paginated', False, 'from webapp import controllers\n'), (354, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (364, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (365, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (366, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (477, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (485, 'webapp.utils.utils.fix_journal_last_issue', 'utils.fix_journal_last_issue', False, 'from webapp.utils import utils\n'), (489, 'webapp.controllers.get_latest_news_by_lang', 'controllers.get_latest_news_by_lang', False, 'from webapp import controllers\n'), (492, 'webapp.controllers.get_press_releases', 'controllers.get_press_releases', False, 'from webapp import controllers\n'), (515, 'webapp.controllers.get_journal_metrics', 'controllers.get_journal_metrics', False, 'from webapp import controllers\n'), (533, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (539, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (547, 'webapp.controllers.get_issues_by_jid', 'controllers.get_issues_by_jid', False, 'from webapp import controllers\n'), (549, 'webapp.controllers.get_articles_by_iid', 'controllers.get_articles_by_iid', False, 'from webapp import controllers\n'), (586, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (594, 'webapp.utils.utils.fix_journal_last_issue', 'utils.fix_journal_last_issue', False, 'from webapp.utils import utils\n'), (604, 'webapp.controllers.get_page_by_journal_acron_lang', 'controllers.get_page_by_journal_acron_lang', False, 'from webapp import controllers\n'), (620, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (630, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (631, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (632, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (635, 'webapp.controllers.get_alpha_list_from_paginated_journals', 'controllers.get_alpha_list_from_paginated_journals', False, 'from webapp import controllers\n'), (641, 'flask.jsonify', 'jsonify', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (651, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (652, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (653, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (667, 'flask.jsonify', 'jsonify', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (699, 'webapp.utils.utils.is_recaptcha_valid', 'utils.is_recaptcha_valid', False, 'from webapp.utils import utils\n'), (729, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (736, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (750, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (762, 'webapp.controllers.get_issues_for_grid_by_jid', 'controllers.get_issues_for_grid_by_jid', False, 'from webapp import controllers\n'), (784, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (803, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (819, 'webapp.controllers.get_issue_by_url_seg', 'controllers.get_issue_by_url_seg', False, 'from webapp import controllers\n'), (831, 'webapp.utils.utils.fix_journal_last_issue', 'utils.fix_journal_last_issue', False, 'from webapp.utils import utils\n'), (840, 'webapp.controllers.get_articles_by_iid', 'controllers.get_articles_by_iid', False, 'from webapp import controllers\n'), (885, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (919, 'webapp.utils.utils.get_prev_issue', 'utils.get_prev_issue', False, 'from webapp.utils import utils\n'), (932, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (942, 'webapp.utils.utils.fix_journal_last_issue', 'utils.fix_journal_last_issue', False, 'from webapp.utils import utils\n'), (980, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (986, 'webapp.controllers.get_issue_by_url_seg', 'controllers.get_issue_by_url_seg', False, 'from webapp import controllers\n'), (998, 'webapp.controllers.get_articles_by_iid', 'controllers.get_articles_by_iid', False, 'from webapp import controllers\n'), (1034, 'webapp.controllers.get_article_by_pid', 'controllers.get_article_by_pid', False, 'from webapp import controllers\n'), (1057, 'packtools.HTMLGenerator.parse', 'HTMLGenerator.parse', False, 'from packtools import HTMLGenerator\n'), (1130, 'webapp.controllers.get_issue_by_url_seg', 'controllers.get_issue_by_url_seg', False, 'from webapp import controllers\n'), (1134, 'webapp.controllers.get_article_by_issue_article_seg', 'controllers.get_article_by_issue_article_seg', False, 'from webapp import controllers\n'), (1159, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1324, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1325, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1326, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1327, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1361, 'mimetypes.guess_type', 'mimetypes.guess_type', False, 'import mimetypes\n'), (1385, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1406, 'webapp.controllers.get_issue_by_url_seg', 'controllers.get_issue_by_url_seg', False, 'from webapp import controllers\n'), (1410, 'webapp.controllers.get_article_by_issue_article_seg', 'controllers.get_article_by_issue_article_seg', False, 'from webapp import controllers\n'), (1430, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (1435, 'webapp.controllers.get_article_by_pdf_filename', 'controllers.get_article_by_pdf_filename', False, 'from webapp import controllers\n'), (1455, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1456, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1461, 'webapp.controllers.get_article_by_pid_v1', 'controllers.get_article_by_pid_v1', False, 'from webapp import controllers\n'), (1484, 'webapp.forms.EmailShareForm', 'forms.EmailShareForm', False, 'from webapp import forms\n'), (1506, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1515, 'webapp.forms.ErrorForm', 'forms.ErrorForm', False, 'from webapp import forms\n'), (1540, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1550, 'flask.send_from_directory', 'send_from_directory', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1555, 'flask.send_from_directory', 'send_from_directory', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1560, 'flask.send_from_directory', 'send_from_directory', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1599, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1606, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1610, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1620, 'webapp.controllers.get_articles_by_date_range', 'controllers.get_articles_by_date_range', False, 'from webapp import controllers\n'), (1629, 'flask.jsonify', 'jsonify', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1658, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1685, 'flask.redirect', 'redirect', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (78, 'requests.get', 'requests.get', False, 'import requests\n'), (123, 'webapp.forms.EmailShareForm', 'forms.EmailShareForm', False, 'from webapp import forms\n'), (124, 'webapp.forms.ContactForm', 'forms.ContactForm', False, 'from webapp import forms\n'), (125, 'webapp.forms.ErrorForm', 'forms.ErrorForm', False, 'from webapp import forms\n'), (149, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (239, 'webapp.controllers.get_journal_json_data', 'controllers.get_journal_json_data', False, 'from webapp import controllers\n'), (303, 'webapp.controllers.get_issues_by_jid', 'controllers.get_issues_by_jid', False, 'from webapp import controllers\n'), (311, 'collections.OrderedDict', 'OrderedDict', False, 'from collections import OrderedDict\n'), (345, 'webapp.controllers.get_page_by_slug_name', 'controllers.get_page_by_slug_name', False, 'from webapp import controllers\n'), (351, 'webapp.controllers.get_pages_by_lang', 'controllers.get_pages_by_lang', False, 'from webapp import controllers\n'), (470, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (500, 'webapp.controllers.get_recent_articles_of_issue', 'controllers.get_recent_articles_of_issue', False, 'from webapp import controllers\n'), (657, 'webapp.controllers.get_journals_grouped_by', 'controllers.get_journals_grouped_by', False, 'from webapp import controllers\n'), (701, 'webapp.forms.ContactForm', 'forms.ContactForm', False, 'from webapp import forms\n'), (703, 'webapp.controllers.get_journal_by_url_seg', 'controllers.get_journal_by_url_seg', False, 'from webapp import controllers\n'), (744, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (793, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (835, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (837, 'flask.redirect', 'redirect', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (869, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (893, 'webapp.controllers.get_issues_by_jid', 'controllers.get_issues_by_jid', False, 'from webapp import controllers\n'), (895, 'webapp.utils.utils.get_next_issue', 'utils.get_next_issue', False, 'from webapp.utils import utils\n'), (906, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (916, 'webapp.controllers.get_issues_by_jid', 'controllers.get_issues_by_jid', False, 'from webapp import controllers\n'), (918, 'webapp.utils.utils.get_next_issue', 'utils.get_next_issue', False, 'from webapp.utils import utils\n'), (928, 'webapp.controllers.get_aop_issues', 'controllers.get_aop_issues', False, 'from webapp import controllers\n'), (946, 'webapp.controllers.get_articles_by_iid', 'controllers.get_articles_by_iid', False, 'from webapp import controllers\n'), (965, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1037, 'webapp.controllers.get_article_by_oap_pid', 'controllers.get_article_by_oap_pid', False, 'from webapp import controllers\n'), (1042, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1055, 'io.BytesIO', 'BytesIO', False, 'from io import BytesIO\n'), (1118, 'urllib.parse.urlparse', 'urlparse', False, 'from urllib.parse import urlparse\n'), (1136, 'webapp.controllers.get_article_by_aop_url_segs', 'controllers.get_article_by_aop_url_segs', False, 'from webapp import controllers\n'), (1149, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1156, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1157, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1158, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1169, 'webapp.controllers.get_article', 'controllers.get_article', False, 'from webapp import controllers\n'), (1282, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1306, 'flask.make_response', 'make_response', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1338, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1352, 'mimetypes.guess_type', 'mimetypes.guess_type', False, 'import mimetypes\n'), (1353, 'flask.Response', 'Response', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1370, 'flask.Response', 'Response', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1422, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1442, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1466, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1489, 'webapp.controllers.send_email_share', 'controllers.send_email_share', False, 'from webapp import controllers\n'), (1505, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1521, 'webapp.controllers.send_email_error', 'controllers.send_email_error', False, 'from webapp import controllers\n'), (1539, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1601, 'datetime.datetime.strptime', 'datetime.strptime', False, 'from datetime import datetime, timedelta\n'), (1604, 'datetime.timedelta', 'timedelta', False, 'from datetime import datetime, timedelta\n'), (1662, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1663, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1664, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (102, 'webapp.controllers.get_current_collection', 'controllers.get_current_collection', False, 'from webapp import controllers\n'), (143, 'flask.session.keys', 'session.keys', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (159, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (179, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (240, 'webapp.controllers.get_journals', 'controllers.get_journals', False, 'from webapp import controllers\n'), (287, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (308, 'webapp.controllers.get_articles_by_iid', 'controllers.get_articles_by_iid', False, 'from webapp import controllers\n'), (325, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (372, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (464, 'flask.redirect', 'redirect', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (480, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (542, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (554, 'webapp.utils.utils.get_label_issue', 'utils.get_label_issue', False, 'from webapp.utils import utils\n'), (567, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (589, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (628, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (649, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (659, 'webapp.controllers.get_journals_grouped_by', 'controllers.get_journals_grouped_by', False, 'from webapp import controllers\n'), (674, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (682, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (688, 'flask.Response', 'Response', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (697, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (711, 'webapp.controllers.send_email_contact', 'controllers.send_email_contact', False, 'from webapp import controllers\n'), (724, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (731, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (753, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (790, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (809, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (821, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (897, 'webapp.utils.utils.get_prev_issue', 'utils.get_prev_issue', False, 'from webapp.utils import utils\n'), (926, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (930, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (936, 'flask.redirect', 'redirect', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (951, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (989, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1003, 'webapp.utils.utils.get_label_issue', 'utils.get_label_issue', False, 'from webapp.utils import utils\n'), (1014, 'flask.render_template', 'render_template', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1040, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1132, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1140, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1228, 'urllib.parse.urlparse', 'urlparse', False, 'from urllib.parse import urlparse\n'), (1259, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1278, 'webapp.controllers.related_links', 'controllers.related_links', False, 'from webapp import controllers\n'), (1299, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1330, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1387, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1408, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1412, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1433, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1439, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1459, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1463, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1482, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1513, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1603, 'datetime.datetime.now', 'datetime.now', False, 'from datetime import datetime, timedelta\n'), (209, 'opac_schema.v1.models.Journal.objects.filter', 'Journal.objects.filter', False, 'from opac_schema.v1.models import Journal, Issue, Article, Collection\n'), (212, 'opac_schema.v1.models.Article.objects.filter', 'Article.objects.filter', False, 'from opac_schema.v1.models import Journal, Issue, Article, Collection\n'), (300, 'datetime.datetime.now', 'datetime.now', False, 'from datetime import datetime, timedelta\n'), (347, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (377, 'webapp.controllers.get_journal_by_issn', 'controllers.get_journal_by_issn', False, 'from webapp import controllers\n'), (483, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (545, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (566, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (592, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (661, 'webapp.controllers.get_journals_grouped_by', 'controllers.get_journals_grouped_by', False, 'from webapp import controllers\n'), (676, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (706, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (756, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (816, 'flask.request.args.get', 'request.args.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (823, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (828, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (940, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (992, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (995, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1187, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1190, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1193, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1202, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1217, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1286, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1290, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1348, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1350, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1366, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1368, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1519, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1589, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1660, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (133, 'flask.current_app.config.get', 'current_app.config.get', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (385, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (390, 'webapp.controllers.get_issue_by_pid', 'controllers.get_issue_by_pid', False, 'from webapp import controllers\n'), (686, 'datetime.datetime.now', 'datetime.now', False, 'from datetime import datetime, timedelta\n'), (1165, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1186, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1238, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1240, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1246, 'webapp.config.lang_names.display_original_lang_name', 'display_original_lang_name', False, 'from webapp.config.lang_names import display_original_lang_name\n'), (1247, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (1295, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (1317, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (380, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (406, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (414, 'webapp.controllers.get_article_by_pid_v2', 'controllers.get_article_by_pid_v2', False, 'from webapp import controllers\n'), (665, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (383, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (393, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (403, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (424, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (433, 'webapp.controllers.get_journal_by_issn', 'controllers.get_journal_by_issn', False, 'from webapp import controllers\n'), (396, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (399, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (416, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (441, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (446, 'webapp.controllers.get_article_by_pid_v2', 'controllers.get_article_by_pid_v2', False, 'from webapp import controllers\n'), (436, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (451, 'flask.url_for', 'url_for', False, 'from flask import render_template, abort, current_app, request, session, redirect, jsonify, url_for, Response, send_from_directory, g, make_response\n'), (461, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (439, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n'), (448, 'flask_babelex.gettext', '_', True, 'from flask_babelex import gettext as _\n')]