# -*- coding: utf-8 -*-

from __future__ import with_statement

import base64
import email
import unittest
import time
import re
import mock
from contextlib import contextmanager

from email.header import Header
from email import charset

from flask import Flask
from flask_mail import Mail, Message, BadHeaderError, sanitize_address, PY3
from speaklater import make_lazy_string

class TestCase(unittest.TestCase):

    TESTING = True
    MAIL_DEFAULT_SENDER = "support@mysite.com"

    def setUp(self):
        self.app = Flask(__name__)
        self.app.config.from_object(self)
        self.assertTrue(self.app.testing)
        self.mail = Mail(self.app)
        self.ctx = self.app.test_request_context()
        self.ctx.push()

    def tearDown(self):
        self.ctx.pop()

    @contextmanager
    def mail_config(self, **settings):
        """
        Context manager to alter mail config during a test and restore it after,
        even in case of a failure.
        """
        original = {}
        state = self.mail.state
        for key in settings:
            assert hasattr(state, key)
            original[key] = getattr(state, key)
            setattr(state, key, settings[key])

        yield
        # restore
        for k, v in original.items():
            setattr(state, k, v)

    def assertIn(self, member, container, msg=None):
        if hasattr(unittest.TestCase, 'assertIn'):
            return unittest.TestCase.assertIn(self, member, container, msg)
        return self.assertTrue(member in container)

    def assertNotIn(self, member, container, msg=None):
        if hasattr(unittest.TestCase, 'assertNotIn'):
            return unittest.TestCase.assertNotIn(self, member, container, msg)
        return self.assertFalse(member in container)

    def assertIsNone(self, obj, msg=None):
        if hasattr(unittest.TestCase, 'assertIsNone'):
            return unittest.TestCase.assertIsNone(self, obj, msg)
        return self.assertTrue(obj is None)

    def assertIsNotNone(self, obj, msg=None):
        if hasattr(unittest.TestCase, 'assertIsNotNone'):
            return unittest.TestCase.assertIsNotNone(self, obj, msg)
        return self.assertTrue(obj is not None)

class TestInitialization(TestCase):

    def test_init_mail(self):
        mail = self.mail.init_mail(
            self.app.config,
            self.app.debug,
            self.app.testing
        )

        self.assertEquals(self.mail.state.__dict__, mail.__dict__)

class TestMessage(TestCase):

    def test_initialize(self):
        msg = Message(subject="subject",
                      recipients=["to@example.com"])
        self.assertEqual(msg.sender, self.app.extensions['mail'].default_sender)
        self.assertEqual(msg.recipients, ["to@example.com"])

    def test_recipients_properly_initialized(self):
        msg = Message(subject="subject")
        self.assertEqual(msg.recipients, [])
        msg2 = Message(subject="subject")
        msg2.add_recipient("somebody@here.com")
        self.assertEqual(len(msg2.recipients), 1)

    def test_esmtp_options_properly_initialized(self):
        msg = Message(subject="subject")
        self.assertEqual(msg.mail_options, [])
        self.assertEqual(msg.rcpt_options, [])

        msg = Message(subject="subject", mail_options=['BODY=8BITMIME'])
        self.assertEqual(msg.mail_options, ['BODY=8BITMIME'])

        msg2 = Message(subject="subject", rcpt_options=['NOTIFY=SUCCESS'])
        self.assertEqual(msg2.rcpt_options, ['NOTIFY=SUCCESS'])

    def test_sendto_properly_set(self):
        msg = Message(subject="subject", recipients=["somebody@here.com"],
                      cc=["cc@example.com"], bcc=["bcc@example.com"])
        self.assertEqual(len(msg.send_to), 3)
        msg.add_recipient("cc@example.com")
        self.assertEqual(len(msg.send_to), 3)

    def test_add_recipient(self):
        msg = Message("testing")
        msg.add_recipient("to@example.com")
        self.assertEqual(msg.recipients, ["to@example.com"])

    def test_sender_as_tuple(self):
        msg = Message(subject="testing",
                      sender=("tester", "tester@example.com"))
        self.assertEqual('tester <tester@example.com>', msg.sender)

    def test_default_sender_as_tuple(self):
        self.app.extensions['mail'].default_sender = ('tester', 'tester@example.com')
        msg = Message(subject="testing")
        self.assertEqual('tester <tester@example.com>', msg.sender)

    def test_reply_to(self):
        msg = Message(subject="testing",
                      recipients=["to@example.com"],
                      sender="spammer <spammer@example.com>",
                      reply_to="somebody <somebody@example.com>",
                      body="testing")
        response = msg.as_string()

        h = Header("Reply-To: %s" % sanitize_address('somebody <somebody@example.com>'))
        self.assertIn(h.encode(), str(response))

    def test_send_without_sender(self):
        self.app.extensions['mail'].default_sender = None
        msg = Message(subject="testing", recipients=["to@example.com"], body="testing")
        self.assertRaises(AssertionError, self.mail.send, msg)

    def test_send_without_recipients(self):
        msg = Message(subject="testing",
                      recipients=[],
                      body="testing")
        self.assertRaises(AssertionError, self.mail.send, msg)

    def test_bcc(self):
        msg = Message(sender="from@example.com",
                      subject="testing",
                      recipients=["to@example.com"],
                      body="testing",
                      bcc=["tosomeoneelse@example.com"])
        response = msg.as_string()
        self.assertNotIn("tosomeoneelse@example.com", str(response))

    def test_cc(self):
        msg = Message(sender="from@example.com",
                      subject="testing",
                      recipients=["to@example.com"],
                      body="testing",
                      cc=["tosomeoneelse@example.com"])
        response = msg.as_string()
        self.assertIn("Cc: tosomeoneelse@example.com", str(response))

    def test_attach(self):
        msg = Message(subject="testing",
                      recipients=["to@example.com"],
                      body="testing")
        msg.attach(data=b"this is a test",
                   content_type="text/plain")
        a = msg.attachments[0]
        self.assertIsNone(a.filename)
        self.assertEqual(a.disposition, 'attachment')
        self.assertEqual(a.content_type, "text/plain")
        self.assertEqual(a.data, b"this is a test")

    def test_bad_header_subject(self):
        msg = Message(subject="testing\r\n",
                      sender="from@example.com",
                      body="testing",
                      recipients=["to@example.com"])
        self.assertRaises(BadHeaderError, self.mail.send, msg)

    def test_multiline_subject(self):
        msg = Message(subject="testing\r\n testing\r\n testing \r\n \ttesting",
                      sender="from@example.com",
                      body="testing",
                      recipients=["to@example.com"])
        self.mail.send(msg)
        response = msg.as_string()
        self.assertIn("From: from@example.com", str(response))
        self.assertIn("testing\r\n testing\r\n testing \r\n \ttesting", str(response))

    def test_bad_multiline_subject(self):
        msg = Message(subject="testing\r\n testing\r\n ",
                      sender="from@example.com",
                      body="testing",
                      recipients=["to@example.com"])
        self.assertRaises(BadHeaderError, self.mail.send, msg)

        msg = Message(subject="testing\r\n testing\r\n\t",
                      sender="from@example.com",
                      body="testing",
                      recipients=["to@example.com"])
        self.assertRaises(BadHeaderError, self.mail.send, msg)

        msg = Message(subject="testing\r\n testing\r\n\n",
                      sender="from@example.com",
                      body="testing",
                      recipients=["to@example.com"])
        self.assertRaises(BadHeaderError, self.mail.send, msg)

    def test_bad_header_sender(self):
        msg = Message(subject="testing",
                      sender="from@example.com\r\n",
                      recipients=["to@example.com"],
                      body="testing")

        self.assertIn('From: from@example.com', msg.as_string())

    def test_bad_header_reply_to(self):
        msg = Message(subject="testing",
                      sender="from@example.com",
                      reply_to="evil@example.com\r",
                      recipients=["to@example.com"],
                      body="testing")

        self.assertIn('From: from@example.com', msg.as_string())
        self.assertIn('To: to@example.com', msg.as_string())
        self.assertIn('Reply-To: evil@example.com', msg.as_string())

    def test_bad_header_recipient(self):
        msg = Message(subject="testing",
                      sender="from@example.com",
                      recipients=[
                          "to@example.com",
                          "to\r\n@example.com"],
                      body="testing")

        self.assertIn('To: to@example.com', msg.as_string())

    def test_emails_are_sanitized(self):
        msg = Message(subject="testing",
                      sender="sender\r\n@example.com",
                      reply_to="reply_to\r\n@example.com",
                      recipients=["recipient\r\n@example.com"])
        self.assertIn('sender@example.com', msg.as_string())
        self.assertIn('reply_to@example.com', msg.as_string())
        self.assertIn('recipient@example.com', msg.as_string())

    def test_plain_message(self):
        plain_text = "Hello Joe,\nHow are you?"
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body=plain_text)
        self.assertEqual(plain_text, msg.body)
        self.assertIn('Content-Type: text/plain', msg.as_string())

    def test_message_str(self):
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body="some plain text")
        self.assertEqual(msg.as_string(), str(msg))

    def test_plain_message_with_attachments(self):
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body="hello")

        msg.attach(data=b"this is a test",
                   content_type="text/plain")

        self.assertIn('Content-Type: multipart/mixed', msg.as_string())

    def test_plain_message_with_ascii_attachment(self):
        msg = Message(subject="subject",
                      recipients=["to@example.com"],
                      body="hello")

        msg.attach(data=b"this is a test",
                   content_type="text/plain",
                   filename='test doc.txt')

        self.assertIn('Content-Disposition: attachment; filename="test doc.txt"', msg.as_string())

    def test_plain_message_with_unicode_attachment(self):
        msg = Message(subject="subject",
                      recipients=["to@example.com"],
                      body="hello")

        msg.attach(data=b"this is a test",
                   content_type="text/plain",
                   filename=u'ünicöde ←→ ✓.txt')

        parsed = email.message_from_string(msg.as_string())

        self.assertIn(re.sub(r'\s+', ' ', parsed.get_payload()[1].get('Content-Disposition')), [
            'attachment; filename*="UTF8\'\'%C3%BCnic%C3%B6de%20%E2%86%90%E2%86%92%20%E2%9C%93.txt"',
            'attachment; filename*=UTF8\'\'%C3%BCnic%C3%B6de%20%E2%86%90%E2%86%92%20%E2%9C%93.txt'
            ])

    def test_plain_message_with_ascii_converted_attachment(self):
        with self.mail_config(ascii_attachments=True):
            msg = Message(subject="subject",
                          recipients=["to@example.com"],
                          body="hello")

            msg.attach(data=b"this is a test",
                       content_type="text/plain",
                       filename=u'ünicödeß ←.→ ✓.txt')

            parsed = email.message_from_string(msg.as_string())
            self.assertIn(
                'Content-Disposition: attachment; filename="unicode . .txt"',
                msg.as_string())

    def test_html_message(self):
        html_text = "
Hello World
"
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      html=html_text)

        self.assertEqual(html_text, msg.html)
        self.assertIn('Content-Type: multipart/alternative', msg.as_string())

    def test_json_message(self):
        json_text = '{"msg": "Hello World!}'
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      alts={'json': json_text})

        self.assertEqual(json_text, msg.alts['json'])
        self.assertIn('Content-Type: multipart/alternative', msg.as_string())

    def test_html_message_with_attachments(self):
        html_text = "
