<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2014 Compassion CH (http://www.compassion.ch)
#    Releasing children from poverty in Jesus' name
#    @author: Emanuel Cino <ecino@compassion.ch>
#
#    The licence is in the file __openerp__.py
#
##############################################################################

from openerp.osv import orm, fields
from openerp.tools import mod10r
from openerp.tools.translate import _
from openerp import netsvc

from sponsorship_compassion.model.product import GIFT_CATEGORY, GIFT_NAMES, \
    SPONSORSHIP_CATEGORY

import time

class AccountStatement(orm.Model):

    """ Adds a relation to a recurring invoicer. """

    _inherit = 'account.bank.statement'

    _columns = {
        'recurring_invoicer_id': fields.many2one(
            'recurring.invoicer', 'Invoicer'),
    }

    def button_auto_completion(self, cr, uid, ids, context=None):
        invoicer = self.browse(cr, uid, ids[0], context).recurring_invoicer_id
        invoicer_obj = self.pool.get('recurring.invoicer')
        if invoicer:
            invoicer_id = invoicer.id
        else:
            invoicer_id = invoicer_obj.create(cr, uid, {}, context=context)
            self.write(
                cr, uid, ids, {'recurring_invoicer_id': invoicer_id},
                context=context)
            invoicer = invoicer_obj.browse(
                cr, uid, invoicer_id, context=context)

        super(AccountStatement, self).button_auto_completion(
            cr, uid, ids, context=context)

        if not invoicer.invoice_ids:
            invoicer_obj.unlink(cr, uid, invoicer_id, context=context)

    def button_invoices(self, cr, uid, ids, context=None):
        invoicer_id = self.browse(
            cr, uid, ids[0], context=context).recurring_invoicer_id.id

        return {
            'name': 'Generated Invoices',
            'view_mode': 'tree,form',
            'view_type': 'form',
            'res_model': 'account.invoice',
            'domain': [('recurring_invoicer_id', '=', invoicer_id)],
            'type': 'ir.actions.act_window',
            'target': 'current',
            'context': {'form_view_ref': 'account.invoice_form',
                        'journal_type': 'sale'},
        }

    def button_confirm_bank(self, cr, uid, ids, context=None):
        """Confirm invoices generated by statment lines."""
        res = super(AccountStatement, self).button_confirm_bank(cr, uid, ids,
                                                                context)
        wf_service = netsvc.LocalService('workflow')
        for statement in self.browse(cr, uid, ids, context):
            for line in statement.line_ids:
                if line.invoice_id:
                    # Validate the invoice
                    wf_service.trg_validate(
                        uid, 'account.invoice', line.invoice_id.id,
                        'invoice_open', cr)
        return res

    def button_cancel(self, cr, uid, ids, context=None):
        """ Cancel invoices generated by statement lines. """
        res = super(AccountStatement, self).button_cancel(cr, uid, ids,
                                                          context)
        invoice_obj = self.pool.get('account.invoice')
        for statement in self.browse(cr, uid, ids, context):
            invoice_ids = [l.invoice_id.id for l in statement.line_ids
                           if l.invoice_id]
            # Reset the invoices to draft state
            invoice_obj.action_cancel_draft(cr, uid, invoice_ids)
        return res

class AccountStatementLine(orm.Model):
    """ Adds products to statement lines to generate invoices. """

    _inherit = 'account.bank.statement.line'

    _columns = {
        'product_id': fields.many2one('product.product', _('Product')),
        'contract_id': fields.many2one('recurring.contract', _('Sponsorship')),
        'user_id': fields.many2one('res.partner', _('Ambassador')),
        'invoice_id': fields.many2one('account.invoice', 'Invoice'),
    }

    def create(self, cr, uid, vals, context=None):
        """Generate invoice if a product is selected."""
        res_id = super(AccountStatementLine, self).create(cr, uid, vals,
                                                          context)
        if 'product_id' in vals or 'contract_id' in vals:
            # Generate new invoices
            self._create_invoice_from_line(cr, uid, self.browse(
                cr, uid, res_id, {'lang': 'en_US'}), context)
        return res_id

    def write(self, cr, uid, ids, vals, context=None):
        """Generate invoice if a product is selected."""
        if 'product_id' in vals or 'contract_id' in vals:
            for line in self.browse(cr, uid, ids, context):
                # Remove old invoice
                invoice = line.invoice_id
                if invoice:
                    if not invoice.internal_number:
                        invoice.unlink()
                    else:
                        invoice.write({
                            'recurring_invoicer_id': False})
                    line.write({'invoice_id': False, 'ref': '/'})
        res = super(AccountStatementLine, self).write(cr, uid, ids, vals,
                                                      context)
        if 'product_id' in vals or 'contract_id' in vals:
            # Generate new invoices
            [self._create_invoice_from_line(cr, uid, line, context)
             for line in self.browse(cr, uid, ids, {'lang': 'en_US'})]
        return res

    def _create_invoice_from_line(self, cr, uid, b_line, context=None):
        if not b_line.product_id:
            return True
        # Get the attached recurring invoicer
        invoicer = b_line.statement_id.recurring_invoicer_id
        invoice_obj = self.pool.get('account.invoice')
        if not invoicer:
            invoicer_obj = self.pool.get('recurring.invoicer')
            invoicer_id = invoicer_obj.create(cr, uid, {'source': self._name},
                                              context)
            b_line.statement_id.write(
                {'recurring_invoicer_id': invoicer_id})
            invoicer = invoicer_obj.browse(cr, uid, invoicer_id, context)

        # Generate a unique bvr_reference
        ref = mod10r((b_line.date.replace('-', '') + str(
            b_line.statement_id.id) + str(b_line.id)).ljust(26, '0'))

        # Lookup for an existing open invoice matching the criterias
        invoice_ids = self._find_open_invoice(cr, uid, b_line, context)
        if invoice_ids:
            # Get the bvr reference of the invoice or set it
            invoice = invoice_obj.browse(cr, uid, invoice_ids[0], context)
            invoice.write({'recurring_invoicer_id': invoicer.id})
            if invoice.bvr_reference:
                ref = invoice.bvr_reference
            else:
                invoice.write({'bvr_reference': ref})
            b_line.write({
                'ref': ref,
                'invoice_id': invoice.id})
            return True

        # Setup a new invoice if no existing invoice is found
        journal_ids = self.pool.get('account.journal').search(
            cr, uid, [('type', '=', 'sale')], limit=1)
        payment_term_ids = self.pool.get('account.payment.term').search(
            cr, uid, [('name', '=', 'Bank Transfer')],
            context={'lang': 'en_US'})
        inv_data = {
            'account_id': b_line.partner_id.property_account_receivable.id,
            'type': 'out_invoice',
            'partner_id': b_line.partner_id.id,
            'journal_id': journal_ids[0] if journal_ids else False,
            'date_invoice': b_line.date,
            'payment_term': payment_term_ids and payment_term_ids[0] or 1,
            'bvr_reference': ref,
            'recurring_invoicer_id': invoicer.id,
            'currency_id': b_line.statement_id.currency.id,
        }
        if b_line.product_id.name == GIFT_NAMES[0] and b_line.contract_id \
                and b_line.contract_id.child_id and \
                b_line.contract_id.child_id.birthdate:
            inv_data['date_invoice'] = self.pool.get(
                'generate.gift.wizard').compute_date_birthday_invoice(
                b_line.contract_id.child_id.birthdate, b_line.date)
        invoice_id = invoice_obj.create(cr, uid, inv_data, context)

        inv_line_data = {
            'name': b_line.name,
            'account_id': b_line.product_id.property_account_income.id,
            'price_unit': b_line.amount,
            'price_subtotal': b_line.amount,
            'contract_id': b_line.contract_id and
            b_line.contract_id.id or False,
            'user_id': b_line.user_id and b_line.user_id.id or False,
            'quantity': 1,
            'uos_id': False,
            'product_id': b_line.product_id.id,
            'partner_id': b_line.partner_id.id,
            'invoice_id': invoice_id,
        }

        if b_line.product_id.categ_name in (GIFT_CATEGORY,
                                            SPONSORSHIP_CATEGORY) and not \
                b_line.contract_id:
            raise orm.except_orm(_('A field is required'),
                                 _('Add a Sponsorship'))

        if b_line.account_analytic_id:
            inv_line_data['account_analytic_id'] = \
                b_line.account_analytic_id.id
        else:
            analytic = self.pool.get('account.analytic.default').account_get(
                cr, uid, b_line.product_id.id, b_line.partner_id.id, uid,
                time.strftime('%Y-%m-%d'), context=context)
            if analytic and analytic.analytics_id:
                inv_line_data['analytics_id'] = analytic.analytics_id.id

        self.pool.get('account.invoice.line').create(
            cr, uid, inv_line_data, context)

        invoice_obj.button_compute(cr, uid, [invoice_id], context)
        b_line.write({
            'ref': ref,
            'invoice_id': invoice_id})

        return True

    def _find_open_invoice(self, cr, uid, b_line, context=None):
        """ Find an open invoice that matches the statement line and which
        could be reconciled with. """
        invoice_line_obj = self.pool.get('account.invoice.line')
        invl_ids = invoice_line_obj.search(cr, uid, [
            ('partner_id', '=', b_line.partner_id.id),
            ('state', 'in', ('open', 'draft')),
            ('product_id', '=', b_line.product_id.id),
            ('invoice_id.account_id', '=', b_line.account_id.id),
            ('price_subtotal', '=', b_line.amount)], context=context)
        return list(set([
            l.invoice_id.id for l in invoice_line_obj.browse(
                cr, uid, invl_ids, context)
            if l.invoice_id.amount_total == b_line.amount]))
<commit_msg>Small bug fix<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2014 Compassion CH (http://www.compassion.ch)
#    Releasing children from poverty in Jesus' name
#    @author: Emanuel Cino <ecino@compassion.ch>
#
#    The licence is in the file __openerp__.py
#
##############################################################################

from openerp.osv import orm, fields
from openerp.tools import mod10r
from openerp.tools.translate import _
from openerp import netsvc

from sponsorship_compassion.model.product import GIFT_CATEGORY, GIFT_NAMES, \
    SPONSORSHIP_CATEGORY

import time

class AccountStatement(orm.Model):

    """ Adds a relation to a recurring invoicer. """

    _inherit = 'account.bank.statement'

    _columns = {
        'recurring_invoicer_id': fields.many2one(
            'recurring.invoicer', 'Invoicer'),
    }

    def button_auto_completion(self, cr, uid, ids, context=None):
        invoicer = self.browse(cr, uid, ids[0], context).recurring_invoicer_id
        invoicer_obj = self.pool.get('recurring.invoicer')
        if invoicer:
            invoicer_id = invoicer.id
        else:
            invoicer_id = invoicer_obj.create(cr, uid, {}, context=context)
            self.write(
                cr, uid, ids, {'recurring_invoicer_id': invoicer_id},
                context=context)
            invoicer = invoicer_obj.browse(
                cr, uid, invoicer_id, context=context)

        super(AccountStatement, self).button_auto_completion(
            cr, uid, ids, context=context)

        if not invoicer.invoice_ids:
            invoicer_obj.unlink(cr, uid, invoicer_id, context=context)

    def button_invoices(self, cr, uid, ids, context=None):
        invoicer_id = self.browse(
            cr, uid, ids[0], context=context).recurring_invoicer_id.id

        return {
            'name': 'Generated Invoices',
            'view_mode': 'tree,form',
            'view_type': 'form',
            'res_model': 'account.invoice',
            'domain': [('recurring_invoicer_id', '=', invoicer_id)],
            'type': 'ir.actions.act_window',
            'target': 'current',
            'context': {'form_view_ref': 'account.invoice_form',
                        'journal_type': 'sale'},
        }

    def button_confirm_bank(self, cr, uid, ids, context=None):
        """Confirm invoices generated by statment lines."""
        res = super(AccountStatement, self).button_confirm_bank(cr, uid, ids,
                                                                context)
        wf_service = netsvc.LocalService('workflow')
        for statement in self.browse(cr, uid, ids, context):
            for line in statement.line_ids:
                if line.invoice_id:
                    # Validate the invoice
                    wf_service.trg_validate(
                        uid, 'account.invoice', line.invoice_id.id,
                        'invoice_open', cr)
        return res

    def button_cancel(self, cr, uid, ids, context=None):
        """ Cancel invoices generated by statement lines. """
        res = super(AccountStatement, self).button_cancel(cr, uid, ids,
                                                          context)
        invoice_obj = self.pool.get('account.invoice')
        for statement in self.browse(cr, uid, ids, context):
            invoice_ids = [l.invoice_id.id for l in statement.line_ids
                           if l.invoice_id]
            # Reset the invoices to draft state
            invoice_obj.action_cancel_draft(cr, uid, invoice_ids)
        return res

class AccountStatementLine(orm.Model):
    """ Adds products to statement lines to generate invoices. """

    _inherit = 'account.bank.statement.line'

    _columns = {
        'product_id': fields.many2one('product.product', _('Product')),
        'contract_id': fields.many2one('recurring.contract', _('Sponsorship')),
        'user_id': fields.many2one('res.partner', _('Ambassador')),
        'invoice_id': fields.many2one('account.invoice', 'Invoice'),
    }

    def create(self, cr, uid, vals, context=None):
        """Generate invoice if a product is selected."""
        res_id = super(AccountStatementLine, self).create(cr, uid, vals,
                                                          context)
        if 'product_id' in vals or 'contract_id' in vals:
            # Generate new invoices
            self._create_invoice_from_line(cr, uid, self.browse(
                cr, uid, res_id, {'lang': 'en_US'}), context)
        return res_id

    def write(self, cr, uid, ids, vals, context=None):
        """Generate invoice if a product is selected."""
        if 'product_id' in vals or 'contract_id' in vals:
            for line in self.browse(cr, uid, ids, context):
                # Remove old invoice
                invoice = line.invoice_id
                if invoice:
                    if not invoice.internal_number:
                        invoice.unlink()
                    else:
                        invoice.write({
                            'recurring_invoicer_id': False})
                    line.write({'invoice_id': False, 'ref': '/'})
        res = super(AccountStatementLine, self).write(cr, uid, ids, vals,
                                                      context)
        if 'product_id' in vals or 'contract_id' in vals:
            # Generate new invoices
            [self._create_invoice_from_line(cr, uid, line, context)
             for line in self.browse(cr, uid, ids, {'lang': 'en_US'})]
        return res

    def _create_invoice_from_line(self, cr, uid, b_line, context=None):
        if not b_line.product_id:
            return True
        # Get the attached recurring invoicer
        invoicer = b_line.statement_id.recurring_invoicer_id
        invoice_obj = self.pool.get('account.invoice')
        if not invoicer:
            invoicer_obj = self.pool.get('recurring.invoicer')
            invoicer_id = invoicer_obj.create(cr, uid, {'source': self._name},
                                              context)
            b_line.statement_id.write(
                {'recurring_invoicer_id': invoicer_id})
            invoicer = invoicer_obj.browse(cr, uid, invoicer_id, context)

        # Generate a unique bvr_reference
        ref = mod10r((b_line.date.replace('-', '') + str(
            b_line.statement_id.id) + str(b_line.id)).ljust(26, '0'))

        # Lookup for an existing open invoice matching the criterias
        invoice_ids = self._find_open_invoice(cr, uid, b_line, context)
        if invoice_ids:
            # Get the bvr reference of the invoice or set it
            invoice = invoice_obj.browse(cr, uid, invoice_ids[0], context)
            invoice.write({'recurring_invoicer_id': invoicer.id})
            if invoice.bvr_reference:
                ref = invoice.bvr_reference
            else:
                invoice.write({'bvr_reference': ref})
            b_line.write({
                'ref': ref,
                'invoice_id': invoice.id})
            return True

        # Setup a new invoice if no existing invoice is found
        journal_ids = self.pool.get('account.journal').search(
            cr, uid, [('type', '=', 'sale')], limit=1)
        payment_term_ids = self.pool.get('account.payment.term').search(
            cr, uid, [('name', '=', 'Bank Transfer')],
            context={'lang': 'en_US'})
        inv_data = {
            'account_id': b_line.partner_id.property_account_receivable.id,
            'type': 'out_invoice',
            'partner_id': b_line.partner_id.id,
            'journal_id': journal_ids[0] if journal_ids else False,
            'date_invoice': b_line.date,
            'payment_term': payment_term_ids and payment_term_ids[0] or 1,
            'bvr_reference': ref,
            'recurring_invoicer_id': invoicer.id,
            'currency_id': b_line.statement_id.currency.id,
        }
        if b_line.product_id.name == GIFT_NAMES[0] and b_line.contract_id \
                and b_line.contract_id.child_id and \
                b_line.contract_id.child_id.birthdate:
            inv_data['date_invoice'] = self.pool.get(
                'generate.gift.wizard').compute_date_birthday_invoice(
                b_line.contract_id.child_id.birthdate, b_line.date)
        invoice_id = invoice_obj.create(cr, uid, inv_data, context)

        inv_line_data = {
            'name': b_line.name,
            'account_id': b_line.product_id.property_account_income.id,
            'price_unit': b_line.amount,
            'price_subtotal': b_line.amount,
            'contract_id': b_line.contract_id and
            b_line.contract_id.id or False,
            'user_id': b_line.user_id and b_line.user_id.id or False,
            'quantity': 1,
            'uos_id': False,
            'product_id': b_line.product_id.id,
            'partner_id': b_line.partner_id.id,
            'invoice_id': invoice_id,
        }

        if b_line.product_id.categ_name in (GIFT_CATEGORY,
                                            SPONSORSHIP_CATEGORY) and not \
                b_line.contract_id:
            raise orm.except_orm(_('A field is required'),
                                 _('Add a Sponsorship'))

        if b_line.analytic_account_id:
            inv_line_data['account_analytic_id'] = \
                b_line.account_analytic_id.id
        else:
            analytic = self.pool.get('account.analytic.default').account_get(
                cr, uid, b_line.product_id.id, b_line.partner_id.id, uid,
                time.strftime('%Y-%m-%d'), context=context)
            if analytic and analytic.analytics_id:
                inv_line_data['analytics_id'] = analytic.analytics_id.id

        self.pool.get('account.invoice.line').create(
            cr, uid, inv_line_data, context)

        invoice_obj.button_compute(cr, uid, [invoice_id], context)
        b_line.write({
            'ref': ref,
            'invoice_id': invoice_id})

        return True

    def _find_open_invoice(self, cr, uid, b_line, context=None):
        """ Find an open invoice that matches the statement line and which
        could be reconciled with. """
        invoice_line_obj = self.pool.get('account.invoice.line')
        invl_ids = invoice_line_obj.search(cr, uid, [
            ('partner_id', '=', b_line.partner_id.id),
            ('state', 'in', ('open', 'draft')),
            ('product_id', '=', b_line.product_id.id),
            ('invoice_id.account_id', '=', b_line.account_id.id),
            ('price_subtotal', '=', b_line.amount)], context=context)
        return list(set([
            l.invoice_id.id for l in invoice_line_obj.browse(
                cr, uid, invl_ids, context)
            if l.invoice_id.amount_total == b_line.amount]))
<|endoftext|>


<commit_before># Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt

from __future__ import unicode_literals
import webnotes
from webnotes import _
from webnotes.utils import getdate, nowdate, flt

class AccountsReceivableReport(object):
	def __init__(self, filters=None):
		self.filters = webnotes._dict(filters or {})
		self.filters.report_date = getdate(self.filters.report_date or nowdate())
		self.age_as_on = getdate(nowdate()) \
			if self.filters.report_date > getdate(nowdate()) \
			else self.filters.report_date

	def run(self):
		return self.get_columns(), self.get_data()

	def get_columns(self):
		return [
			"Posting Date:Date:80", "Account:Link/Account:150", "Customer::150",
			"Voucher Type::110", "Voucher No::120", "Remarks::150",
			"Due Date:Date:80", "Territory:Link/Territory:80",
			"Invoiced Amount:Currency:100", "Payment Received:Currency:100",
			"Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100",
			"30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100"
		]

	def get_data(self):
		data = []
		future_vouchers = self.get_entries_after(self.filters.report_date)
		for gle in self.get_entries_till(self.filters.report_date):
			if self.is_receivable(gle, future_vouchers):
				outstanding_amount = self.get_outstanding_amount(gle, self.filters.report_date)
				if abs(outstanding_amount) > 0.01:
					due_date = self.get_due_date(gle)
					invoiced_amount = gle.debit if (gle.debit > 0) else 0
					payment_received = invoiced_amount - outstanding_amount
					row = [gle.posting_date, gle.account, self.get_customer(gle.account),
						gle.voucher_type, gle.voucher_no, gle.remarks, due_date,
						self.get_territory(gle.account), invoiced_amount, payment_received,
						outstanding_amount]
					entry_date = due_date if self.filters.ageing_based_on=="Due Date" \
						else gle.posting_date
					row += get_ageing_data(self.age_as_on, entry_date, outstanding_amount)
					data.append(row)
		return data

	def get_entries_after(self, report_date):
		# returns a distinct list
		return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries()
			if getdate(e.posting_date) > report_date]))

	def get_entries_till(self, report_date):
		# returns a generator
		return (e for e in self.get_gl_entries()
			if getdate(e.posting_date) <= report_date)

	def is_receivable(self, gle, future_vouchers):
		return ((not gle.against_voucher) or (gle.against_voucher==gle.voucher_no) or
			((gle.against_voucher_type, gle.against_voucher) in future_vouchers))

	def get_outstanding_amount(self, gle, report_date):
		payment_received = 0.0
		for e in self.get_gl_entries_for(gle.account, gle.voucher_type, gle.voucher_no):
			if getdate(e.posting_date) <= report_date and e.name!=gle.name:
				payment_received += (flt(e.credit) - flt(e.debit))

		return flt(gle.debit) - flt(gle.credit) - payment_received

	def get_customer(self, account):
		return self.get_account_map().get(account).get("customer_name") or ""

	def get_territory(self, account):
		return self.get_account_map().get(account).get("territory") or ""

	def get_account_map(self):
		if not hasattr(self, "account_map"):
			self.account_map = dict(((r.name, r) for r in webnotes.conn.sql("""select
				account.name, customer.customer_name, customer.territory
				from `tabAccount` account, `tabCustomer` customer
				where account.master_type="Customer"
				and customer.name=account.master_name""", as_dict=True)))

		return self.account_map

	def get_due_date(self, gle):
		if not hasattr(self, "invoice_due_date_map"):
			# TODO can be restricted to posting date
			self.invoice_due_date_map = dict(webnotes.conn.sql("""select name, due_date
				from `tabSales Invoice` where docstatus=1"""))

		return gle.voucher_type == "Sales Invoice" \
			and self.invoice_due_date_map.get(gle.voucher_no) or ""

	def get_gl_entries(self):
		if not hasattr(self, "gl_entries"):
			conditions, values = self.prepare_conditions()
			self.gl_entries = webnotes.conn.sql("""select * from `tabGL Entry`
				where docstatus < 2 {} order by posting_date, account""".format(conditions),
				values, as_dict=True)

		return self.gl_entries

	def prepare_conditions(self):
		conditions = [""]
		values = {}

		if self.filters.company:
			conditions.append("company=%(company)s")
			values["company"] = self.filters.company

		if self.filters.account:
			conditions.append("account=%(account)s")
			values["account"] = self.filters.account
		else:
			account_map = self.get_account_map()
			if not account_map:
				webnotes.throw(_("No Customer Accounts found."))
			else:
				accounts_list = ['"{}"'.format(ac) for ac in account_map]
				conditions.append("account in ({})".format(", ".join(accounts_list)))

		return " and ".join(conditions), values

	def get_gl_entries_for(self, account, against_voucher_type, against_voucher):
		if not hasattr(self, "gl_entries_map"):
			self.gl_entries_map = {}
			for gle in self.get_gl_entries():
				if gle.against_voucher_type and gle.against_voucher:
					self.gl_entries_map.setdefault(gle.account, {})\
						.setdefault(gle.against_voucher_type, {})\
						.setdefault(gle.against_voucher, [])\
						.append(gle)

		return self.gl_entries_map.get(account, {})\
			.get(against_voucher_type, {})\
			.get(against_voucher, [])

