<commit_before># Copyright (C) 2019  KMEE INFORMATICA LTDA
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import _, api, fields, models
from odoo.exceptions import UserError

from ..constants.fiscal import (
    SITUACAO_EDOC,
    SITUACAO_EDOC_A_ENVIAR,
    SITUACAO_EDOC_AUTORIZADA,
    SITUACAO_EDOC_CANCELADA,
    SITUACAO_EDOC_DENEGADA,
    SITUACAO_EDOC_EM_DIGITACAO,
    SITUACAO_EDOC_ENVIADA,
    SITUACAO_EDOC_INUTILIZADA,
    SITUACAO_EDOC_REJEITADA, SITUACAO_FISCAL,
    SITUACAO_FISCAL_SPED_CONSIDERA_CANCELADO,
    WORKFLOW_DOCUMENTO_NAO_ELETRONICO,
    WORKFLOW_EDOC,
    PROCESSADOR_NENHUM,
    PROCESSADOR,
    DOCUMENT_ISSUER_COMPANY,
)

class DocumentWorkflow(models.AbstractModel):
    _name = 'l10n_br_fiscal.document.workflow'
    _description = 'Fiscal Document Workflow'

    state_edoc = fields.Selection(
        selection=SITUACAO_EDOC,
        string='Situação e-doc',
        default=SITUACAO_EDOC_EM_DIGITACAO,
        copy=False,
        required=True,
        track_visibility='onchange',
        index=True,
    )

    state_fiscal = fields.Selection(
        selection=SITUACAO_FISCAL,
        string='Situação Fiscal',
        copy=False,
        track_visibility='onchange',
        index=True,
    )

    cancel_reason = fields.Char(
        string='Cancel Reason',
    )

    correction_reason = fields.Char(
        string='Correction Reason',
    )

    processador_edoc = fields.Selection(
        string='Processador',
        selection=PROCESSADOR,
        default=PROCESSADOR_NENHUM,
    )

    def _direct_draft_send(self):
        return False

    @api.model
    def _avaliable_transition(self, old_state, new_state):
        """ Verifica as transições disponiveis, para não permitir alterações
         de estado desconhecidas. Para mais detalhes verificar a variável
          WORKFLOW_EDOC

           (old_state, new_state) in (SITUACAO_EDOC_EM_DIGITACAO,
                                      SITUACAO_EDOC_A_ENVIAR)

        :param old_state: estado antigo
        :param new_state: novo estado
        :return:
        """
        if self.document_electronic:
            return (old_state, new_state) in WORKFLOW_EDOC
        else:
            return (old_state, new_state) in WORKFLOW_DOCUMENTO_NAO_ELETRONICO

    def _exec_before_SITUACAO_EDOC_EM_DIGITACAO(self, old_state, new_state):
        pass

    def _exec_before_SITUACAO_EDOC_A_ENVIAR(self, old_state, new_state):
        self.document_date()
        self.document_number()
        self.document_check()

    def _exec_before_SITUACAO_EDOC_ENVIADA(self, old_state, new_state):
        pass

    def _exec_before_SITUACAO_EDOC_REJEITADA(self, old_state, new_state):
        pass

    def _exec_before_SITUACAO_EDOC_AUTORIZADA(self, old_state, new_state):
        pass

    def _exec_before_SITUACAO_EDOC_CANCELADA(self, old_state, new_state):
        pass

    def _exec_before_SITUACAO_EDOC_DENEGADA(self, old_state, new_state):
        pass

    def _exec_before_SITUACAO_EDOC_INUTILIZADA(self, old_state, new_state):
        pass

    def _before_change_state(self, old_state, new_state):
        """ Hook para realizar alterações depois da alteração do estado do doc.

        A variável self.state_edoc já estará com o novo estado neste momento.

        :param old_state:
        :param new_state:
        :return:
        """
        self.ensure_one()
        if new_state == SITUACAO_EDOC_EM_DIGITACAO:
            self._exec_before_SITUACAO_EDOC_EM_DIGITACAO(old_state, new_state)
        elif new_state == SITUACAO_EDOC_A_ENVIAR:
            self._exec_before_SITUACAO_EDOC_A_ENVIAR(old_state, new_state)
        elif new_state == SITUACAO_EDOC_ENVIADA:
            self._exec_before_SITUACAO_EDOC_ENVIADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_REJEITADA:
            self._exec_before_SITUACAO_EDOC_REJEITADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_AUTORIZADA:
            self._exec_before_SITUACAO_EDOC_AUTORIZADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_CANCELADA:
            self._exec_before_SITUACAO_EDOC_CANCELADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_DENEGADA:
            self._exec_before_SITUACAO_EDOC_DENEGADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_INUTILIZADA:
            self._exec_before_SITUACAO_EDOC_INUTILIZADA(old_state, new_state)

    def _exec_after_SITUACAO_EDOC_EM_DIGITACAO(self, old_state, new_state):
        if self.state_fiscal in SITUACAO_FISCAL_SPED_CONSIDERA_CANCELADO:
            raise (
                _(
                    "Não é possível retornar o documento para em \n"
                    "digitação, quando o mesmo esta na situação: \n"
                    "{0}, {1]".format(old_state, self.state_fiscal)
                )
            )

    def _exec_after_SITUACAO_EDOC_A_ENVIAR(self, old_state, new_state):
        if self._direct_draft_send():
            self.action_document_send()

    def _exec_after_SITUACAO_EDOC_ENVIADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_REJEITADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_AUTORIZADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_CANCELADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_DENEGADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_INUTILIZADA(self, old_state, new_state):
        pass

    def _after_change_state(self, old_state, new_state):
        """ Hook para realizar alterações depois da alteração do estado do doc.

        A variável self.state_edoc já estará com o novo estado neste momento.

        :param old_state:
        :param new_state:
        :return:
        """
        self.ensure_one()
        if new_state == SITUACAO_EDOC_EM_DIGITACAO:
            self._exec_after_SITUACAO_EDOC_EM_DIGITACAO(old_state, new_state)
        elif new_state == SITUACAO_EDOC_A_ENVIAR:
            self._exec_after_SITUACAO_EDOC_A_ENVIAR(old_state, new_state)
        elif new_state == SITUACAO_EDOC_ENVIADA:
            self._exec_after_SITUACAO_EDOC_ENVIADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_REJEITADA:
            self._exec_after_SITUACAO_EDOC_REJEITADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_AUTORIZADA:
            self._exec_after_SITUACAO_EDOC_AUTORIZADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_CANCELADA:
            self._exec_after_SITUACAO_EDOC_CANCELADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_DENEGADA:
            self._exec_after_SITUACAO_EDOC_DENEGADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_INUTILIZADA:
            self._exec_after_SITUACAO_EDOC_INUTILIZADA(old_state, new_state)

    @api.multi
    def _change_state(self, new_state):
        """ Método para alterar o estado do documento fiscal, mantendo a
        integridade do workflow da invoice.

        Tenha muito cuidado ao alterar o workflow da invoice manualmente,
        prefira alterar o estado do documento fiscal e ele se encarregar de
        alterar o estado da invoice.

        :param new_state: Novo estado
        :return:
        """

        for record in self:
            old_state = record.state_edoc

            if not record._avaliable_transition(old_state, new_state):
                raise UserError(
                    _(
                        "Não é possível realizar esta operação,\n"
                        "esta transição não é permitida:\n\n"
                        "De: {old_state}\n\n Para: {new_state}".format(
                            old_state=old_state, new_state=new_state
                        )
                    )
                )

            record._before_change_state(old_state, new_state)
            record.state_edoc = new_state
            record._after_change_state(old_state, new_state)

    def document_date(self):
        if not self.date:
            self.date = self._date_server_format()

    def document_check(self):
        return True

    def _generate_key(self):
        pass

    def document_number(self):
        if self.issuer == DOCUMENT_ISSUER_COMPANY:
            if not self.number and self.document_serie_id:
                self.number = self.document_serie_id.next_seq_number()

            if not self.operation_name:
                self.operation_name = ', '.join(
                    [l.name for l in self.line_ids.mapped(
                        'fiscal_operation_id')])

            if self.document_electronic and not self.key:
                self.document_serie = self.document_serie_id.code
                self.key = self._generate_key()

    def _document_confirm(self):
        self._change_state(SITUACAO_EDOC_A_ENVIAR)

    def action_document_confirm(self):
        to_confirm = self.filtered(
            lambda inv: inv.state_edoc != SITUACAO_EDOC_A_ENVIAR
        )
        if to_confirm:
            to_confirm._document_confirm()

    def _document_send(self):
        self._change_state(SITUACAO_EDOC_AUTORIZADA)

    def _document_export(self):
        pass

    def action_document_send(self):
        to_send = self.filtered(lambda d: d.state_edoc in (
            SITUACAO_EDOC_A_ENVIAR,
            SITUACAO_EDOC_ENVIADA,
            SITUACAO_EDOC_REJEITADA,
        ))
        if to_send:
            to_send._document_send()

    def action_document_back2draft(self):
        self._change_state(SITUACAO_EDOC_EM_DIGITACAO)

    def _document_cancel(self, justificative):
        self.cancel_reason = justificative
        self._change_state(SITUACAO_EDOC_CANCELADA)
        msg = "Cancelamento: {}".format(justificative)
        self.message_post(body=msg)

    @api.multi
    def action_document_cancel(self):
        result = self.env["ir.actions.act_window"].for_xml_id(
            "l10n_br_fiscal", "document_cancel_wizard_action"
        )
        return result

    @api.multi
    def action_document_invalidate(self):
        result = self.env["ir.actions.act_window"].for_xml_id(
            "l10n_br_fiscal", "wizard_document_invalidate_action"
        )
        return result

    def _document_correction(self, justificative):
        self.correction_reason = justificative
        msg = "Carta de correção: {}".format(justificative)
        self.message_post(body=msg)

    @api.multi
    def action_document_correction(self):
        result = self.env["ir.actions.act_window"].for_xml_id(
            "l10n_br_fiscal", "document_correction_wizard_action"
        )
        return result