Hello World
"
        plain_text = 'Hello World'
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body=plain_text,
                      html=html_text)
        msg.attach(data=b"this is a test",
                   content_type="text/plain")

        self.assertEqual(html_text, msg.html)
        self.assertIn('Content-Type: multipart/alternative', msg.as_string())

        parsed = email.message_from_string(msg.as_string())
        self.assertEqual(len(parsed.get_payload()), 2)

        body, attachment = parsed.get_payload()
        self.assertEqual(len(body.get_payload()), 2)

        plain, html = body.get_payload()
        self.assertEqual(plain.get_payload(), plain_text)
        self.assertEqual(html.get_payload(), html_text)
        self.assertEqual(base64.b64decode(attachment.get_payload()), b'this is a test')

    def test_date_header(self):
        before = time.time()
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body="hello",
                      date=time.time())
        after = time.time()

        self.assertTrue(before <= msg.date <= after)
        dateFormatted = email.utils.formatdate(msg.date, localtime=True)
        self.assertIn('Date: ' + dateFormatted, msg.as_string())

    def test_msgid_header(self):
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body="hello")

        # see RFC 5322 section 3.6.4. for the exact format specification
        r = re.compile(r"<\S+@\S+>").match(msg.msgId)
        self.assertIsNotNone(r)
        self.assertIn('Message-ID: ' + msg.msgId, msg.as_string())

    def test_unicode_sender_tuple(self):
        msg = Message(subject="subject",
                      sender=(u"ÄÜÖ → ✓", 'from@example.com>'),
                      recipients=["to@example.com"])

        self.assertIn('From: =?utf-8?b?w4TDnMOWIOKGkiDinJM=?= <from@example.com>', msg.as_string())

    def test_unicode_sender(self):
        msg = Message(subject="subject",
                      sender=u'ÄÜÖ → ✓ <from@example.com>>',
                      recipients=["to@example.com"])

        self.assertIn('From: =?utf-8?b?w4TDnMOWIOKGkiDinJM=?= <from@example.com>', msg.as_string())

    def test_unicode_headers(self):
        msg = Message(subject="subject",
                      sender=u'ÄÜÖ → ✓ <from@example.com>',
                      recipients=[u"Ä <t1@example.com>", u"Ü <t2@example.com>"],
                      cc=[u"Ö <cc@example.com>"])

        response = msg.as_string()
        a1 = sanitize_address(u"Ä <t1@example.com>")
        a2 = sanitize_address(u"Ü <t2@example.com>")
        h1_a = Header("To: %s, %s" % (a1, a2))
        h1_b = Header("To: %s, %s" % (a2, a1))
        h2 = Header("From: %s" % sanitize_address(u"ÄÜÖ → ✓ <from@example.com>"))
        h3 = Header("Cc: %s" % sanitize_address(u"Ö <cc@example.com>"))

        # Ugly, but there's no guaranteed order of the recipieints in the header
        try:
            self.assertIn(h1_a.encode(), response)
        except AssertionError:
            self.assertIn(h1_b.encode(), response)

        self.assertIn(h2.encode(), response)
        self.assertIn(h3.encode(), response)

    def test_unicode_subject(self):
        msg = Message(subject=make_lazy_string(lambda a: a, u"sübject"),
                      sender='from@example.com',
                      recipients=["to@example.com"])
        self.assertIn('=?utf-8?q?s=C3=BCbject?=', msg.as_string())

    def test_extra_headers(self):
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["to@example.com"],
                      body="hello",
                      extra_headers={'X-Extra-Header': 'Yes'})
        self.assertIn('X-Extra-Header: Yes', msg.as_string())

    def test_message_charset(self):
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"],
                      charset='us-ascii')

        # ascii body
        msg.body = "normal ascii text"
        self.assertIn('Content-Type: text/plain; charset="us-ascii"', msg.as_string())

        # ascii html
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"],
                      charset='us-ascii')
        msg.body = None
        msg.html = "
hello
"
        self.assertIn('Content-Type: text/html; charset="us-ascii"', msg.as_string())

        # unicode body
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"])
        msg.body = u"ünicöde ←→ ✓"
        self.assertIn('Content-Type: text/plain; charset="utf-8"', msg.as_string())

        # unicode body and unicode html
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"])
        msg.html = u"ünicöde ←→ ✓"
        self.assertIn('Content-Type: text/plain; charset="utf-8"', msg.as_string())
        self.assertIn('Content-Type: text/html; charset="utf-8"', msg.as_string())

        # unicode body and attachments
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"])
        msg.html = None
        msg.attach(data=b"foobar", content_type='text/csv')
        self.assertIn('Content-Type: text/plain; charset="utf-8"', msg.as_string())

        # unicode sender as tuple
        msg = Message(sender=(u"送信者", "from@example.com"),
                      subject=u"表題",
                      recipients=["foo@bar.com"],
                      reply_to=u"返信先 <somebody@example.com>",
                      charset='shift_jis')  # japanese
        msg.body = u'内容'
        self.assertIn('From: =?iso-2022-jp?', msg.as_string())
        self.assertNotIn('From: =?utf-8?', msg.as_string())
        self.assertIn('Subject: =?iso-2022-jp?', msg.as_string())
        self.assertNotIn('Subject: =?utf-8?', msg.as_string())
        self.assertIn('Reply-To: =?iso-2022-jp?', msg.as_string())
        self.assertNotIn('Reply-To: =?utf-8?', msg.as_string())
        self.assertIn('Content-Type: text/plain; charset="iso-2022-jp"', msg.as_string())

        # unicode subject sjis
        msg = Message(sender="from@example.com",
                      subject=u"表題",
                      recipients=["foo@bar.com"],
                      charset='shift_jis')  # japanese
        msg.body = u'内容'
        self.assertIn('Subject: =?iso-2022-jp?', msg.as_string())
        self.assertIn('Content-Type: text/plain; charset="iso-2022-jp"', msg.as_string())

        # unicode subject utf-8
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"],
                      charset='utf-8')
        msg.body = u'内容'
        self.assertIn('Subject: subject', msg.as_string())
        self.assertIn('Content-Type: text/plain; charset="utf-8"', msg.as_string())

        # ascii subject
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"],
                      charset='us-ascii')
        msg.body = "normal ascii text"
        self.assertNotIn('Subject: =?us-ascii?', msg.as_string())
        self.assertIn('Content-Type: text/plain; charset="us-ascii"', msg.as_string())

        # default charset
        msg = Message(sender="from@example.com",
                      subject="subject",
                      recipients=["foo@bar.com"])
        msg.body = "normal ascii text"
        self.assertNotIn('Subject: =?', msg.as_string())
        self.assertIn('Content-Type: text/plain; charset="utf-8"', msg.as_string())

    def test_empty_subject_header(self):
        msg = Message(sender="from@example.com",
                      recipients=["foo@bar.com"])
        msg.body = "normal ascii text"
        self.mail.send(msg)
        self.assertNotIn('Subject:', msg.as_string())

class TestMail(TestCase):

    def test_send(self):

        with self.mail.record_messages() as outbox:
            msg = Message(subject="testing",
                          recipients=["tester@example.com"],
                          body="test")
            self.mail.send(msg)
            self.assertIsNotNone(msg.date)
            self.assertEqual(len(outbox), 1)
            sent_msg = outbox[0]
            self.assertEqual(msg.sender, self.app.extensions['mail'].default_sender)

    def test_send_message(self):

        with self.mail.record_messages() as outbox:
            self.mail.send_message(subject="testing",
                                   recipients=["tester@example.com"],
                                   body="test")
            self.assertEqual(len(outbox), 1)
            msg = outbox[0]
            self.assertEqual(msg.subject, "testing")
            self.assertEqual(msg.recipients, ["tester@example.com"])
            self.assertEqual(msg.body, "test")
            self.assertEqual(msg.sender, self.app.extensions['mail'].default_sender)

class TestConnection(TestCase):

    def test_send_message(self):
        with self.mail.record_messages() as outbox:
            with self.mail.connect() as conn:
                conn.send_message(subject="testing",
                                  recipients=["to@example.com"],
                                  body="testing")
            self.assertEqual(len(outbox), 1)
            sent_msg = outbox[0]
            self.assertEqual(sent_msg.sender, self.app.extensions['mail'].default_sender)

    def test_send_single(self):
        with self.mail.record_messages() as outbox:
            with self.mail.connect() as conn:
                msg = Message(subject="testing",
                              recipients=["to@example.com"],
                              body="testing")
                conn.send(msg)
            self.assertEqual(len(outbox), 1)
            sent_msg = outbox[0]
            self.assertEqual(sent_msg.subject, "testing")
            self.assertEqual(sent_msg.recipients, ["to@example.com"])
            self.assertEqual(sent_msg.body, "testing")
            self.assertEqual(sent_msg.sender, self.app.extensions['mail'].default_sender)

    def test_send_many(self):
        with self.mail.record_messages() as outbox:
            with self.mail.connect() as conn:
                for i in range(100):
                    msg = Message(subject="testing",
                                  recipients=["to@example.com"],
                                  body="testing")
                    conn.send(msg)
            self.assertEqual(len(outbox), 100)
            sent_msg = outbox[0]
            self.assertEqual(sent_msg.sender, self.app.extensions['mail'].default_sender)

    def test_send_without_sender(self):
        self.app.extensions['mail'].default_sender = None
        msg = Message(subject="testing", recipients=["to@example.com"], body="testing")
        with self.mail.connect() as conn:
            self.assertRaises(AssertionError, conn.send, msg)

    def test_send_without_recipients(self):
        msg = Message(subject="testing",
                      recipients=[],
                      body="testing")
        with self.mail.connect() as conn:
            self.assertRaises(AssertionError, conn.send, msg)

    def test_bad_header_subject(self):
        msg = Message(subject="testing\n\r",
                      body="testing",
                      recipients=["to@example.com"])
        with self.mail.connect() as conn:
            self.assertRaises(BadHeaderError, conn.send, msg)

    def test_sendmail_with_ascii_recipient(self):
        with self.mail.connect() as conn:
            with mock.patch.object(conn, 'host') as host:
                msg = Message(subject="testing",
                              sender="from@example.com",
                              recipients=["to@example.com"],
                              body="testing")
                conn.send(msg)

                host.sendmail.assert_called_once_with(
                    "from@example.com",
                    ["to@example.com"],
                    msg.as_bytes() if PY3 else msg.as_string(),
                    msg.mail_options,
                    msg.rcpt_options
                )

    def test_sendmail_with_non_ascii_recipient(self):
        with self.mail.connect() as conn:
            with mock.patch.object(conn, 'host') as host:
                msg = Message(subject="testing",
                              sender="from@example.com",
                              recipients=[u'ÄÜÖ → ✓ <to@example.com>'],
                              body="testing")
                conn.send(msg)

                host.sendmail.assert_called_once_with(
                    "from@example.com",
                    ["=?utf-8?b?w4TDnMOWIOKGkiDinJM=?= <to@example.com>"],
                    msg.as_bytes() if PY3 else msg.as_string(),
                    msg.mail_options,
                    msg.rcpt_options
                )

    def test_sendmail_with_ascii_body(self):
        with self.mail.connect() as conn:
            with mock.patch.object(conn, 'host') as host:
                msg = Message(subject="testing",
                              sender="from@example.com",
                              recipients=["to@example.com"],
                              body="body")
                conn.send(msg)

                host.sendmail.assert_called_once_with(
                    "from@example.com",
                    ["to@example.com"],
                    msg.as_bytes() if PY3 else msg.as_string(),
                    msg.mail_options,
                    msg.rcpt_options
                )

    def test_sendmail_with_non_ascii_body(self):
        with self.mail.connect() as conn:
            with mock.patch.object(conn, 'host') as host:
                msg = Message(subject="testing",
                              sender="from@example.com",
                              recipients=["to@example.com"],
                              body=u"Öö")

                conn.send(msg)

                host.sendmail.assert_called_once_with(
                    "from@example.com",
                    ["to@example.com"],
                    msg.as_bytes() if PY3 else msg.as_string(),
                    msg.mail_options,
                    msg.rcpt_options
                )