def execute(filters=None):
	return AccountsReceivableReport(filters).run()

def get_ageing_data(age_as_on, entry_date, outstanding_amount):
	# [0-30, 30-60, 60-90, 90-above]
	outstanding_range = [0.0, 0.0, 0.0, 0.0]
	if not (age_as_on and entry_date):
		return [0] + outstanding_range

	age = (getdate(age_as_on) - getdate(entry_date)).days or 0
	index = None
	for i, days in enumerate([30, 60, 90]):
		if age <= days:
			index = i
			break

	if index is None: index = 3
	outstanding_range[index] = outstanding_amount

	return [age] + outstanding_range<commit_msg>Updated Accounts receivable report<commit_after># Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
# License: GNU General Public License v3. See license.txt

from __future__ import unicode_literals
import webnotes
from webnotes import _
from webnotes.utils import getdate, nowdate, flt

class AccountsReceivableReport(object):
	def __init__(self, filters=None):
		self.filters = webnotes._dict(filters or {})
		self.filters.report_date = getdate(self.filters.report_date or nowdate())
		self.age_as_on = getdate(nowdate()) \
			if self.filters.report_date > getdate(nowdate()) \
			else self.filters.report_date

	def run(self):
		return self.get_columns(), self.get_data()

	def get_columns(self):
		return [
			"Posting Date:Date:80", "Account:Link/Account:150",
			"Voucher Type::110", "Voucher No::120", "::30",
			"Due Date:Date:80",
			"Invoiced Amount:Currency:100", "Payment Received:Currency:100",
			"Outstanding Amount:Currency:100", "Age:Int:50", "0-30:Currency:100",
			"30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100",
			"Customer:Link/Customer:200", "Territory:Link/Territory:80", "Remarks::200"
		]

	def get_data(self):
		data = []
		future_vouchers = self.get_entries_after(self.filters.report_date)
		for gle in self.get_entries_till(self.filters.report_date):
			if self.is_receivable(gle, future_vouchers):
				outstanding_amount = self.get_outstanding_amount(gle, self.filters.report_date)
				if abs(outstanding_amount) > 0.01:
					due_date = self.get_due_date(gle)
					invoiced_amount = gle.debit if (gle.debit > 0) else 0
					payment_received = invoiced_amount - outstanding_amount
					row = [gle.posting_date, gle.account,
						gle.voucher_type, gle.voucher_no, due_date,
						invoiced_amount, payment_received,
						outstanding_amount]
					entry_date = due_date if self.filters.ageing_based_on=="Due Date" \
						else gle.posting_date
					row += get_ageing_data(self.age_as_on, entry_date, outstanding_amount)
					row += [self.get_customer(gle.account), self.get_territory(gle.account), gle.remarks]
					data.append(row)

		for i in range(0,len(data)):
			data[i].insert(4, """""" \
				% ("/".join(["#Form", data[i][2], data[i][3]]),))

		return data

	def get_entries_after(self, report_date):
		# returns a distinct list
		return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries()
			if getdate(e.posting_date) > report_date]))

	def get_entries_till(self, report_date):
		# returns a generator
		return (e for e in self.get_gl_entries()
			if getdate(e.posting_date) <= report_date)

	def is_receivable(self, gle, future_vouchers):
		return ((not gle.against_voucher) or (gle.against_voucher==gle.voucher_no) or
			((gle.against_voucher_type, gle.against_voucher) in future_vouchers))

	def get_outstanding_amount(self, gle, report_date):
		payment_received = 0.0
		for e in self.get_gl_entries_for(gle.account, gle.voucher_type, gle.voucher_no):
			if getdate(e.posting_date) <= report_date and e.name!=gle.name:
				payment_received += (flt(e.credit) - flt(e.debit))

		return flt(gle.debit) - flt(gle.credit) - payment_received

	def get_customer(self, account):
		return self.get_account_map().get(account).get("customer_name") or ""

	def get_territory(self, account):
		return self.get_account_map().get(account).get("territory") or ""

	def get_account_map(self):
		if not hasattr(self, "account_map"):
			self.account_map = dict(((r.name, r) for r in webnotes.conn.sql("""select
				account.name, customer.customer_name, customer.territory
				from `tabAccount` account, `tabCustomer` customer
				where account.master_type="Customer"
				and customer.name=account.master_name""", as_dict=True)))

		return self.account_map

	def get_due_date(self, gle):
		if not hasattr(self, "invoice_due_date_map"):
			# TODO can be restricted to posting date
			self.invoice_due_date_map = dict(webnotes.conn.sql("""select name, due_date
				from `tabSales Invoice` where docstatus=1"""))

		return gle.voucher_type == "Sales Invoice" \
			and self.invoice_due_date_map.get(gle.voucher_no) or ""

	def get_gl_entries(self):
		if not hasattr(self, "gl_entries"):
			conditions, values = self.prepare_conditions()
			self.gl_entries = webnotes.conn.sql("""select * from `tabGL Entry`
				where docstatus < 2 {} order by posting_date, account""".format(conditions),
				values, as_dict=True)

		return self.gl_entries

	def prepare_conditions(self):
		conditions = [""]
		values = {}

		if self.filters.company:
			conditions.append("company=%(company)s")
			values["company"] = self.filters.company

		if self.filters.account:
			conditions.append("account=%(account)s")
			values["account"] = self.filters.account
		else:
			account_map = self.get_account_map()
			if not account_map:
				webnotes.throw(_("No Customer Accounts found."))
			else:
				accounts_list = ['"{}"'.format(ac) for ac in account_map]
				conditions.append("account in ({})".format(", ".join(accounts_list)))

		return " and ".join(conditions), values

	def get_gl_entries_for(self, account, against_voucher_type, against_voucher):
		if not hasattr(self, "gl_entries_map"):
			self.gl_entries_map = {}
			for gle in self.get_gl_entries():
				if gle.against_voucher_type and gle.against_voucher:
					self.gl_entries_map.setdefault(gle.account, {})\
						.setdefault(gle.against_voucher_type, {})\
						.setdefault(gle.against_voucher, [])\
						.append(gle)

		return self.gl_entries_map.get(account, {})\
			.get(against_voucher_type, {})\
			.get(against_voucher, [])