<commit_msg>[IMP] Before change state validation<commit_after># Copyright (C) 2019  KMEE INFORMATICA LTDA
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import _, api, fields, models
from odoo.exceptions import UserError

from ..constants.fiscal import (
    SITUACAO_EDOC,
    SITUACAO_EDOC_A_ENVIAR,
    SITUACAO_EDOC_AUTORIZADA,
    SITUACAO_EDOC_CANCELADA,
    SITUACAO_EDOC_DENEGADA,
    SITUACAO_EDOC_EM_DIGITACAO,
    SITUACAO_EDOC_ENVIADA,
    SITUACAO_EDOC_INUTILIZADA,
    SITUACAO_EDOC_REJEITADA, SITUACAO_FISCAL,
    SITUACAO_FISCAL_SPED_CONSIDERA_CANCELADO,
    WORKFLOW_DOCUMENTO_NAO_ELETRONICO,
    WORKFLOW_EDOC,
    PROCESSADOR_NENHUM,
    PROCESSADOR,
    DOCUMENT_ISSUER_COMPANY,
)

class DocumentWorkflow(models.AbstractModel):
    _name = 'l10n_br_fiscal.document.workflow'
    _description = 'Fiscal Document Workflow'

    state_edoc = fields.Selection(
        selection=SITUACAO_EDOC,
        string='Situação e-doc',
        default=SITUACAO_EDOC_EM_DIGITACAO,
        copy=False,
        required=True,
        track_visibility='onchange',
        index=True,
    )

    state_fiscal = fields.Selection(
        selection=SITUACAO_FISCAL,
        string='Situação Fiscal',
        copy=False,
        track_visibility='onchange',
        index=True,
    )

    cancel_reason = fields.Char(
        string='Cancel Reason',
    )

    correction_reason = fields.Char(
        string='Correction Reason',
    )

    processador_edoc = fields.Selection(
        string='Processador',
        selection=PROCESSADOR,
        default=PROCESSADOR_NENHUM,
    )

    def _direct_draft_send(self):
        return False

    @api.model
    def _avaliable_transition(self, old_state, new_state):
        """ Verifica as transições disponiveis, para não permitir alterações
         de estado desconhecidas. Para mais detalhes verificar a variável
          WORKFLOW_EDOC

           (old_state, new_state) in (SITUACAO_EDOC_EM_DIGITACAO,
                                      SITUACAO_EDOC_A_ENVIAR)

        :param old_state: estado antigo
        :param new_state: novo estado
        :return:
        """
        if self.document_electronic:
            return (old_state, new_state) in WORKFLOW_EDOC
        else:
            return (old_state, new_state) in WORKFLOW_DOCUMENTO_NAO_ELETRONICO

    def _exec_before_SITUACAO_EDOC_EM_DIGITACAO(self, old_state, new_state):
        return True

    def _exec_before_SITUACAO_EDOC_A_ENVIAR(self, old_state, new_state):
        self.document_date()
        self.document_number()
        self.document_check()
        return True

    def _exec_before_SITUACAO_EDOC_ENVIADA(self, old_state, new_state):
        return True

    def _exec_before_SITUACAO_EDOC_REJEITADA(self, old_state, new_state):
        return True

    def _exec_before_SITUACAO_EDOC_AUTORIZADA(self, old_state, new_state):
        return True

    def _exec_before_SITUACAO_EDOC_CANCELADA(self, old_state, new_state):
        return True

    def _exec_before_SITUACAO_EDOC_DENEGADA(self, old_state, new_state):
        return True

    def _exec_before_SITUACAO_EDOC_INUTILIZADA(self, old_state, new_state):
        return True

    def _before_change_state(self, old_state, new_state):
        """ Hook para realizar alterações depois da alteração do estado do doc.

        A variável self.state_edoc já estará com o novo estado neste momento.

        :param old_state:
        :param new_state:
        :return:
        """
        self.ensure_one()
        if new_state == SITUACAO_EDOC_EM_DIGITACAO:
            return self._exec_before_SITUACAO_EDOC_EM_DIGITACAO(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_A_ENVIAR:
            return self._exec_before_SITUACAO_EDOC_A_ENVIAR(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_ENVIADA:
            return self._exec_before_SITUACAO_EDOC_ENVIADA(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_REJEITADA:
            return self._exec_before_SITUACAO_EDOC_REJEITADA(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_AUTORIZADA:
            return self._exec_before_SITUACAO_EDOC_AUTORIZADA(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_CANCELADA:
            return self._exec_before_SITUACAO_EDOC_CANCELADA(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_DENEGADA:
            return self._exec_before_SITUACAO_EDOC_DENEGADA(
                old_state, new_state)
        elif new_state == SITUACAO_EDOC_INUTILIZADA:
            return self._exec_before_SITUACAO_EDOC_INUTILIZADA(
                old_state, new_state)

    def _exec_after_SITUACAO_EDOC_EM_DIGITACAO(self, old_state, new_state):
        if self.state_fiscal in SITUACAO_FISCAL_SPED_CONSIDERA_CANCELADO:
            raise (
                _(
                    "Não é possível retornar o documento para em \n"
                    "digitação, quando o mesmo esta na situação: \n"
                    "{0}, {1]".format(old_state, self.state_fiscal)
                )
            )

    def _exec_after_SITUACAO_EDOC_A_ENVIAR(self, old_state, new_state):
        if self._direct_draft_send():
            self.action_document_send()

    def _exec_after_SITUACAO_EDOC_ENVIADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_REJEITADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_AUTORIZADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_CANCELADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_DENEGADA(self, old_state, new_state):
        pass

    def _exec_after_SITUACAO_EDOC_INUTILIZADA(self, old_state, new_state):
        pass

    def _after_change_state(self, old_state, new_state):
        """ Hook para realizar alterações depois da alteração do estado do doc.

        A variável self.state_edoc já estará com o novo estado neste momento.

        :param old_state:
        :param new_state:
        :return:
        """
        self.ensure_one()
        if new_state == SITUACAO_EDOC_EM_DIGITACAO:
            self._exec_after_SITUACAO_EDOC_EM_DIGITACAO(old_state, new_state)
        elif new_state == SITUACAO_EDOC_A_ENVIAR:
            self._exec_after_SITUACAO_EDOC_A_ENVIAR(old_state, new_state)
        elif new_state == SITUACAO_EDOC_ENVIADA:
            self._exec_after_SITUACAO_EDOC_ENVIADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_REJEITADA:
            self._exec_after_SITUACAO_EDOC_REJEITADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_AUTORIZADA:
            self._exec_after_SITUACAO_EDOC_AUTORIZADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_CANCELADA:
            self._exec_after_SITUACAO_EDOC_CANCELADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_DENEGADA:
            self._exec_after_SITUACAO_EDOC_DENEGADA(old_state, new_state)
        elif new_state == SITUACAO_EDOC_INUTILIZADA:
            self._exec_after_SITUACAO_EDOC_INUTILIZADA(old_state, new_state)

    @api.multi
    def _change_state(self, new_state):
        """ Método para alterar o estado do documento fiscal, mantendo a
        integridade do workflow da invoice.

        Tenha muito cuidado ao alterar o workflow da invoice manualmente,
        prefira alterar o estado do documento fiscal e ele se encarregar de
        alterar o estado da invoice.

        :param new_state: Novo estado
        :return:
        """

        for record in self:
            old_state = record.state_edoc

            if not record._avaliable_transition(old_state, new_state):
                raise UserError(
                    _(
                        "Não é possível realizar esta operação,\n"
                        "esta transição não é permitida:\n\n"
                        "De: {old_state}\n\n Para: {new_state}".format(
                            old_state=old_state, new_state=new_state
                        )
                    )
                )

            if record._before_change_state(old_state, new_state):
                record.state_edoc = new_state
                record._after_change_state(old_state, new_state)

    def document_date(self):
        if not self.date:
            self.date = self._date_server_format()

    def document_check(self):
        return True

    def _generate_key(self):
        pass

    def document_number(self):
        if self.issuer == DOCUMENT_ISSUER_COMPANY:
            if not self.number and self.document_serie_id:
                self.number = self.document_serie_id.next_seq_number()

            if not self.operation_name:
                self.operation_name = ', '.join(
                    [l.name for l in self.line_ids.mapped(
                        'fiscal_operation_id')])

            if self.document_electronic and not self.key:
                self.document_serie = self.document_serie_id.code
                self.key = self._generate_key()

    def _document_confirm(self):
        self._change_state(SITUACAO_EDOC_A_ENVIAR)

    def action_document_confirm(self):
        to_confirm = self.filtered(
            lambda inv: inv.state_edoc != SITUACAO_EDOC_A_ENVIAR
        )
        if to_confirm:
            to_confirm._document_confirm()

    def _document_send(self):
        self._change_state(SITUACAO_EDOC_AUTORIZADA)

    def _document_export(self):
        pass

    def action_document_send(self):
        to_send = self.filtered(lambda d: d.state_edoc in (
            SITUACAO_EDOC_A_ENVIAR,
            SITUACAO_EDOC_ENVIADA,
            SITUACAO_EDOC_REJEITADA,
        ))
        if to_send:
            to_send._document_send()

    def action_document_back2draft(self):
        self._change_state(SITUACAO_EDOC_EM_DIGITACAO)

    def _document_cancel(self, justificative):
        self.cancel_reason = justificative
        self._change_state(SITUACAO_EDOC_CANCELADA)
        msg = "Cancelamento: {}".format(justificative)
        self.message_post(body=msg)

    @api.multi
    def action_document_cancel(self):
        result = self.env["ir.actions.act_window"].for_xml_id(
            "l10n_br_fiscal", "document_cancel_wizard_action"
        )
        return result

    @api.multi
    def action_document_invalidate(self):
        result = self.env["ir.actions.act_window"].for_xml_id(
            "l10n_br_fiscal", "wizard_document_invalidate_action"
        )
        return result

    def _document_correction(self, justificative):
        self.correction_reason = justificative
        msg = "Carta de correção: {}".format(justificative)
        self.message_post(body=msg)

    @api.multi
    def action_document_correction(self):
        result = self.env["ir.actions.act_window"].for_xml_id(
            "l10n_br_fiscal", "document_correction_wizard_action"
        )
        return result
<|endoftext|>


<commit_before>"""Calculate the perimeter of a glyph."""

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from fontTools.misc.bezierTools import splitQuadraticAtT, splitCubicAtT
import math

def _distance(p0, p1):
	return math.hypot(p0[0] - p1[0], p0[1] - p1[1])
def _dot(v1, v2):
    return (v1 * v2.conjugate()).real
def _intSecAtan(x):
	# In : sympy.integrate(sp.sec(sp.atan(x)))
	# Out: x*sqrt(x**2 + 1)/2 + asinh(x)/2
	return x * math.sqrt(x**2 + 1)/2 + math.asinh(x)/2

class PerimeterPen(BasePen):

	def __init__(self, glyphset=None, tolerance=0.005):
		BasePen.__init__(self, glyphset)
		self.value = 0
		self._mult = 1.+1.5*tolerance # The 1.5 is a empirical hack; no math

	def _moveTo(self, p0):
		self.__startPoint = p0

	def _lineTo(self, p1):
		p0 = self._getCurrentPoint()
		self.value += _distance(p0, p1)

	def _qCurveToOne(self, p1, p2):
		# Analytical solution to the length of a quadratic bezier.
		# I'll explain how I arrived at this later.
		p0 = self._getCurrentPoint()
		_p1 = complex(*p1)
		d0 = _p1 - complex(*p0)
		d1 = complex(*p2) - _p1
		d = d1 - d0
		n = d * 1j
		scale = abs(n)
		if scale == 0.:
			self._lineTo(p2)
			return
		origDist = _dot(n,d0)
		if origDist == 0.:
			if _dot(d0,d1) > 0:
				self._lineTo(p2)
				return
			assert 0 # TODO handle cusps
		x0 = _dot(d,d0) / origDist
		x1 = _dot(d,d1) / origDist
		Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
		self.value += Len

	def _addCubic(self, p0, p1, p2, p3):
		arch = _distance(p0, p3)
		box = _distance(p0, p1) + _distance(p1, p2) + _distance(p2, p3)
		if arch * self._mult >= box:
			self.value += (arch + box) * .5
		else:
			for c in splitCubicAtT(p0,p1,p2,p3,.2,.4,.6,.8):
				self._addCubic(*c)

	def _curveToOne(self, p1, p2, p3):
		p0 = self._getCurrentPoint()
		self._addCubic(p0, p1, p2, p3)

	def _closePath(self):
		p0 = self._getCurrentPoint()
		if p0 != self.__startPoint:
			self.value += _distance(p0, self.__startPoint)
<commit_msg>[perimeterPen] 5x speed-up cubic by using complex numbers as points<commit_after>"""Calculate the perimeter of a glyph."""

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from fontTools.misc.bezierTools import splitQuadraticAtT, splitCubicAtT
import math

def _distance(p0, p1):
	return math.hypot(p0[0] - p1[0], p0[1] - p1[1])
def _dot(v1, v2):
    return (v1 * v2.conjugate()).real
def _intSecAtan(x):
	# In : sympy.integrate(sp.sec(sp.atan(x)))
	# Out: x*sqrt(x**2 + 1)/2 + asinh(x)/2
	return x * math.sqrt(x**2 + 1)/2 + math.asinh(x)/2

def _split_cubic_into_two(p0, p1, p2, p3):
    mid = (p0 + 3 * (p1 + p2) + p3) * .125
    deriv3 = (p3 + p2 - p1 - p0) * .125
    return ((p0, (p0 + p1) * .5, mid - deriv3, mid),
            (mid, mid + deriv3, (p2 + p3) * .5, p3))

class PerimeterPen(BasePen):

	def __init__(self, glyphset=None, tolerance=0.005):
		BasePen.__init__(self, glyphset)
		self.value = 0
		self._mult = 1.+1.5*tolerance # The 1.5 is a empirical hack; no math

	def _moveTo(self, p0):
		self.__startPoint = p0

	def _lineTo(self, p1):
		p0 = self._getCurrentPoint()
		self.value += _distance(p0, p1)

	def _qCurveToOne(self, p1, p2):
		# Analytical solution to the length of a quadratic bezier.
		# I'll explain how I arrived at this later.
		p0 = self._getCurrentPoint()
		_p1 = complex(*p1)
		d0 = _p1 - complex(*p0)
		d1 = complex(*p2) - _p1
		d = d1 - d0
		n = d * 1j
		scale = abs(n)
		if scale == 0.:
			self._lineTo(p2)
			return
		origDist = _dot(n,d0)
		if origDist == 0.:
			if _dot(d0,d1) > 0:
				self._lineTo(p2)
				return
			assert 0 # TODO handle cusps
		x0 = _dot(d,d0) / origDist
		x1 = _dot(d,d1) / origDist
		Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
		self.value += Len

	def _addCubic(self, p0, p1, p2, p3):
		arch = abs(p0-p3)
		box = abs(p0-p1) + abs(p1-p2) + abs(p2-p3)
		if arch * self._mult >= box:
			self.value += (arch + box) * .5
		else:
			one,two = _split_cubic_into_two(p0,p1,p2,p3)
			self._addCubic(*one)
			self._addCubic(*two)

	def _curveToOne(self, p1, p2, p3):
		p0 = self._getCurrentPoint()
		self._addCubic(complex(*p0), complex(*p1), complex(*p2), complex(*p3))

	def _closePath(self):
		p0 = self._getCurrentPoint()
		if p0 != self.__startPoint:
			self.value += _distance(p0, self.__startPoint)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
In this example, we want to show you how you can take isolated blocks of code
(featuring different kinds of Bokeh visualizations) and rearrange them in a
bigger (encompassing) flask-based application without losing the independence
of each example. This is the reason of some weirdness through the code.
We are using this "building blocks" approach here because we believe it has some
conceptual advantages for people trying to quickly understand, and more
importantly, use the embed API, in a more complex way than just a simple script.
"""
import time
from threading import Thread

import numpy as np
import scipy.special

from bokeh.embed import autoload_server
from bokeh.models import GlyphRenderer
from bokeh.plotting import (annular_wedge, curplot, cursession, figure, hold,
                            legend, line, output_server, push, quad, xgrid, ygrid)

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def render_plot():
    """
    Get the script tags from each plot object and "insert" them into the template.

    This also starts different threads for each update function, so you can have
    a non-blocking update.
    """
    dist_plot, dist_session = distribution()
    dist_tag = autoload_server(dist_plot, dist_session)

    anim_plot, anim_session = animated()
    anim_tag = autoload_server(anim_plot, anim_session)
    # for update_animation as target we need to pass the anim_plot and anim_session as args
    thread = Thread(target=update_animation, args=(anim_plot, anim_session))
    thread.start()

    pop = Population()
    pop_tag = autoload_server(pop.layout, pop.session)
    # for update_population as target we need to pass the pop instance as args
    thread = Thread(target=update_population, args=(pop,))
    thread.start()

    return render_template('app_plot.html', tag1=dist_tag, tag2=anim_tag, tag3=pop_tag)

def distribution():

    mu, sigma = 0, 0.5

    measured = np.random.normal(mu, sigma, 1000)
    hist, edges = np.histogram(measured, density=True, bins=20)

    x = np.linspace(-2, 2, 1000)
    pdf = 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(-(x - mu) ** 2 / (2 * sigma ** 2))
    cdf = (1 + scipy.special.erf((x - mu) / np.sqrt(2 * sigma ** 2))) / 2

    output_server("distribution_reveal")

    p = figure(title="Interactive plots",
               background_fill="#E5E5E5")
    p.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
           fill_color="#333333", line_color="#E5E5E5", line_width=3)

    # Use `line` renderers to display the PDF and CDF
    p.line(x, pdf, line_color="#348abd", line_width=8, alpha=0.7, legend="PDF")
    p.line(x, cdf, line_color="#7a68a6", line_width=8, alpha=0.7, legend="CDF")

    p.legend.orientation = "top_left"

    p.xaxis.axis_label = 'x'
    p.xgrid[0].grid_line_color = "white"
    p.xgrid[0].grid_line_width = 3

    p.yaxis.axis_label = 'Pr(x)'
    p.ygrid[0].grid_line_color = "white"
    p.ygrid[0].grid_line_width = 3

    push()

    return p, cursession()

def animated():

    from numpy import pi, cos, sin, linspace

    N = 50 + 1
    r_base = 8
    theta = linspace(0, 2 * pi, N)
    r_x = linspace(0, 6 * pi, N - 1)
    rmin = r_base - cos(r_x) - 1
    rmax = r_base + sin(r_x) + 1

    colors = ["FFFFCC", "#C7E9B4", "#7FCDBB", "#41B6C4", "#2C7FB8",
              "#253494", "#2C7FB8", "#41B6C4", "#7FCDBB", "#C7E9B4"] * 5

    output_server("animated_reveal")

    p = figure(title="Animations", x_range=[-11, 11], y_range=[-11, 11])

    p.annular_wedge(
        0, 0, rmin, rmax, theta[:-1], theta[1:],
        inner_radius_units="data",
        outer_radius_units="data",
        fill_color=colors,
        line_color="black",
    )

    push()

    return p, cursession()

def update_animation(plot, session):

    from numpy import roll

    renderer = plot.select(dict(type=GlyphRenderer))
    ds = renderer[0].data_source

    while True:

        rmin = ds.data["inner_radius"]
        rmin = roll(rmin, 1)
        ds.data["inner_radius"] = rmin

        rmax = ds.data["outer_radius"]
        rmax = roll(rmax, -1)
        ds.data["outer_radius"] = rmax

        cursession().store_objects(ds)
        time.sleep(0.1)

class Population(object):

    year = 2010
    location = "World"

    def __init__(self):
        from bokeh.models import ColumnDataSource
        from bokeh.document import Document
        from bokeh.session import Session
        from bokeh.sampledata.population import load_population

        self.document = Document()
        self.session = Session()
        self.session.use_doc('population_reveal')
        self.session.load_document(self.document)

        self.df = load_population()
        self.source_pyramid = ColumnDataSource(data=dict())

        # just render at the initialization
        self._render()

    def _render(self):
        self.pyramid_plot()
        self.create_layout()
        self.document.add(self.layout)
        self.update_pyramid()

    def pyramid_plot(self):
        from bokeh.models import (Plot, DataRange1d, LinearAxis, Grid,
                                  Legend, SingleIntervalTicker)
        from bokeh.models.glyphs import Quad

        xdr = DataRange1d(sources=[self.source_pyramid.columns("male"),
                                   self.source_pyramid.columns("female")])
        ydr = DataRange1d(sources=[self.source_pyramid.columns("groups")])

        self.plot = Plot(title="Widgets", x_range=xdr, y_range=ydr,
                         plot_width=600, plot_height=600)

        xaxis = LinearAxis()
        self.plot.add_layout(xaxis, 'below')
        yaxis = LinearAxis(ticker=SingleIntervalTicker(interval=5))
        self.plot.add_layout(yaxis, 'left')

        self.plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
        self.plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))

        male_quad = Quad(left="male", right=0, bottom="groups", top="shifted",
                         fill_color="#3B8686")
        male_quad_glyph = self.plot.add_glyph(self.source_pyramid, male_quad)

        female_quad = Quad(left=0, right="female", bottom="groups", top="shifted",
                           fill_color="#CFF09E")
        female_quad_glyph = self.plot.add_glyph(self.source_pyramid, female_quad)

        self.plot.add_layout(Legend(legends=dict(Male=[male_quad_glyph],
                                                 Female=[female_quad_glyph])))

    def on_year_change(self, obj, attr, old, new):
        self.year = int(new)
        self.update_pyramid()

    def on_location_change(self, obj, attr, old, new):
        self.location = new
        self.update_pyramid()

    def create_layout(self):
        from bokeh.widgets import Select, HBox, VBox

        years = list(map(str, sorted(self.df.Year.unique())))
        locations = sorted(self.df.Location.unique())

        year_select = Select(title="Year:", value="2010", options=years)
        location_select = Select(title="Location:", value="World", options=locations)

        year_select.on_change('value', self.on_year_change)
        location_select.on_change('value', self.on_location_change)

        controls = HBox(year_select, location_select)
        self.layout = VBox(controls, self.plot)

    def update_pyramid(self):
        pyramid = self.df[(self.df.Location == self.location) & (self.df.Year == self.year)]

        male = pyramid[pyramid.Sex == "Male"]
        female = pyramid[pyramid.Sex == "Female"]

        total = male.Value.sum() + female.Value.sum()

        male_percent = -male.Value / total
        female_percent = female.Value / total

        groups = male.AgeGrpStart.tolist()
        shifted = groups[1:] + [groups[-1] + 5]

        self.source_pyramid.data = dict(
            groups=groups,
            shifted=shifted,
            male=male_percent,
            female=female_percent,
        )
        self.session.store_document(self.document)

def update_population(plot):
    while True:
        plot.session.load_document(plot.document)
        time.sleep(0.1)

if __name__ == '__main__':
    app.run(debug=True)
<commit_msg>Fix widget import.<commit_after># -*- coding: utf-8 -*-
"""
In this example, we want to show you how you can take isolated blocks of code
(featuring different kinds of Bokeh visualizations) and rearrange them in a
bigger (encompassing) flask-based application without losing the independence
of each example. This is the reason of some weirdness through the code.
We are using this "building blocks" approach here because we believe it has some
conceptual advantages for people trying to quickly understand, and more
importantly, use the embed API, in a more complex way than just a simple script.
"""
import time
from threading import Thread

import numpy as np
import scipy.special

from bokeh.embed import autoload_server
from bokeh.models import GlyphRenderer
from bokeh.plotting import (annular_wedge, curplot, cursession, figure, hold,
                            legend, line, output_server, push, quad, xgrid, ygrid)

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def render_plot():
    """
    Get the script tags from each plot object and "insert" them into the template.

    This also starts different threads for each update function, so you can have
    a non-blocking update.
    """
    dist_plot, dist_session = distribution()
    dist_tag = autoload_server(dist_plot, dist_session)

    anim_plot, anim_session = animated()
    anim_tag = autoload_server(anim_plot, anim_session)
    # for update_animation as target we need to pass the anim_plot and anim_session as args
    thread = Thread(target=update_animation, args=(anim_plot, anim_session))
    thread.start()

    pop = Population()
    pop_tag = autoload_server(pop.layout, pop.session)
    # for update_population as target we need to pass the pop instance as args
    thread = Thread(target=update_population, args=(pop,))
    thread.start()

    return render_template('app_plot.html', tag1=dist_tag, tag2=anim_tag, tag3=pop_tag)

def distribution():

    mu, sigma = 0, 0.5

    measured = np.random.normal(mu, sigma, 1000)
    hist, edges = np.histogram(measured, density=True, bins=20)

    x = np.linspace(-2, 2, 1000)
    pdf = 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(-(x - mu) ** 2 / (2 * sigma ** 2))
    cdf = (1 + scipy.special.erf((x - mu) / np.sqrt(2 * sigma ** 2))) / 2

    output_server("distribution_reveal")

    p = figure(title="Interactive plots",
               background_fill="#E5E5E5")
    p.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:],
           fill_color="#333333", line_color="#E5E5E5", line_width=3)

    # Use `line` renderers to display the PDF and CDF
    p.line(x, pdf, line_color="#348abd", line_width=8, alpha=0.7, legend="PDF")
    p.line(x, cdf, line_color="#7a68a6", line_width=8, alpha=0.7, legend="CDF")

    p.legend.orientation = "top_left"

    p.xaxis.axis_label = 'x'
    p.xgrid[0].grid_line_color = "white"
    p.xgrid[0].grid_line_width = 3

    p.yaxis.axis_label = 'Pr(x)'
    p.ygrid[0].grid_line_color = "white"
    p.ygrid[0].grid_line_width = 3

    push()

    return p, cursession()

def animated():

    from numpy import pi, cos, sin, linspace

    N = 50 + 1
    r_base = 8
    theta = linspace(0, 2 * pi, N)
    r_x = linspace(0, 6 * pi, N - 1)
    rmin = r_base - cos(r_x) - 1
    rmax = r_base + sin(r_x) + 1

    colors = ["FFFFCC", "#C7E9B4", "#7FCDBB", "#41B6C4", "#2C7FB8",
              "#253494", "#2C7FB8", "#41B6C4", "#7FCDBB", "#C7E9B4"] * 5

    output_server("animated_reveal")

    p = figure(title="Animations", x_range=[-11, 11], y_range=[-11, 11])

    p.annular_wedge(
        0, 0, rmin, rmax, theta[:-1], theta[1:],
        inner_radius_units="data",
        outer_radius_units="data",
        fill_color=colors,
        line_color="black",
    )

    push()

    return p, cursession()

def update_animation(plot, session):

    from numpy import roll

    renderer = plot.select(dict(type=GlyphRenderer))
    ds = renderer[0].data_source

    while True:

        rmin = ds.data["inner_radius"]
        rmin = roll(rmin, 1)
        ds.data["inner_radius"] = rmin

        rmax = ds.data["outer_radius"]
        rmax = roll(rmax, -1)
        ds.data["outer_radius"] = rmax

        cursession().store_objects(ds)
        time.sleep(0.1)

class Population(object):

    year = 2010
    location = "World"

    def __init__(self):
        from bokeh.models import ColumnDataSource
        from bokeh.document import Document
        from bokeh.session import Session
        from bokeh.sampledata.population import load_population

        self.document = Document()
        self.session = Session()
        self.session.use_doc('population_reveal')
        self.session.load_document(self.document)

        self.df = load_population()
        self.source_pyramid = ColumnDataSource(data=dict())

        # just render at the initialization
        self._render()

    def _render(self):
        self.pyramid_plot()
        self.create_layout()
        self.document.add(self.layout)
        self.update_pyramid()

    def pyramid_plot(self):
        from bokeh.models import (Plot, DataRange1d, LinearAxis, Grid,
                                  Legend, SingleIntervalTicker)
        from bokeh.models.glyphs import Quad

        xdr = DataRange1d(sources=[self.source_pyramid.columns("male"),
                                   self.source_pyramid.columns("female")])
        ydr = DataRange1d(sources=[self.source_pyramid.columns("groups")])

        self.plot = Plot(title="Widgets", x_range=xdr, y_range=ydr,
                         plot_width=600, plot_height=600)

        xaxis = LinearAxis()
        self.plot.add_layout(xaxis, 'below')
        yaxis = LinearAxis(ticker=SingleIntervalTicker(interval=5))
        self.plot.add_layout(yaxis, 'left')

        self.plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
        self.plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))

        male_quad = Quad(left="male", right=0, bottom="groups", top="shifted",
                         fill_color="#3B8686")
        male_quad_glyph = self.plot.add_glyph(self.source_pyramid, male_quad)

        female_quad = Quad(left=0, right="female", bottom="groups", top="shifted",
                           fill_color="#CFF09E")
        female_quad_glyph = self.plot.add_glyph(self.source_pyramid, female_quad)

        self.plot.add_layout(Legend(legends=dict(Male=[male_quad_glyph],
                                                 Female=[female_quad_glyph])))

    def on_year_change(self, obj, attr, old, new):
        self.year = int(new)
        self.update_pyramid()

    def on_location_change(self, obj, attr, old, new):
        self.location = new
        self.update_pyramid()

    def create_layout(self):
        from bokeh.models.widgets import Select, HBox, VBox

        years = list(map(str, sorted(self.df.Year.unique())))
        locations = sorted(self.df.Location.unique())

        year_select = Select(title="Year:", value="2010", options=years)
        location_select = Select(title="Location:", value="World", options=locations)

        year_select.on_change('value', self.on_year_change)
        location_select.on_change('value', self.on_location_change)

        controls = HBox(year_select, location_select)
        self.layout = VBox(controls, self.plot)

    def update_pyramid(self):
        pyramid = self.df[(self.df.Location == self.location) & (self.df.Year == self.year)]

        male = pyramid[pyramid.Sex == "Male"]
        female = pyramid[pyramid.Sex == "Female"]

        total = male.Value.sum() + female.Value.sum()

        male_percent = -male.Value / total
        female_percent = female.Value / total

        groups = male.AgeGrpStart.tolist()
        shifted = groups[1:] + [groups[-1] + 5]

        self.source_pyramid.data = dict(
            groups=groups,
            shifted=shifted,
            male=male_percent,
            female=female_percent,
        )
        self.session.store_document(self.document)

def update_population(plot):
    while True:
        plot.session.load_document(plot.document)
        time.sleep(0.1)

if __name__ == '__main__':
    app.run(debug=True)
<|endoftext|>


<commit_before>import requests
import re, json

from datetime import datetime, date

from wallstreet.constants import DATE_FORMAT, DATETIME_FORMAT
from wallstreet.blackandscholes import riskfree, BlackandScholes

from functools import wraps

def parse(val):
    if val == '-':
        return None
    if isinstance(val, str):
        val = val.replace(',', '')
    val = float(val)
    if val.is_integer():
        return int(val)
    return val

def strike_required(func):
    @wraps(func)
    def deco(self, *args, **kwargs):
        if self.strike:
            self.update()
            return func(self, *args, **kwargs)
        else:
            raise AttributeError('Use set_strike() method first')
    return deco

rate = riskfree()

class Stock:
    G_API = 'http://finance.google.com/finance/info?client=ig&q='

    def __init__(self, quote, exchange=None):
        quote = quote.upper()
        query = str(quote)
        if exchange: query = exchange.upper() + ":" + quote

        if not hasattr(self, 'session'):
            self.session = requests.Session()
        r = self.session.get(__class__.G_API + query)

        jayson = r.text.replace('\n','')
        jayson = json.loads(jayson[2:])[0]

        try:
            self.ticker = jayson['t']
        except:
            self.ticker = None

        if r.status_code == 400 or self.ticker != quote:
            raise LookupError('Ticker symbol not found. Try adding the exchange parameter')

        self.id= jayson["id"]
        self.exchange = jayson['e']
        self._price = parse(jayson['l'])
        try:
            self.change = parse(jayson['c'])
            self.cp = parse(jayson['cp'])
        except ValueError:
            self.change = jayson['c']
            self.cp = jayson['cp']
        self._last_trade = datetime.strptime(jayson['lt_dts'], '%Y-%m-%dT%H:%M:%SZ')

    def update(self):
        self.__init__(self.ticker)

    def __repr__(self):
        return 'Stock(ticker=%s, price=%s)' % (self.ticker, self.price)

    @property
    def price(self):
        self.update()
        return self._price

    @property
    def last_trade(self):
        self.update()
        return self._last_trade.strftime(DATETIME_FORMAT)

class Option:
    G_API = 'https://www.google.com/finance/option_chain?q='

    def __init__(self,quote, d=date.today().day, m=date.today().month,
                 y=date.today().year, strict=False):
        quote = quote.upper()
        query = str(quote)

        if d: query += '&expd=' + str(d)
        if m: query += '&expm=' + str(m)
        if y: query += '&expy=' + str(y)

        if not hasattr(self, 'session'):
            self.session = requests.Session()
        r = self.session.get(__class__.G_API + query + '&output=json')
        if r.status_code == 400:
            raise LookupError('Ticker symbol not found.')

        valid_json = re.sub(r'(?<={|,)([a-zA-Z][a-zA-Z0-9_]*)(?=:)', r'"\1"', r.text)
        data = json.loads(valid_json)

        if 'expirations' not in data:
            raise LookupError('No options listed for this stock.')

        self._exp = [date(int(dic['y']), int(dic['m']), int(dic['d']))
                     for dic in data['expirations']]
        self.expiration = date(y,m,d)
        self.expirations = [exp.strftime(DATE_FORMAT) for exp in self._exp]
        self.underlying = Stock(quote)

        try:
            self.calls = data['calls']
            self.puts = data['puts']
        except KeyError:
            if all((d,m,y)) and 'loop' not in locals() and not strict:
                closest_date = min(self._exp, key=lambda x: abs(x - self._expiration))
                print('No options listed for given date, using %s instead' % closest_date.strftime(DATE_FORMAT))
                loop = True
                self.__init__(quote, closest_date.day, closest_date.month, closest_date.year)
            else:
                raise ValueError('Possible expiration dates for this stock are:', self.expirations) from None

    @property
    def expiration(self):
        return self._expiration.strftime(DATE_FORMAT)

    @expiration.setter
    def expiration(self, val):
        self._expiration = val

class Call(Option):
    Option_type = 'Call'

    def __init__(self, quote, d=date.today().day, m=date.today().month,
                 y=date.today().year, strike=None, strict=False):
        quote = quote.upper()
        kw = {'d': d, 'm': m, 'y': y, 'strict': strict}
        super().__init__(quote, **kw)

        if self.__class__.Option_type == 'Call':
            self.data = self.calls
        elif self.__class__.Option_type == 'Put':
            self.data = self.puts

        self.T = (self._expiration - date.today()).days/365
        self.q = 0
        self.ticker = quote
        self.strike = None
        self.strikes = tuple(parse(dic['strike']) for dic in self.data
                        if dic.get('p') != '-' )
        if strike:
            if strike in self.strikes:
                self.set_strike(strike)
            else:
                if strict:
                    raise LookupError('No options listed for given strike price.')
                else:
                    closest_strike = min(self.strikes, key=lambda x: abs(x - strike))
                    print('No option for given strike, using %s instead' % closest_strike)
                    self.set_strike(closest_strike)

    def set_strike(self, val):
        d = {}
        for dic in self.data:
            if parse(dic['strike']) == val and val in self.strikes:
                d = dic
                break
        if d:
            self._price = parse(d['p'])
            self.id = d['cid']
            self.exchange = d['e']
            self._bid = parse(d['b'])
            self._ask = parse(d['a'])
            self.strike = parse(d['strike'])
            self._change = parse(d.get('c', 0)) # change in currency
            self._cp = parse(d.get('cp', 0))  # percentage change
            self._volume = parse(d['vol'])
            self._open_interest = parse(d['oi'])
            self.code = d['s']
            self.ticker = self.code[:-15]
            self.BandS = BlackandScholes(
                    self.underlying.price,
                    self.strike,
                    self.T,
                    self._price,
                    rate(self.T),
                    self.__class__.Option_type,
                    self.q
                    )

        else:
            raise LookupError('No options listed for given strike price.')

    def __repr__(self):
        if self.strike:
            return self.__class__.Option_type + "(ticker=%s, expiration=%s, strike=%s)" % (self.ticker, self.expiration, self.strike)
        else:
            return self.__class__.Option_type + "(ticker=%s, expiration=%s)" % (self.ticker, self.expiration)

    def update(self):
        self.__init__(self.ticker, self._expiration.day,
                     self._expiration.month, self._expiration.year,
                     self.strike)

    @property
    @strike_required
    def bid(self):
        return self._bid

    @property
    @strike_required
    def ask(self):
        return self._ask

    @property
    @strike_required
    def price(self):
        return self._price

    @property
    @strike_required
    def change(self):
        return self._change

    @property
    @strike_required
    def cp(self):
        return self._cp

    @property
    @strike_required
    def open_interest(self):
        return self._open_interest

    @property
    @strike_required
    def volume(self):
        return self._volume

    @strike_required
    def implied_volatility(self):
        return self.BandS.impvol

    @strike_required
    def delta(self):
        return self.BandS.delta()

    @strike_required
    def gamma(self):
        return self.BandS.gamma()

    @strike_required
    def vega(self):
        return self.BandS.vega()

    @strike_required
    def rho(self):
        return self.BandS.rho()

    @strike_required
    def theta(self):
        return self.BandS.theta()

class Put(Call):
    Option_type = 'Put'
<commit_msg>add Yahoo Finance support<commit_after>import requests
import re, json

from datetime import datetime, date
from time import mktime

from wallstreet.constants import DATE_FORMAT, DATETIME_FORMAT
from wallstreet.blackandscholes import riskfree, BlackandScholes

from functools import wraps

def parse(val):
    if val == '-':
        return 0
    elif val is None:
        return None

    if isinstance(val, str):
        val = val.replace(',', '')
    val = float(val)
    if val.is_integer():
        return int(val)
    return val

def strike_required(func):
    @wraps(func)
    def deco(self, *args, **kwargs):
        if self.strike:
            self.update()
            return func(self, *args, **kwargs)
        else:
            raise AttributeError('Use set_strike() method first')
    return deco

rate = riskfree()

class Stock:
    _G_API = 'http://finance.google.com/finance/info?client=ig&q='
    _Y_API = 'https://query2.finance.yahoo.com/v7/finance/options/'

    def __init__(self, quote, exchange=None, source='google'):
        quote = quote.upper()
        query = str(quote)
        if exchange: query = exchange.upper() + ":" + quote
        self.source = source.lower()

        if self.source == 'google':
            self._google(query)
        elif self.source == 'yahoo':
            self._yahoo(query)

    def _yahoo(self, query):
        if not hasattr(self, 'session_y'):
            self._session_y = requests.Session()
        r = self._session_y.get(__class__._Y_API + query)

        if r.status_code == 404:
            raise LookupError('Ticker symbol not found.')

        jayson = r.json()['optionChain']['result'][0]['quote']

        self.ticker = jayson['symbol']
        self._price = jayson['regularMarketPrice']
        self.currency = jayson['currency']
        self.exchange = jayson['exchange']
        self.change = jayson['regularMarketChange']
        self.cp = jayson['regularMarketChangePercent']
        self._last_trade = datetime.fromtimestamp(jayson['regularMarketTime'])

    def _google(self, query):
        if not hasattr(self, 'session'):
            self._session = requests.Session()
        r = self._session.get(__class__._G_API + query)

        jayson = r.text.replace('\n','')
        jayson = json.loads(jayson[2:])[0]

        try:
            self.ticker = jayson['t']
        except:
            self.ticker = None

        if r.status_code == 400 or self.ticker != query:
            raise LookupError('Ticker symbol not found. Try adding the exchange parameter')

        self.id= jayson["id"]
        self.exchange = jayson['e']
        self._price = parse(jayson['l'])
        try:
            self.change = parse(jayson['c'])
            self.cp = parse(jayson['cp'])
        except ValueError:
            self.change = jayson['c']
            self.cp = jayson['cp']
        self._last_trade = datetime.strptime(jayson['lt_dts'], '%Y-%m-%dT%H:%M:%SZ')

    def update(self):
        self.__init__(self.ticker, source=self.source)

    def __repr__(self):
        return 'Stock(ticker=%s, price=%s)' % (self.ticker, self.price)

    @property
    def price(self):
        self.update()
        return self._price

    @property
    def last_trade(self):
        self.update()
        return self._last_trade.strftime(DATETIME_FORMAT)

class Option:
    _G_API = 'https://www.google.com/finance/option_chain?q='
    _Y_API = 'https://query2.finance.yahoo.com/v7/finance/options/'

    def __init__(self,quote, d=date.today().day, m=date.today().month,
                 y=date.today().year, strict=False, source='google'):

        self.source = source.lower()
        self.underlying = Stock(quote, source=self.source)

        if self.source == 'google':
            self._google(quote, d, m, y, strict)

        elif self.source == 'yahoo':
            self._yahoo(quote, d, m, y, strict)

        self.expirations = [exp.strftime(DATE_FORMAT) for exp in self._exp]
        self.expiration = date(y,m,d)

        try:
            self.calls = self.data['calls']
            self.puts = self.data['puts']
            assert self.calls and self.puts

        except (KeyError, AssertionError):
            if all((d,m,y)) and 'loop' not in locals() and not strict:
                closest_date = min(self._exp, key=lambda x: abs(x - self._expiration))
                print('No options listed for given date, using %s instead' % closest_date.strftime(DATE_FORMAT))
                loop = True
                self.__init__(quote, closest_date.day, closest_date.month, closest_date.year, source=source)
            else:
                raise ValueError('Possible expiration dates for this stock are:', self.expirations) from None

    def _yahoo(self, quote, d, m, y, strict):
        epoch = int(round(mktime(date(y, m, d).timetuple())/86400,0)*86400)

        if not hasattr(self, 'session_y'):
            self.session_y = requests.Session()
        r = self.session_y.get(__class__._Y_API + quote + '?date=' + str(epoch))

        if r.status_code == 404:
            raise LookupError('Ticker symbol not found.')

        json = r.json()

        try:
            self.data = json['optionChain']['result'][0]['options'][0]
        except IndexError:
            raise LookupError('No options listed for this stock.')

        self._exp = [ date.fromtimestamp(i) for i in json['optionChain']['result'][0]['expirationDates'] ]

    def _google(self, quote, d, m, y, strict):

        quote = quote.upper()
        query = str(quote)

        if d: query += '&expd=' + str(d)
        if m: query += '&expm=' + str(m)
        if y: query += '&expy=' + str(y)

        if not hasattr(self, 'session'):
            self.session = requests.Session()
        r = self.session.get(__class__._G_API + query + '&output=json')
        if r.status_code == 400:
            raise LookupError('Ticker symbol not found.')

        valid_json = re.sub(r'(?<={|,)([a-zA-Z][a-zA-Z0-9_]*)(?=:)', r'"\1"', r.text)
        self.data = json.loads(valid_json)

        if 'expirations' not in self.data:
            raise LookupError('No options listed for this stock.')

        self._exp = [date(int(dic['y']), int(dic['m']), int(dic['d']))
                     for dic in self.data['expirations']]

    @property
    def expiration(self):
        return self._expiration.strftime(DATE_FORMAT)

    @expiration.setter
    def expiration(self, val):
        self._expiration = val

class Call(Option):
    Option_type = 'Call'

    def __init__(self, quote, d=date.today().day, m=date.today().month,
                 y=date.today().year, strike=None, strict=False, source='google'):
        quote = quote.upper()
        kw = {'d': d, 'm': m, 'y': y, 'strict': strict, 'source': source}
        super().__init__(quote, **kw)

        if self.__class__.Option_type == 'Call':
            self.data = self.calls
        elif self.__class__.Option_type == 'Put':
            self.data = self.puts

        self.T = (self._expiration - date.today()).days/365
        self.q = 0
        self.ticker = quote
        self.strike = None
        self.strikes = tuple(parse(dic['strike']) for dic in self.data
                        if dic.get('p') != '-' )
        if strike:
            if strike in self.strikes:
                self.set_strike(strike)
            else:
                if strict:
                    raise LookupError('No options listed for given strike price.')
                else:
                    closest_strike = min(self.strikes, key=lambda x: abs(x - strike))
                    print('No option for given strike, using %s instead' % closest_strike)
                    self.set_strike(closest_strike)

    def set_strike(self, val):
        d = {}
        for dic in self.data:
            if parse(dic['strike']) == val and val in self.strikes:
                d = dic
                break
        if d:
            self._price = parse(d.get('p')) or d.get('lastPrice')
            self.id = d.get('cid')
            self.exchange = d.get('e')
            self._bid = parse(d.get('b')) or d.get('bid', 0)
            self._ask = parse(d.get('a')) or d.get('ask', 0)
            self.strike = parse(d['strike'])
            self._change = parse(d.get('c')) or d.get('change', 0) # change in currency
            self._cp = parse(d.get('cp', 0)) or d.get('percentChange', 0) # percentage change
            self._volume = parse(d.get('vol')) or d.get('volume', 0)
            self._open_interest = parse(d.get('oi')) or d.get('openInterest', 0)
            self.code = d.get('s') or d.get('contractSymbol')
            self.itm = ((self.__class__.Option_type == 'Call' and self.underlying.price > self.strike) or
                (self.__class__.Option_type == 'Put' and self.underlying.price < self.strike)) # in the money
            self.BandS = BlackandScholes(
                    self.underlying.price,
                    self.strike,
                    self.T,
                    self._price,
                    rate(self.T),
                    self.__class__.Option_type,
                    self.q
                    )

        else:
            raise LookupError('No options listed for given strike price.')

    def __repr__(self):
        if self.strike:
            return self.__class__.Option_type + "(ticker=%s, expiration=%s, strike=%s)" % (self.ticker, self.expiration, self.strike)
        else:
            return self.__class__.Option_type + "(ticker=%s, expiration=%s)" % (self.ticker, self.expiration)

    def update(self):
        self.__init__(self.ticker, self._expiration.day,
                     self._expiration.month, self._expiration.year,
                     self.strike, source=self.source)

    @property
    @strike_required
    def bid(self):
        return self._bid

    @property
    @strike_required
    def ask(self):
        return self._ask

    @property
    @strike_required
    def price(self):
        return self._price

    @property
    @strike_required
    def change(self):
        return self._change

    @property
    @strike_required
    def cp(self):
        return self._cp

    @property
    @strike_required
    def open_interest(self):
        return self._open_interest

    @property
    @strike_required
    def volume(self):
        return self._volume

    @strike_required
    def implied_volatility(self):
        return self.BandS.impvol

    @strike_required
    def delta(self):
        return self.BandS.delta()

    @strike_required
    def gamma(self):
        return self.BandS.gamma()

    @strike_required
    def vega(self):
        return self.BandS.vega()

    @strike_required
    def rho(self):
        return self.BandS.rho()

    @strike_required
    def theta(self):
        return self.BandS.theta()

class Put(Call):
    Option_type = 'Put'
<|endoftext|>


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

"""
Demo service handler
"""
from __future__ import division

import os
import pkg_resources
import mimetypes
from collections import defaultdict
from xml.sax.saxutils import escape

from mapproxy.config.config import base_config
from mapproxy.compat import PY2
from mapproxy.exception import RequestError
from mapproxy.service.base import Server
from mapproxy.response import Response
from mapproxy.srs import SRS, get_epsg_num
from mapproxy.layer import SRSConditional, CacheMapLayer, ResolutionConditional
from mapproxy.source.wms import WMSSource

if PY2:
    import urllib2
else:
    from urllib import request as urllib2

from mapproxy.template import template_loader, bunch
env = {'bunch': bunch}
get_template = template_loader(__name__, 'templates', namespace=env)

def static_filename(name):
    if base_config().template_dir:
        return os.path.join(base_config().template_dir, name)
    else:
        return pkg_resources.resource_filename(__name__, os.path.join('templates', name))

class DemoServer(Server):
    names = ('demo',)
    def __init__(self, layers, md, request_parser=None, tile_layers=None,
                 srs=None, image_formats=None, services=None, restful_template=None):
        Server.__init__(self)
        self.layers = layers
        self.tile_layers = tile_layers or {}
        self.md = md
        self.image_formats = image_formats
        filter_image_format = []
        for format in self.image_formats:
            if 'image/jpeg' == format or 'image/png' == format:
                filter_image_format.append(format)
        self.image_formats = filter_image_format
        self.srs = srs
        self.services = services or []
        self.restful_template = restful_template

    def handle(self, req):
        if req.path.startswith('/demo/static/'):
            filename = req.path.lstrip('/')
            filename = static_filename(filename)
            if not os.path.isfile(filename):
                return Response('file not found', content_type='text/plain', status=404)
            type, encoding = mimetypes.guess_type(filename)
            return Response(open(filename, 'rb'), content_type=type)

        # we don't authorize the static files (css, js)
        # since they are not confidential
        try:
            authorized = self.authorized_demo(req.environ)
        except RequestError as ex:
            return ex.render()
        if not authorized:
            return Response('forbidden', content_type='text/plain', status=403)

        if 'wms_layer' in req.args:
            demo = self._render_wms_template('demo/wms_demo.html', req)
        elif 'tms_layer' in req.args:
            demo = self._render_tms_template('demo/tms_demo.html', req)
        elif 'wmts_layer' in req.args:
            demo = self._render_wmts_template('demo/wmts_demo.html', req)
        elif 'wms_capabilities' in req.args:
            url = '%s/service?REQUEST=GetCapabilities'%(req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMS', url)
        elif 'wmsc_capabilities' in req.args:
            url = '%s/service?REQUEST=GetCapabilities&tiled=true'%(req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMS-C', url)
        elif 'wmts_capabilities_kvp' in req.args:
            url = '%s/service?REQUEST=GetCapabilities&SERVICE=WMTS' % (req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMTS', url)
        elif 'wmts_capabilities' in req.args:
            url = '%s/wmts/1.0.0/WMTSCapabilities.xml' % (req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMTS', url)
        elif 'tms_capabilities' in req.args:
            if 'layer' in req.args and 'srs' in req.args:
                # prevent dir traversal (seems it's not possible with urllib2, but better safe then sorry)
                layer = req.args['layer'].replace('..', '')
                srs = req.args['srs'].replace('..', '')
                url = '%s/tms/1.0.0/%s/%s'%(req.script_url, layer, srs)
            else:
                url = '%s/tms/1.0.0/'%(req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'TMS', url)
        elif req.path == '/demo/':
            demo = self._render_template('demo/demo.html')
        else:
            resp = Response('', status=301)
            resp.headers['Location'] = req.script_url.rstrip('/') + '/demo/'
            return resp
        return Response(demo, content_type='text/html')

    def layer_srs(self, layer):
        """
        Return a list tuples with title and name of all SRS for the layer.
        The title of SRS that are native to the layer are suffixed with a '*'.
        """
        cached_srs = []
        for map_layer in layer.map_layers:
            # TODO unify map_layers interface
            if isinstance(map_layer, SRSConditional):
                for srs_key in map_layer.srs_map.keys():
                    cached_srs.append(srs_key.srs_code)
            elif isinstance(map_layer, CacheMapLayer):
                cached_srs.append(map_layer.grid.srs.srs_code)
            elif isinstance(map_layer, ResolutionConditional):
                cached_srs.append(map_layer.srs.srs_code)
            elif isinstance(map_layer, WMSSource):
                if map_layer.supported_srs:
                    for supported_srs in map_layer.supported_srs:
                        cached_srs.append(supported_srs.srs_code)

        uncached_srs = []

        for srs_code in self.srs:
            if srs_code not in cached_srs:
                uncached_srs.append(srs_code)

        sorted_cached_srs = sorted(cached_srs, key=lambda srs: get_epsg_num(srs))
        sorted_uncached_srs = sorted(uncached_srs, key=lambda srs: get_epsg_num(srs))
        sorted_cached_srs = [(s + '*', s) for s in sorted_cached_srs]
        sorted_uncached_srs = [(s, s) for s in sorted_uncached_srs]
        return sorted_cached_srs + sorted_uncached_srs

    def _render_template(self, template):
        template = get_template(template, default_inherit="demo/static.html")
        tms_tile_layers = defaultdict(list)
        for layer in self.tile_layers:
            name = self.tile_layers[layer].md.get('name')
            tms_tile_layers[name].append(self.tile_layers[layer])
        wmts_layers = tms_tile_layers.copy()
        return template.substitute(layers=self.layers,
                                   formats=self.image_formats,
                                   srs=self.srs,
                                   layer_srs=self.layer_srs,
                                   tms_layers=tms_tile_layers,
                                   wmts_layers=wmts_layers,
                                   services=self.services)

    def _render_wms_template(self, template, req):
        template = get_template(template, default_inherit="demo/static.html")
        layer = self.layers[req.args['wms_layer']]
        srs = escape(req.args['srs'])
        bbox = layer.extent.bbox_for(SRS(srs))
        width = bbox[2] - bbox[0]
        height = bbox[3] - bbox[1]
        min_res = max(width/256, height/256)
        return template.substitute(layer=layer,
                                   image_formats=self.image_formats,
                                   format=escape(req.args['format']),
                                   srs=srs,
                                   layer_srs=self.layer_srs,
                                   bbox=bbox,
                                   res=min_res)

    def _render_tms_template(self, template, req):
        template = get_template(template, default_inherit="demo/static.html")
        for layer in self.tile_layers.values():
            if (layer.name == req.args['tms_layer'] and
                    layer.grid.srs.srs_code == req.args['srs']):
                tile_layer = layer
                break

        resolutions = tile_layer.grid.tile_sets
        res = []
        for level, resolution in resolutions:
            res.append(resolution)

        if tile_layer.grid.srs.is_latlong:
            units = 'degree'
        else:
            units = 'm'

        if tile_layer.grid.profile == 'local':
            add_res_to_options = True
        else:
            add_res_to_options = False
        return template.substitute(layer=tile_layer,
                                   srs=escape(req.args['srs']),
                                   format=escape(req.args['format']),
                                   resolutions=res,
                                   units=units,
                                   add_res_to_options=add_res_to_options,
                                   all_tile_layers=self.tile_layers)

    def _render_wmts_template(self, template, req):
        template = get_template(template, default_inherit="demo/static.html")
        for layer in self.tile_layers.values():
            if (layer.name == req.args['wmts_layer'] and
                    layer.grid.srs.srs_code == req.args['srs']):
                wmts_layer = layer
                break

        restful_url = self.restful_template.replace('{Layer}', wmts_layer.name, 1)
        if '{Format}' in restful_url:
            restful_url = restful_url.replace('{Format}', wmts_layer.format)

        if wmts_layer.grid.srs.is_latlong:
            units = 'degree'
        else:
            units = 'm'
        return template.substitute(layer=wmts_layer,
                                   matrix_set=wmts_layer.grid.name,
                                   format=escape(req.args['format']),
                                   srs=escape(req.args['srs']),
                                   resolutions=wmts_layer.grid.resolutions,
                                   units=units,
                                   all_tile_layers=self.tile_layers,
                                   restful_url=restful_url)

    def _render_capabilities_template(self, template, xmlfile, service, url):
        template = get_template(template, default_inherit="demo/static.html")
        return template.substitute(capabilities = xmlfile,
                                   service = service,
                                   url = url)

    def authorized_demo(self, environ):
        if 'mapproxy.authorize' in environ:
            result = environ['mapproxy.authorize']('demo', [], environ=environ)
            if result['authorized'] == 'unauthenticated':
                raise RequestError('unauthorized', status=401)
            if result['authorized'] == 'full':
                return True
            return False
        return True
<commit_msg>demo: extend args escaping for javascript examples to avoid XSS<commit_after># This file is part of the MapProxy project.
# Copyright (C) 2010 Omniscale <http://omniscale.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Demo service handler
"""
from __future__ import division

import os
import pkg_resources
import mimetypes
from collections import defaultdict

from mapproxy.config.config import base_config
from mapproxy.compat import PY2
from mapproxy.exception import RequestError
from mapproxy.service.base import Server
from mapproxy.response import Response
from mapproxy.srs import SRS, get_epsg_num
from mapproxy.layer import SRSConditional, CacheMapLayer, ResolutionConditional
from mapproxy.source.wms import WMSSource

if PY2:
    import urllib2
else:
    from urllib import request as urllib2

from mapproxy.template import template_loader, bunch
env = {'bunch': bunch}
get_template = template_loader(__name__, 'templates', namespace=env)

def static_filename(name):
    if base_config().template_dir:
        return os.path.join(base_config().template_dir, name)
    else:
        return pkg_resources.resource_filename(__name__, os.path.join('templates', name))

class DemoServer(Server):
    names = ('demo',)
    def __init__(self, layers, md, request_parser=None, tile_layers=None,
                 srs=None, image_formats=None, services=None, restful_template=None):
        Server.__init__(self)
        self.layers = layers
        self.tile_layers = tile_layers or {}
        self.md = md
        self.image_formats = image_formats
        filter_image_format = []
        for format in self.image_formats:
            if 'image/jpeg' == format or 'image/png' == format:
                filter_image_format.append(format)
        self.image_formats = filter_image_format
        self.srs = srs
        self.services = services or []
        self.restful_template = restful_template

    def handle(self, req):
        if req.path.startswith('/demo/static/'):
            filename = req.path.lstrip('/')
            filename = static_filename(filename)
            if not os.path.isfile(filename):
                return Response('file not found', content_type='text/plain', status=404)
            type, encoding = mimetypes.guess_type(filename)
            return Response(open(filename, 'rb'), content_type=type)

        # we don't authorize the static files (css, js)
        # since they are not confidential
        try:
            authorized = self.authorized_demo(req.environ)
        except RequestError as ex:
            return ex.render()
        if not authorized:
            return Response('forbidden', content_type='text/plain', status=403)

        if 'wms_layer' in req.args:
            demo = self._render_wms_template('demo/wms_demo.html', req)
        elif 'tms_layer' in req.args:
            demo = self._render_tms_template('demo/tms_demo.html', req)
        elif 'wmts_layer' in req.args:
            demo = self._render_wmts_template('demo/wmts_demo.html', req)
        elif 'wms_capabilities' in req.args:
            url = '%s/service?REQUEST=GetCapabilities'%(req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMS', url)
        elif 'wmsc_capabilities' in req.args:
            url = '%s/service?REQUEST=GetCapabilities&tiled=true'%(req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMS-C', url)
        elif 'wmts_capabilities_kvp' in req.args:
            url = '%s/service?REQUEST=GetCapabilities&SERVICE=WMTS' % (req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMTS', url)
        elif 'wmts_capabilities' in req.args:
            url = '%s/wmts/1.0.0/WMTSCapabilities.xml' % (req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'WMTS', url)
        elif 'tms_capabilities' in req.args:
            if 'layer' in req.args and 'srs' in req.args:
                # prevent dir traversal (seems it's not possible with urllib2, but better safe then sorry)
                layer = req.args['layer'].replace('..', '')
                srs = req.args['srs'].replace('..', '')
                url = '%s/tms/1.0.0/%s/%s'%(req.script_url, layer, srs)
            else:
                url = '%s/tms/1.0.0/'%(req.script_url)
            capabilities = urllib2.urlopen(url)
            demo = self._render_capabilities_template('demo/capabilities_demo.html', capabilities, 'TMS', url)
        elif req.path == '/demo/':
            demo = self._render_template('demo/demo.html')
        else:
            resp = Response('', status=301)
            resp.headers['Location'] = req.script_url.rstrip('/') + '/demo/'
            return resp
        return Response(demo, content_type='text/html')

    def layer_srs(self, layer):
        """
        Return a list tuples with title and name of all SRS for the layer.
        The title of SRS that are native to the layer are suffixed with a '*'.
        """
        cached_srs = []
        for map_layer in layer.map_layers:
            # TODO unify map_layers interface
            if isinstance(map_layer, SRSConditional):
                for srs_key in map_layer.srs_map.keys():
                    cached_srs.append(srs_key.srs_code)
            elif isinstance(map_layer, CacheMapLayer):
                cached_srs.append(map_layer.grid.srs.srs_code)
            elif isinstance(map_layer, ResolutionConditional):
                cached_srs.append(map_layer.srs.srs_code)
            elif isinstance(map_layer, WMSSource):
                if map_layer.supported_srs:
                    for supported_srs in map_layer.supported_srs:
                        cached_srs.append(supported_srs.srs_code)

        uncached_srs = []

        for srs_code in self.srs:
            if srs_code not in cached_srs:
                uncached_srs.append(srs_code)

        sorted_cached_srs = sorted(cached_srs, key=lambda srs: get_epsg_num(srs))
        sorted_uncached_srs = sorted(uncached_srs, key=lambda srs: get_epsg_num(srs))
        sorted_cached_srs = [(s + '*', s) for s in sorted_cached_srs]
        sorted_uncached_srs = [(s, s) for s in sorted_uncached_srs]
        return sorted_cached_srs + sorted_uncached_srs

    def _render_template(self, template):
        template = get_template(template, default_inherit="demo/static.html")
        tms_tile_layers = defaultdict(list)
        for layer in self.tile_layers:
            name = self.tile_layers[layer].md.get('name')
            tms_tile_layers[name].append(self.tile_layers[layer])
        wmts_layers = tms_tile_layers.copy()
        return template.substitute(layers=self.layers,
                                   formats=self.image_formats,
                                   srs=self.srs,
                                   layer_srs=self.layer_srs,
                                   tms_layers=tms_tile_layers,
                                   wmts_layers=wmts_layers,
                                   services=self.services)

    def _render_wms_template(self, template, req):
        template = get_template(template, default_inherit="demo/static.html")
        layer = self.layers[req.args['wms_layer']]
        srs = escape(req.args['srs'])
        bbox = layer.extent.bbox_for(SRS(srs))
        width = bbox[2] - bbox[0]
        height = bbox[3] - bbox[1]
        min_res = max(width/256, height/256)
        return template.substitute(layer=layer,
                                   image_formats=self.image_formats,
                                   format=escape(req.args['format']),
                                   srs=srs,
                                   layer_srs=self.layer_srs,
                                   bbox=bbox,
                                   res=min_res)

    def _render_tms_template(self, template, req):
        template = get_template(template, default_inherit="demo/static.html")
        for layer in self.tile_layers.values():
            if (layer.name == req.args['tms_layer'] and
                    layer.grid.srs.srs_code == req.args['srs']):
                tile_layer = layer
                break

        resolutions = tile_layer.grid.tile_sets
        res = []
        for level, resolution in resolutions:
            res.append(resolution)

        if tile_layer.grid.srs.is_latlong:
            units = 'degree'
        else:
            units = 'm'

        if tile_layer.grid.profile == 'local':
            add_res_to_options = True
        else:
            add_res_to_options = False
        return template.substitute(layer=tile_layer,
                                   srs=escape(req.args['srs']),
                                   format=escape(req.args['format']),
                                   resolutions=res,
                                   units=units,
                                   add_res_to_options=add_res_to_options,
                                   all_tile_layers=self.tile_layers)

    def _render_wmts_template(self, template, req):
        template = get_template(template, default_inherit="demo/static.html")
        for layer in self.tile_layers.values():
            if (layer.name == req.args['wmts_layer'] and
                    layer.grid.srs.srs_code == req.args['srs']):
                wmts_layer = layer
                break

        restful_url = self.restful_template.replace('{Layer}', wmts_layer.name, 1)
        if '{Format}' in restful_url:
            restful_url = restful_url.replace('{Format}', wmts_layer.format)

        if wmts_layer.grid.srs.is_latlong:
            units = 'degree'
        else:
            units = 'm'
        return template.substitute(layer=wmts_layer,
                                   matrix_set=wmts_layer.grid.name,
                                   format=escape(req.args['format']),
                                   srs=escape(req.args['srs']),
                                   resolutions=wmts_layer.grid.resolutions,
                                   units=units,
                                   all_tile_layers=self.tile_layers,
                                   restful_url=restful_url)

    def _render_capabilities_template(self, template, xmlfile, service, url):
        template = get_template(template, default_inherit="demo/static.html")
        return template.substitute(capabilities = xmlfile,
                                   service = service,
                                   url = url)

    def authorized_demo(self, environ):
        if 'mapproxy.authorize' in environ:
            result = environ['mapproxy.authorize']('demo', [], environ=environ)
            if result['authorized'] == 'unauthenticated':
                raise RequestError('unauthorized', status=401)
            if result['authorized'] == 'full':
                return True
            return False
        return True

def escape(data):
    """
    Escape user-provided input data for safe inclusion in HTML _and_ JS to prevent XSS.
    """
    data = data.replace('&', '&')
    data = data.replace('>', '>')
    data = data.replace('<', '<')
    data = data.replace("'", '')
    data = data.replace('"', '')
    return data
<|endoftext|>


<commit_before>from django import forms
from djangular.forms.angular_model import NgModelFormMixin
from huts.models import Hut, HutSuggestion, HutEdit
from huts.widgets import PointWidget

SELECT_ATTRS = {'ui-select2': 'select2Options'}

class HutCommonForm(NgModelFormMixin, forms.ModelForm):
  location = forms.CharField(widget=PointWidget, help_text="Format: 'latitude, longitude'")

  def __init__(self, *args, **kwargs):
    kwargs.update(scope_prefix='hut')
    super(HutCommonForm, self).__init__(*args, **kwargs)

  class Media:
    css = {
      'all': ('hutmap/css/admin.css',),
    }
  class Meta:
    widgets = {
      'region': forms.widgets.Select(attrs=SELECT_ATTRS),
      'country': forms.widgets.Select(attrs=SELECT_ATTRS),
      'agency': forms.widgets.Select(attrs=SELECT_ATTRS),
      'photo': forms.widgets.FileInput(attrs={'ng-file-select': 'hut.photo = $files[0]'}),
    }

class HutForm(HutCommonForm):
  class Meta(HutCommonForm.Meta):
    model = Hut

class HutSuggestionForm(HutCommonForm):
  class Meta(HutCommonForm.Meta):
    model = HutSuggestion

class HutEditForm(HutCommonForm):
  class Meta(HutCommonForm.Meta):
    model = HutEdit
    widgets = dict(
      [('hut', forms.widgets.Select(attrs=SELECT_ATTRS))] +
      HutCommonForm.Meta.widgets.items()
    )

<commit_msg>Use new PointFormField.<commit_after>from django import forms
from djangular.forms.angular_model import NgModelFormMixin
from huts.models import Hut, HutSuggestion, HutEdit
from huts.fields import PointFormField

SELECT_ATTRS = {'ui-select2': 'select2Options'}

class HutCommonForm(NgModelFormMixin, forms.ModelForm):
  location = PointFormField()

  def __init__(self, *args, **kwargs):
    kwargs.update(scope_prefix='hut')
    super(HutCommonForm, self).__init__(*args, **kwargs)

  class Media:
    css = {
      'all': ('hutmap/css/admin.css',),
    }
  class Meta:
    widgets = {
      'region': forms.widgets.Select(attrs=SELECT_ATTRS),
      'country': forms.widgets.Select(attrs=SELECT_ATTRS),
      'agency': forms.widgets.Select(attrs=SELECT_ATTRS),
      'photo': forms.widgets.FileInput(attrs={'ng-file-select': 'hut.photo = $files[0]'}),
    }

class HutForm(HutCommonForm):
  class Meta(HutCommonForm.Meta):
    model = Hut

class HutSuggestionForm(HutCommonForm):
  class Meta(HutCommonForm.Meta):
    model = HutSuggestion

class HutEditForm(HutCommonForm):
  class Meta(HutCommonForm.Meta):
    model = HutEdit
    widgets = dict(
      [('hut', forms.widgets.Select(attrs=SELECT_ATTRS))] +
      HutCommonForm.Meta.widgets.items()
    )

<|endoftext|>