from reportlab.graphics import widgetbase
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate
from reportlab.graphics.shapes import Drawing
story = []
drawing = Drawing(400, 200)
face = widgetbase.Face()
face.skinColor = colors.yellow
face.eyeColor = colors.green
face.mood = 'happy'
drawing.add(face)
story.append(drawing)
pdf = SimpleDocTemplate('face.pdf')
pdf.build(story)


import datetime
#Determinar signo zodiacal
def Signo(d, m):
    signo = ['acuario','piscis','aries','tauro','geminis','cancer','leo','virgo','libra','escorpio','sagitario','capricornio']
    if((d >= 20) and (m == 1) or (d < 19) and (m == 2)):
        return signo[0]
    elif((d >= 19) and (m == 2) or (d < 21) and (m == 3)):
        return signo[1]
    elif((d >= 21) and (m == 3) or (d < 20) and (m == 4)):
        return signo[2]
    elif((d >= 20) and (m == 4) or (d < 21) and (m == 5)):
        return signo[3]
    elif((d >= 21) and (m == 5) or (d < 21) and (m == 6)):
        return signo[4]
    elif((d >= 21) and (m == 6) or (d < 23) and (m == 7)):
        return signo[5]
    elif((d >= 23) and (m == 7) or (d < 23) and (m == 8)):
        return signo[6]
    elif((d >= 23) and (m == 8) or (d < 23) and (m == 9)):
        return signo[7]
    elif((d >= 23) and (m == 9) or (d < 23) and (m == 10)):
        return signo[8]
    elif((d >= 23) and (m == 10) or (d < 22) and (m == 11)):
        return signo[9]
    elif((d >= 22) and (m == 10) or (d < 22) and (m == 12)):
        return signo[10]
    else:
        return signo[11]
#Solicitar la fecha
print('Por favor, indique su fecha de nacimiento')
dia = int(input('Indique el dia de nacimiento ---> '))
mes = int(input('Indique el numero del mes en que nacio ---> '))
año = int(input('Indique el año en que nacio ---> '))
#Declarar la fecha
date = datetime.datetime(año, mes, dia)
now = datetime.datetime.now()
#Encontrar el tiempo de vida
time = now - date
#Encontrar el tiempo vivido
days = time.days
print("")
print('Dias vividos: ', days)
seconds = time.seconds + ((days*24)*3600)
hours = seconds / 3600
print('Horas vividas:', hours)
print('Segundos vividos: ', seconds)
zodiaco = Signo(dia, mes)
print('Signo zodiacal: ', zodiaco)


class Node:
    """класс односвязного списка"""
    def __init__(self, value=None, next=None):
        self.value = value
        self.next = next

def print_linked_list(vertex):
    while vertex:
        print(vertex.value, end=" -> ")
        vertex = vertex.next
    print("None")

if __name__ == '__main__':
    n3 = Node('third')
    n2 = Node('second', n3)
    n1 = Node('first', n2)
    print_linked_list(n1)

    n0 = Node('zero', n1)
    print_linked_list(n0)


import rclpy
from rclpy.node import Node
import time
import numpy as np

from . import util_atmo_drag as atmo_drag

from std_msgs.msg import Float64MultiArray, Float64

class poliastro_atmo_drag(Node):

    def __init__(self):
        super().__init__('dynamics')

        self.state_pub = self.create_publisher(Float64MultiArray , '/poliastro_atmo_drag/state', 10)
        self.res_pub = self.create_publisher(Float64 , '/poliastro_atmo_drag/res', 10)

        self.get_logger().info(f"Starting poliastro_atmo_drag version = 0.0.0")

        #Defining states  a, ecc, inc, raan, argp, nu, c_d, t_limit 6.3781366e6
        #Initial orbit parameters
        self.declare_parameter('earth_r', 6378136.6)
        self.declare_parameter('a', 6550.85876692)
        self.declare_parameter('ecc', 0.0005480)
        self.declare_parameter('inc', 0.0)
        self.declare_parameter('raan', 0.0)
        self.declare_parameter('argp', 0.0)
        self.declare_parameter('nu', 0.0)
        self.declare_parameter('c_d', 2.2)

        self.declare_parameter('t_limit', 100.0)

        self.declare_parameter('publish_freq', 10.0)

        self.earth_r = self.get_parameter('earth_r').get_parameter_value().double_value
        self.a = self.get_parameter('a').get_parameter_value().double_value
        self.ecc = self.get_parameter('ecc').get_parameter_value().double_value
        self.inc = self.get_parameter('inc').get_parameter_value().double_value
        self.raan = self.get_parameter('raan').get_parameter_value().double_value
        self.argp = self.get_parameter('argp').get_parameter_value().double_value
        self.nu = self.get_parameter('nu').get_parameter_value().double_value
        self.c_d = self.get_parameter('c_d').get_parameter_value().double_value

        self.t_limit = self.get_parameter('t_limit').get_parameter_value().double_value

        self.get_logger().info(f"With Cd = {self.c_d}")

        # Defining encounter for publisher
        self.i = 0

        #################################
        # Calling simulation function using parameters and functions declared above
        self.res_orb, self.res_t =  atmo_drag.run(self,
                                                  earth_r=self.earth_r,
                                                  a=self.a,
                                                  ecc=self.ecc,
                                                  inc=self.inc,
                                                  raan=self.raan,
                                                  argp=self.argp,
                                                  nu=self.nu,
                                                  c_d=self.c_d,
                                                  t_limit=self.t_limit)

        self.state_msg = Float64MultiArray()
        self.res_msg = Float64()
        timer_period = 1/self.get_parameter('publish_freq').get_parameter_value().double_value  # frequency of publishing
        self.timer = self.create_timer(timer_period, self.timer_callback)

    def timer_callback(self):

        self.state_msg.data = [self.res_orb[self.i][0], self.res_orb[self.i][1], self.res_orb[self.i][2]]

        self.state_pub.publish(self.state_msg)
        self.get_logger().info(f"Publishing = {self.state_msg.data}")

        self.i += 1
        if self.i==len(self.res_orb):
            self.res_msg.data = self.res_t
            self.res_pub.publish(self.res_msg)
            self.get_logger().info(f"Publishing res_t = {self.res_t}")
            self.get_logger().info('All data published successfully')
            exit()

def main(args=None):
    rclpy.init(args=args)
    dynamics = poliastro_atmo_drag()
    rclpy.spin(dynamics)
    dynamics.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()


# Design and implement a data structure for a compressed string iterator.
# It should support the following operations: next and hasNext.
# The given compressed string will be in the form of each letter
# followed by a positive integer representing the number of this letter existing in the original uncompressed string.
# next() - if the original string still has uncompressed characters, return the next letter; Otherwise return a white space.
# hasNext() - Judge whether there is any letter needs to be uncompressed.

# Note:
# Please remember to RESET your class variables declared in StringIterator,
# as static/class variables are persisted across multiple test cases. Please see here for more details.

# Example:
# StringIterator iterator = new StringIterator("L1e2t1C1o1d1e1");
# iterator.next(); // return 'L'
# iterator.next(); // return 'e'
# iterator.next(); // return 'e'
# iterator.next(); // return 't'
# iterator.next(); // return 'C'
# iterator.next(); // return 'o'
# iterator.next(); // return 'd'
# iterator.hasNext(); // return true
# iterator.next(); // return 'e'
# iterator.hasNext(); // return false
# iterator.next(); // return ' '

# https://leetcode.com/problems/design-compressed-string-iterator/solution/
# Demand-Computation
class StringIterator(object):

    def __init__(self, compressedString):
        """
        :type compressedString: str
        """
        self.string = compressedString
        self.cnt = 0
        self.char = ""

    def next(self):
        """
        :rtype: str
        """
        if self.cnt > 0:
            self.cnt -= 1
            return self.char
        elif len(self.string) == 0:
            return ' '
        else:
            self.char = self.string[0]
            self.string = self.string[1:]
            cnt = ""
            while len(self.string) > 0 and self.string[0].isdigit():
                cnt += self.string[0]
                self.string = self.string[1:]
            self.cnt = int(cnt)
            return self.next()

    def hasNext(self):
        """
        :rtype: bool
        """
        return self.cnt > 0 or len(self.string) > 0

# Your StringIterator object will be instantiated and called as such:
# obj = StringIterator(compressedString)
# param_1 = obj.next()
# param_2 = obj.hasNext()

LeetCode/Python3/Design/604. Design Compressed String Iterator.py


import sys
from _ import smart_compile_file, terminal

# This is a command line interface for underscore.

if len(sys.argv) <= 1:
    # sys.argv[0] is the script name.
    # Run the terminal.
    terminal()
else:
    # If there are any arguments passed, attempt to compile_file the first one.
    compiled = smart_compile_file(sys.argv[1])
    # Display the memory to the user.
    print(compiled.run())


ACC_PUBLIC = 0x1
ACC_PRIVATE = 0x2
ACC_PROTECTED = 0x4
ACC_STATIC = 0x8
ACC_FINAL = 0x10
ACC_SYNCHRONIZED = 0x20
ACC_VOLATILE = 0x40
ACC_BRIDGE = 0x40
ACC_TRANSIENT = 0x80
ACC_VARARGS = 0x80
ACC_NATIVE = 0x100
ACC_INTERFACE = 0x200
ACC_ABSTRACT = 0x400
ACC_STRICT = 0x800
ACC_SYNTHETIC = 0x1000
ACC_ANNOTATION = 0x2000
ACC_ENUM = 0x4000
ACC_CONSTRUCTOR = 0x10000
ACC_DECLARED_SYNCHRONIZED = 0x20000

# Might as well include this for completeness even though modern JVMs ignore it
ACC_SUPER = 0x20

CLASS_FLAGS = ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM
FIELD_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM
METHOD_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC


from typing import Dict, List

# Name:  Add Bold Tag in String
# Link: https://leetcode.com/problems/add-bold-tag-in-string/
# Method: Trie and array of bolded indexes, check words starting at index
# Time: O(w + n*m)
# Space: O(w + n)
# Difficulty: Mediun
# Notes: w = characters in all words, m = longest word