def execute(filters=None):
	return AccountsReceivableReport(filters).run()

def get_ageing_data(age_as_on, entry_date, outstanding_amount):
	# [0-30, 30-60, 60-90, 90-above]
	outstanding_range = [0.0, 0.0, 0.0, 0.0]
	if not (age_as_on and entry_date):
		return [0] + outstanding_range

	age = (getdate(age_as_on) - getdate(entry_date)).days or 0
	index = None
	for i, days in enumerate([30, 60, 90]):
		if age <= days:
			index = i
			break

	if index is None: index = 3
	outstanding_range[index] = outstanding_amount

	return [age] + outstanding_range
<|endoftext|>


<commit_before>import numpy as np
import matplotlib.pyplot as plt

def hinton_diagram(matrix, max_weight=None, ax=None):
    """Draw Hinton diagram for visualizing a weight matrix.

    Note that depending on the size of your matrix you might have to adjust
    the size of your figure in order to display the squares right.
    """
    if ax is None:
        ax = plt.gca()

    max_weight = np.abs(matrix).max()
    # if not max_weight:
    #     max_weight = 2 ** np.ceil(np.log(np.abs(matrix).max()) / np.log(2))

    ax.patch.set_facecolor('gray')
    ax.set_aspect('equal', 'box')
    ax.xaxis.set_major_locator(plt.NullLocator())
    ax.yaxis.set_major_locator(plt.NullLocator())

    for (x, y), w in np.ndenumerate(matrix):
        color = 'white' if w > 0 else 'black'
        size = np.tanh(np.abs(w)) #/ max_weight
        half = size / 2
        # half = 0.5
        rect = plt.Rectangle([x - half, y - half], size, size,
                             facecolor=color, edgecolor=color)
        ax.add_patch(rect)

    # Not quite sure what these do atm
    ax.autoscale_view()
    # ax.invert_yaxis()
<commit_msg>Fixed (hopefully!!) the size calculation<commit_after>import numpy as np
import matplotlib.pyplot as plt

def hinton_diagram(matrix, max_weight=None, ax=None):
    """Draw Hinton diagram for visualizing a weight matrix.

    Note that depending on the size of your matrix you might have to adjust
    the size of your figure in order to display the squares right.
    """
    if ax is None:
        ax = plt.gca()

    max_weight = np.abs(matrix).max()
    # if not max_weight:
    #     max_weight = 2 ** np.ceil(np.log(np.abs(matrix).max()) / np.log(2))

    ax.patch.set_facecolor('gray')
    ax.set_aspect('equal', 'box')
    ax.xaxis.set_major_locator(plt.NullLocator())
    ax.yaxis.set_major_locator(plt.NullLocator())

    for (x, y), w in np.ndenumerate(matrix):
        color = 'white' if w > 0 else 'black'
        size = np.sqrt(np.abs(w) / max_weight)
        half = size / 2
        # half = 0.5
        rect = plt.Rectangle([x - half, y - half], size, size,
                             facecolor=color, edgecolor=color)
        ax.add_patch(rect)

    # Not quite sure what these do atm
    ax.autoscale_view()
    # ax.invert_yaxis()
<|endoftext|>


<commit_before>"""Tests for app_namespace"""
import os
import sys
import shutil
import tempfile
import unittest

import django
from django.test import TestCase
from django.template.base import Context
from django.template.base import Template
from django.template.base import TemplateDoesNotExist
from django.template.loaders import app_directories
from django.test.utils import override_settings

try:
    from django.template.engine import Engine
except ImportError:  # Django < 1.8
    class Engine(object):
        pass

from app_namespace import Loader

class LoaderTestCase(TestCase):

    def test_load_template(self):
        app_namespace_loader = Loader(Engine())
        app_directory_loader = app_directories.Loader(Engine())

        template_directory = app_directory_loader.load_template(
            'admin/base.html')[0]
        template_namespace = app_namespace_loader.load_template(
            'admin:admin/base.html')[0]
        context = Context({})
        self.assertEquals(template_directory.render(context),
                          template_namespace.render(context))

    def test_load_template_source(self):
        app_namespace_loader = Loader(Engine())
        app_directory_loader = app_directories.Loader(Engine())

        template_directory = app_directory_loader.load_template_source(
            'admin/base.html')
        template_namespace = app_namespace_loader.load_template_source(
            'admin:admin/base.html')
        self.assertEquals(template_directory[0], template_namespace[0])
        self.assertTrue('app_namespace:admin:' in template_namespace[1])
        self.assertTrue('admin/base.html' in template_namespace[1])

        self.assertRaises(TemplateDoesNotExist,
                          app_namespace_loader.load_template_source,
                          'no-namespace-template')
        self.assertRaises(TemplateDoesNotExist,
                          app_namespace_loader.load_template_source,
                          'no.app.namespace:template')

    def test_load_template_source_empty_namespace(self):
        app_namespace_loader = Loader(Engine())
        app_directory_loader = app_directories.Loader(Engine())

        template_directory = app_directory_loader.load_template_source(
            'admin/base.html')
        template_namespace = app_namespace_loader.load_template_source(
            ':admin/base.html')

        self.assertEquals(template_directory[0], template_namespace[0])
        self.assertTrue('app_namespace:django.contrib.admin:' in
                        template_namespace[1])
        self.assertTrue('admin/base.html' in template_namespace[1])

        self.assertRaises(TemplateDoesNotExist,
                          app_namespace_loader.load_template_source,
                          ':template')

    def test_load_template_source_dotted_namespace(self):
        app_namespace_loader = Loader(Engine())

        template_short = app_namespace_loader.load_template_source(
            'admin:admin/base.html')
        template_dotted = app_namespace_loader.load_template_source(
            'django.contrib.admin:admin/base.html')

        self.assertEquals(template_short[0], template_dotted[0])