class Solution:
    def addBoldTag(self, s: str, words: List[str]) -> str:
        return Solution.bolden_word(s, words)

    @staticmethod
    def words_to_trie(words: List[str]) -> Dict:
        trie = {}
        for word in words:
            trie_head = trie
            for ltr in word:
                trie_head = trie_head.setdefault(ltr, {})
            trie_head["_is_finish"] = True
        return trie

    @staticmethod
    def bolden_word(s: str, words: List[str]) -> str:
        is_bold = [False for _ in range(len(s))]
        trie = Solution.words_to_trie(words)
        for i in range(len(s)):
            Solution.bold_words_starting_at(i, s, trie, is_bold)

        res = ""
        i = 0
        while i < len(s):
            if is_bold[i]:
                res += "<b>"
                while i < len(s) and is_bold[i]:
                    res += s[i]
                    i += 1
                res += "</b>"
            else:
                res += s[i]
                i += 1
        return res

    @staticmethod
    def bold_words_starting_at(idx, s, trie, is_bold):
        start = idx
        trie_head = trie
        while idx < len(s) and s[idx] in trie_head:
            # print(f"Looking for {idx=} next={trie_head[s[idx]].keys()} ltrs: {s[idx]=}")
            if "_is_finish" in trie_head[s[idx]]:
                for j in range(start, idx + 1):
                    is_bold[j] = True
            trie_head = trie_head[s[idx]]
            idx += 1


# -*- coding: utf-8 -*-
from selenium import webdriver
from fixture.session import SessionHelper
from fixture.james import JamesHelper
from fixture.signup import SignupHelper
from fixture.mail import MailHelper
from fixture.soap import SoapHelper
#from fixture.contact import ContactHelper
from selenium.webdriver.support.ui import Select

import random
import string

class Application:
    def __init__(self, browser, config):
        if browser == 'chrome':
            chrome_options = webdriver.ChromeOptions()
            chrome_options.binary_location = "C:/Program Files/Google/Chrome/Application/chrome.exe"
            self.wd = webdriver.Chrome(executable_path=r'c:/Python39/chromedriver.exe')
        elif browser == 'firefox':
            self.wd = webdriver.Firefox(executable_path=r'geckodriver.exe')
        elif browser == 'edge':
            self.wd = webdriver.Edge(executable_path=r'msedgedriver.exe')
        else:
            raise ValueError("Unrecognazed browser %s" % browser)
        #self.wd.implicitly_wait(5)
        self.session = SessionHelper(self)
        self.james = JamesHelper(self)
        self.signup = SignupHelper(self)
        self.mail = MailHelper(self)
        self.soap = SoapHelper(self)
        self.config = config
        self.base_url = config["web"]["baseUrl"]

    def is_valid(self):
        try:
            self.wd.current_url
            return True
        except:
            return False

    def count(self, key):  # key = contact - подсчет контактов, key = group - подсчет групп
        wd = self.wd
        if key == 'group':
            self.group.open_groups_page()
        elif key == 'contact':
            self.contact.open_home_page()
        else:
            print('Неверный параметр! key = contact - подсчет контактов, key = group - подсчет групп')
        return len(wd.find_elements_by_name("selected[]"))

    def change_field_value(self, field_name, text):
        wd = self.wd
        if text is not None:
            wd.find_element_by_name(field_name).click()
            wd.find_element_by_name(field_name).clear()
            wd.find_element_by_name(field_name).send_keys(text)

    def change_field_value_select(self, field_name, text):
        wd = self.wd
        if text is not None:
            wd.find_element_by_name(field_name).click()
            Select(wd.find_element_by_name(field_name)).select_by_visible_text(text)

    def random_string(prefix, maxlen):
        symbols = string.ascii_letters + string.digits + string.punctuation + " " * 10
        return prefix + "".join([random.choice(symbols) for i in range(random.randrange(maxlen))])

    def random_number(prefix, maxlen):
        symbols = string.digits
        return prefix + "".join([random.choice(symbols) for i in range(random.randrange(maxlen))])

    def open_home_page(self):
        wd = self.wd
        # Если страница не открыта, то откроем страницу
        # menu "home"
        #if not (wd.current_url.endswith("/index.php") and len(wd.find_elements_by_css_selector("[value='Send e-Mail']")) > 0):
        wd.get(self.base_url)

    def destroy(self):
        self.wd.quit()


from typing import Any, Dict, Iterable, List

from flask_batteries_included.helpers import schema
from neomodel import One, RelationshipFrom, RelationshipTo, StructuredNode

from dhos_services_api.helpers.responses import QueryResponse, response_to_dict
from dhos_services_api.models.diagnosis import Diagnosis
from dhos_services_api.models.history import History
from dhos_services_api.models.note import Note
from dhos_services_api.models.pregnancy import Pregnancy
from dhos_services_api.models.visit import Visit
from dhos_services_api.neodb import NeomodelIdentifier

_MARKER: Any = object()

class Record(NeomodelIdentifier, StructuredNode):
    notes = RelationshipTo(".note.Note", "HAS_NOTE")
    diagnoses = RelationshipTo(".diagnosis.Diagnosis", "HAS_DIAGNOSIS")
    pregnancies = RelationshipTo(".pregnancy.Pregnancy", "HAS_PREGNANCY")
    visits = RelationshipTo(".visit.Visit", "HAD_VISIT")
    history = RelationshipTo(".history.History", "HAS_HISTORY", cardinality=One)
    patient = RelationshipFrom(".patient.Patient", "HAS_RECORD", cardinality=One)

    @classmethod
    def new(cls, *args: Any, **kwargs: Any) -> "Record":

        schema.post(json_in=kwargs, **cls.schema())

        notes = [Note.new(**note) for note in kwargs.pop("notes", [])]

        diagnoses = [Diagnosis.new(**diag) for diag in kwargs.pop("diagnoses", [])]

        pregnancies = [Pregnancy.new(**preg) for preg in kwargs.pop("pregnancies", [])]

        visits = [Visit.new(**visit) for visit in kwargs.pop("visits", [])]

        history = History.new(**kwargs.pop("history", dict()))

        obj = cls(*args, **kwargs)
        obj.save()

        history.save()
        obj.history.connect(history)

        for note in notes:
            note.save()
            obj.notes.connect(note)

        for visit in visits:
            visit.save()
            obj.visits.connect(visit)

        for diagnosis in diagnoses:
            diagnosis.save()
            obj.diagnoses.connect(diagnosis)

        for preg in pregnancies:
            preg.save()
            obj.pregnancies.connect(preg)

        return obj

    @classmethod
    def schema(cls) -> Dict[str, Dict[str, Any]]:
        return {
            "optional": {
                "pregnancies": [dict],
                "history": dict,
                "notes": [dict],
                "diagnoses": [dict],
                "visits": [dict],
            },
            "required": {},
            "updatable": {
                "pregnancies": [dict],
                "notes": [dict],
                "diagnoses": [dict],
                "visits": [dict],
                "history": dict,
            },
        }

    def to_dict_no_relations(
        self,
        notes: Iterable[Dict] = (),
        diagnoses: Iterable[Dict] = (),
        pregnancies: Iterable[Dict] = (),
        visits: Iterable[Dict] = (),
        history: Dict = None,
    ) -> Dict:
        return {
            "notes": sorted(notes, key=lambda x: x["created"], reverse=True),
            "diagnoses": sorted(diagnoses, key=lambda x: x["created"], reverse=True),
            "pregnancies": sorted(
                pregnancies, key=lambda x: x["created"], reverse=True
            ),
            "visits": sorted(visits, key=lambda x: x["created"], reverse=True),
            "history": history,
            **self.pack_identifier(),
        }

    def to_dict(self) -> Dict[str, Any]:
        return self.to_dict_no_relations(
            notes=[note.to_dict() for note in self.notes],
            diagnoses=[diagnosis.to_dict() for diagnosis in self.diagnoses],
            pregnancies=[pregnancy.to_dict() for pregnancy in self.pregnancies],
            visits=[visit.to_dict() for visit in self.visits],
            history=self.history.single().to_dict(),
        )

    def to_compact_dict(
        self,
        diagnoses: List[Dict] = _MARKER,
        pregnancies: List[Dict] = _MARKER,
        notes: Iterable[Dict] = None,
        visits: Iterable[Dict] = None,
        history: Dict = None,
    ) -> Dict:
        if diagnoses is _MARKER:
            diagnoses = [diagnosis.to_compact_dict() for diagnosis in self.diagnoses]

        if pregnancies is _MARKER:
            pregnancies = [
                pregnancy.to_compact_dict() for pregnancy in self.pregnancies
            ]

        return {
            "diagnoses": sorted(diagnoses, key=lambda x: x["created"], reverse=True),
            "pregnancies": sorted(
                pregnancies, key=lambda x: x["created"], reverse=True
            ),
            **self.compack_identifier(),
        }

    @classmethod
    def convert_response_to_dict(cls, response: QueryResponse, method: str) -> Dict:
        return response_to_dict(
            cls,
            response,
            primary="record",
            single={"history": History},
            multiple={
                "notes": Note,
                "diagnoses": Diagnosis,
                "pregnancies": Pregnancy,
                "visits": Visit,
            },
            method=method,
        )


# 12933
# 함수 solution은 정수 n을 매개변수로 입력받습니다.
# n의 각 자릿수를 큰것부터 작은 순으로 정렬한 새로운 정수를 리턴해주세요. 예를들어 n이 118372면 873211을 리턴하면 됩니다.

def solution(n):
    nList = list(map(int, str(n)))
    newL = sorted(nList, reverse=True)
    strings = [str(i) for i in newL]
    a_string = "".join(strings)
    return int(a_string)

print(solution(118372))


import requests
import sys

URL = "https://en.wikipedia.org/w/api.php"
query = sys.argv[1]
PARAMS = {
    'action': 'query',
    'list': 'search',
    'srsearch': query,
    'format': 'json'
}

r = requests.get(url=URL, params=PARAMS)
data = r.json()

print('Top five results for ' + query + ' on Wikipedia')
print('----------------')

for result in data['query']['search'][:5]:
    print (result['title'])


import json

class JsonREad:

#REad based on dictonaty
    def JSONReadData(self):
        person = '{"name": "Bob", "languages": ["English", "French"]}'
        person_dict = json.loads(person)

        # Output: {'name': 'Bob', 'languages': ['English', 'French']}
        print(person_dict)

        # Output: ['English', 'French']
        print(person_dict['languages'])

        print(person_dict['name'])

        for eachvalue in  person_dict['languages']:
            print(eachvalue)

    def REadJsonFromFile(self):
        with open('C:\\Users\\sathishkumar\\PycharmProjects\\weekendSunday\\input\\Input.Json','r') as f:
            data =json.load(f)

        for eachvalue in data:
            print(eachvalue)
            print(data[eachvalue])
            acttype= type(data[eachvalue])
            if acttype == list:
                for each in data[eachvalue]:
                    print(each)
        print(data)
        for eachvalue in data['Hotels']:
            print(eachvalue)

    def WriteJsonFile(self):

        person_dict = {"name": "Bob",
                   "languages": ["English", "French"],
                   "married": True,
                   "age": 32
                   }

        with open('person.txt', 'w') as json_file:
            json.dump(person_dict, json_file)

    def GitBrachconcept(self):
        print("Branconcept")

    def change(self):
        print("newchange")