@override_settings(
    TEMPLATE_LOADERS=(
        'app_namespace.Loader',
        'django.template.loaders.app_directories.Loader',
    ),
    TEMPLATES=[
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'OPTIONS': {
                'loaders': ('app_namespace.Loader',
                            'django.template.loaders.app_directories.Loader')
            }
        }
    ]
)
class TemplateTestCase(TestCase):
    maxDiff = None

    def test_extend_and_override(self):
        """
        Here we simulate the existence of a template
        named admin/base_site.html on the filesystem
        overriding the title markup of the template.
        In this test we can view the advantage of using
        the app_namespace template loader.
        """
        context = Context({})
        mark = 'Django administration'
        mark_title = '<title>APP NAMESPACE</title>'

        template_directory = Template(
            '{% extends "admin/base.html" %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            ).render(context)

        template_namespace = Template(
            '{% extends "admin:admin/base_site.html" %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            ).render(context)

        self.assertTrue(mark in template_namespace)
        self.assertTrue(mark_title in template_namespace)
        self.assertTrue(mark not in template_directory)
        self.assertTrue(mark_title in template_directory)

        template_directory = Template(
            '{% extends "admin/base.html" %}'
            '{% load i18n %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            '{% block branding %}'
            '
{% trans \'Django administration\' %}
'
            '{% endblock %}'
            '{% block nav-global %}{% endblock %}'
            ).render(context)

        try:
            self.assertHTMLEqual(template_directory, template_namespace)
        except AssertionError:
            # This test will fail under Python > 2.7.3 and Django 1.4
            # - https://code.djangoproject.com/ticket/18027
            # - http://hg.python.org/cpython/rev/333e3acf2008/
            pass
        self.assertTrue(mark in template_directory)
        self.assertTrue(mark_title in template_directory)

    def test_extend_empty_namespace(self):
        """
        Test that a ":" prefix (empty namespace) gets handled.
        """
        context = Context({})
        mark = 'Django administration'
        mark_title = '<title>APP NAMESPACE</title>'

        template_namespace = Template(
            '{% extends ":admin/base_site.html" %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            ).render(context)

        self.assertTrue(mark in template_namespace)
        self.assertTrue(mark_title in template_namespace)

    def test_extend_with_super(self):
        """
        Here we simulate the existence of a template
        named admin/base_site.html on the filesystem
        overriding the title markup of the template
        with a {{ super }}.
        """
        context = Context({})
        mark_ok = '<title> | Django site admin - APP NAMESPACE</title>'
        mark_ko = '<title> - APP NAMESPACE</title>'

        template_directory = Template(
            '{% extends "admin/base.html" %}'
            '{% block title %}{{ block.super }} - APP NAMESPACE{% endblock %}'
            ).render(context)

        template_namespace = Template(
            '{% extends "admin:admin/base_site.html" %}'
            '{% block title %}{{ block.super }} - APP NAMESPACE{% endblock %}'
            ).render(context)

        self.assertTrue(mark_ok in template_namespace)
        self.assertTrue(mark_ko in template_directory)

class MultiAppTestCase(TestCase):
    """
    Test case creating multiples apps containing templates
    with the same path which extends with an empty namespace.

    Each template will use a {{ block.super }} with an unique
    identifier to test the multiple cumulations in the final
    rendering.
    """
    maxDiff = None
    template_initial = """
    {%% block content %%}
    %(app)s
    {%% endblock content %%}
    """
    template_extend = """
    {%% extends ":template.html" %%}
    {%% block content %%}
    %(app)s
    {{ block.super }}
    {%% endblock content %%}
    """

    def setUp(self):
        super(MultiAppTestCase, self).setUp()
        # Create a temp directory containing apps
        # accessible on the PYTHONPATH.
        self.app_directory = tempfile.mkdtemp()
        sys.path.append(self.app_directory)

        # Create the apps with the overrided template
        self.apps = ['test-template-app-%s' % i for i in range(5)]
        for app in self.apps:
            app_path = os.path.join(self.app_directory, app)
            app_template_path = os.path.join(app_path, 'templates')
            os.makedirs(app_template_path)
            with open(os.path.join(app_path, '__init__.py'), 'w') as f:
                f.write('')
            with open(os.path.join(app_template_path,
                                   'template.html'), 'w') as f:
                f.write((app != self.apps[-1] and
                         self.template_extend or self.template_initial) %
                        {'app': app})

    def tearDown(self):
        super(MultiAppTestCase, self).tearDown()
        sys.path.remove(self.app_directory)
        for app in self.apps:
            del sys.modules[app]
        shutil.rmtree(self.app_directory)

    def multiple_extend_empty_namespace(self):
        with self.settings(INSTALLED_APPS=self.apps +
                           ['django.contrib.admin']):  # Django 1.4 Fix
            context = Context({})
            template = Template(
                self.template_extend % {'app': 'top-level'}
                ).render(context)
            previous_app = ''
            for test_app in ['top-level'] + self.apps:
                self.assertTrue(test_app in template)
                if previous_app:
                    self.assertTrue(template.index(test_app) >
                                    template.index(previous_app))
                previous_app = test_app

    @override_settings(
        TEMPLATE_LOADERS=(
            'app_namespace.Loader',
            'django.template.loaders.app_directories.Loader',
        ),
        TEMPLATES=[
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'OPTIONS': {
                    'loaders': (
                        'app_namespace.Loader',
                        'django.template.loaders.app_directories.Loader')
                }
            }
        ]
    )
    def test_multiple_extend_empty_namespace(self):
        self.multiple_extend_empty_namespace()

    @unittest.skipIf(django.VERSION[1] == 4,
                     'Django 1.4 will continue to use the cached.Loader')
    @override_settings(
        TEMPLATE_LOADERS=(
            ('django.template.loaders.cached.Loader', (
                'app_namespace.Loader',
                'django.template.loaders.app_directories.Loader')
             ),
        ),
        TEMPLATES=[
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'OPTIONS': {
                    'loaders': [
                        ('django.template.loaders.cached.Loader', [
                            'app_namespace.Loader',
                            'django.template.loaders.app_directories.Loader']),
                    ]
                }
            }
        ]
    )
    def test_cached_multiple_extend_empty_namespace(self):
        with self.assertRaises(RuntimeError):
            self.multiple_extend_empty_namespace()
<commit_msg>Register explicitly libraries used to render<commit_after>"""Tests for app_namespace"""
import os
import sys
import shutil
import tempfile
import unittest

import django
from django.test import TestCase
from django.template.base import Context
from django.template.base import Template
from django.template import TemplateDoesNotExist
from django.template.loaders import app_directories
from django.test.utils import override_settings

try:
    from django.template.engine import Engine
except ImportError:  # Django < 1.8
    class Engine(object):
        pass

from app_namespace import Loader

class LoaderTestCase(TestCase):

    def test_load_template(self):
        libraries = {
            'i18n': 'django.templatetags.i18n',
            'static': 'django.templatetags.static',
            'admin_static': 'django.contrib.admin.templatetags.admin_static'}
        app_namespace_loader = Loader(Engine(
            libraries=libraries))
        app_directory_loader = app_directories.Loader(Engine(
            libraries=libraries))

        template_directory = app_directory_loader.load_template(
            'admin/base.html')[0]
        template_namespace = app_namespace_loader.load_template(
            'admin:admin/base.html')[0]
        context = Context({})
        self.assertEquals(template_directory.render(context),
                          template_namespace.render(context))

    def test_load_template_source(self):
        app_namespace_loader = Loader(Engine())
        app_directory_loader = app_directories.Loader(Engine())

        template_directory = app_directory_loader.load_template_source(
            'admin/base.html')
        template_namespace = app_namespace_loader.load_template_source(
            'admin:admin/base.html')
        self.assertEquals(template_directory[0], template_namespace[0])
        self.assertTrue('app_namespace:admin:' in template_namespace[1])
        self.assertTrue('admin/base.html' in template_namespace[1])

        self.assertRaises(TemplateDoesNotExist,
                          app_namespace_loader.load_template_source,
                          'no-namespace-template')
        self.assertRaises(TemplateDoesNotExist,
                          app_namespace_loader.load_template_source,
                          'no.app.namespace:template')

    def test_load_template_source_empty_namespace(self):
        app_namespace_loader = Loader(Engine())
        app_directory_loader = app_directories.Loader(Engine())

        template_directory = app_directory_loader.load_template_source(
            'admin/base.html')
        template_namespace = app_namespace_loader.load_template_source(
            ':admin/base.html')

        self.assertEquals(template_directory[0], template_namespace[0])
        self.assertTrue('app_namespace:django.contrib.admin:' in
                        template_namespace[1])
        self.assertTrue('admin/base.html' in template_namespace[1])

        self.assertRaises(TemplateDoesNotExist,
                          app_namespace_loader.load_template_source,
                          ':template')

    def test_load_template_source_dotted_namespace(self):
        app_namespace_loader = Loader(Engine())

        template_short = app_namespace_loader.load_template_source(
            'admin:admin/base.html')
        template_dotted = app_namespace_loader.load_template_source(
            'django.contrib.admin:admin/base.html')

        self.assertEquals(template_short[0], template_dotted[0])

@override_settings(
    TEMPLATE_LOADERS=(
        'app_namespace.Loader',
        'django.template.loaders.app_directories.Loader',
    ),
    TEMPLATES=[
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'OPTIONS': {
                'loaders': ('app_namespace.Loader',
                            'django.template.loaders.app_directories.Loader')
            }
        }
    ]
)
class TemplateTestCase(TestCase):
    maxDiff = None

    def test_extend_and_override(self):
        """
        Here we simulate the existence of a template
        named admin/base_site.html on the filesystem
        overriding the title markup of the template.
        In this test we can view the advantage of using
        the app_namespace template loader.
        """
        context = Context({})
        mark = 'Django administration'
        mark_title = '<title>APP NAMESPACE</title>'

        template_directory = Template(
            '{% extends "admin/base.html" %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            ).render(context)

        template_namespace = Template(
            '{% extends "admin:admin/base_site.html" %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            ).render(context)

        self.assertTrue(mark in template_namespace)
        self.assertTrue(mark_title in template_namespace)
        self.assertTrue(mark not in template_directory)
        self.assertTrue(mark_title in template_directory)

        template_directory = Template(
            '{% extends "admin/base.html" %}'
            '{% load i18n %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            '{% block branding %}'
            '
{% trans \'Django administration\' %}
'
            '{% endblock %}'
            '{% block nav-global %}{% endblock %}'
            ).render(context)

        try:
            self.assertHTMLEqual(template_directory, template_namespace)
        except AssertionError:
            # This test will fail under Python > 2.7.3 and Django 1.4
            # - https://code.djangoproject.com/ticket/18027
            # - http://hg.python.org/cpython/rev/333e3acf2008/
            pass
        self.assertTrue(mark in template_directory)
        self.assertTrue(mark_title in template_directory)

    def test_extend_empty_namespace(self):
        """
        Test that a ":" prefix (empty namespace) gets handled.
        """
        context = Context({})
        mark = 'Django administration'
        mark_title = '<title>APP NAMESPACE</title>'

        template_namespace = Template(
            '{% extends ":admin/base_site.html" %}'
            '{% block title %}APP NAMESPACE{% endblock %}'
            ).render(context)

        self.assertTrue(mark in template_namespace)
        self.assertTrue(mark_title in template_namespace)

    def test_extend_with_super(self):
        """
        Here we simulate the existence of a template
        named admin/base_site.html on the filesystem
        overriding the title markup of the template
        with a {{ super }}.
        """
        context = Context({})
        mark_ok = '<title> | Django site admin - APP NAMESPACE</title>'
        mark_ko = '<title> - APP NAMESPACE</title>'

        template_directory = Template(
            '{% extends "admin/base.html" %}'
            '{% block title %}{{ block.super }} - APP NAMESPACE{% endblock %}'
            ).render(context)

        template_namespace = Template(
            '{% extends "admin:admin/base_site.html" %}'
            '{% block title %}{{ block.super }} - APP NAMESPACE{% endblock %}'
            ).render(context)

        self.assertTrue(mark_ok in template_namespace)
        self.assertTrue(mark_ko in template_directory)

class MultiAppTestCase(TestCase):
    """
    Test case creating multiples apps containing templates
    with the same path which extends with an empty namespace.

    Each template will use a {{ block.super }} with an unique
    identifier to test the multiple cumulations in the final
    rendering.
    """
    maxDiff = None
    template_initial = """
    {%% block content %%}
    %(app)s
    {%% endblock content %%}
    """
    template_extend = """
    {%% extends ":template.html" %%}
    {%% block content %%}
    %(app)s
    {{ block.super }}
    {%% endblock content %%}
    """

    def setUp(self):
        super(MultiAppTestCase, self).setUp()
        # Create a temp directory containing apps
        # accessible on the PYTHONPATH.
        self.app_directory = tempfile.mkdtemp()
        sys.path.append(self.app_directory)

        # Create the apps with the overrided template
        self.apps = ['test-template-app-%s' % i for i in range(5)]
        for app in self.apps:
            app_path = os.path.join(self.app_directory, app)
            app_template_path = os.path.join(app_path, 'templates')
            os.makedirs(app_template_path)
            with open(os.path.join(app_path, '__init__.py'), 'w') as f:
                f.write('')
            with open(os.path.join(app_template_path,
                                   'template.html'), 'w') as f:
                f.write((app != self.apps[-1] and
                         self.template_extend or self.template_initial) %
                        {'app': app})

    def tearDown(self):
        super(MultiAppTestCase, self).tearDown()
        sys.path.remove(self.app_directory)
        for app in self.apps:
            del sys.modules[app]
        shutil.rmtree(self.app_directory)

    def multiple_extend_empty_namespace(self):
        with self.settings(INSTALLED_APPS=self.apps +
                           ['django.contrib.admin']):  # Django 1.4 Fix
            context = Context({})
            template = Template(
                self.template_extend % {'app': 'top-level'}
                ).render(context)
            previous_app = ''
            for test_app in ['top-level'] + self.apps:
                self.assertTrue(test_app in template)
                if previous_app:
                    self.assertTrue(template.index(test_app) >
                                    template.index(previous_app))
                previous_app = test_app

    @override_settings(
        TEMPLATE_LOADERS=(
            'app_namespace.Loader',
            'django.template.loaders.app_directories.Loader',
        ),
        TEMPLATES=[
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'OPTIONS': {
                    'loaders': (
                        'app_namespace.Loader',
                        'django.template.loaders.app_directories.Loader')
                }
            }
        ]
    )
    def test_multiple_extend_empty_namespace(self):
        self.multiple_extend_empty_namespace()

    @unittest.skipIf(django.VERSION[1] == 4,
                     'Django 1.4 will continue to use the cached.Loader')
    @override_settings(
        TEMPLATE_LOADERS=(
            ('django.template.loaders.cached.Loader', (
                'app_namespace.Loader',
                'django.template.loaders.app_directories.Loader')
             ),
        ),
        TEMPLATES=[
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'OPTIONS': {
                    'loaders': [
                        ('django.template.loaders.cached.Loader', [
                            'app_namespace.Loader',
                            'django.template.loaders.app_directories.Loader']),
                    ]
                }
            }
        ]
    )
    def test_cached_multiple_extend_empty_namespace(self):
        with self.assertRaises(RuntimeError):
            self.multiple_extend_empty_namespace()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import, print_function

from predicthq.endpoints.schemas import (
    PaginatedMixin, SortableMixin, Model, ResultSet, ListType, StringType, GeoJSONPointType,
    StringListType, StringModelType, Area, ModelType, IntRange, IntType, DateTimeRange,
    DateTimeType, FloatType, ResultType, DictType, DateType, Place, Signal, DateAround,
    LocationAround, BooleanType, BrandUnsafe, Entity
)

class SearchParams(PaginatedMixin, SortableMixin, Model):

    class Options:
        serialize_when_none = False

    id = ListType(StringType)
    q = StringType()
    label = ListType(StringType)
    category = ListType(StringType)
    start = ModelType(DateTimeRange)
    start_around = ModelType(DateAround)
    end = ModelType(DateTimeRange)
    end_around = ModelType(DateAround)
    active = ModelType(DateTimeRange)
    updated = ModelType(DateTimeRange)
    state = StringType(choices=('active', 'deleted'))
    rank = ModelType(IntRange)
    rank_level = ListType(IntType(min_value=1, max_value=5))
    local_rank = ModelType(IntRange)
    local_rank_level = ListType(IntType(min_value=1, max_value=5))
    aviation_rank = ModelType(IntRange)
    aviation_rank_level = ListType(IntType(min_value=1, max_value=5))
    country = ListType(StringType)
    location_around = ModelType(LocationAround)
    within = StringListType(StringModelType(Area), separator="+")
    place = ModelType(Place)
    signal = ModelType(Signal)
    relevance = ListType(StringType)
    brand_unsafe = ModelType(BrandUnsafe)
    entity = ModelType(Entity)

class Entities(Model):

    class Options:
        serialize_when_none = True

    entity_id = StringType()
    name = StringType()
    type = StringType()
    formatted_address = StringType()

class Event(Model):

    class Options:
        serialize_when_none = True

    id = StringType()
    title = StringType()
    description = StringType()
    start = DateTimeType()
    end = DateTimeType()
    timezone = StringType()
    duration = IntType()
    category = StringType()
    labels = ListType(StringType())
    country = StringType()
    rank = IntType()
    local_rank = IntType()
    aviation_rank = IntType()
    entities = ListType(ModelType(Entities))
    location = GeoJSONPointType()
    place_hierarchies = ListType(ListType(StringType()))
    scope = StringType()
    relevance = FloatType()
    state = StringType()
    first_seen = DateTimeType()
    updated = DateTimeType()
    deleted_reason = StringType()
    duplicate_of_id = StringType()

class EventResultSet(ResultSet):

    overflow = BooleanType()

    results = ResultType(Event)

class Count(Model):

    count = IntType()
    top_rank = FloatType()
    rank_levels = DictType(IntType)
    categories = DictType(IntType)
    labels = DictType(IntType)

class TopEventsSearchParams(SortableMixin, Model):

    limit = IntType(min_value=0, max_value=10)

class CalendarParams(SearchParams):

    top_events = ModelType(TopEventsSearchParams)
    view = StringType(choices=('active', 'start'))

class CalendarDay(Model):

    date = DateType()
    count = IntType()
    top_rank = FloatType()
    rank_levels = DictType(IntType)
    categories = DictType(IntType)
    labels = DictType(IntType)
    top_events = ModelType(EventResultSet)

class CalendarResultSet(ResultSet):

    results = ResultType(CalendarDay)
<commit_msg>Clarify that local_rank and aviation_rank are paid features<commit_after># -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import, print_function

from predicthq.endpoints.schemas import (
    PaginatedMixin, SortableMixin, Model, ResultSet, ListType, StringType, GeoJSONPointType,
    StringListType, StringModelType, Area, ModelType, IntRange, IntType, DateTimeRange,
    DateTimeType, FloatType, ResultType, DictType, DateType, Place, Signal, DateAround,
    LocationAround, BooleanType, BrandUnsafe, Entity
)

class SearchParams(PaginatedMixin, SortableMixin, Model):

    class Options:
        serialize_when_none = False

    id = ListType(StringType)
    q = StringType()
    label = ListType(StringType)
    category = ListType(StringType)
    start = ModelType(DateTimeRange)
    start_around = ModelType(DateAround)
    end = ModelType(DateTimeRange)
    end_around = ModelType(DateAround)
    active = ModelType(DateTimeRange)
    updated = ModelType(DateTimeRange)
    state = StringType(choices=('active', 'deleted'))
    rank = ModelType(IntRange)
    rank_level = ListType(IntType(min_value=1, max_value=5))

    # `local_rank` and `aviation_rank` are paid features.
    # If you haven't subscribed to a paid feature, using it as a
    # search param will have no effect on your search results.
    local_rank = ModelType(IntRange)
    local_rank_level = ListType(IntType(min_value=1, max_value=5))
    aviation_rank = ModelType(IntRange)
    aviation_rank_level = ListType(IntType(min_value=1, max_value=5))

    country = ListType(StringType)
    location_around = ModelType(LocationAround)
    within = StringListType(StringModelType(Area), separator="+")
    place = ModelType(Place)
    signal = ModelType(Signal)
    relevance = ListType(StringType)
    brand_unsafe = ModelType(BrandUnsafe)
    entity = ModelType(Entity)

class Entities(Model):

    class Options:
        serialize_when_none = True

    entity_id = StringType()
    name = StringType()
    type = StringType()
    formatted_address = StringType()

class Event(Model):

    class Options:
        serialize_when_none = True

    id = StringType()
    title = StringType()
    description = StringType()
    start = DateTimeType()
    end = DateTimeType()
    timezone = StringType()
    duration = IntType()
    category = StringType()
    labels = ListType(StringType())
    country = StringType()
    rank = IntType()

    # `local_rank` and `aviation_rank` are paid features.
    # They will only show up in your response body if you
    # have subscribed to them.
    local_rank = IntType()
    aviation_rank = IntType()

    entities = ListType(ModelType(Entities))
    location = GeoJSONPointType()
    place_hierarchies = ListType(ListType(StringType()))
    scope = StringType()
    relevance = FloatType()
    state = StringType()
    first_seen = DateTimeType()
    updated = DateTimeType()
    deleted_reason = StringType()
    duplicate_of_id = StringType()

class EventResultSet(ResultSet):

    overflow = BooleanType()

    results = ResultType(Event)

class Count(Model):

    count = IntType()
    top_rank = FloatType()
    rank_levels = DictType(IntType)
    categories = DictType(IntType)
    labels = DictType(IntType)

class TopEventsSearchParams(SortableMixin, Model):

    limit = IntType(min_value=0, max_value=10)

class CalendarParams(SearchParams):

    top_events = ModelType(TopEventsSearchParams)
    view = StringType(choices=('active', 'start'))

class CalendarDay(Model):

    date = DateType()
    count = IntType()
    top_rank = FloatType()
    rank_levels = DictType(IntType)
    categories = DictType(IntType)
    labels = DictType(IntType)
    top_events = ModelType(EventResultSet)

class CalendarResultSet(ResultSet):

    results = ResultType(CalendarDay)
<|endoftext|>


<commit_before># Copyright 2018 The Bazel Authors. All rights reserved.
#
# 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.

"""Core bundling logic.

The processor module handles the execution of logic for different parts of the
bundling process. This logic is encapsulated into blocks of code called
partials. Each partial will then process a specific aspect of the build process
and will return information on how the bundles should be built.

All partials handled by this processor must follow this API:

  - The only expected argument has to be ctx.
  - The expected output is a struct with the following optional fields:
    * bundle_files: Contains tuples of the format
      (location_type, parent_dir, files) where location_type is a field of the
      location enum. The files are then placed at the given location in the
      output bundle.
    * bundle_zips: Contains tuples of the format
      (location_type, parent_dir, files) where location_type is a field of the
      location enum and each file is a ZIP file. The files extracted from the
      ZIPs are then placed at the given location in the output bundle.
    * output_files: Depset of `File`s that should be returned as outputs of the
      target.
    * providers: Providers that will be collected and returned by the rule.

Location types can be 7:
  - archive: Files are to be placed relative to the archive of the bundle
    (i.e. the root of the zip/IPA file to generate).
  - binary: Files are to be placed in the binary section of the bundle.
  - bundle: Files are to be placed at the root of the bundle.
  - content: Files are to be placed in the contents section of the bundle.
  - framework: Files are to be placed in the Frameworks section of the bundle.
  - plugin: Files are to be placed in the PlugIns section of the bundle.
  - resources: Files are to be placed in the resources section of the bundle.
  - watch: Files are to be placed inside the Watch section of the bundle. Only applicable for iOS
    apps.

For iOS, tvOS and watchOS, binary, content and resources all refer to the same
location. Only in macOS these paths differ.

All the files given will be symlinked into their expected location in the
bundle, and once complete, the processor will codesign and compress the bundle
into a zip file.

The processor will output a single file, which is the final compressed and
code-signed bundle, and a list of providers that need to be propagated from the
rule.
"""

load(
    "@build_bazel_rules_apple//apple/internal:bundling_support.bzl",
    "bundling_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:codesigning_support.bzl",
    "codesigning_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:entitlements_support.bzl",
    "entitlements_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:experimental.bzl",
    "is_experimental_tree_artifact_enabled",
)
load(
    "@build_bazel_rules_apple//apple/internal:intermediates.bzl",
    "intermediates",
)
load(
    "@build_bazel_rules_apple//apple/internal:platform_support.bzl",
    "platform_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:outputs.bzl",
    "outputs",
)
load(
    "@build_bazel_rules_apple//apple/internal:rule_support.bzl",
    "rule_support",
)
load(
    "@build_bazel_apple_support//lib:apple_support.bzl",
    "apple_support",
)
load(
    "@bazel_skylib//lib:partial.bzl",
    "partial",
)
load(
    "@bazel_skylib//lib:paths.bzl",
    "paths",
)

# Location enum that can be used to tag files into their appropriate location
# in the final archive.
_LOCATION_ENUM = struct(
    archive = "archive",
    binary = "binary",
    bundle = "bundle",
    content = "content",
    framework = "framework",
    plugin = "plugin",
    resource = "resource",
    watch = "watch",
    xpc_service = "xpc_service",
)

def _invalid_top_level_directories_for_platform(platform_type):
    """List of invalid top level directories for the given platform."""

    # As far as we know, there are no locations in macOS bundles that would break
    # codesigning.
    if platform_type == apple_common.platform_type.macos:
        return []

    # Non macOS bundles can't have a top level Resources folder, as it breaks
    # codesigning for some reason. With this, we validate that there are no
    # Resources folder going to be created in the bundle, with a message that
    # better explains which files are incorrectly placed.
    return ["Resources"]

def _is_parent_dir_valid(invalid_top_level_dirs, parent_dir):
    """Validates that the files to bundle are not placed in invalid locations.

    codesign will complain when building a non macOS bundle that contains certain
    folders at the top level. We check if there are files that would break
    codesign, and fail early with a nicer message.

    Args:
      invalid_top_level_dirs: String list containing the top level
          directories that have to be avoided when bundling resources.
      parent_dir: String containing the a parent directory inside a bundle.

    Returns:
      False if the parent_dir value is invalid.
    """
    if not parent_dir:
        return True
    for invalid_dir in invalid_top_level_dirs:
        if parent_dir == invalid_dir or parent_dir.startswith(invalid_dir + "/"):
            return False
    return True

def _archive_paths(ctx):
    """Returns the map of location type to final archive path."""
    rule_descriptor = rule_support.rule_descriptor(ctx)

    if is_experimental_tree_artifact_enabled(ctx):
        # If experimental tree artifacts are enabled, base all the outputs to be relative to the
        # bundle path.
        bundle_path = ""
    else:
        bundle_name_with_extension = (
            bundling_support.bundle_name(ctx) + bundling_support.bundle_extension(ctx)
        )
        bundle_path = paths.join(
            rule_descriptor.bundle_locations.archive_relative,
            bundle_name_with_extension,
        )

    contents_path = paths.join(
        bundle_path,
        rule_descriptor.bundle_locations.bundle_relative_contents,
    )

    # Map of location types to relative paths in the archive.
    return {
        _LOCATION_ENUM.archive: "",
        _LOCATION_ENUM.binary: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_binary,
        ),
        _LOCATION_ENUM.bundle: bundle_path,
        _LOCATION_ENUM.content: contents_path,
        _LOCATION_ENUM.framework: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_frameworks,
        ),
        _LOCATION_ENUM.plugin: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_plugins,
        ),
        _LOCATION_ENUM.resource: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_resources,
        ),
        _LOCATION_ENUM.watch: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_watch,
        ),
        _LOCATION_ENUM.xpc_service: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_xpc_service,
        ),
    }

def _bundle_partial_outputs_files(
        ctx,
        partial_outputs,
        output_file,
        codesigning_command = None,
        extra_input_files = []):
    """Invokes bundletool to bundle the files specified by the partial outputs.

    Args:
      ctx: The target's rule context.
      partial_outputs: List of partial outputs from which to collect the files
        that will be bundled inside the final archive.
      output_file: The file where the final zipped bundle should be created.
      codesigning_command: When building tree artifact outputs, the command to codesign the output
          bundle.
      extra_input_files: Extra files to include in the bundling action.
    """
    control_files = []
    control_zips = []
    input_files = []

    location_to_paths = _archive_paths(ctx)

    platform_type = platform_support.platform_type(ctx)
    invalid_top_level_dirs = _invalid_top_level_directories_for_platform(platform_type)

    processed_file_target_paths = {}
    for partial_output in partial_outputs:
        for location, parent_dir, files in getattr(partial_output, "bundle_files", []):
            if is_experimental_tree_artifact_enabled(ctx) and location == _LOCATION_ENUM.archive:
                # Skip bundling archive related files, as we're only building the bundle directory.
                continue
            if (invalid_top_level_dirs and
                not _is_parent_dir_valid(invalid_top_level_dirs, parent_dir)):
                fail(("Error: For %s bundles, the following top level " +
                      "directories are invalid: %s") %
                     (platform_type, ", ".join(invalid_top_level_dirs)))

            sources = files.to_list()
            input_files.extend(sources)

            for source in sources:
                target_path = paths.join(location_to_paths[location], parent_dir or "")

                if not source.is_directory:
                    target_path = paths.join(target_path, source.basename)
                    if target_path in processed_file_target_paths:
                        fail(
                            ("Multiple files would be placed at \"%s\" in the bundle, which " +
                             "is not allowed") % target_path,
                        )
                    processed_file_target_paths[target_path] = None
                control_files.append(struct(src = source.path, dest = target_path))

        for location, parent_dir, zip_files in getattr(partial_output, "bundle_zips", []):
            if is_experimental_tree_artifact_enabled(ctx) and location == _LOCATION_ENUM.archive:
                # Skip bundling archive related files, as we're only building the bundle directory.
                continue
            if (invalid_top_level_dirs and
                not _is_parent_dir_valid(invalid_top_level_dirs, parent_dir)):
                fail(("Error: For %s bundles, the following top level " +
                      "directories are invalid: %s") %
                     (platform_type, ", ".join(invalid_top_level_dirs)))

            sources = zip_files.to_list()
            input_files.extend(sources)

            for source in sources:
                target_path = paths.join(location_to_paths[location], parent_dir or "")
                control_zips.append(struct(src = source.path, dest = target_path))

    post_processor = ctx.executable.ipa_post_processor
    post_processor_path = ""

    if post_processor:
        post_processor_path = post_processor.path

    control = struct(
        bundle_merge_files = control_files,
        bundle_merge_zips = control_zips,
        output = output_file.path,
        code_signing_commands = codesigning_command or "",
        post_processor = post_processor_path,
    )

    control_file = intermediates.file(
        ctx.actions,
        ctx.label.name,
        "bundletool_control.json",
    )
    ctx.actions.write(
        output = control_file,
        content = control.to_json(),
    )

    action_args = {
        "inputs": input_files + [control_file] + extra_input_files,
        "outputs": [output_file],
        "arguments": [control_file.path],
    }

    if is_experimental_tree_artifact_enabled(ctx):
        # Since the tree artifact bundler also runs the post processor and codesigning, this
        # action needs to run on a macOS machine.

        bundling_tools = [ctx.executable._codesigningtool]
        if post_processor:
            bundling_tools.append(post_processor)

        apple_support.run(
            ctx,
            executable = ctx.executable._bundletool_experimental,
            mnemonic = "BundleTreeApp",
            progress_message = "Bundling, processing and signing %s" % ctx.label.name,
            tools = bundling_tools,
            **action_args
        )
    else:
        ctx.actions.run(
            executable = ctx.executable._bundletool,
            mnemonic = "BundleApp",
            progress_message = "Bundling %s" % ctx.label.name,
            **action_args
        )