obj=JsonREad()
obj.JSONReadData()
obj.REadJsonFromFile()
obj.WriteJsonFile()


import unittest
from roguelike import *

# Graphics isn't tested, it's simple and if things go wrong it's obvious

# Game tests

# Fake input by messing with the builtin function.
# This is kind of a terrible idea, but it's the only way I could hack it
real_input = __builtins__.input
def mock_input(char):
    __builtins__.input = lambda: char
    return None

class EntityTests(unittest.TestCase):
    def setUp(self):
        self.game = Game(16, 8, 16)
        dim_x, dim_y = self.game.dim_x, self.game.dim_y

        self.movegirl = Entity(self.game, 'Move Girl', '#', Pos(dim_x//2, dim_y//2, 0))
        def move_act():
            return Move(E)
        self.movegirl.get_action = move_act

        self.digboy = Entity(self.game, 'Dig Boy', '$', Pos(dim_x//2, dim_y//2, 0))
        self.digboy.get_action = lambda: Dig(E)

    def test_moving(self):
        self.game.entities.append(self.movegirl)
        # make sure she can move without digging
        self.game.world[0] = [
                empty_space for i in range(self.game.dim_x*self.game.dim_y)]
        original_pos = self.movegirl.pos
        self.game.step()
        self.assertEqual(self.movegirl.pos, (original_pos + E))
        #remove movegirl so she doesn't mess with the next test
        self.game.entites.pop()

    def test_digging(self):
        self.game.entities.append(self.digboy)
        original_pos = self.movegirl.pos
        self.game.step()
        self.assertEqual(self.digboy.pos, (original_pos + E))
        #remove digboy so he doesn't mess with the next test
        self.game.entites.pop()

    def test_attacking(self):
        pass

class HeroTests(unittest.TestCase):
    def setUp(self):
        self.game = Game(16, 8, 16)
        self.game.world[0] = [
                empty_space for i in range(self.game.dim_x*self.game.dim_y)]
        hero = self.game.hero

    def test_movement_off_grid(self):
        mock_input('w')
        for i in range(self.game.dim_y+5):
            self.game.step()
            # Hackishly prevent the hero from drifting endlessly
            self.game.hero.next_action = None

        mock_input('a')
        for i in range(self.game.dim_x+5):
            self.game.step()
            self.game.hero.next_action = None

    def test_movement_off_top(self):
        mock_input('\x1b[A')
        for i in range(self.game.dim_z+5):
            self.game.step()
            self.game.hero.next_action = None

    def test_movement_off_bot(self):
        mock_input('\x1b[B')
        for i in range(self.game.dim_z+5):
            self.game.step()
            self.game.hero.next_action = None

__builtins__.input = real_input

if __name__ == "__main__":
    unittest.main()


for _ in range(int(input())):
    r = int(input())-1
    data = list(map(int, input().split()))
    data.sort()
    l = 0
    runningSum = 0
    while l<=r:
        runningSum+= data[r]-data[l]
        l+=1
        r-=1
    # print(f"ans: {runningSum}")
    print(runningSum)

# [1 5 6 3 4]
# [1,3,4,5,6]
# 6-1 5-3


from PyQt4.QtGui import QToolBar
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QObject
import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

#http://article.gmane.org/gmane.comp.python.pyqt-pykde/9245
_oldConnect = QtCore.QObject.connect
_oldDisconnect = QtCore.QObject.disconnect
_oldEmit = QtCore.QObject.emit

def _wrapConnect(callableObject):
    """Returns a wrapped call to the old version of QtCore.QObject.connect"""

    logger.debug(">>_wrapConnect()")

    @staticmethod
    def call(*args):
        logger.debug(">>call()")
        callableObject(*args)
        _oldConnect(*args)
    return call

def _wrapDisconnect(callableObject):
    """Returns a wrapped call to the old version of QtCore.QObject.disconnect"""

    logger.debug(">>_wrapDisconnect()")

    @staticmethod
    def call(*args):
        logger.debug(">>call()")
        callableObject(*args)
        _oldDisconnect(*args)
    return call

def enableSignalDebugging(**kwargs):
    """Call this to enable Qt Signal debugging. This will trap all
    connect, and disconnect calls."""

    logger.debug(">>enableSignalDebugging()")

    f = lambda *args: None
    connectCall = kwargs.get('connectCall', f)
    disconnectCall = kwargs.get('disconnectCall', f)
    emitCall = kwargs.get('emitCall', f)

    def printIt(msg):
        logger.debug(">>printIt()")
        def call(*args):
            print ("--printIt() "+msg, args)
        return call
    QtCore.QObject.connect = _wrapConnect(connectCall)
    QtCore.QObject.disconnect = _wrapDisconnect(disconnectCall)

    def new_emit(self, *args):
        logger.debug(">>new_emit()")
        emitCall(self, *args)
        _oldEmit(self, *args)

    QtCore.QObject.emit = new_emit


"""
Time Planner
Implement a function meetingPlanner that given the availability, slotsA and slotsB, of two people and a meeting duration dur, returns the earliest time slot that works for both of them and is of duration dur. If there is no common time slot that satisfies the duration requirement, return an empty array.

Time is given in a Unix format called Epoch, which is a nonnegative integer holding the number of seconds that have elapsed since 00:00:00 UTC, Thursday, 1 January 1970.

Each person’s availability is represented by an array of pairs. Each pair is an epoch array of size two. The first epoch in a pair represents the start time of a slot. The second epoch is the end time of that slot. The input variable dur is a positive integer that represents the duration of a meeting in seconds. The output is also a pair represented by an epoch array of size two.

In your implementation assume that the time slots in a person’s availability are disjointed, i.e, time slots in a person’s availability don’t overlap. Further assume that the slots are sorted by slots’ start time.

Implement an efficient solution and analyze its time and space complexities.

Examples:

input:  slotsA = [[10, 50], [60, 120], [140, 210]]
        slotsB = [[0, 15], [60, 70]]
        dur = 8
output: [60, 68]

input:  slotsA = [[10, 50], [60, 120], [140, 210]]
        slotsB = [[0, 15], [60, 70]]
        dur = 12
output: [] # since there is no common slot whose dur
"""

def meeting_planner(slotsA, slotsB, dur):

  pointer_a, pointer_b = 0 , 0

  len_a , len_b  = len(slotsA), len(slotsB)

  while pointer_a < len_a and pointer_b < len_b:

    start_a, end_a = slotsA[pointer_a]
    start_b, end_b = slotsB[pointer_b]

    t1 = max(start_a, start_b)
    t2 = min(end_a, end_b)

    #print(t1, t2 )

    if abs(t1-t2) >= dur:
      return [t1, t1+dur]

    else:
      if end_a > end_b:
        pointer_b +=1

      elif end_a < end_b:
        pointer_a +=1

  return []

#slotsA = [[10, 50], [60, 120], [140, 210]]
#slotsB = [[0, 15], [60, 70]]
#print( meeting_planner(slotsA, slotsB, dur ))

#Otime O(N+M) space O(1)


from itertools import product
from math import log2, ceil

n, a, b = map(int, input().split())
commits = input()

steps = ()
# for i in product(('Л', 'Х'), repeat=3):
#     steps += (i, )

center = n // 2 if (n - 1) % 2 == 0 else n // 2 - 1
start = 0
end = n
k = 0

garant_time = []
if commits[center + 1:end].find('B') == -1:
    garant_time.append(a)
else:
    garant_time.append(b)

i = ceil(log2(n))

while i != 0 and start != center and center != end:
    i -= 1
    print('начало:', start)
    print('середина:', center)
    print('конец:', end)
    print('время:', garant_time)
    print(i)
    print()
    k += 1
    if commits[start:center].find('B') != -1:
        if commits[center + 1:end].find('B') != -1:
            steps += (k, )
    if commits[start:(center + start) // 2 if (center + start) % 2 == 0 else (center + start) // 2 + 1].find('B') == -1:
        if commits[center + 1:(center + end - 1) // 2 if (center + end - 1) % 2 == 0 else (center + end - 1) // 2 + 1 + 1].find('B') == -1:
            steps += (k, )
            end = center
            center = (center + start) // 2 if (center + start) % 2 == 0 else (center + start) // 2 + 1
            garant_time.append(a)
        else:
            if ((center + end - 1) // 2 if (center + end - 1) % 2 == 0 else (center + end - 1) // 2 + 1) == center:
                end = center
                center = (center + start) // 2 if (center + start) % 2 == 0 else (center + start) // 2 + 1
            else:
                start = center
                center = (center + end - 1) // 2 if (center + end - 1) % 2 == 0 else (center + end - 1) // 2 + 1
            garant_time.append(b)
    else:
        if ((center + start) // 2 if (center + start) % 2 == 0 else (center + start) // 2 + 1) == center:
            start = center
            center = (center + end - 1) // 2 if (center + end - 1) % 2 == 0 else (center + end - 1) // 2 + 1
        else:
            end = center
            center = (center + start) // 2 if (center + start) % 2 == 0 else (center + start) // 2 + 1
        garant_time.append(b)
print(steps)
print(sum(garant_time[0:len(garant_time) - 1]))


"""Caper's HPC Wrapper based on job engine's CLI (shell command).
e.g. sbatch, squeue, qsub, qstat
"""
import logging
import os
import subprocess
from abc import ABC, abstractmethod
from pathlib import Path
from tempfile import NamedTemporaryFile

logger = logging.getLogger(__name__)

CAPER_LEADER_JOB_NAME_PREFIX = 'CAPER_'
ILLEGAL_CHARS_IN_JOB_NAME = [',', ' ', '\t']

def get_user_from_os_environ():
    return os.environ['USER']

def make_bash_script_contents(contents):
    return f'#!/bin/bash\n{contents}\n'

def make_caper_leader_job_name(job_name):
    """Check if job name contains Comma, TAB or whitespace.
    They are not allowed since they can be used as separators.
    """
    for illegal_char in ILLEGAL_CHARS_IN_JOB_NAME:
        if illegal_char in job_name:
            raise ValueError(
                'Illegal character {chr} in job name {job}'.format(
                    chr=illegal_char, job=job_name
                )
            )
    return CAPER_LEADER_JOB_NAME_PREFIX + job_name

class HpcWrapper(ABC):
    def __init__(
        self,
        leader_job_resource_param=[],
    ):
        """Base class for HPC job engine wrapper."""
        self._leader_job_resource_param = leader_job_resource_param

    def submit(self, job_name, caper_run_command):
        """Submits a caper leader job to HPC (e.g. sbatch, qsub).
        Such leader job will be prefixed with CAPER_LEADER_JOB_NAME_PREFIX.

        Returns output STDOUT from submission command.
        """
        home_dir = f'{str(Path.home())}{os.sep}'
        with NamedTemporaryFile(prefix=home_dir, suffix='.sh') as shell_script:
            contents = make_bash_script_contents(' '.join(caper_run_command))
            shell_script.write(contents.encode())
            shell_script.flush()

            return self._submit(job_name, shell_script.name)

    def list(self):
        """Filters out non-caper jobs from the job list keeping the first line (header).
        And then returns output STDOUT.
        """
        result = []
        lines = self._list().split('\n')

        # keep header
        result.append(lines[0])

        # filter out non-caper lines
        logger.info('Filtering out non-caper leader jobs...')
        for line in lines[1:]:
            if CAPER_LEADER_JOB_NAME_PREFIX in line:
                result.append(line)

        return '\n'.join(result)

    def abort(self, job_ids):
        """Returns output STDOUT from job engine's abort command (e.g. scancel, qdel)."""
        return self._abort(job_ids)

    @abstractmethod
    def _submit(self, job_name, shell_script):
        pass

    def _list(self):
        pass

    @abstractmethod
    def _abort(self, job_ids):
        """Sends SIGINT (or SIGTERM) to Caper for a graceful shutdown."""
        pass

    def _run_command(self, command):
        """Runs a shell command line and returns STDOUT."""
        logger.info(f'Running shell command: {" ".join(command)}')
        return (
            subprocess.run(
                command,
                stdout=subprocess.PIPE,
                env=os.environ,
            )
            .stdout.decode()
            .strip()
        )

class SlurmWrapper(HpcWrapper):
    DEFAULT_LEADER_JOB_RESOURCE_PARAM = ['-t', '48:00:00', '--mem', '4G']

    def __init__(
        self,
        leader_job_resource_param=DEFAULT_LEADER_JOB_RESOURCE_PARAM,
        slurm_partition=None,
        slurm_account=None,
    ):
        super().__init__(
            leader_job_resource_param=leader_job_resource_param,
        )
        slurm_partition_param = ['-p', slurm_partition] if slurm_partition else []
        slurm_account_param = ['-A', slurm_account] if slurm_account else []
        self._slurm_extra_param = slurm_partition_param + slurm_account_param

    def _submit(self, job_name, shell_script):
        command = (
            ['sbatch']
            + self._leader_job_resource_param
            + self._slurm_extra_param
            + [
                '--export=ALL',
                '-J',
                make_caper_leader_job_name(job_name),
                shell_script,
            ]
        )
        return self._run_command(command)

    def _list(self):
        return self._run_command(
            [
                'squeue',
                '-u',
                get_user_from_os_environ(),
                '--Format=JobID,Name,State,SubmitTime',
            ]
        )

    def _abort(self, job_ids):
        """Notes: --full is necessary to correctly send SIGINT to the leader job (Cromwell process).
        Sending SIGTERM may result in an immediate shutdown of the leaderjob on some clusters.
        SIGINT is much better to trigger a graceful shutdown.
        """
        return self._run_command(['scancel', '--full', '--signal=SIGINT'] + job_ids)

class SgeWrapper(HpcWrapper):
    DEFAULT_LEADER_JOB_RESOURCE_PARAM = ['-l', 'h_rt=48:00:00,h_vmem=4G']

    def __init__(
        self,
        leader_job_resource_param=DEFAULT_LEADER_JOB_RESOURCE_PARAM,
        sge_queue=None,
    ):
        super().__init__(
            leader_job_resource_param=leader_job_resource_param,
        )
        self._sge_queue_param = ['-q', sge_queue] if sge_queue else []

    def _submit(self, job_name, shell_script):
        command = (
            ['qsub']
            + self._leader_job_resource_param
            + self._sge_queue_param
            + ['-V', '-terse', '-N', make_caper_leader_job_name(job_name), shell_script]
        )
        return self._run_command(command)

    def _list(self):
        return self._run_command(['qstat', '-u', get_user_from_os_environ()])

    def _abort(self, job_ids):
        return self._run_command(['qdel'] + job_ids)

class PbsWrapper(HpcWrapper):
    DEFAULT_LEADER_JOB_RESOURCE_PARAM = ['-l', 'walltime=48:00:00,mem=4gb']

    def __init__(
        self,
        leader_job_resource_param=DEFAULT_LEADER_JOB_RESOURCE_PARAM,
        pbs_queue=None,
    ):
        super().__init__(
            leader_job_resource_param=leader_job_resource_param,
        )
        self._pbs_queue_param = ['-q', pbs_queue] if pbs_queue else []

    def _submit(self, job_name, shell_script):
        command = (
            ['qsub']
            + self._leader_job_resource_param
            + self._pbs_queue_param
            + ['-V', '-N', make_caper_leader_job_name(job_name), shell_script]
        )
        return self._run_command(command)

    def _list(self):
        return self._run_command(['qstat', '-u', get_user_from_os_environ()])

    def _abort(self, job_ids):
        return self._run_command(['qdel', '-W', '30'] + job_ids)

class LsfWrapper(HpcWrapper):
    DEFAULT_LEADER_JOB_RESOURCE_PARAM = ['-W', '2880', '-M', '4g']

    def __init__(
        self,
        leader_job_resource_param=DEFAULT_LEADER_JOB_RESOURCE_PARAM,
        lsf_queue=None,
    ):
        super().__init__(
            leader_job_resource_param=leader_job_resource_param,
        )
        self._lsf_queue_param = ['-q', lsf_queue] if lsf_queue else []

    def _submit(self, job_name, shell_script):
        command = (
            ['bsub']
            + self._leader_job_resource_param
            + self._lsf_queue_param
            + ['-env', 'all', '-J', make_caper_leader_job_name(job_name), shell_script]
        )
        return self._run_command(command)

    def _list(self):
        return self._run_command(['bjobs', '-u', get_user_from_os_environ()])

    def _abort(self, job_ids):
        return self._run_command(['bkill'] + job_ids)


from time import gmtime, strftime
from django.shortcuts import render, HttpResponse, get_object_or_404, redirect, render_to_response
from django.urls import reverse_lazy
from django.contrib import messages

# Importación de modelos
from .models import Person
from .models import Solicitud
from .models import Metas
from .models import Tarifas
from .models import Feriados

# Importación de formularios
from .forms import PersonForm
from .forms import SolicitudForm
from .forms import MetaForm
from .forms import TarifaForm
from .forms import FeriadoForm

# Importación de vistas basadas en clases
from django.views.generic.list import ListView
from django.views.generic.edit import UpdateView
from django.views.generic import CreateView

from django.core.paginator import Paginator
from datetime import datetime, timedelta, date
import calendar
import json
import xlwt

# ----------------------------------------------------------------------------
#   Vista basada en clase para listar las personas o vendedores registrados
# -----------------------------------------------------------------------------

class personIndex(ListView):
    model = Person
    template_name = 'sellingsapp/person_list.html'
    context_object_name = 'person_list'
    paginate_by = 7
    ordering = ['id']

# ------------------------------------------------------------
#       Método o función para crear una persona o vendedor
# -------------------------------------------------------------

def personAdd(request):
    if request.method == 'POST':
        form = PersonForm(request.POST)
        if form.is_valid():
            person = form.save(commit=False)
            person.first_name = request.POST['first_name']
            person.last_name = request.POST['last_name']
            person.celphone = request.POST['celphone']
            person.address = request.POST['address']
            person.email = request.POST['email']
            person.canal_de_venta = request.POST['canal_de_venta']
            person.save()
            messages.success(request, 'El registro ha sido ingresado.')
            form = PersonForm()
    else:
        form = PersonForm()

    return render(request, 'sellingsapp/person_add.html', {'form': form})

# ------------------------------------------------------------
#       Vista basada en clase que utiliza la clase UpdateView
#       para editar una persona o vendendor
# -------------------------------------------------------------
class personDetail(UpdateView):
    model = Person
    fields = ['cc_id', 'first_name', 'last_name',
              'celphone', 'address', 'email', 'canal_de_venta']
    template_name = 'sellingsapp/person_detail.html'

    def form_valid(self, form):
        person = form.save(commit=False)
        person.save()
        messages.success(self.request, 'El registro ha sido actualizado.')
        return self.render_to_response(self.get_context_data(form=form))

# ---------------------------------------------------------
#            Método que elimina una persona o vendedor
# ----------------------------------------------------------

def personDelete(request, id_person):
    # busca el modelo idenficado por el id
    person_delete = get_object_or_404(Person, pk=id_person)
    person_delete.delete()
    custom_message = " | "+person_delete.first_name + \
        " " + person_delete.last_name + " | "
    messages.success(request, "El registro " + str(id_person) +
                     "-" + custom_message+" ha sido eliminado")

    return redirect('personList')

def handler404(request, exception, template_name="404.html"):
    response = render_to_response("404.html")
    response.status_code = 404
    return response

# --------------------------------------------------------------------------------
#       Utiliza la clase ListView para para crear una vista basada en clase
# --------------------------------------------------------------------------------
class solicitudIndex(ListView):
    model = Solicitud
    template_name = 'sellingsapp/solicitud_list.html'
    context_object_name = 'solicitud_list'
    paginate_by = 7
    ordering = ['product_name']

    def get_queryset(self):

        if self.request.user.is_anonymous == False:
            user_email = self.request.user.email
        else:
            result = []
            return result

        if self.request.user.get_username() == '@dm1n':
            result = Solicitud.objects.all()
            return result

        try:
            ObjPerson = Person.objects.get(email=user_email)

            if ObjPerson.id:
                findId = ObjPerson.id
                result = Solicitud.objects.filter(asesor_id=findId)

        except Person.DoesNotExist:
            result = []

        return result

# --------------------------------------------------------------------------------
#       Utiliza la clase ListView para para crear una vista basada en clase
# --------------------------------------------------------------------------------

def proyectionIndex(request):

    model = Solicitud
    template_name = 'sellingsapp/proyection_list.html'
    context_object_name = 'proyection_list'

    today = date.today()
    search_mes = ["enero", "febrero", "marzo", "abril", "mayo", "junio",
                  "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"]

    if request.user.is_anonymous == True:
        return redirect('sign_out')

    query_person = ''  # Se establece un query para admin o usuario común

    # if (request.user.get_username() == "@dm1n" or request.user.get_username() == "supervis@r"):
    search_cc_id = request.GET.get('search_cc_id')

    if search_cc_id != '':
        query_person = "Person.objects.get(cc_id=search_cc_id)"
    else:
        search_email = ""

    # Buscamos el email asociado al cc_id o al usuario de la sesión
    if query_person != '':
        try:
            ObjPerson = eval(query_person)
            search_email = ObjPerson.email
        except Person.DoesNotExist:
            search_email = ""

    # Consultamos la cantidad de ventas realizadas (ingresadas e instaladas)
    if search_email != '' and search_email != None:
        cant_solicitadas = proyectionReview(
            search_email, search_mes[today.month-1], today.day, 'solicitado')
        cant_instaladas = proyectionReview(
            search_email, search_mes[today.month-1], today.day, 'instalado')
    else:
        result = {}
        return render(request, template_name, result)

    # Obtenemos días hábiles,días transcurridos,y total de ventas
    dias_habiles = validDays(lastDayMonth())
    dias_transcurridos = validDays("")-1
    tot_ingresadas = cant_solicitadas+cant_instaladas

    # Calculamos la proyección de ventas ingresadas e instaladas
    if dias_transcurridos == 0:
        proyeccion_ingresadas = 0
        proyeccion_instaladas = 0
    else:
        proyeccion_ingresadas = (
            (cant_solicitadas+cant_instaladas)/dias_transcurridos)*dias_habiles
        proyeccion_instaladas = (
            cant_instaladas/dias_transcurridos)*dias_habiles

    # Calculamos el porcentaje de ventas ingresadas e instaladas con respecto a la meta
    if (request.GET.get('meta_instalada') != "" and request.GET.get('meta_ingresada') != "" and int(request.GET.get('meta_instalada')) > 0 and int(request.GET.get('meta_ingresada')) > 0):
        meta_instalada = int(request.GET.get('meta_instalada'))
        meta_ingresada = int(request.GET.get('meta_ingresada'))
        porc_meta_ingresadas = (proyeccion_ingresadas/meta_ingresada)*100
        porc_meta_instaladas = (proyeccion_instaladas/meta_instalada)*100
    else:
        meta_ingresada = 0
        meta_instalada = 0
        porc_meta_ingresadas = 0
        porc_meta_instaladas = 0

    # calcula el valor de la tarifa de comisión
    comision = getComision(cant_instaladas)

    if comision != []:
        tabla_comision = comision
        rango_comision = comision["nombre_rango"]
    else:
        valor_comision = 0
        rango_comision = 'no aplica'
        tabla_comision = 'no aplica'

    return render(request, template_name,
                  {"dias_habiles": dias_habiles,
                   "dias_transcurridos": dias_transcurridos,
                   "cant_instaladas": cant_instaladas,
                   "cant_solicitadas": tot_ingresadas,
                   "proyeccion_ingre": format(proyeccion_ingresadas, ".2f"),
                   "proyeccion_instal": format(proyeccion_instaladas, ".2f"),
                   "meta_ingresadas": meta_ingresada,
                   "meta_instaladas": meta_instalada,
                   "porc_meta_ingresadas": format(porc_meta_ingresadas, ".2f"),
                   "porc_meta_instaladas": format(porc_meta_instaladas, ".2f"),
                   "nombre_rango": rango_comision,
                   "tabla_comision": tabla_comision
                   })

#----------------------------------------------------------#
#   Obtiene la comisión a partir del rango de venta
# -----------------------------------------------------------
def getComision(cant_instaladas):

    array_range = {}

    try:
        sql_query = "limite_inf <= %s and limite_sup >=%s"
        result = Tarifas.objects.extra(where=[sql_query], params=[
                                       cant_instaladas, cant_instaladas])
    except Tarifas.DoesNotExist:
        result = []

    if result.exists():

        for record in result:
            array_range[record.porce_title] = format(
                (int(record.comision) * cant_instaladas), ",d")
            array_range["nombre_rango"] = record.nombre_rango
            array_range["escala"] = str(
                record.limite_inf)+"-"+str(record.limite_sup)

        return array_range

    return []
# ---------------------------------------------------------------------------
#       Calcula la proyección de cada asesor
# ---------------------------------------------------------------------------

def proyectionReview(user_email, mes, dia, status):
    tot_status = 0
    try:
        ObjPerson = Person.objects.get(email=user_email)
        asesor_id = ObjPerson.id
    except Person.DoesNotExist:
        return 0

    if user_email != '':
        sql_query = "status=%s and asesor_id=%s and mes=%s and dia != %s"
        result = Solicitud.objects.extra(where=[sql_query], params=[
                                         status, asesor_id, mes, dia])
        for record in result:
            tot_status += record.product_cant

    return tot_status

# ----------------------------------------------------------------------------
#       Función para obtener días hábiles del  mes transcurridos hasta hora
# ----------------------------------------------------------------------------
def validDays(finalday):
    # Día actual
    today = date.today()

    formato = '%d/%m/%Y'
    contador = 0

    fechadesde = "01/"+str(today.month)+"/"+str(today.year)

    if finalday != "":
        fechahasta = finalday
    else:
        fechahasta = str(today.day)+"/"+str(today.month)+"/"+str(today.year)

    fechadesde = datetime.strptime(fechadesde, formato)
    fechahasta = datetime.strptime(fechahasta, formato)

    while fechadesde <= fechahasta:
        if datetime.weekday(fechadesde) != 6:
            if isHoliday(fechadesde) == False:
                contador += 1
        fechadesde = fechadesde + timedelta(days=1)

    return contador

# -----------------------------------------------------------------------
#      Función que verifica si el día es feriado
# -----------------------------------------------------------------------

def isHoliday(dia):
    formato = '%Y-%m-%d'
    hollidays = '[{"feriado":"14/10/2019"}]'
    dias_feriados = json.loads(hollidays)

    if existFeriado(datetime.strftime(dia, formato)) == True:
        return True

    return False

# -----------------------------------------------------------------------
#       Función que obtiene el último día del mes
# -----------------------------------------------------------------------

def lastDayMonth():
    today = date.today()
    day = calendar.monthrange(int(strftime("%Y", gmtime())),
                              int(strftime("%m", gmtime())))[1]
    lastDate = str(day)+"/"+str(today.month)+"/"+str(today.year)
    return lastDate

# -----------------------------------------------------------------------
#   Función para verificar si ya existe la meta resgistrada
# -------------------------------------------------------------------------

def getMeta(mes_meta, anio_meta, canal_venta):

    try:
        sql_query = "mes=%s and anio=%s and canal_venta=%s"
        result = Metas.objects.extra(where=[sql_query], params=[
                                     mes_meta, anio_meta, canal_venta])
    except Metas.DoesNotExist:
        result = []

    return result

# -------------------------------------------------------------------------
#    Función para verificar si ya existe la TARIFA resgistrada
# -------------------------------------------------------------------------

def getTarifa(nombre_rango, porce_title):

    try:
        sql_query = "nombre_rango=%s and porce_title=%s"
        result = Tarifas.objects.extra(where=[sql_query], params=[
                                       nombre_rango, porce_title])
    except Tarifas.DoesNotExist:
        result = []

    return result

# -----------------------------------------------------------------------
#       Clase ListView que utiliza filter para búsqueda personalizada
# -----------------------------------------------------------------------

class searchSolicitud(ListView):
    model = Solicitud
    template_name = 'sellingsapp/solicitud_list.html'
    context_object_name = 'solicitud_list'
    paginate_by = 7
    ordering = ['product_name']

    def get_queryset(self):
        queryset = super(searchSolicitud, self).get_queryset()

        search_field = self.request.GET.get('search_field').strip()
        search_value = self.request.GET.get('search_text').strip()

        if self.request.user.is_anonymous == False:
            user_email = self.request.user.email
        else:
            result = []
            return result

        if self.request.user.get_username() == '@dm1n':
            result = Solicitud.objects.extra(
                where=[search_field+"= %s"], params=[search_value])
            return result

        try:
            ObjPerson = Person.objects.get(email=user_email)
            asesor_id = ObjPerson.id

        except Person.DoesNotExist:
            result = []

        if search_value != '':
            result = Solicitud.objects.extra(
                where=[search_field+"=%s and asesor_id=%s"], params=[search_value, asesor_id])
        else:
            result = Solicitud.objects.filter(asesor_id=asesor_id)

        return result

# -------------------------------------------------------------------------
#             Auto-complete del campo de búsqueda de campañas
#  ------------------------------------------------------------------------

def autoCompleteSearchSolicitud(request):

    if request.is_ajax():
        q = request.GET.get('term')
        field_search = request.GET.get('field')
        solicitudes = Solicitud.objects.extra(
            where=[field_search+" LIKE %s"], params=["%"+str(q)+"%"])
        results = []

        for pl in solicitudes:
            place_json = {}
            place_json = str(eval("pl."+field_search))
            results.append(place_json)

        data = json.dumps(results)
        mimetype = ''

    else:
        data = 'fail'
        mimetype = 'application/json'

    return HttpResponse(data, mimetype)

# -----------------------------------------------------------------------
#                Utiliza una función para la view
# -----------------------------------------------------------------------
def solicitudAdd(request):

    if request.user.is_anonymous == True:
        return redirect('sign_out')

    if request.method == 'POST':
        form = SolicitudForm(request.POST)
        solicitud = form.save(commit=False)
        solicitud.product_name = request.POST['product_name']
        solicitud.status = request.POST['status']
        solicitud.dia = request.POST['dia']
        solicitud.mes = request.POST['mes']
        solicitud.anio = request.POST['anio']
        solicitud.nomcliente = request.POST['nomcliente']
        solicitud.celcliente = request.POST['celcliente']
        solicitud.product_cant = request.POST['product_cant']
        solicitud.notes = request.POST['notes']
        solicitud.asesor = Person.objects.get(id=request.POST['asesor'])
        solicitud.save()
        messages.success(request, 'El registro ha sido ingresado.')
    else:
        form = SolicitudForm(request)

    return render(request, 'sellingsapp/solicitud_add.html', {'form': form})

# ---------------------------------------------------------------------------
#    Utiliza una vista basada en clase inyectando la clase Createview
# ---------------------------------------------------------------------------
# class campaignAdd(CreateView):
#     model = Campaign
#     fields = ['title', 'url', 'status', 'mes', 'anio', 'notes', 'num_campaign']
#     template_name = 'sellingsapp/campaign_add.html'

# ---------------------------------------------------------------------------
#    Utiliza la vista basada en clase inyectando la clase UpdateView
# ---------------------------------------------------------------------------
# class campaignDetail(UpdateView):
#     model = Campaign
#     fields = ['title', 'url', 'status', 'mes', 'anio', 'notes', 'num_campaign']
#     template_name = 'sellingsapp/campaign_detail.html'

#     def form_valid(self, form):
#         campaign = form.save(commit=False)
#         campaign.save()
#         messages.success(self.request, 'El registro ha sido actualizado.')
#         return self.render_to_response(self.get_context_data(form=form))

def solicitudDetail(request, pk):
    solicitud = get_object_or_404(Solicitud, pk=pk)

    if request.method == "POST":
        form = SolicitudForm(request.POST, instance=solicitud)
        solicitud = form.save(commit=False)
        solicitud.product_name = request.POST['product_name']
        solicitud.status = request.POST['status']
        solicitud.dia = request.POST['dia']
        solicitud.mes = request.POST['mes']
        solicitud.anio = request.POST['anio']
        solicitud.nomcliente = request.POST['nomcliente']
        solicitud.celcliente = request.POST['celcliente']
        solicitud.product_cant = request.POST['product_cant']
        solicitud.notes = request.POST['notes']
        solicitud.asesor = Person.objects.get(id=request.POST['asesor'])
        solicitud.save()
        messages.success(request, 'El registro ha sido actualizado.')

    form = SolicitudForm(request, instance=solicitud)

    return render(request, 'sellingsapp/solicitud_detail.html', {'form': form})

# -----------------------------------------------------------------------------
#               Función para eliminar una solicitud
# -----------------------------------------------------------------------------
def solicitudDelete(request, id_solicitud):
    # busca el modelo idenficado por el id
    solicitud_delete = get_object_or_404(Solicitud, pk=id_solicitud)
    solicitud_delete.delete()
    custom_message = " | "+solicitud_delete.product_name + " | "
    messages.success(request, "El registro " + str(id_solicitud) +
                     "-" + custom_message+" ha sido eliminado")

    return redirect('solicitudList')

# ---------------------------------------------------------------------------
#       Funcion para registrar una nueva Meta para diferente canal de venta
# ----------------------------------------------------------------------------
def metaAdd(request):
    if request.method == 'POST':
        form = MetaForm(request.POST)
        if form.is_valid():
            if (getMeta(request.POST['mes'], request.POST['anio'], request.POST['canal_venta']).exists() == False):
                meta = form.save(commit=False)

                if (meta.meta_ingresada != 0 and meta.meta_instalada != 0):
                    meta.meta_ingresada = request.POST['meta_ingresada']
                    meta.meta_instalada = request.POST['meta_instalada']
                    meta.mes = request.POST['mes']
                    meta.anio = request.POST['anio']
                    meta.canal_venta = request.POST['canal_venta']
                    meta.save()
                    messages.success(
                        request, 'El registro ha sido ingresado!.')
                    form = MetaForm()
                else:
                    messages.error(
                        request, 'Las metas no pueden tener un valor  = 0 (cero)!.')
            else:
                messages.error(
                    request, 'Ya existe una meta para el mes indicado!.')

    else:
        form = MetaForm()

    return render(request, 'sellingsapp/metas_add.html', {'form': form})

# ---------------------------------------------------------
#   Vista basada en clase para listar las metas de venta
# ------------------------------------------------------------

class metaIndex(ListView):
    model = Metas
    template_name = 'sellingsapp/metas_list.html'
    context_object_name = 'metas_list'
    paginate_by = 7
    ordering = ['id']

# -----------------------------------------------------------
#   Vista basada en clase para Editar las metas de venta
# ------------------------------------------------------------
class metaDetail(UpdateView):
    model = Metas
    fields = ['id', 'meta_ingresada', 'meta_instalada',
              'mes', 'anio', 'canal_venta']
    template_name = 'sellingsapp/metas_detail.html'

    def form_valid(self, form):
        meta = form.save(commit=False)
        objTarifa = getMeta(meta.mes, meta.anio, meta.canal_venta)

        if (meta.meta_ingresada != 0 and meta.meta_instalada != 0):

            if (objTarifa.exists() == True):
                if (objTarifa[0].id == meta.id):
                    meta.save()
                    messages.success(
                        self.request, 'El registro ha sido actualizado!.')
                else:
                    messages.error(
                        self.request, 'Ya existe una meta para el mes indicado!.')
            else:
                meta.save()
                messages.success(
                    self.request, 'El registro ha sido actualizado!.')
        else:
            messages.error(
                self.request, 'Las metas no pueden tener un valor  = 0 (cero)!.')

        return self.render_to_response(self.get_context_data(form=form))

# ------------------------------------------------------------
#       Función para eliminar las metas registradas
# ------------------------------------------------------------
def metaDelete(request, id_meta):
    # busca el modelo idenficado por el id
    meta_delete = get_object_or_404(Metas, pk=id_meta)
    meta_delete.delete()
    custom_message = " | "+meta_delete.mes + \
        " " + str(meta_delete.anio) + " | "
    messages.success(request, "El registro " + str(id_meta) +
                     "-" + custom_message+" ha sido eliminado")

    return redirect('metaList')

# ---------------------------------------------------------------------------
#       Funcion para registrar una nueva Meta para diferente canal de venta
# ----------------------------------------------------------------------------

def tarifaAdd(request):
    if request.method == 'POST':
        form = TarifaForm(request.POST)
        if form.is_valid():
            if (getTarifa(request.POST['nombre_rango'], request.POST['porce_title']).exists() == False):
                tarifa = form.save(commit=False)

                if (tarifa.limite_inf != 0 and tarifa.limite_sup != 0):

                    if(tarifa.limite_inf < tarifa.limite_sup):
                        tarifa.limite_inf = request.POST['limite_inf']
                        tarifa.limite_sup = request.POST['limite_sup']
                        tarifa.nombre_rango = request.POST['nombre_rango']
                        tarifa.porce_title = request.POST['porce_title']
                        tarifa.comision = request.POST['comision']
                        tarifa.save()
                        messages.success(
                            request, 'El registro ha sido ingresado!.')
                        form = TarifaForm()
                    else:
                        messages.error(
                            request, 'El límite inferior del rango debe ser menor al límite superior!')

                else:
                    messages.error(
                        request, 'Los  límites de las tarifas no pueden tener un valor  = 0 (cero)!.')
            else:
                messages.error(
                    request, 'Ya existe una tarifa registrada para el rango límite indicado!.')

    else:
        form = TarifaForm()

    return render(request, 'sellingsapp/tarifas_add.html', {'form': form})

# ----------------------------------------------------------------------
#   Vista basada en clase para listar las TARIFAS de comision de ventas
# ----------------------------------------------------------------------
class tarifaIndex(ListView):
    model = Tarifas
    template_name = 'sellingsapp/tarifas_list.html'
    context_object_name = 'tarifas_list'
    paginate_by = 7
    ordering = ['id']

# -----------------------------------------------------------
#   Vista basada en clase para Editar las metas de venta
# ------------------------------------------------------------

class tarifaDetail(UpdateView):
    model = Tarifas
    fields = ['id', 'limite_inf', 'limite_sup',
              'nombre_rango', 'porce_title', 'comision']
    template_name = 'sellingsapp/tarifas_detail.html'

    def form_valid(self, form):
        isValid = True
        tarifa = form.save(commit=False)
        objTarifa = getTarifa(tarifa.nombre_rango, tarifa.porce_title)

        if (tarifa.limite_inf == 0 and tarifa.limite_sup == 0):
            isValid = False
            messages.error(
                self.request, 'Los  límites de las tarifas no pueden tener un valor  = 0 (cero)!.')

        if (objTarifa.exists() == True):
            if (objTarifa[0].id != tarifa.id):
                isValid = False
                messages.error(
                    self.request, 'Ya existe una tarifa registrada para el rango límite indicado!.')

        if(tarifa.limite_inf > tarifa.limite_sup):
            isValid = False
            messages.error(
                self.request, 'El límite inferior del rango debe ser menor al límite superior!')

        if isValid == True:
            tarifa.save()
            messages.success(self.request, 'El registro ha sido actualizado!.')

        return self.render_to_response(self.get_context_data(form=form))

# ------------------------------------------------------------
#       Función para Eliminar las tarifas registradas
# ------------------------------------------------------------
def tarifaDelete(request, id_tarifa):
    # busca el modelo idenficado por el id
    tarifa_delete = get_object_or_404(Tarifas, pk=id_tarifa)
    tarifa_delete.delete()
    custom_message = " | rango "+tarifa_delete.nombre_rango + \
        " Comisión " + str(tarifa_delete.comision) + " | "
    messages.success(request, "El registro " + str(id_tarifa) +
                     "-" + custom_message+" ha sido eliminado")

    return redirect('tarifaList')

# ---------------------------------------------------------------
#                Función para adicionar días Off
# --------------------------------------------------------------
def feriadoAdd(request):
    if request.method == 'POST':
        form = FeriadoForm(request.POST)

        if form.is_valid():
            if existFeriado(request.POST['fecha'])==False:
                feriado = form.save(commit=False)
                feriado.fecha = request.POST['fecha']
                feriado.notes = request.POST['notes']
                feriado.save()
                messages.success(request, 'El registro ha sido ingresado!.')
                form = FeriadoForm()
            else:
                messages.error(request, 'La fecha indicada ya existe')

    else:
        form = FeriadoForm()

    return render(request, 'sellingsapp/feriados_add.html', {'form': form})

# -------------------------------------------------------------------------------------
#     Función para verificar si una fecha ya se encuentra ingresada como feriado
# -------------------------------------------------------------------------------------
def existFeriado(fecha,pk='none'):
    try:
       if pk=='none':
            sql_query = "fecha=%s"
            result = Feriados.objects.extra(where=[sql_query], params=[fecha])
       else:
            sql_query = "fecha=%s and id!=%s"
            result = Feriados.objects.extra(where=[sql_query], params=[fecha,pk])

    except Feriados.DoesNotExist:
        result = []

    if (result.exists() == True):
        return True
    else:
        return False

# ------------------------------------------------------------------
#   Vista basada en clase para Editar los días feriados o días Off
# ------------------------------------------------------------------

class feriadoDetail(UpdateView):
    model = Feriados
    fields = ['fecha','notes']
    template_name = 'sellingsapp/feriados_detail.html'

    def form_valid(self, form):
        feriado = form.save(commit=False)

        if (existFeriado(self.request.POST['fecha'],feriado.id)):
            messages.error(self.request, 'La fecha indicada ya se encuentra registrada!')
        else:
            feriado.save()
            messages.success(self.request, 'El registro ha sido actualizado!.')

        return self.render_to_response(self.get_context_data(form=form))

# ----------------------------------------------------------------------
#   Vista basada en clase para listar los días feriados o días OFF
# ----------------------------------------------------------------------
class feriadoIndex(ListView):
    model = Feriados
    template_name = 'sellingsapp/feriados_list.html'
    context_object_name = 'feriados_list'
    paginate_by = 7
    ordering = ['id']

# ---------------------------------------------------------------------
#    Función para Eliminar los días feriados o dias Off registrados
# ---------------------------------------------------------------------
def feriadoDelete(request, id_feriado):
    # busca el modelo idenficado por el id
    feriado_delete = get_object_or_404(Feriados, pk=id_feriado)
    feriado_delete.delete()
    custom_message = " | fecha "+str(feriado_delete.fecha)
    messages.success(request, "El registro " + str(id_feriado) +
                     "-" + custom_message+" ha sido eliminado")

    return redirect('feriadoList')

#----------------------------------------------------------------------
#     Función para exportar las ventas registradas a EXCEL
# ---------------------------------------------------------------------
def export_solicitudes_xls(request):
    response = HttpResponse(content_type='application/ms-excel')
    response['Content-Disposition'] = 'attachment; filename="solicitudes.xls"'

    wb = xlwt.Workbook(encoding='utf-8')
    ws = wb.add_sheet('Solicitudes')

    # Sheet header, first row
    row_num = 0

    font_style = xlwt.XFStyle()
    font_style.font.bold = True

    columns = ['Producto', 'Estado', 'dia', 'mes','anio','notes','Rel.productos','Asesor','Cliente','cel. cliente' ]

    for col_num in range(len(columns)):
        ws.write(row_num, col_num, columns[col_num], font_style)

    # Sheet body, remaining rows
    font_style = xlwt.XFStyle()

    sql_query = "mes=%s and anio=%s"
    rows = Solicitud.objects.extra(where=[sql_query], params=[
        request.POST['search_mes_excel'], request.POST['anio']])

    # rows = Solicitud.objects.all().select_related('asesor')

    for row in rows:
        row_num = row_num + 1
        ws.write(row_num, 0, row.product_name, font_style)
        ws.write(row_num, 1, row.status, font_style)
        ws.write(row_num, 2, row.dia, font_style)
        ws.write(row_num, 3, row.mes, font_style)
        ws.write(row_num, 4, row.anio, font_style)
        ws.write(row_num, 5, row.notes, font_style)
        ws.write(row_num, 6, row.product_cant, font_style)
        ws.write(row_num, 7, row.asesor.cc_id, font_style)
        ws.write(row_num, 8, row.nomcliente, font_style)
        ws.write(row_num, 9, row.celcliente, font_style)

    wb.save(response)
    return response