def _bundle_post_process_and_sign(ctx, partial_outputs, output_archive):
    """Bundles, post-processes and signs the files in partial_outputs.

    Args:
        ctx: The rule context.
        partial_outputs: The outputs of the partials used to process this target's bundle.
        output_archive: The file representing the final bundled, post-processed and signed archive.
    """
    archive_paths = _archive_paths(ctx)
    entitlements = entitlements_support.entitlements(ctx)

    if is_experimental_tree_artifact_enabled(ctx):
        extra_input_files = []

        if entitlements:
            extra_input_files.append(entitlements)

        provisioning_profile = getattr(ctx.file, "provisioning_profile", None)
        if provisioning_profile:
            extra_input_files.append(provisioning_profile)

        codesigning_command = codesigning_support.codesigning_command(
            ctx,
            entitlements = entitlements,
            frameworks_path = archive_paths[_LOCATION_ENUM.framework],
        )

        _bundle_partial_outputs_files(
            ctx,
            partial_outputs,
            output_archive,
            codesigning_command = codesigning_command,
            extra_input_files = extra_input_files,
        )

        ctx.actions.write(
            output = ctx.outputs.archive,
            content = "test",
        )
    else:
        # This output, while an intermediate artifact not exposed through the AppleBundleInfo
        # provider, is used by Tulsi for custom processing logic. (b/120221708)
        unprocessed_archive = intermediates.file(
            ctx.actions,
            ctx.label.name,
            "unprocessed_archive.zip",
        )
        _bundle_partial_outputs_files(ctx, partial_outputs, unprocessed_archive)

        archive_codesigning_path = archive_paths[_LOCATION_ENUM.bundle]
        frameworks_path = archive_paths[_LOCATION_ENUM.framework]

        output_archive_root_path = outputs.archive_root_path(ctx)
        codesigning_support.post_process_and_sign_archive_action(
            ctx,
            archive_codesigning_path,
            frameworks_path,
            unprocessed_archive,
            output_archive,
            output_archive_root_path,
            entitlements = entitlements,
        )

def _process(ctx, partials):
    """Processes a list of partials that provide the files to be bundled.

    Args:
      ctx: The ctx object for the target being processed.
      partials: The list of partials to process to construct the complete bundle.

    Returns:
      A struct with the results of the processing. The files to make outputs of
      the rule are contained under the `output_files` field, and the providers to
      return are contained under the `providers` field.
    """
    partial_outputs = [partial.call(p, ctx) for p in partials]

    output_archive = outputs.archive(ctx)
    _bundle_post_process_and_sign(ctx, partial_outputs, output_archive)

    providers = []
    transitive_output_files = [depset([output_archive])]
    for partial_output in partial_outputs:
        if hasattr(partial_output, "providers"):
            providers.extend(partial_output.providers)
        if hasattr(partial_output, "output_files"):
            transitive_output_files.append(partial_output.output_files)

    return struct(
        output_files = depset(transitive = transitive_output_files),
        providers = providers,
    )

processor = struct(
    process = _process,
    location = _LOCATION_ENUM,
)
<commit_msg>Added violating input files in error messages for output checks.<commit_after># Copyright 2018 The Bazel Authors. All rights reserved.
#
# 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.

"""Core bundling logic.

The processor module handles the execution of logic for different parts of the
bundling process. This logic is encapsulated into blocks of code called
partials. Each partial will then process a specific aspect of the build process
and will return information on how the bundles should be built.

All partials handled by this processor must follow this API:

  - The only expected argument has to be ctx.
  - The expected output is a struct with the following optional fields:
    * bundle_files: Contains tuples of the format
      (location_type, parent_dir, files) where location_type is a field of the
      location enum. The files are then placed at the given location in the
      output bundle.
    * bundle_zips: Contains tuples of the format
      (location_type, parent_dir, files) where location_type is a field of the
      location enum and each file is a ZIP file. The files extracted from the
      ZIPs are then placed at the given location in the output bundle.
    * output_files: Depset of `File`s that should be returned as outputs of the
      target.
    * providers: Providers that will be collected and returned by the rule.

Location types can be 7:
  - archive: Files are to be placed relative to the archive of the bundle
    (i.e. the root of the zip/IPA file to generate).
  - binary: Files are to be placed in the binary section of the bundle.
  - bundle: Files are to be placed at the root of the bundle.
  - content: Files are to be placed in the contents section of the bundle.
  - framework: Files are to be placed in the Frameworks section of the bundle.
  - plugin: Files are to be placed in the PlugIns section of the bundle.
  - resources: Files are to be placed in the resources section of the bundle.
  - watch: Files are to be placed inside the Watch section of the bundle. Only applicable for iOS
    apps.

For iOS, tvOS and watchOS, binary, content and resources all refer to the same
location. Only in macOS these paths differ.

All the files given will be symlinked into their expected location in the
bundle, and once complete, the processor will codesign and compress the bundle
into a zip file.

The processor will output a single file, which is the final compressed and
code-signed bundle, and a list of providers that need to be propagated from the
rule.
"""

load(
    "@build_bazel_rules_apple//apple/internal:bundling_support.bzl",
    "bundling_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:codesigning_support.bzl",
    "codesigning_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:entitlements_support.bzl",
    "entitlements_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:experimental.bzl",
    "is_experimental_tree_artifact_enabled",
)
load(
    "@build_bazel_rules_apple//apple/internal:intermediates.bzl",
    "intermediates",
)
load(
    "@build_bazel_rules_apple//apple/internal:platform_support.bzl",
    "platform_support",
)
load(
    "@build_bazel_rules_apple//apple/internal:outputs.bzl",
    "outputs",
)
load(
    "@build_bazel_rules_apple//apple/internal:rule_support.bzl",
    "rule_support",
)
load(
    "@build_bazel_apple_support//lib:apple_support.bzl",
    "apple_support",
)
load(
    "@bazel_skylib//lib:partial.bzl",
    "partial",
)
load(
    "@bazel_skylib//lib:paths.bzl",
    "paths",
)

# Location enum that can be used to tag files into their appropriate location
# in the final archive.
_LOCATION_ENUM = struct(
    archive = "archive",
    binary = "binary",
    bundle = "bundle",
    content = "content",
    framework = "framework",
    plugin = "plugin",
    resource = "resource",
    watch = "watch",
    xpc_service = "xpc_service",
)

def _invalid_top_level_directories_for_platform(platform_type):
    """List of invalid top level directories for the given platform."""

    # As far as we know, there are no locations in macOS bundles that would break
    # codesigning.
    if platform_type == apple_common.platform_type.macos:
        return []

    # Non macOS bundles can't have a top level Resources folder, as it breaks
    # codesigning for some reason. With this, we validate that there are no
    # Resources folder going to be created in the bundle, with a message that
    # better explains which files are incorrectly placed.
    return ["Resources"]

def _is_parent_dir_valid(invalid_top_level_dirs, parent_dir):
    """Validates that the files to bundle are not placed in invalid locations.

    codesign will complain when building a non macOS bundle that contains certain
    folders at the top level. We check if there are files that would break
    codesign, and fail early with a nicer message.

    Args:
      invalid_top_level_dirs: String list containing the top level
          directories that have to be avoided when bundling resources.
      parent_dir: String containing the a parent directory inside a bundle.

    Returns:
      False if the parent_dir value is invalid.
    """
    if not parent_dir:
        return True
    for invalid_dir in invalid_top_level_dirs:
        if parent_dir == invalid_dir or parent_dir.startswith(invalid_dir + "/"):
            return False
    return True

def _archive_paths(ctx):
    """Returns the map of location type to final archive path."""
    rule_descriptor = rule_support.rule_descriptor(ctx)

    if is_experimental_tree_artifact_enabled(ctx):
        # If experimental tree artifacts are enabled, base all the outputs to be relative to the
        # bundle path.
        bundle_path = ""
    else:
        bundle_name_with_extension = (
            bundling_support.bundle_name(ctx) + bundling_support.bundle_extension(ctx)
        )
        bundle_path = paths.join(
            rule_descriptor.bundle_locations.archive_relative,
            bundle_name_with_extension,
        )

    contents_path = paths.join(
        bundle_path,
        rule_descriptor.bundle_locations.bundle_relative_contents,
    )

    # Map of location types to relative paths in the archive.
    return {
        _LOCATION_ENUM.archive: "",
        _LOCATION_ENUM.binary: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_binary,
        ),
        _LOCATION_ENUM.bundle: bundle_path,
        _LOCATION_ENUM.content: contents_path,
        _LOCATION_ENUM.framework: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_frameworks,
        ),
        _LOCATION_ENUM.plugin: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_plugins,
        ),
        _LOCATION_ENUM.resource: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_resources,
        ),
        _LOCATION_ENUM.watch: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_watch,
        ),
        _LOCATION_ENUM.xpc_service: paths.join(
            contents_path,
            rule_descriptor.bundle_locations.contents_relative_xpc_service,
        ),
    }

def _bundle_partial_outputs_files(
        ctx,
        partial_outputs,
        output_file,
        codesigning_command = None,
        extra_input_files = []):
    """Invokes bundletool to bundle the files specified by the partial outputs.

    Args:
      ctx: The target's rule context.
      partial_outputs: List of partial outputs from which to collect the files
        that will be bundled inside the final archive.
      output_file: The file where the final zipped bundle should be created.
      codesigning_command: When building tree artifact outputs, the command to codesign the output
          bundle.
      extra_input_files: Extra files to include in the bundling action.
    """
    control_files = []
    control_zips = []
    input_files = []

    location_to_paths = _archive_paths(ctx)

    platform_type = platform_support.platform_type(ctx)
    invalid_top_level_dirs = _invalid_top_level_directories_for_platform(platform_type)

    processed_file_target_paths = {}
    for partial_output in partial_outputs:
        for location, parent_dir, files in getattr(partial_output, "bundle_files", []):
            if is_experimental_tree_artifact_enabled(ctx) and location == _LOCATION_ENUM.archive:
                # Skip bundling archive related files, as we're only building the bundle directory.
                continue
            if (invalid_top_level_dirs and
                not _is_parent_dir_valid(invalid_top_level_dirs, parent_dir)):
                file_paths = "\n".join([f.path for f in files.to_list()])
                fail(("Error: For %s bundles, the following top level " +
                      "directories are invalid: %s, check input files:\n%s") %
                     (platform_type, ", ".join(invalid_top_level_dirs), file_paths))

            sources = files.to_list()
            input_files.extend(sources)

            for source in sources:
                target_path = paths.join(location_to_paths[location], parent_dir or "")

                if not source.is_directory:
                    target_path = paths.join(target_path, source.basename)
                    if target_path in processed_file_target_paths:
                        fail(
                            ("Multiple files would be placed at \"%s\" in the bundle, which " +
                             "is not allowed. check input file:\n%s") % (target_path, source.path),
                        )
                    processed_file_target_paths[target_path] = None
                control_files.append(struct(src = source.path, dest = target_path))

        for location, parent_dir, zip_files in getattr(partial_output, "bundle_zips", []):
            if is_experimental_tree_artifact_enabled(ctx) and location == _LOCATION_ENUM.archive:
                # Skip bundling archive related files, as we're only building the bundle directory.
                continue
            if (invalid_top_level_dirs and
                not _is_parent_dir_valid(invalid_top_level_dirs, parent_dir)):
                fail(("Error: For %s bundles, the following top level " +
                      "directories are invalid: %s") %
                     (platform_type, ", ".join(invalid_top_level_dirs)))

            sources = zip_files.to_list()
            input_files.extend(sources)

            for source in sources:
                target_path = paths.join(location_to_paths[location], parent_dir or "")
                control_zips.append(struct(src = source.path, dest = target_path))

    post_processor = ctx.executable.ipa_post_processor
    post_processor_path = ""

    if post_processor:
        post_processor_path = post_processor.path

    control = struct(
        bundle_merge_files = control_files,
        bundle_merge_zips = control_zips,
        output = output_file.path,
        code_signing_commands = codesigning_command or "",
        post_processor = post_processor_path,
    )

    control_file = intermediates.file(
        ctx.actions,
        ctx.label.name,
        "bundletool_control.json",
    )
    ctx.actions.write(
        output = control_file,
        content = control.to_json(),
    )

    action_args = {
        "inputs": input_files + [control_file] + extra_input_files,
        "outputs": [output_file],
        "arguments": [control_file.path],
    }

    if is_experimental_tree_artifact_enabled(ctx):
        # Since the tree artifact bundler also runs the post processor and codesigning, this
        # action needs to run on a macOS machine.

        bundling_tools = [ctx.executable._codesigningtool]
        if post_processor:
            bundling_tools.append(post_processor)

        apple_support.run(
            ctx,
            executable = ctx.executable._bundletool_experimental,
            mnemonic = "BundleTreeApp",
            progress_message = "Bundling, processing and signing %s" % ctx.label.name,
            tools = bundling_tools,
            **action_args
        )
    else:
        ctx.actions.run(
            executable = ctx.executable._bundletool,
            mnemonic = "BundleApp",
            progress_message = "Bundling %s" % ctx.label.name,
            **action_args
        )

def _bundle_post_process_and_sign(ctx, partial_outputs, output_archive):
    """Bundles, post-processes and signs the files in partial_outputs.

    Args:
        ctx: The rule context.
        partial_outputs: The outputs of the partials used to process this target's bundle.
        output_archive: The file representing the final bundled, post-processed and signed archive.
    """
    archive_paths = _archive_paths(ctx)
    entitlements = entitlements_support.entitlements(ctx)

    if is_experimental_tree_artifact_enabled(ctx):
        extra_input_files = []

        if entitlements:
            extra_input_files.append(entitlements)

        provisioning_profile = getattr(ctx.file, "provisioning_profile", None)
        if provisioning_profile:
            extra_input_files.append(provisioning_profile)

        codesigning_command = codesigning_support.codesigning_command(
            ctx,
            entitlements = entitlements,
            frameworks_path = archive_paths[_LOCATION_ENUM.framework],
        )

        _bundle_partial_outputs_files(
            ctx,
            partial_outputs,
            output_archive,
            codesigning_command = codesigning_command,
            extra_input_files = extra_input_files,
        )

        ctx.actions.write(
            output = ctx.outputs.archive,
            content = "test",
        )
    else:
        # This output, while an intermediate artifact not exposed through the AppleBundleInfo
        # provider, is used by Tulsi for custom processing logic. (b/120221708)
        unprocessed_archive = intermediates.file(
            ctx.actions,
            ctx.label.name,
            "unprocessed_archive.zip",
        )
        _bundle_partial_outputs_files(ctx, partial_outputs, unprocessed_archive)

        archive_codesigning_path = archive_paths[_LOCATION_ENUM.bundle]
        frameworks_path = archive_paths[_LOCATION_ENUM.framework]

        output_archive_root_path = outputs.archive_root_path(ctx)
        codesigning_support.post_process_and_sign_archive_action(
            ctx,
            archive_codesigning_path,
            frameworks_path,
            unprocessed_archive,
            output_archive,
            output_archive_root_path,
            entitlements = entitlements,
        )

def _process(ctx, partials):
    """Processes a list of partials that provide the files to be bundled.

    Args:
      ctx: The ctx object for the target being processed.
      partials: The list of partials to process to construct the complete bundle.

    Returns:
      A struct with the results of the processing. The files to make outputs of
      the rule are contained under the `output_files` field, and the providers to
      return are contained under the `providers` field.
    """
    partial_outputs = [partial.call(p, ctx) for p in partials]

    output_archive = outputs.archive(ctx)
    _bundle_post_process_and_sign(ctx, partial_outputs, output_archive)

    providers = []
    transitive_output_files = [depset([output_archive])]
    for partial_output in partial_outputs:
        if hasattr(partial_output, "providers"):
            providers.extend(partial_output.providers)
        if hasattr(partial_output, "output_files"):
            transitive_output_files.append(partial_output.output_files)

    return struct(
        output_files = depset(transitive = transitive_output_files),
        providers = providers,
    )

processor = struct(
    process = _process,
    location = _LOCATION_ENUM,
)
<|endoftext|>


<commit_before>from django.http import HttpResponse
from django.template import loader

from .models import Product

def products(request):
    products = Product.objects.order_by('price')
    template = loader.get_template('products.html')
    context = {
        'products': products
    }
    return HttpResponse(template.render(context, request))

def product_detail(request, product_id):
    product = Product.objects.get(pk=product_id)
    title = product.name
    template = loader.get_template('product_detail.html')
    context = {
        'title': title,
        'product': product
    }
    return HttpResponse(template.render(context, request))
<commit_msg>Control de errores cuando el producto no existe<commit_after>from django.http import HttpResponse, Http404
from django.template import loader

from .models import Product

def products(request):
    products = Product.objects.order_by('price')
    template = loader.get_template('products.html')
    context = {
        'products': products
    }
    return HttpResponse(template.render(context, request))

def product_detail(request, product_id):
    try:
        product = Product.objects.get(pk=product_id)
    except Product.DoesNotExist:
        raise Http404
    title = product.name
    template = loader.get_template('product_detail.html')
    context = {
        'title': title,
        'product': product
    }
    return HttpResponse(template.render(context, request))
<|endoftext|>