import hashlib
import hmac
import json
import time
import logging
import os.path
from datetime import datetime

import unittest
from unittest.mock import patch, MagicMock

from django.conf import settings
from django.test import TestCase as DjangoTestCase
from django.test.client import RequestFactory
from django.urls import reverse
from django.contrib.auth.models import User
from django.contrib.staticfiles import finders
from django.utils import timezone

from crossbot.slack.commands import parse_date
from crossbot.slack.api import SLACK_URL
from crossbot.views import slash_command
from crossbot.models import (
    CBUser,
    MiniCrosswordTime,
    CrosswordTime,
    EasySudokuTime,
    QueryShorthand,
    Item,
    ItemOwnershipRecord,
    Prediction,
)
from crossbot.cron import ReleaseAnnouncement, MorningAnnouncement
from crossbot.settings import CROSSBUCKS_PER_SOLVE

class TestCase(DjangoTestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)
        super().setUp()

    def tearDown(self):
        logging.disable(logging.NOTSET)
        super().tearDown()

class MockResponse:
    def __init__(self, text, status_code):
        self.text = text
        self.status_code = status_code

    def json(self):
        if type(self.text) is dict:
            return self.text
        else:
            return json.loads(self.text)

class MockedRequestTestCase(TestCase):
    def setUp(self):
        super().setUp()
        self.router = {}
        self._patcher_get = patch(
            'requests.get', side_effect=self.mocked_requests_get
        )
        self._patcher_post = patch(
            'requests.post', side_effect=self.mocked_requests_post
        )
        self._patcher_get.start()
        self._patcher_post.start()

    def tearDown(self):
        super().tearDown()
        self.router = {}
        self._patcher_get.stop()
        self._patcher_post.stop()

    def mocked_requests_get(self, url, **kwargs):
        return self.mocked_request('GET', url, **kwargs)

    def mocked_requests_post(self, url, **kwargs):
        return self.mocked_request('POST', url, **kwargs)

    def check_headers(self, method, url, headers):
        pass

    def mocked_request(self, method, url, *, headers, params=None, data=None):
        self.check_headers(method, url, headers)
        func = self.router.get(url)
        if func:
            return func(method, url, headers, params, data)
        else:
            MockResponse(None, 404)

class SlackTestCase(MockedRequestTestCase):
    RESPONSE_URL = SLACK_URL + 'response_url/'

    def setUp(self):
        super().setUp()
        self.factory = RequestFactory()

        self.router[SLACK_URL + 'chat.postMessage'] = self._slack_chat_post
        self.router[SLACK_URL + 'reactions.add'] = self._slack_reaction_add
        self.router[SLACK_URL + 'users.list'] = self._slack_users_list
        self.router[SLACK_URL + 'users.info'] = self._slack_users_info
        self.router[self.RESPONSE_URL] = self._slack_response_post

        self.users = {
            'UALICE': {
                'id': 'UALICE',
                'name': 'alice',
                'profile': {
                    'real_name': 'Alice',
                    'image_48': 'http://example.com/alice.png'
                }
            },
            'UBOB': {
                'id': 'UBOB',
                'name': 'bob',
                'profile': {
                    'real_name': 'Bob',
                    'image_48': 'http://example.com/bob.png'
                }
            }
        }

        self.slack_timestamp = 0
        self.messages = []

        self.slack_sk = b'8f742231b10e8888abcd99yyyzzz85a5'

        self.patch(
            'django.conf.settings.SLACK_SECRET_SIGNING_KEY', self.slack_sk
        )
        self.patch(
            'django.conf.settings.SLACK_OAUTH_ACCESS_TOKEN', 'oauth_token'
        )
        self.patch(
            'django.conf.settings.SLACK_OAUTH_BOT_ACCESS_TOKEN',
            'bot_oauth_token'
        )
        self.patch(
            'django.conf.settings.CROSSBOT_MAIN_CHANNEL', 'main_channel'
        )

    def patch(self, *args, **kwargs):
        patcher = patch(*args, **kwargs)
        patcher.start()
        self.addCleanup(patcher.stop)
        return patcher

    def check_headers(self, method, url, headers):
        if url.startswith(SLACK_URL):
            self.assertEqual(
                headers['Authorization'], 'Bearer bot_oauth_token'
            )

    def _slack_reaction_add(self, method, url, headers, params, data):
        return MockResponse(json.dumps({'ok': True}), 200)

    def _slack_chat_post(self, method, url, headers, params, data):
        self.assertEqual(method, 'POST')
        self.messages.append(data)
        ts = self.slack_timestamp
        self.slack_timestamp += 1
        return MockResponse(json.dumps({'ok': True, 'ts': ts}), 200)

    def _slack_response_post(self, method, url, headers, params, data):
        # TODO: handle this better?
        self.assertEqual(method, 'POST')
        self.messages.append(data)
        ts = self.slack_timestamp
        self.slack_timestamp += 1
        return MockResponse('ok', 200)

    def _slack_users_list(self, method, url, headers, params, data):
        self.assertEqual(method, 'GET')
        return MockResponse({'ok': True, 'members': self.users.values()}, 200)

    def _slack_users_info(self, method, url, headers, params, data):
        self.assertEqual(method, 'GET')
        if params['user'] in self.users:
            return MockResponse({
                'ok': True,
                'user': self.users[params['user']]
            }, 200)
        return MockResponse({'ok': False, 'error': 'user_not_found'}, 400)

    def post_valid_request(self, post_data):
        request = self.factory.post(reverse('slash_command'), post_data)
        ts = str(time.time())
        request.META['HTTP_X_SLACK_REQUEST_TIMESTAMP'] = ts
        request.META['HTTP_X_SLACK_SIGNATURE'] = 'v0=' + hmac.new(
            key=self.slack_sk,
            msg=b'v0:' + bytes(ts, 'utf8') + b':' + request.body,
            digestmod=hashlib.sha256
        ).hexdigest()
        return slash_command(request)

    def slack_post(
            self,
            text,
            who='alice',
            expected_status_code=200,
            expected_response_type='ephemeral'
    ):
        response = self.post_valid_request({
            'type': 'event_callback',
            'text': text,
            'response_url': self.RESPONSE_URL,
            'trigger_id': 'foobar',
            'channel_id': 'main_channel',
            'user_id': 'U' + who.upper(),
            'user_name': '@' + who,
        })

        self.assertEqual(response.status_code, expected_status_code)

        if response.content:
            body = json.loads(response.content)
            self.assertEqual(body['response_type'], expected_response_type)
            return body

        self.assertEqual(expected_response_type, 'ephemeral')
        return None

# TODO: this shouldn't be a subclass of SlackTestCase?
# TODO: make sure these tests check that save() is properly called
class ModelTests(SlackTestCase):
    def test_from_slackid(self):
        alice = CBUser.from_slackid('UALICE', 'alice')
        self.assertIsInstance(alice, CBUser)
        self.assertEqual(alice.slackname, 'alice')
        self.assertEqual(alice.slack_fullname, 'Alice')
        self.assertEqual(alice.image_url, 'http://example.com/alice.png')
        self.assertEqual(alice, CBUser.from_slackid('UALICE', 'bob'))
        alice = CBUser.from_slackid('UALICE')
        self.assertEqual(CBUser.from_slackid('UALICE').slackname, 'bob')

        fake_user = CBUser.from_slackid('UFAKE')
        self.assertIsNone(fake_user)
        with self.assertRaises(ValueError) as exception_context:
            fake_user = CBUser.from_slackid('UFAKE', 'fake_user')

        self.assertIn('user_not_found', str(exception_context.exception))

    def test_add_time(self):
        alice = CBUser.from_slackid('UALICE', 'alice')

        a, t = alice.add_mini_crossword_time(10, parse_date(None))
        self.assertTrue(a)
        self.assertEqual(t.user, alice)
        self.assertEqual(t.seconds, 10)
        self.assertEqual(t.date, parse_date(None))

        self.assertEqual(alice.get_mini_crossword_time(parse_date(None)), t)

    def test_add_remove_time(self):
        alice = CBUser.from_slackid('UALICE', 'alice')

        alice.add_mini_crossword_time(10, parse_date(None))

        alice.remove_mini_crossword_time(parse_date(None))
        self.assertEqual(alice.get_mini_crossword_time(parse_date(None)), None)

        a, t = alice.add_mini_crossword_time(10, parse_date(None))
        self.assertTrue(a)
        self.assertEqual(t.user, alice)
        self.assertEqual(t.seconds, 10)
        self.assertEqual(t.date, parse_date(None))
        self.assertNotEqual(
            alice.get_mini_crossword_time(parse_date(None)), None
        )

    def test_add_fail(self):
        alice = CBUser.from_slackid('UALICE', 'alice')
        a, t = alice.add_mini_crossword_time(-1, parse_date(None))
        self.assertTrue(a)
        self.assertEqual(t.seconds, -1)
        self.assertEqual(t.date, parse_date(None))
        self.assertTrue(t.is_fail())

    def test_streak(self):
        alice = CBUser.from_slackid('UALICE', 'alice')

        # set up a broken 10 streak
        _, t1 = alice.add_mini_crossword_time(18, parse_date('2018-01-01'))
        _, t2 = alice.add_mini_crossword_time(12, parse_date('2018-01-02'))
        _, t3 = alice.add_mini_crossword_time(12, parse_date('2018-01-03'))
        _, t4 = alice.add_mini_crossword_time(15, parse_date('2018-01-04'))
        # t5 is missing
        _, t6 = alice.add_mini_crossword_time(15, parse_date('2018-01-06'))
        _, t7 = alice.add_mini_crossword_time(15, parse_date('2018-01-07'))
        _, t8 = alice.add_mini_crossword_time(15, parse_date('2018-01-08'))
        _, t9 = alice.add_mini_crossword_time(15, parse_date('2018-01-09'))
        _, t0 = alice.add_mini_crossword_time(18, parse_date('2018-01-10'))

        # make sure the streak is broken
        streaks = MiniCrosswordTime.participation_streaks(alice)
        self.assertListEqual(streaks, [[t1, t2, t3, t4], [t6, t7, t8, t9, t0]])

        # fix the broken streak
        _, t5 = alice.add_mini_crossword_time(15, parse_date('2018-01-05'))

        streaks = MiniCrosswordTime.participation_streaks(alice)
        self.assertListEqual(
            streaks, [[t1, t2, t3, t4, t5, t6, t7, t8, t9, t0]]
        )

        # now break it again with a deleted time (t2)
        alice.remove_mini_crossword_time(parse_date('2018-01-02'))
        streaks = MiniCrosswordTime.participation_streaks(alice)
        self.assertListEqual(streaks, [[t1], [t3, t4, t5, t6, t7, t8, t9, t0]])

    def test_crossbucks_add_remove(self):
        # Checks that removing a time actually removes crossbucks
        alice = CBUser.from_slackid('UALICE', 'alice')
        alice.add_mini_crossword_time(10, parse_date(None))
        self.assertEqual(alice.crossbucks, CROSSBUCKS_PER_SOLVE)
        alice.add_mini_crossword_time(10, parse_date(None))
        self.assertEqual(alice.crossbucks, CROSSBUCKS_PER_SOLVE)
        alice.remove_mini_crossword_time(parse_date(None))
        self.assertEqual(alice.crossbucks, 0)
        alice.remove_mini_crossword_time(parse_date(None))
        self.assertEqual(alice.crossbucks, 0)
        alice.add_mini_crossword_time(10, parse_date(None))
        self.assertEqual(alice.crossbucks, CROSSBUCKS_PER_SOLVE)

    def test_wins(self):
        alice = CBUser.from_slackid('UALICE', 'alice')
        bob = CBUser.from_slackid('UBOB', 'bob')

        d = {x: parse_date('2018-01-0' + str(x)) for x in range(1, 6)}

        # make sure winners is empty right now
        self.assertEqual([], MiniCrosswordTime.winners(d[1]))

        # alice wins
        _, a1 = alice.add_mini_crossword_time(10, d[1])
        _, b1 = bob.add_mini_crossword_time(11, d[1])

        # they tie
        _, a2 = alice.add_mini_crossword_time(15, d[2])
        _, b2 = bob.add_mini_crossword_time(15, d[2])

        # bob wins
        _, a3 = alice.add_mini_crossword_time(21, d[3])
        _, b3 = bob.add_mini_crossword_time(20, d[3])

        # alice wins
        _, a4 = alice.add_mini_crossword_time(18, d[4])
        _, b4 = bob.add_mini_crossword_time(19, d[4])

        # check the winners
        self.assertEqual([a1], MiniCrosswordTime.winners(d[1]))
        self.assertEqual([a2, b2], MiniCrosswordTime.winners(d[2]))
        self.assertEqual([b3], MiniCrosswordTime.winners(d[3]))
        self.assertEqual([a4], MiniCrosswordTime.winners(d[4]))

        # check the winning times
        winning_times = MiniCrosswordTime.winning_times()
        expected = {d[1]: 10, d[2]: 15, d[3]: 20, d[4]: 18}
        self.assertEqual(winning_times, expected)

        # check the actual wins
        a_wins = MiniCrosswordTime.wins(alice)
        self.assertEqual(a_wins, [a1, a2, a4])
        b_wins = MiniCrosswordTime.wins(bob)
        self.assertEqual(b_wins, [b2, b3])

        # check the streaks
        a_win_streaks = MiniCrosswordTime.win_streaks(alice)
        self.assertEqual(a_win_streaks, [[a1, a2], [a4]])
        b_win_streaks = MiniCrosswordTime.win_streaks(bob)
        self.assertEqual(b_win_streaks, [[b2, b3]])

        # check the win streaks
        self.assertEqual({
            alice: [a1, a2],
            bob: [b2]
        }, MiniCrosswordTime.current_win_streaks(d[2]))
        self.assertEqual({
            bob: [b2, b3]
        }, MiniCrosswordTime.current_win_streaks(d[3]))
        self.assertEqual({}, MiniCrosswordTime.current_win_streaks(d[5]))

    def test_items(self):
        # Just add one item
        alice = CBUser.from_slackid('UALICE', 'alice')
        tophat = Item.from_key('tophat')
        self.assertEqual(tophat.key, 'tophat')
        self.assertEqual(alice.quantity_owned(tophat), 0)

        alice.add_item(tophat, amount=2)
        self.assertEqual(alice.quantity_owned(tophat), 2)
        self.assertEqual(alice.quantity_owned(Item.from_key('tophat')), 2)
        record = ItemOwnershipRecord.objects.get(
            owner=alice, item_key=tophat.key
        )
        self.assertEqual(record.quantity, 2)
        self.assertEqual(record.item, tophat)

        self.assertTrue(alice.remove_item(tophat, amount=2))
        self.assertEqual(alice.quantity_owned(tophat), 0)
        try:
            ItemOwnershipRecord.objects.get(owner=alice, item_key=tophat.key)
            self.fail()
        except ItemOwnershipRecord.DoesNotExist:
            pass

        # Check that the item image actually exists on disk
        url = tophat.image_url()
        self.assertEqual(settings.STATIC_URL, url[:len(settings.STATIC_URL)])
        url = url.replace(settings.STATIC_URL, '', 1)
        path = finders.find(url)
        self.assertTrue(os.path.isfile(path))

    def test_game_specific_items(self):
        alice = CBUser.from_slackid('UALICE', 'alice')
        title = Item.from_key('mini_completed3_title')
        self.assertEqual(title.key, 'mini_completed3_title')
        self.assertEqual(title.name, 'Mini Dabbler')

    def test_show_title(self):
        alice = CBUser.from_slackid('UALICE', 'alice')
        key = 'mini_completed3_title'
        title = Item.from_key(key)
        alice.title_key = key
        alice.save()

        self.slack_post('add :10 2018-01-1')
        self.assertIn(title.name, self.messages[-1])

        alice = CBUser.from_slackid('UALICE')
        alice.custom_title = 'foobar'
        alice.save()
        self.slack_post('add :10 2018-01-02')
        self.assertNotIn(title.name, self.messages[-1])
        self.assertIn('foobar', self.messages[-1])

    def test_equip_item(self):
        alice = CBUser.from_slackid('UALICE', 'alice')
        tophat = Item.from_key('tophat')

        # try to equip hat without owning one, should fail
        self.assertFalse(alice.equip(tophat))
        self.assertFalse(alice.is_equipped(tophat))
        self.assertIsNone(alice.hat)

        # give one and equip it
        alice.add_item(tophat)
        self.assertTrue(alice.equip(tophat))
        self.assertTrue(alice.is_equipped(tophat))
        self.assertEqual(alice.hat, tophat)

        # make sure the changes persisted
        alice = CBUser.from_slackid('UALICE', 'alice')
        self.assertTrue(alice.is_equipped(tophat))
        self.assertEqual(alice.hat, tophat)

        # shouldn't be able to remove it
        self.assertFalse(alice.remove_item(tophat))
        self.assertEqual(alice.quantity_owned(tophat), 1)

        # unequip it both ways
        alice.unequip(tophat)
        self.assertFalse(alice.is_equipped(tophat))
        self.assertIsNone(alice.hat)
        alice = CBUser.from_slackid('UALICE', 'alice')
        self.assertFalse(alice.is_equipped(tophat))
        self.assertIsNone(alice.hat)

        self.assertTrue(alice.equip(tophat))
        self.assertTrue(alice.is_equipped(tophat))
        self.assertEqual(alice.hat, tophat)
        alice.unequip_hat()
        self.assertFalse(alice.is_equipped(tophat))
        self.assertIsNone(alice.hat)
        alice = CBUser.from_slackid('UALICE', 'alice')
        self.assertFalse(alice.is_equipped(tophat))
        self.assertIsNone(alice.hat)

class SlackAuthTests(SlackTestCase):
    def test_bad_signature(self):
        response = self.client.post(
            reverse('slash_command'),
            HTTP_X_SLACK_REQUEST_TIMESTAMP=str(time.time()),
            HTTP_X_SLACK_SIGNATURE=b''
        )
        self.assertEqual(response.status_code, 400)

class SlackAppTests(SlackTestCase):
    def test_add(self):

        self.slack_post(text='add :10')
        self.slack_post(text='add :10 2017-01-01')

        messages = [json.loads(m) for m in self.messages]

        # each post gets two responses, one ephemeral and one in channel.
        self.assertEqual(len(messages), 4)

        # this one was for the current date, so the date shouldn't be mentioned.
        self.assertEqual(messages[0]['response_type'], 'ephemeral')
        self.assertEqual(messages[1]['channel'], 'main_channel')
        self.assertEqual(
            messages[1]['attachments'][0]['text'], '*Mini Added*: 0:10'
        )

        # this one was for a different date and should mention it
        self.assertEqual(messages[2]['response_type'], 'ephemeral')
        self.assertEqual(messages[3]['channel'], 'main_channel')
        self.assertEqual(
            messages[3]['attachments'][0]['text'],
            '*Mini Added*: 0:10 2017-01-01'
        )

        # make sure the database reflects this
        alice = CBUser.objects.get(slackid='UALICE')
        self.assertEqual(len(alice.minicrosswordtime_set.all()), 2)

    def test_double_add(self):

        # two adds on the same day should trigger an error
        self.slack_post(text='add :10 2018-08-01')
        response = self.slack_post(text='add :11 2018-08-01')

        # make sure the error message refers to the previous time
        self.assertIn(':10', response['text'])

        alice = CBUser.objects.get(slackid='UALICE')

        # make sure both times didn't get submitted
        times = alice.minicrosswordtime_set.all()
        self.assertEqual(len(times), 1)

        # make sure the original time was preserved
        self.assertEqual(times[0].seconds, 10)

    def test_times(self):
        self.slack_post('add :15 2018-08-01', who='alice')
        self.slack_post('add :40 2018-08-01', who='bob')

        # check date parsing here too
        response = self.slack_post(
            'times 2018-8-1', expected_response_type='in_channel'
        )

        lines = response['text'].split('\n')

        # line 0 is date, line 1 should be Alice
        self.assertIn('Alice', lines[1])
        self.assertIn(':fire:', lines[1])

    def test_help(self):
        response = self.slack_post(text='')
        self.assertIn('usage:', response['text'])

        response = self.slack_post(text='   ')
        self.assertIn('usage:', response['text'])

        response = self.slack_post(text='-h')
        self.assertIn('usage:', response['text'])

        response = self.slack_post(text='help')
        self.assertIn('usage:', response['text'])

        # this is an actual bad command, should respond with a different error
        response = self.slack_post(text='asdfasdiufpasdfa')
        self.assertIn('invalid choice:', response['text'])

    def test_add_delete(self):
        self.slack_post(text='add :23 2018-08-01')

        # Make sure the delete response actually references the time
        response = self.slack_post(text='delete 2018-08-01')
        self.assertIn(':23', response['text'])

        # Ensure the time doesn't show up in the times list
        response = self.slack_post(
            text='times 2018-08-01', expected_response_type='in_channel'
        )
        self.assertNotIn(':23', response['text'])

    @unittest.skipUnless(os.path.isfile('crossbot.db'), 'No existing db found')
    def test_sql(self):
        # should be able to handle an empty query
        response = self.slack_post(text='sql')
        self.assertIn('Please type', response['text'])
        response = self.slack_post(text='sql  ')
        self.assertIn('Please type', response['text'])

        query_text = 'select count(*) from mini_crossword_time'
        response = self.slack_post(text='sql ' + query_text)
        self.assertIn(query_text, response['text'])
        # just check that we can turn the response into an int
        # because we are just making raw sqlite, the django testing thing
        # doesn't clear out the model
        int(response['text'].split('\n')[-1])

        response = self.slack_post(
            text='sql select * from mini_crossword_time'
        )
        self.assertNotIn('reported', response['text'])

    @unittest.skipUnless(os.path.isfile('crossbot.db'), 'No existing db found')
    def test_query(self):
        # make sure the command tells you how to do it if there are no saved queries
        response = self.slack_post(text='query')
        self.assertIn('no saved', response['text'])
        self.assertIn('query --save', response['text'])

        # make sure we can save a new query
        query_text = 'select count(*) from mini_crossword_time'
        response = self.slack_post('query --save num_minis ' + query_text)
        self.assertIn('Saved new', response['text'])

        # make sure it's the right command
        query = QueryShorthand.objects.first()
        self.assertEqual(query.command, query_text)
        self.assertEqual(query.user_id, 'UALICE')

        # make sure the empty query tells you about queries
        response = self.slack_post('query')
        self.assertIn('num_minis', response['text'])

        # make sure we can run it
        # again, we can only check that the result is an int because we aren't
        # going through the django testing thing
        response = self.slack_post('query num_minis')
        int(response['text'].split('\n')[-1])

    def test_add_streak(self):
        # build up to a streak of 3
        self.slack_post(text='add :10 2018-08-01')
        self.slack_post(text='add :10 2018-08-02')
        # make sure the message didn't come in early
        self.assertNotIn('streak', self.messages[-1])

        # get the streak of 3 and check for acknowledgment
        self.slack_post(text='add :10 2018-08-03')
        # the messages for streaks of 3 have the word row in them
        self.assertIn('3', self.messages[-1])
        self.assertIn('row', self.messages[-1])

        # go for the streak of 10
        for i in range(4, 11):
            self.slack_post(text='add :10 2018-08-{:02d}'.format(i))
        self.assertIn('10', self.messages[-1])
        self.assertIn('streak', self.messages[-1])

    def test_plot(self):
        self.slack_post(text='add :10 2018-08-01')
        self.slack_post(text='add :10 2018-08-02')
        self.slack_post(text='add :10 2018-08-03')
        self.slack_post(text='add :10 2018-08-04')
        response = self.slack_post(
            text='plot', expected_response_type='in_channel'
        )
        self.assertIn(
            settings.MEDIA_URL, response['attachments'][0]['image_url']
        )

    def test_get_title(self):
        self.slack_post(text='add :10 2018-08-01')
        self.slack_post(text='add :10 2018-08-02')
        self.slack_post(text='add :10 2018-08-03')
        self.assertIn('Mini Dabbler', self.messages[-1])

# Again, shouldn't be a subclass of "SlackTestsCase"
class WebViewTests(SlackTestCase):
    def test_equip_item(self):
        tophat = Item.from_key('tophat')

        alice = CBUser.from_slackid('UALICE', 'alice')
        auth_alice = User.objects.get_or_create(username='UALICE')[0]
        alice.auth_user = auth_alice
        alice.save()
        self.client.force_login(auth_alice)

        response = self.client.get(reverse('inventory'))
        self.assertNotContains(response, tophat.name)

        alice.add_item(tophat)

        response = self.client.get(reverse('inventory'))
        self.assertContains(response, tophat.name)
        self.assertNotContains(response, 'Un-equip')

        # Try to equip an item through the POST method
        response = self.client.post(
            reverse('equip_item'), data={'itemkey': tophat.key}
        )
        self.assertRedirects(response, reverse('inventory'))
        self.assertEquals(CBUser.from_slackid('UALICE', 'alice').hat, tophat)

        response = self.client.get(reverse('inventory'))
        self.assertContains(response, 'Un-equip')

        response = self.client.post(
            reverse('unequip_item', kwargs={'item_type': 'hat'})
        )
        self.assertIsNone(CBUser.from_slackid('UALICE', 'alice').hat)
        self.assertRedirects(response, reverse('inventory'))

class AnnouncementTests(SlackTestCase):
    def setUp(self):
        super().setUp()
        self.release_announcement = ReleaseAnnouncement()
        self.morning_announcement = MorningAnnouncement()
        tz = timezone.get_default_timezone()
        self.weekday_wrong_time = datetime(2018, 10, 25, 15, tzinfo=tz)
        self.weekday_right_time = datetime(2018, 10, 25, 19, tzinfo=tz)
        self.weekend_wrong_time = datetime(2018, 10, 27, 19, tzinfo=tz)
        self.weekend_right_time = datetime(2018, 10, 27, 15, tzinfo=tz)

    def test_release_announcement_should_run_now(self):
        self.assertFalse(
            self.release_announcement.should_run_now(self.weekday_wrong_time)
        )
        self.assertTrue(
            self.release_announcement.should_run_now(self.weekday_right_time)
        )
        self.assertFalse(
            self.release_announcement.should_run_now(self.weekend_wrong_time)
        )
        self.assertTrue(
            self.release_announcement.should_run_now(self.weekend_right_time)
        )

    def test_release_announcement_run(self):
        with patch.object(timezone, 'localtime',
                          return_value=self.weekday_right_time):
            self.release_announcement.do()
            self.assertEqual(len(self.messages), 1)

    def test_announcement_streak(self):
        self.slack_post("add :10 2018-10-24")
        self.slack_post("add :10 2018-10-25")
        with patch.object(timezone, 'localtime',
                          return_value=self.weekday_right_time):
            self.release_announcement.do()

        self.assertIn('Alice is on a 2-day win streak', self.messages[-1])

    def test_morning_announcement_run(self):
        self.morning_announcement.do()
        self.assertEqual(len(self.messages), 1)

class MiscTests(TestCase):
    def test_comma_and(self):
        from crossbot.util import comma_and

        self.assertEqual('', comma_and([]))
        self.assertEqual('bob', comma_and(['bob']))
        self.assertEqual('alice and bob', comma_and(['alice', 'bob']))
        self.assertEqual(
            'alice, bob, and charlie', comma_and(['alice', 'bob', 'charlie'])
        )

class PredictorTests(SlackTestCase):
    data = {
        'U1': [None, 62, 38, 28, 42, 17],
        'U2': [73, 72, 36, 37, 51, None],
    }

    def setUp(self):
        super().setUp()
        self.maxDiff = None
        for u, ts in self.data.items():
            user = CBUser(slackid=u)
            user.save()
            for i, t in enumerate(ts):
                if t is not None:
                    user.add_mini_crossword_time(
                        t, parse_date("2018-01-0" + str(i + 1))
                    )

    def run_predictor(self):
        import crossbot.predictor as p
        data = p.data()
        fit = p.fit(data, quiet=True)
        model = p.extract_model(data, fit)
        p.save(model)
        return model

    def test_predictor(self):
        import crossbot.predictor as p
        model = self.run_predictor()
        model2 = p.load()
        self.assertEqual(model, model2)

        users = {m.user: m for m in model[2]}
        u1, u2 = CBUser.from_slackid("U1"), CBUser.from_slackid("U2")
        self.assertLess(users[u1].skill, users[u2].skill)

    def test_cron(self):
        from crossbot.cron import Predictor
        Predictor().do()

    def test_announcement(self):
        self.run_predictor()
        announce_data = MiniCrosswordTime.announcement_data(
            parse_date("2018-01-03")
        )
        self.assertIn('overperformers', announce_data)
        self.assertEqual([u for u, r in announce_data['overperformers']],
                         ['U2'])
        self.assertIn('difficulty', announce_data)
        self.assertLess(announce_data['difficulty'], 0)

    def test_slack_command(self):
        self.run_predictor()
        response = self.slack_post(text='predictor')
        response2 = self.slack_post(text='predictor details')
        self.assertIn("*log(P)* = ", response2["text"])
        response3 = self.slack_post(text='predictor validate')
        parts = response3["text"].split()
        self.assertEqual(len(parts), 7)
        mse, baseline = float(parts[3]), float(parts[5])
        self.assertLess(mse, baseline)


"""
Author: Himel Das
"""

import requests

from utilities.logger import logging

def get_country(country_name: str) -> dict:
    api_url = "https://restcountries.com/v3.1/name"
    response = requests.get(url=f"{api_url}/{country_name}")

    if response.status_code == 200:
        country_data = response.json()[0]
        reply = {
            "success": True,
            "body": {
                "name": country_data["name"]["common"],
                "currencies": list(country_data["currencies"].keys()),
                "region": country_data["region"],
                "population": country_data["population"],
            },
        }
    else:
        reply = {"success": False, "body": response.text}

    return reply

def get_countries_with_population(country_names: [str]) -> list:
    res = []
    for country_name in country_names:
        response = get_country(country_name=country_name)
        data = response["body"]

        if response["success"]:
            country = {
                "name": data["name"],
                "population": data["population"],
            }
            res.append(country)
    return res

if __name__ == "__main__":
    logging.info(get_country(country_name="usa"))
    countries = ["usa", "canada", "australia", "unknown"]
    logging.info(
        msg=f"Countries with population: {get_countries_with_population(country_names=countries)}"
    )


import math

import backtrader as bt

from backtrader.indicators import Highest
from backtrader.indicators import Lowest

from matplotlib.pyplot import subplot

import symbol_data

class DonchianChannel(bt.Indicator):

    lines = (
        'basis',
        'high', 'low',
        'high_volume', 'low_volume'
    )
    params = (
        ('period', 21),
    )

    plotinfo = dict(
        # Add extra margins above and below the 1s and -1s
        # plotymargin=0.15,

        # Plot a reference horizontal line at 1.0 and -1.0
        # plothlines=[0, 1, 2, 3],

        # Simplify the y scale to 1.0 and -1.0
        # plotyticks=[1.0, -1.0],

        subplot=False,
    )

    plotlines = dict(
        high_volume=dict(_plotskip=True,),
        low_volume=dict(_plotskip=True,),
    )

    def __init__(self):

        self.l.high     = Highest(self.data.high, period=self.p.period)
        self.l.low      = Lowest(self.data.low, period=self.p.period)
        self.l.basis    = (self.l.high + self.l.low) / 2

        self.l.high_volume  = Highest(self.data.volume, period=self.p.period)
        self.l.low_volume   = Lowest(self.data.volume, period=self.p.period)


from typing import List, Union

from asyncpg.exceptions import UniqueViolationError

from blockchain.services.coingecko import get_token_price_from_cg
from bot.models import TokenContract
from settings import settings

async def get_tokens_price(tokens: List[dict], chain: str, currency: str):
    addresses = ''
    for token in tokens:
        addresses += f'{token["token_address"]},'
    prices = await get_token_price_from_cg(addresses, chain, [currency])
    for token in tokens:
        price = prices.get(token["token_address"].lower())
        if price:
            token['price'] = price[currency]
        try:
            contract = TokenContract(token_address=token['token_address'], name=token.get('name'),
                                     chain=chain, symbol=token.get('symbol'),
                                     decimals=token.get('decimals', settings.chain_decimals[chain]))
            if price:
                contract.price = price[currency]
            await contract.save()
        except UniqueViolationError:
            contract = await TokenContract.objects.get_or_none(token_address=token['token_address'], chain=chain)
            if contract and price:
                await contract.update(price=price[currency], name=token.get('name'), symbol=token.get('symbol'),
                                      decimals=token.get('decimals', settings.chain_decimals[chain]))

def convert_balance(value: Union[str, int], decimal: Union[str, int]) -> float:
    return int(value) / 10 ** int(decimal)


import hopsworks
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import KFold
from xgboost import XGBRegressor
from hsml.schema import Schema
from hsml.model_schema import ModelSchema
import os
import joblib

project = hopsworks.login()
fs = project.get_feature_store()

data = fs.get_feature_group('aqi_prediction', version=1)

query = data.select(["pm25", "pm10", "o3", "no2", "so2", "co", "aqi"])

feature_view = fs.get_or_create_feature_view(
    name='aqi_prediction_fv',
    version=1,
    query=query,
    labels=["aqi"]
)

TEST_SIZE = 0.25

td_version, td_job = feature_view.create_train_test_split(
    description = 'pollution dataset',
    data_format = 'csv',
    test_size = TEST_SIZE,
    write_options = {'wait_for_job': True}
)

X_train, X_test, y_train, y_test = feature_view.get_train_test_split(td_version)

losses = []
r2_scores = []

model = XGBRegressor(
    n_estimators = 933,
    max_depth = 3,
    learning_rate = 0.0659553078597774,
    reg_lambda = 0.49527985837464644
)

kfold = KFold(n_splits=5, shuffle=True, random_state=42)

for train_index, val_index in kfold.split(X_train):
    X_train_fold, X_val_fold = X_train.iloc[train_index], X_train.iloc[val_index]
    y_train_fold, y_val_fold = y_train.iloc[train_index], y_train.iloc[val_index]

    model.fit(X_train_fold, y_train_fold)

input_schema = Schema(X_train.values)
output_schema = Schema(y_train)
model_schema = ModelSchema(input_schema=input_schema, output_schema=output_schema)

model_schema.to_dict()

model_dir="aqi_prediction_model"
if os.path.isdir(model_dir) == False:
    os.mkdir(model_dir)

joblib.dump(model, model_dir + '/xgboost_aqi_prediction_model.pkl')

mr = project.get_model_registry()

aqi_prediction_model = mr.python.create_model(
    name="aqi_prediction_model",
    model_schema=model_schema,
    input_example=X_train.sample(),
    description="AQI predictor")

aqi_prediction_model.save(model_dir)


import os

import pytest

from google.protobuf.json_format import MessageToDict

from xcalar.compute.util.Qa import (buildNativePath, XcalarQaDatasetPath,
                                    buildS3Path, buildGcsPath, buildAzBlobPath)
from xcalar.compute.util.Qa import XcalarQaHDFSHost, DefaultTargetName
from xcalar.compute.util.Qa import XcalarQaS3ClientArgs

from xcalar.external.LegacyApi.XcalarApi import XcalarApiStatusException
from xcalar.compute.localtypes.Connectors_pb2 import ListFilesRequest

from xcalar.compute.coretypes.Status.ttypes import StatusT
from xcalar.external.exceptions import XDPException

hdfsHost = "hdfs-sanity"
if XcalarQaHDFSHost:
    hdfsHost = XcalarQaHDFSHost

hdfsCrimeUrl = "hdfs://{}:8020/datasets/qa/nycCrimeSmall".format(hdfsHost)
hdfsCrimeUrlNoPort = "hdfs://{}/datasets/qa/nycCrimeSmall".format(hdfsHost)

expectedFiles = [
    " ", "tab\tsep", "<", ",", "?", "'", "~", "duplicate", "#", "testFile",
    "|", "!", "`", "@", "^", "]", "&", "_", "[", "\"", "DUPLICATE", "}", ")",
    "+", "(", ";", ".dotPre", "$", ">", "\\", "{", "=", "*", ":", "%", "\t"
]
subFiles = ["subdir/subfile", "subdir/subdir2/subfile2"]
subDirs = ["subdir/subdir2"]
expectedDirs = ["subdir"]
expectedContents = expectedFiles + expectedDirs

expRecurFiles = expectedFiles + subFiles
expRecurDirs = expectedDirs + subDirs
expRecurContents = expRecurFiles + expRecurDirs

# HDFS test expected results
hdfsTopFiles = ["2003.csv", "2014.csv"]
hdfsSubFiles = ["nycCrimeSmall/2003.csv", "nycCrimeSmall/2014.csv"]

expHdfsDirs = ["nycCrimeSmall"]
expHdfsFiles = hdfsTopFiles
expHdfsContents = expHdfsFiles + expHdfsDirs

expHdfsRecurDirs = expHdfsDirs
expHdfsRecurFiles = hdfsTopFiles + hdfsSubFiles
expHdfsRecurContents = expHdfsRecurDirs + expHdfsRecurFiles

expHugeDir = ["hugeDirectoryPart{:07}".format(ii) for ii in range(0, 150000)]

# Target tests
shared_qa_target_name = "QA shared target"
s3_minio_target_name = "QA S3 minio"
gcs_target_name = "QA GCS"
azblob_target_name = "QA azblob"
unsharedSymmTargetName = "QA unshared symmetric"
unsharedSingleNodeTargetName = "QA unshared single node"
targets = [
    (shared_qa_target_name, "shared", {
        "mountpoint": XcalarQaDatasetPath
    }),
    (unsharedSymmTargetName, "sharednothingsymm", {
        "mountpoint": XcalarQaDatasetPath
    }),
    (unsharedSingleNodeTargetName, "sharednothingsingle", {}),
    (s3_minio_target_name, "s3fullaccount", {
        "access_key": XcalarQaS3ClientArgs["aws_access_key_id"],
        "secret_access_key": XcalarQaS3ClientArgs["aws_secret_access_key"],
        "endpoint_url": XcalarQaS3ClientArgs["endpoint_url"],
        "region_name": XcalarQaS3ClientArgs["region_name"],
        "verify": "true" if XcalarQaS3ClientArgs["verify"] else "false",
    }),
    (gcs_target_name, "gcsenviron", {}),
    (
        azblob_target_name,
        "azblobfullaccount",
        {
            "account_name": "xcdatasetswestus2",
            "sas_token": ""    # Fill this in with a sas_token to test
        }),
]

ENABLE_CLOUD_TESTS = False

# We're going to run the edge case tests on these targets
standard_targets = [
    (shared_qa_target_name, (lambda x: x)),
    (s3_minio_target_name, buildS3Path),
    pytest.param(
        gcs_target_name,
        buildGcsPath,
        marks=pytest.mark.skipif(
            not ENABLE_CLOUD_TESTS,
            reason="jenkins running cloud tests is expensive")),
    pytest.param(
        azblob_target_name,
        buildAzBlobPath,
        marks=pytest.mark.skipif(
            not ENABLE_CLOUD_TESTS,
            reason="jenkins running cloud tests is expensive")),
]

@pytest.fixture(scope="module", autouse=True)
def setup(client):
    for target_name, target_type, target_args in targets:
        client.add_data_target(target_name, target_type, target_args)

    yield client

    for target_name, target_type, target_args in targets:
        client.get_data_target(target_name).delete()

def test_root(client):
    target = client.get_data_target(DefaultTargetName)
    resp = target.list_files("/")
    assert all([item['name'] for item in resp['files']])
    assert all(["/" not in item['name'] for item in resp['files']])

def test_bucket_root_s3(client):
    target = client.get_data_target(s3_minio_target_name)
    resp = target.list_files("/qa.xcalar")
    assert "qa.xcalar" not in [item['name'] for item in resp['files']]

def test_top_list_s3(client):
    try:
        target = client.get_data_target(s3_minio_target_name)
        resp = target.list_files("/", recursive=True)
    except XDPException as e:
        assert "Recursive search only available for a given bucket" in str(e)

@pytest.mark.parametrize("target_name,path_builder", [
    (DefaultTargetName, buildNativePath),
    (shared_qa_target_name, lambda x: x),
])
def test_edge_characters(client, target_name, path_builder):
    target = client.get_data_target(target_name)
    path1 = path_builder("edgeCases/iterTests/")
    path2 = path_builder("edgeCases/iterTests")
    resp1 = target.list_files(path1)
    resp2 = target.list_files(path2)

    def flatten(items):
        return set([tuple([v for v in item.values()]) for item in items])

    assert flatten(resp1['files']) == flatten(resp2['files'])
    assert set(
        [item['name'] for item in resp1['files']]) == set(expectedContents)

    files = [item['name'] for item in resp1['files'] if not item['isDir']]
    dirs = [item['name'] for item in resp1['files'] if item['isDir']]

    assert set(files) == set(expectedFiles)
    assert set(dirs) == set(expectedDirs)

@pytest.mark.parametrize(
    "target_name,path",
    [(DefaultTargetName, buildNativePath("edgeCases/iterTests")),
     (unsharedSingleNodeTargetName,
      os.path.join("0", XcalarQaDatasetPath.strip('/'), "edgeCases",
                   "iterTests"))])
def test_recursion(client, target_name, path):
    target = client.get_data_target(target_name)
    resp = target.list_files(path, recursive=True)

    items = [item['name'] for item in resp['files']]
    files = [item['name'] for item in resp['files'] if not item['isDir']]
    dirs = [item['name'] for item in resp['files'] if item['isDir']]

    assert sorted(items) == sorted(expRecurContents)
    assert sorted(files) == sorted(expRecurFiles)
    assert sorted(dirs) == sorted(expRecurDirs)

def test_unshared_root(client):
    target = client.get_data_target(unsharedSingleNodeTargetName)
    resp = target.list_files("/", recursive=True)
    # listing / returns N for N in num_nodes
    [int(item['name']) for item in resp['files']]

def test_recursive_unshared_duped(client):
    target_name = unsharedSymmTargetName
    path = os.path.join("edgeCases", "iterTests")
    target = client.get_data_target(target_name)

    resp = target.list_files(path, recursive=True)

    items = [item['name'] for item in resp['files']]
    files = [item['name'] for item in resp['files'] if not item['isDir']]
    dirs = [item['name'] for item in resp['files'] if item['isDir']]

    num_nodes = client.get_num_nodes_in_cluster()
    assert sorted(items) == sorted(expRecurContents * num_nodes)
    assert sorted(files) == sorted(expRecurFiles * num_nodes)
    assert sorted(dirs) == sorted(expRecurDirs * num_nodes)

@pytest.mark.parametrize("target_name,path_builder", standard_targets)
def test_dir_with_suffix(client, target_name, path_builder):
    path = path_builder("sisterDirs/a")
    target = client.get_data_target(target_name)

    resp = target.list_files(path, recursive=True)

    exp_dirs = []
    exp_files = ["a.txt"]
    exp_items = exp_dirs + exp_files

    assert set([item['name'] for item in resp['files']]) == set(exp_items)
    assert set([item['name'] for item in resp['files']
                if item['isDir']]) == set(exp_dirs)
    assert set([item['name'] for item in resp['files']
                if not item['isDir']]) == set(exp_files)

#
# Paged List Files Tests
#

def test_paged_list_files_recursive(client):
    # Recursive only returns files
    target = client.get_data_target(s3_minio_target_name)
    path = buildS3Path("edgeCases/20k/")

    dir_names = [f"part{ii:02}" for ii in range(100)]
    file_names = [f"file{ii:03}" for ii in range(200)]
    all_file_names = [
        f"{dir_name}/{file_name}" for dir_name in dir_names
        for file_name in file_names
    ]

    token = ""
    resp_files = []
    for ii in range(100):    # safer than while True
        resp = target.list_files(path, recursive=True, paged=True, token=token)
        resp_files = resp_files + [item['name'] for item in resp['files']]
        token = resp['continuationToken']
        if token == "":
            break
    assert sorted(resp_files) == sorted(all_file_names)

def test_paged_list_files_with_regex_non_recursive(client):
    # Recursive only returns files
    target = client.get_data_target(s3_minio_target_name)
    path = buildS3Path("edgeCases/20k/")

    name_pattern = 're:part[56][0-9]'

    dir_names = [f"part{ii:02}" for ii in range(50, 70)]
    resp_files = []
    resp = target.list_files(
        path, pattern=name_pattern, recursive=False, paged=True)
    assert resp['continuationToken'] == ""
    assert sorted(
        [item['name'] for item in resp['files']]) == sorted(dir_names)

def test_paged_list_files_with_glob_recursive(client):
    # Recursive only returns files
    target = client.get_data_target(s3_minio_target_name)
    path = buildS3Path("edgeCases/20k/")

    name_pattern = 'file0*'

    # 10K files, by filtering
    dir_names = [f"part{ii:02}" for ii in range(100)]
    file_names = [f"file0{ii:02}" for ii in range(100)]
    all_file_names = [
        f"{dir_name}/{file_name}" for dir_name in dir_names
        for file_name in file_names
    ]

    token = ""
    resp_files = []
    for ii in range(100):    # safer than while True
        resp = target.list_files(
            path,
            pattern=name_pattern,
            recursive=True,
            paged=True,
            token=token)
        resp_files = resp_files + [item['name'] for item in resp['files']]
        token = resp['continuationToken']
        if token == "":
            break
    assert sorted(resp_files) == sorted(all_file_names)


from datetime import datetime, timedelta, time
import logging
import sqlite3
import time as tm
import warnings
import pytz

from telegram import Update, ReplyKeyboardMarkup, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (CommandHandler, Filters, MessageHandler, Filters,
                          Updater, CallbackContext, CallbackQueryHandler)

import settings

cold_last_value = 0
hot_last_value = 0
was_sent = False
keyboard_list = [
    ['/help', '/consumption', '/stat', '/subscribe']
]
keyboard_stat_list = [
    ['Month', 'Week', 'Graph']
]

# Logging settings
logging.basicConfig(handlers=[logging.FileHandler('bot.log', 'w', 'utf-8')],
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    level=logging.INFO
                    )

def main():
    '''
    Main function.
    '''
    mybot = Updater(settings.API_KEY)
    logging.info('Bot is starting...')
    dp = mybot.dispatcher
    dp.add_handler(CommandHandler("help", talk_greet_user))
    # dp.add_handler(CommandHandler("sql", read_water_cons))
    dp.add_handler(CommandHandler("stat", get_stats))
    dp.add_handler(CommandHandler("subscribe", subscribe))
    dp.add_handler(CommandHandler('consumption', read_water_cons))
    # dp.add_handler(RegexHandler('^(Stat)$', get_stats))
    # dp.add_handler(RegexHandler('^(Month)$', get_month_stats))
    # dp.add_handler(RegexHandler('^(Week)$', get_week_stats))
    dp.add_handler(MessageHandler(Filters.regex(r'^(Month)$'), get_month_stats))
    dp.add_handler(MessageHandler(Filters.regex(r'^(Week)$'), get_week_stats))
    dp.add_handler(MessageHandler(Filters.text & ~Filters.command, talk_to_me))
    dp.add_handler(CallbackQueryHandler(set_was_sent, pattern="^(sent|)"))

    mybot.job_queue.run_repeating(check_health, interval=settings.HEALTH_CHECK_TIME_SEC)
    mybot.job_queue.run_daily(get_cons_update, time=time(hour=settings.SEND_UPDATE_HOUR, tzinfo=pytz.timezone('Europe/Moscow')))

    mybot.start_polling()
    mybot.idle()

def check_health(context: CallbackContext):
    '''
    Checks that values in database are actual.
    '''
    logging.info('Launching health check function.')
    cur_date = datetime.now()
    cold_date, cold_value, hot_date, hot_value = get_water(datetime.now())
    sec_from_last_cold_date = cur_date - cold_date
    sec_from_last_hot_date = cur_date - hot_date
    answer = ''
    if sec_from_last_cold_date.total_seconds() > settings.HEALTH_CHECK_DELAY_THRESHOLD:
        answer = 'WARNING!\nХОЛОДНАЯ вода обновлено более 2 часов назад\n({0}, значение: {1})'\
            .format(cold_date.strftime('%d.%m.%Y %H:%M:%S'), cold_value)
    if sec_from_last_hot_date.total_seconds() > settings.HEALTH_CHECK_DELAY_THRESHOLD:
        answer += '\nГОРЯЧАЯ вода обновлено более 2 часов назад\n({0}, значение: {1})'\
            .format(hot_date.strftime('%d.%m.%Y %H:%M:%S'), hot_value)
    if answer:
        logging.info(answer)
        subscribers_list = []

        connection = sqlite3.connect(settings.DB_PATH)
        cursor = connection.cursor()
        cursor.execute("SELECT ID FROM SUBSCRIBERS ORDER BY 1 ASC")
        rows = cursor.fetchall()
        for row in rows:
            subscribers_list.append(row[0])
        for client_id in subscribers_list:
            context.bot.sendMessage(chat_id=client_id, text=answer)

        cursor.close()
        connection.close()

    else:
        logging.info('Health check - OK!')

def get_cons_update(context: CallbackContext):
    '''
    Periodic function to get latest info about water consumption.
    '''
    logging.info('Launching getting consumption from database.')
    cur_date = datetime.now()
    global cold_last_value
    global hot_last_value
    global was_sent
    if cur_date.day in settings.SEND_UPDATE_DAYS_LIST and was_sent == False:
        cold_date, cold_value, hot_date, hot_value = get_water(datetime.now())
        if cold_value != cold_last_value or hot_value != hot_last_value:
            answer = 'ХОЛОДНАЯ вода:\nОбновлено: {0}, значение: {1}'\
                .format(cold_date.strftime('%d.%m.%Y %H:%M:%S'), cold_value)
            answer += '\nГОРЯЧАЯ вода:\nОбновлено: {0}, значение: {1}'\
                .format(hot_date.strftime('%d.%m.%Y %H:%M:%S'), hot_value)
            logging.info(answer)
            subscribers_list = []

            connection = sqlite3.connect(settings.DB_PATH)
            cursor = connection.cursor()
            cursor.execute("SELECT ID FROM SUBSCRIBERS ORDER BY 1 ASC")
            rows = cursor.fetchall()
            for row in rows:
                subscribers_list.append(row[0])
            for client_id in subscribers_list:
                context.bot.sendMessage(chat_id=client_id, text=answer)
                tm.sleep(0.5)
                context.bot.sendMessage(chat_id=client_id, text='Отправьте данные на сайте:\nhttps://mos.ru/services/pokazaniya-vodi-i-tepla/new/\
                                            \n\nДанные отправлены?', reply_markup=cons_send_inline_keyboard())

            cold_last_value = cold_value
            hot_last_value = hot_value

            cursor.close()
            connection.close()

        else:
            logging.info('Nothing changed.')
    elif cur_date.day not in settings.SEND_UPDATE_DAYS_LIST:
        was_sent = False
        logging.info('Consumption data will be send on dates: {}.'.format(settings.SEND_UPDATE_DAYS_LIST))

def talk_greet_user(update: Update, context: CallbackContext):
    '''
    /help handler function.
    Greets user and provide to him breaf info about bot usage.
    '''
    text = 'Command /help launched.'
    logging.info(text)
    # update.message.reply_text('Бот, кажется, работает...')
    my_keyboard = ReplyKeyboardMarkup(keyboard_list, resize_keyboard=True)
    update.message.reply_text('/consumption - последние данные о потреблении;\n/stat - статистика потребления за период; \
        \n/subscribe - подписаться на автоматические уведомления о потреблении воды.', reply_markup=my_keyboard)

def get_stats(update: Update, context: CallbackContext):
    '''
    /stat handler function.
    Asks user to select period for water consumption.
    '''
    text = 'Command /stat launched.'
    logging.info(text)
    my_keyboard_stat = ReplyKeyboardMarkup(keyboard_stat_list, resize_keyboard=True)
    update.message.reply_text('Выберите период', reply_markup=my_keyboard_stat)

def get_month_stats(update: Update, context: CallbackContext):
    '''
    Sends to user month stat of water consumption.
    '''
    text = 'Month statistics launched.'
    logging.info(text)
    cold_date, cold_value, hot_date, hot_value = get_water(datetime.now(), 30)
    # print('Month cons: ', cold_value, hot_value)
    answer = 'Сумма потребления за месяц:\nхолодная: {0},\nгорячая: {1}.'.format(cold_value, hot_value)
    logging.info(answer)
    my_keyboard = ReplyKeyboardMarkup(keyboard_list, resize_keyboard=True)
    update.message.reply_text(answer, reply_markup=my_keyboard)

def get_week_stats(update: Update, context: CallbackContext):
    '''
    Sends to user week stat of water consumption.
    '''
    text = 'Week statistics launched.'
    logging.info(text)
    cold_date, cold_value, hot_date, hot_value = get_water(datetime.now(), 7)
    # print('Week cons: ', cold_value, hot_value)
    answer = 'Сумма потребления за неделю:\nхолодная: {0},\nгорячая: {1}.'.format(cold_value, hot_value)
    logging.info(answer)
    my_keyboard = ReplyKeyboardMarkup(keyboard_list, resize_keyboard=True)
    update.message.reply_text(answer, reply_markup=my_keyboard)

def select_max_value(target_date):
    '''
    Get latest amount of water usage and date of last measures.
    '''
    connection = sqlite3.connect(settings.DB_PATH)
    cursor = connection.cursor()
    # data_items=[]
    logging.debug('SQL called. Target date: {}'.format(target_date))
    cursor.execute('WITH \
        max_cold AS (SELECT MAX(DATE) AS DATE_COLD, VALUE AS VALUE_COLD \
            FROM "WATER_VW" WHERE WATER_TYPE = 1 AND DATE <= "{0}"), \
                max_hot AS (SELECT MAX(DATE) AS DATE_HOT, VALUE AS VALUE_HOT \
                    FROM "WATER_VW" WHERE WATER_TYPE = 2 AND DATE <= "{0}") \
                        SELECT * FROM max_cold, max_hot  \
                                        '.format(target_date))
    rows = cursor.fetchall()
    cursor.close()
    connection.close()
    # print(rows)
    cold_date, cold_value, hot_date, hot_value = rows[-1]
    return cold_date, cold_value, hot_date, hot_value

def get_water(date, delta=0):
    '''
    Calculates consumption for a given period: [date-delta; date].
    Returns consumption for both cold and hot water for analiyzed period and dates for last measures.
    '''
    end_period_date = datetime.strftime(date, '%Y-%m-%dT%H:%M:%S')
    cold_date_1, cold_value_1, hot_date_1, hot_value_1 = select_max_value(end_period_date)
    logging.info(str(cold_date_1) + '; ' + str(cold_value_1) + '; ' + str(hot_date_1) + '; ' + str(hot_value_1))
    # print(cold1, hot1, date1)
    cold_value_2, hot_value_2 = 0, 0

    if delta > 0:
        start_period_date = datetime.strftime(date-timedelta(days=delta), '%Y-%m-%dT%H:%M:%S')
        cold_date_2, cold_value_2, hot_date_2, hot_value_2 = select_max_value(start_period_date)
        logging.info(str(cold_date_2) + '; ' + str(cold_value_2) + '; ' + str(hot_date_2) + '; ' + str(hot_value_2))

    return datetime.strptime(cold_date_1, '%Y-%m-%dT%H:%M:%S.%fZ'), cold_value_1 - cold_value_2, datetime.strptime(hot_date_1, '%Y-%m-%dT%H:%M:%S.%fZ'), hot_value_1 - hot_value_2

def talk_to_me(update: Update, context: CallbackContext):
    '''
    Returns to user his own's message.
    '''
    user_text = update.message.text
    # print(user_text)
    logging.info(user_text)
    update.message.reply_text(user_text)

def read_water_cons(update: Update, context: CallbackContext):
    '''
    /consumption handler function.
    Sends to user latest data abust consumption and date of last measures.
    '''
    text = 'Command /consumption launched.'
    logging.info(text)
    cold_date, cold_value, hot_date, hot_value = get_water(datetime.now())
    answer = 'ХОЛОДНАЯ вода:\nОбновлено: {0}, значение: {1}'\
        .format(cold_date.strftime('%d.%m.%Y %H:%M:%S'), cold_value)
    answer += '\nГОРЯЧАЯ вода:\nОбновлено: {0}, значение: {1}'\
        .format(hot_date.strftime('%d.%m.%Y %H:%M:%S'), hot_value)
    logging.info(answer)
    subscribers_list = []
    connection = sqlite3.connect(settings.DB_PATH)
    cursor = connection.cursor()
    cursor.execute("SELECT ID FROM SUBSCRIBERS ORDER BY 1 ASC")
    rows = cursor.fetchall()
    for row in rows:
        subscribers_list.append(row[0])
    for client_id in subscribers_list:
        context.bot.sendMessage(chat_id=client_id, text=answer)

    cursor.close()
    connection.close()

def subscribe(update: Update, context: CallbackContext):
    '''
    /subscribe handler function.
    Adds user to subscribers list.
    '''
    text = 'Command /subscribe launched.'
    logging.info(text)
    subscribers_list = []
    connection = sqlite3.connect(settings.DB_PATH)
    cursor = connection.cursor()
    cursor.execute("SELECT ID FROM SUBSCRIBERS ORDER BY 1 ASC")
    rows = cursor.fetchall()
    for row in rows:
        subscribers_list.append(row[0])

    if update.message.chat_id not in subscribers_list:
        cursor.execute('INSERT INTO SUBSCRIBERS VALUES({}, "{}")'.format(update.message.chat_id, str(datetime.now())))
        connection.commit()
        logging.info('Добавлен новый chat_id: {}, от {}'.format(update.message.chat_id, str(datetime.now())))
        update.message.reply_text('Вы добавлены в список рассылки уведомлений о новых данных потребления воды.')
    else:
        update.message.reply_text('Вы уже в списке рассылки о новых данных потребления воды.')

    cursor.close()
    connection.close()

def cons_send_inline_keyboard():
    inlinekeyboard = [
        [
            InlineKeyboardButton('Да', callback_data='sent|1'),
            InlineKeyboardButton('Нет', callback_data='sent|0')
        ]
    ]
    return InlineKeyboardMarkup(inlinekeyboard)

def set_was_sent(update: Update, context: CallbackContext):
    global was_sent
    update.callback_query.answer()
    callback_type, was_sent_str = update.callback_query.data.split("|")
    was_sent = bool(int(was_sent_str))
    if was_sent:
        text = 'Данные отправлены на mos.ru'
        logging.info('Variable was_sent is set to True.')
    else:
        text = 'Данные НЕ отправлены на mos.ru'
        logging.info('Variable was_sent is set to False.')
    # update.callback_query.edit_message_caption(caption=text)
    update.callback_query.edit_message_text(text=text, reply_markup=None)

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt

class PointData(object):
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def getPoint(self):
        return (self.x, self.y)

def GongZhenFunc(n):
    y=1.08722 * (70000*20 - n*n - (float)(n/3.0)*(n/3.0) - (float)(n/5.0)*(n/5.0) -
    (float)(n/8.0)*(n/8.0) - (float)(250000-(n-499)*(n-499))*2.2)
    return (n,y)

def eocgongzhen(n ):
    return 0.945 * (40000*20 - n*n - (float)(n/3)*(n/3) - (float)(n/5)*(n/5) -
    (float)(n/8)*(n/8) - (float)(250000-(n-499)*(n-499))*2.2)

if __name__ == "__main__":
    '''
    x = np.linspace(0, 2 * np.pi, 100)
    y1, y2 = np.sin(x), np.cos(x)

    plt.plot(x, y1)
    plt.plot(x, y2)

    plt.title('line chart')
    plt.xlabel('x')
    plt.ylabel('y')

    plt.show()
    '''
    '''
    sum = 0.0
    xslice = []
    yslice = []
    for i in range(1000):
        res = GongZhenFunc(i)
        sum = sum + res[1]
        yslice.append(res[1])
        xslice.append(i)
        print("tokennum is %f,  totalnum is %f" %(res[1], sum))

    plt.plot(xslice, yslice)
    plt.title('gognzhen chart')
    plt.xlabel('x')
    plt.ylabel('y')
    #plt.legend()
    plt.show()
    '''

    sum = 0.0
    xslice = []
    yslice = []
    for i in range(465):
        res = eocgongzhen(i)
        sum = sum + res
        yslice.append(res)
        xslice.append(i)
        print("tokennum is %f,  totalnum is %f" %(res, sum))

    plt.plot(xslice, yslice)
    plt.title('gognzhen chart')
    plt.xlabel('x')
    plt.ylabel('y')
    #plt.legend()
    plt.show()


import requests
from django.conf import settings

from loguru import logger

from collector.models import City, WeatherData

logger.add("collector_logs_{time}.log")

CITIES = City.objects.all()

def collect_weather_data():
    logger.info("Функция сбора данных о погоде collect_weather_data запущена.")

    weather_objects = []

    for city in CITIES:
        url = f"https://api.openweathermap.org/data/2.5/weather?q={city.name}&appid={settings.OPENWEAWERMAP_API_KEY}"

        try:
            response = requests.get(url)
            response.raise_for_status()
            data = response.json()
            weather = WeatherData(
                city=city,
                temperature=data["main"]["temp"],
                humidity=data["main"]["humidity"],
                wind_speed=data["wind"]["speed"],
                dt=data["dt"],
                timezone=data["timezone"],
            )
            weather_objects.append(weather)

        except requests.RequestException as error:
            logger.exception(
                f"Произошла ошибка при выполнении запроса для города {city}:",
                error,
            )
        except (KeyError, ValueError) as error:
            logger.exception(
                f"Произошла ошибка парсинга для города {city}:",
                error,
            )
        except Exception as error:
            logger.exception(
                f"Ой, неизвестная ошибка для города {city}:",
                error,
            )

    WeatherData.objects.bulk_create(weather_objects)


#!/usr/bin/python
# -*- coding: utf-8 -*-

import configparser
import socket
import time
import serial
import threading
import rtcmproxy

# load settings from config file
config = configparser.SafeConfigParser()
config.read('config.cfg')

# serial port
bufsize  = config.getint('Serial', 'bufsize')
baudrate = config.getint('Serial', 'baudrate')
serport  = config.get('Serial', 'port')

print("trying to open", serport, "at", baudrate, "speed...")
ser = serial.Serial(serport, baudrate, timeout = 0.5)
print("  success!")

class SerialToQgc(threading.Thread):#{{{
    def __init__(self, ser, tcpconn):
        threading.Thread.__init__(self)
        self.__stop = threading.Event()
        self.ser = ser
        self.conn = tcpconn

    def stop(self):
        self.__stop.set()

    def run(self):
        while True:
            if self.__stop.is_set():
                print("SerialToQgc: terminating")
                return

            data = self.ser.read(bufsize)
            try:
                if data:
                    sent = self.conn.sendall(data)
                else:
                    sent = self.conn.sendall(bytearray(1)) # keep alive
            except BrokenPipeError:
                print ("SerialToQgc: connection closed")
                return;
    #}}}
class QgcToSerial(threading.Thread):#{{{
    def __init__(self, ser, tcpconn):
        threading.Thread.__init__(self)
        self.__stop = threading.Event()
        self.ser = ser
        self.conn = tcpconn

    def stop(self):
        self.__stop.set()

    def run(self):
        while True:
            if self.__stop.is_set():
                print("QgcToSerial: terminating")
                return

            data = self.conn.recv(1024)
            if not data:
                print("QgcToSerial: connection closed")
                return
            else:
                self.ser.write(data)
    #}}}
class ConnListener(threading.Thread):#{{{
    def __init__(self, ser):
        threading.Thread.__init__(self)
        self.__stop = threading.Event()
        self.ser = ser

    def stop(self):
        self.__stop.set()

    def run(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('', 5760))
        sock.settimeout(0.5)
        sock.listen(5)
        conn = None
        addr = None
        print("Starting ConnListener")

        while True:
            if self.__stop.is_set():
                print ("ConnListener: terminating")
                return

            try:
                (conn, addr) = sock.accept()
            except socket.timeout:
                pass

            if conn:
                print ("Connected by", addr)
                q2s = QgcToSerial(ser, conn)
                s2q = SerialToQgc(ser, conn)
                q2s.start()
                s2q.start()
                q2s.join()
                s2q.join()

                print("--- socket closed")
                conn.close()
                conn = None
                addr = None
    #}}}

listener = ConnListener(ser)
listener.start()
rtcm = rtcmproxy.RtcmProxy(ser)
rtcm.start()

while True:
    try:
        time.sleep(0.5)
    except KeyboardInterrupt:
        print("\nKeyboard Interrupt caught")
        listener.stop()
        rtcm.stop()
        listener.join()
        rtcm.join()
        ser.close()
        exit()


import sys
from os import linesep
from six import string_types
from six import binary_type

from distutils.version import LooseVersion

from datalad.dochelpers import exc_str
from datalad.log import lgr
# import version helper from config to have only one implementation
# config needs this to avoid circular imports
from datalad.config import get_git_version as __get_git_version
from .exceptions import CommandError

__all__ = ['UnknownVersion', 'ExternalVersions', 'external_versions']

# To depict an unknown version, which can't be compared by mistake etc
class UnknownVersion:
    """For internal use
    """

    def __str__(self):
        return "UNKNOWN"

    def __cmp__(self, other):
        if other is self:
            return 0
        raise TypeError("UNKNOWN version is not comparable")

#
# Custom handlers
#
from datalad.cmd import Runner
from datalad.cmd import GitRunner
from datalad.support.exceptions import (
    MissingExternalDependency,
    OutdatedExternalDependency,
)
_runner = Runner()
_git_runner = GitRunner()

def _get_annex_version():
    """Return version of available git-annex"""
    try:
        return _runner.run('git annex version --raw'.split())[0]
    except CommandError:
        # fall back on method that could work with older installations
        out, err = _runner.run(['git', 'annex', 'version'])
        return out.split('\n')[0].split(':')[1].strip()

def _get_git_version():
    """Return version of git we use (might be bundled)"""
    return __get_git_version(_git_runner)

def _get_system_git_version():
    """Return version of git available system-wide

    Might be different from the one we are using, which might be
    bundled with git-annex
    """
    return __get_git_version(_runner)

def _get_system_ssh_version():
    """Return version of ssh available system-wide

    Annex prior 20170302 was using bundled version, but now would use system one
    if installed
    """
    try:
        out, err = _runner.run('ssh -V'.split(),
                               expect_fail=True, expect_stderr=True)
        # apparently spits out to err but I wouldn't trust it blindly
        if err.startswith('OpenSSH'):
            out = err
        assert out.startswith('OpenSSH')  # that is the only one we care about atm
        return out.split(' ', 1)[0].rstrip(',.').split('_')[1]
    except CommandError as exc:
        lgr.debug("Could not determine version of ssh available: %s", exc_str(exc))
        return None

class ExternalVersions(object):
    """Helper to figure out/use versions of the externals (modules, cmdline tools, etc).

    To avoid collision between names of python modules and command line tools,
    prepend names for command line tools with `cmd:`.

    It maintains a dictionary of `distuil.version.LooseVersion`s to make
    comparisons easy. Note that even if version string conform the StrictVersion
    "standard", LooseVersion will be used.  If version can't be deduced for the
    external, `UnknownVersion()` is assigned.  If external is not present (can't
    be imported, or custom check throws exception), None is returned without
    storing it, so later call will re-evaluate fully.
    """

    UNKNOWN = UnknownVersion()

    CUSTOM = {
        'cmd:annex': _get_annex_version,
        'cmd:git': _get_git_version,
        'cmd:system-git': _get_system_git_version,
        'cmd:system-ssh': _get_system_ssh_version,
    }
    INTERESTING = (
        'appdirs',
        'boto',
        'exifread',
        'git',
        'gitdb',
        'humanize',
        'iso8601',
        'keyring',
        'keyrings.alt',
        'msgpack',
        'mutagen',
        'patool',
        'requests',
        'scrapy',
        'six',
        'wrapt',
    )

    def __init__(self):
        self._versions = {}

    @classmethod
    def _deduce_version(klass, value):
        version = None

        # see if it is something containing a version
        for attr in ('__version__', 'version'):
            if hasattr(value, attr):
                version = getattr(value, attr)
                break

        # try pkg_resources
        if version is None and hasattr(value, '__name__'):
            try:
                import pkg_resources
                version = pkg_resources.get_distribution(value.__name__).version
            except Exception:
                pass

        # assume that value is the version
        if version is None:
            version = value

        # do type analysis
        if isinstance(version, (tuple, list)):
            #  Generate string representation
            version = ".".join(str(x) for x in version)
        elif isinstance(version, binary_type):
            version = version.decode()
        elif isinstance(version, string_types):
            pass
        else:
            version = None

        if version:
            return LooseVersion(version)
        else:
            return klass.UNKNOWN

    def __getitem__(self, module):
        # when ran straight in its source code -- fails to discover nipy's version.. TODO
        #if module == 'nipy':
        #    import pdb; pdb.set_trace()
        if not isinstance(module, string_types):
            modname = module.__name__
        else:
            modname = module
            module = None

        # Early returns None so we do not store prev result for  them
        # and allow users to install things at run time, so later check
        # doesn't pick it up from the _versions
        if modname not in self._versions:
            version = None   # by default -- not present
            if modname in self.CUSTOM:
                try:
                    version = self.CUSTOM[modname]()
                    version = self._deduce_version(version)
                except Exception as exc:
                    lgr.debug("Failed to deduce version of %s due to %s"
                              % (modname, exc_str(exc)))
                    return None
            else:
                if module is None:
                    if modname not in sys.modules:
                        try:
                            module = __import__(modname)
                        except ImportError:
                            lgr.debug("Module %s seems to be not present" % modname)
                            return None
                        except Exception as exc:
                            lgr.warning("Failed to import module %s due to %s",
                                        modname, exc_str(exc))
                            return None
                    else:
                        module = sys.modules[modname]
                if module:
                    version = self._deduce_version(module)
            self._versions[modname] = version

        return self._versions.get(modname, self.UNKNOWN)

    def keys(self):
        """Return names of the known modules"""
        return self._versions.keys()

    def __contains__(self, item):
        return bool(self[item])

    @property
    def versions(self):
        """Return dictionary (copy) of versions"""
        return self._versions.copy()

    def dumps(self, indent=None, preamble="Versions:", query=False):
        """Return listing of versions as a string

        Parameters
        ----------
        indent: bool or str, optional
          If set would instruct on how to indent entries (if just True, ' '
          is used). Otherwise returned in a single line
        preamble: str, optional
          What preamble to the listing to use
        query : bool, optional
          To query for versions of all "registered" custom externals, so to
          get those which weren't queried for yet
        """
        if query:
            [self[k] for k in tuple(self.CUSTOM) + self.INTERESTING]
        if indent and (indent is True):
            indent = ' '
        items = ["%s=%s" % (k, self._versions[k]) for k in sorted(self._versions)]
        out = "%s" % preamble if preamble else ''
        if indent is not None:
            if preamble:
                preamble += linesep
            indent = ' ' if indent is True else str(indent)
            out += (linesep + indent).join(items) + linesep
        else:
            out += " " + ' '.join(items)
        return out

    def check(self, name, min_version=None, msg=""):
        """Check if an external (optionally of specified min version) present

        Parameters
        ----------
        name: str
          Name of the external (typically a Python module)
        min_version: str or version, optional
          Minimal version to satisfy
        msg: str, optional
          An additional message to include into the exception message

        Raises
        ------
        MissingExternalDependency
          if the external is completely missing
        OutdatedExternalDependency
          if the external is present but does not satisfy the min_version
        """
        ver_present = self[name]
        if ver_present is None:
            raise MissingExternalDependency(
                name, ver=min_version, msg=msg)
        elif min_version and ver_present < min_version:
            raise OutdatedExternalDependency(
                name, ver=min_version, ver_present=ver_present, msg=msg)

external_versions = ExternalVersions()


import argparse
from sklearn.pipeline import Pipeline
from pathlib import Path
import pandas as pd
from importlib import reload
import sys
import shutil

import processers as procs

reload(procs)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "-i",
        "--input",
        default="./input",
        help="Path to input directory w/ data in input/houses or input/floods/ subdirs",
    )
    parser.add_argument(
        "-e",
        "--exclude",
        choices=["houses", "floods"],
        help="Exclude either input/floods/ or input/houses from processing",
    )
    parser.add_argument(
        "-o", "--output", default="./output", help="Path to output directory"
    )
    parser.add_argument(
        "-f",
        "--format",
        default="png",
        help="Format in which skimage.io.imsave will store final processed images",
    )
    parser.add_argument(
        "-p",
        "--flip",
        default=0.0,
        type=float,
        help="Proportion of inputs to flip l/r (in addition to original image)",
    )
    parser.add_argument(
        "-x",
        "--force",
        action="store_true",
        default=False,
        help="Force overwrite potentially existing output dir",
    )

    args = parser.parse_args()
    print(args)

    dirs = [
        d
        for d in Path(args.input).iterdir()
        if d.is_dir() and (args.exclude is None or args.exclude not in d.name)
    ]

    if not Path(args.output).exists():
        Path(args.output).mkdir()

    for input_dir in dirs:

        print(f"\n### Processing {str(input_dir)} ###")

        out = Path(args.output) / input_dir.name

        if out.exists():
            print("Output destination not empty | overwrite?")
            if args.force or "y" in input("[y/n] : "):
                print("Cleaning", str(out))
                shutil.rmtree(str(out))
                out.mkdir()
            else:
                print("Stopping here")
                sys.exit()
        else:
            out.mkdir()

        pipe = Pipeline(
            steps=[
                ("Loader", procs.Loader(input_dir)),
                ("Flipper", procs.Flipper(args.flip)),
                ("Saver", procs.Saver(out, args.format)),
            ]
        )

        df = pd.DataFrame({"path": [f for f in input_dir.iterdir() if f.is_file()]})
        pipe.fit_transform(df)


from django.shortcuts import render
from django.http import HttpResponse
from django.template import loader
from poll.models import User

def index(request):
    if request.method == 'POST':
        name = request.POST.get('name')
        email = request.POST.get('email')
        number = request.POST.get('phone')
        gender = request.POST.get('gender')
        additonal_number = request.POST.get('num')

        new = User(name=name,email=email,number=number,gender=gender,additonal_number=additonal_number)

        new.save()

        # return HttpResponse("You're name is  %s." %new.name )
        return render(request,'details.html')

    return render(request, 'index.html')
    # return HttpResponse("Hello, world. You're at the poll index.")
# Create your views here.
# def detail(request):
#     return Htt=
def about(request):
    return HttpResponse("Hello, world. You're at the poll about.")


import logging
import pytz
from tornado import web
from stocksum.web import utils
from datetime import datetime

class Handler(web.RequestHandler):

    def get_template_namespace(self):
        namespace = web.RequestHandler.get_template_namespace(self)
        namespace.update(
            title='Stocksum.net',
            user_time=self.user_time,
        )
        return namespace

    def get_current_user(self):
        user = self.get_secure_cookie('user')
        if user:
            return utils.json_loads(user)

    def user_time(self, dt=None):
        if not dt:
            dt = datetime.utcnow()
        return datetime.astimezone(
            dt.replace(tzinfo=pytz.utc),
            pytz.timezone(self.current_user['timezone']),
        )

class API_handler(Handler):

    def set_default_headers(self):
        self.set_header('Cache-Control', 'no-cache, must-revalidate')
        self.set_header('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT')
        self.set_header('Content-Type', 'application/json')
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With')
        self.set_header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE')
        self.set_header('Access-Control-Expose-Headers', 'ETag, Link, X-Total-Count, X-Total-Pages')
        self.set_header('Access-Control-Max-Age', '86400')
        self.set_header('Access-Control-Allow-Credentials', 'true')

    def write_object(self, obj):
        self.write(
            utils.json_dumps(obj, indent=4),
        )


from django import forms
from .models import Comment

# Criando formulário à partir do Model `Comment`
# https://docs.djangoproject.com/pt-br/2.0/topics/forms/modelforms/#modelform
class CommentForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # atribuindo classes 'CSS' aos campos do form
        self.fields['name'].widget.attrs.update({'class': 'form-control form-control-lg', 'required': True})
        self.fields['text'].widget.attrs.update({'class': 'form-control form-control-lg', 'required': True})
        # Colocando o Post como um campo invisível no formulário
        self.fields['post'].widget = forms.widgets.HiddenInput()

    class Meta:
        model = Comment
        # Apenas estes campos serão apresentados no formulário html
        fields = ['name', 'text', 'post']


from flask import Flask, url_for, session
from flask import render_template, redirect
from authlib.integrations.flask_client import OAuth
from py_console import console

app = Flask(__name__)
app.secret_key = '!secret'
app.config.from_object('config')

oauth = OAuth(app)
oauth.register(
    name='actionstep',
    access_token_url='https://api.actionstepstaging.com/api/oauth/token',
    authorize_url='https://go.actionstepstaging.com/api/oauth/authorize',
    client_kwargs={
        'scope': 'actions',
    }
)

@app.route('/')
def homepage():
    user = session.get('user')
    return render_template('home.html', user=user)

@app.route('/login')
def login():
    redirect_uri = url_for('auth', _external=True)
    return oauth.actionstep.authorize_redirect(redirect_uri)

@app.route('/auth')
def auth():
    token = oauth.actionstep.authorize_access_token()
    console.log(token)
    session['user'] = token
    return redirect('/')

@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect('/')


#!/usr/bin/env python
# coding: utf-8

# # Differential Equation Solving 1
# Many physical laws governing natural behaviour take the form of differential equations. These are often complicated, coupled equations where the behaviour of quantities in space and time depend on others. While some have analytical solutions, often it is quicker and easier to apply numerical tools to obtain approximate solutions.

# ## Motivation
# A **differential equation** is a relationship between a function, $f(x)$, its independent variable, $x$, and any number of its derivatives. An **ordinary differential equation** or **ODE** is a differential equation where the independent variable, and therefore also the derivatives, is in one dimension. For our purposes, we can assume that an ODE can be written
#
# $$
# F\left(x, f(x), \frac{{\rm d}f(x)}{{\rm d}x}, \frac{{\rm d}^2f(x)}{{\rm d}x^2}, \frac{{\rm d}^3f(x)}{{\rm d}x^3}, \ldots, \frac{{\rm d}^{n-1}f(x)}{{\rm d}x^{n-1}}\right) = \frac{{\rm d}^{n}f(x)}{{\rm d}x^{n}},
# $$
#
# where $F$ is an arbitrary function that incorporates one or all of the input arguments, and $n$ is the **order** of the differential equation. This equation is referred to as an **$n^{\mathrm{th}}$ order ODE**.
#
#
#
#
# One example of an ODE describes the system in the above picture, containing a pendulum of length $l$ supporting a mass, $m$ at the end of its length. The angle the pendulum makes with the vertical axis over time, $\theta(t)$, in the presence of gravity, $g$, can be described by an equation, which is the ODE
#
# $$
# ml\frac{{\rm d}^2\theta(t)}{{\rm d}t^2} = -mg \sin(\theta(t)).
# $$
#
# This equation can be derived by summing the forces in the $x$ and $y$ direction, and then converting to polar coordinates.

# We have learned several simple approaches to solving ODEs in lectures:
#
#
# 1.   Euler's method.
# 2.   Midpoint method.
# 3.   Euler Trapezium.
#
# This notebook will apply each of these methods to a specific problem, to illustrate how they can be implemented numerically. Remember that MAT301 aims to give you a mathematical underpinning of each of these methods.
#

# #### Setting up Libraries
#
# As always, we'll make use of some clever Python tools for plotting and maths, so our first step is to load in the libraries that store these tools:

# In[1]:

## Libraries
get_ipython().run_line_magic('matplotlib', 'inline')
import numpy as np
import math
import matplotlib.pyplot as plt

# ## Euler's method
#
# Let $\frac{dS(t)}{dt} = F(t,S(t))$ be an explicitly defined first order ODE. That is, $F$ is a function that returns the derivative at a given time. Also, let $t$ be a numerical grid of the interval $[t_0, t_f]$ with spacing $h$. Assuming that $t_0 = 0$, and that $t_f = Nh$ for some positive integer, $N$, we can linearly approximate $S(t)$ around $t_j$ at $t_{j+1}$ as:
#
# $$
# S(t_{j+1}) = S(t_j) + (t_{j+1} - t_j)\frac{dS(t_j)}{dt},
# $$
#
# which can also be written
#
# $$
# S(t_{j+1}) = S(t_j) + hF(t_j, S(t_j)).
# $$
#
# This formula is called the **Explicit Euler Formula**, and allows us to approximate the state at $S(t_{j+1})$ given the state at $S(t_j)$. Starting from a given initial value of $S_0 = S(t_0)$, we can use this formula to integrate the states up to $S(t_f)$; these $S(t)$ values are then an approximation for the solution of the differential equation. The Euler formula is the simplest and most intuitive method for solving initial value problems. At any state $(t_j, S(t_j))$ it uses $F$ at that state to "point" toward the next state and then moves in that direction a distance of $h$. Although there are more sophisticated and accurate methods for solving these problems, they all have the same fundamental structure. As such, we enumerate explicitly the steps for solving an initial value problem using the Explicit Euler formula.
#
#

# ## Example 9.1 ##
#
# To see this in action, we will use the equation and question posed in from Example 9.1 in the lectures:
#
# $$
# \frac{{\rm{d}}y}{{\rm{d}}t}=xy,
# $$
#
# requires a solution at $x=0.5$ subject to the initial condition $y(x=0)=1$, using a step size $h=0.1$.
#
# Euler's method requires the re-evaluation of the right hand side of the function at different positions, so lets go ahead and create a function to return this value

# In[2]:

def func(x,y):
    ftw = x * y
    return ftw

# Now we need to create our $x$-range for the problem. We know that the range has to begin at $x=0$ (because we know what $y$ is there), and we have to step along until we reach the desired $x$-value ($x=0.5$) in steps of $h=0.1$:

# In[3]:

a = 0.0
b = 0.5
h = 0.1
N = int( (b - a) / h ) # number of steps
print("N=", N)
x = np.arange( a, b + h, h)  # range of x
print("x=", x)

# You can see that this approach generates 5 steps $N=5$, and the $x$ values are exactly what we would expect them to be using pen and paper.
#
# For each of these $x$-values, we'll now have to create $y$-values. The first of these is already specified in the problem; we have been told the initial condition $y(0)=1$.

# In[4]:

y = np.zeros(len(x))
y[0] = 1

# Now we need to calculate each $y$-value based on the previous value. To help us, we can loop over all the $x$-values and calculate the next in the sequence. The final value will be the one we need (the $n$th):

# In[5]:

for n in range(0, N):
    y[n+1] = y[n] + h * func(x[n], y[n])
print("y=", y)

# This differential equation has an exact solution, $y_{\rm{exact}}=\exp{\left(\frac{x^2}{2}\right)}$, so lets go ahead and compare how our approximation does:

# In[6]:

y_exact = np.exp(0.5 * x * x)
print(y_exact)

# Our approximate solution seems to be behaving in the same way: lets visualise this with a plot:

# In[7]:

plt.style.use('seaborn-poster')
plt.figure(figsize = (12, 8))
plt.plot(x, y, 'bo--', label='Approximate: h=%s'%(h))
plt.plot(x, np.exp(0.5 * x * x), 'g', label='Exact')
plt.title('Approximate and Exact Solution for Simple ODE')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid()
plt.legend(loc='lower right')
plt.show()

# So we see that our approximation displays similar behaviour, but doesn't follow the trend exactly; in fact the graphs appear to be diverging as $x$ increases. What if we make the step size ten times smaller?

# In[8]:

h2 = 0.01
N2 = int( (b - a) / h2) # number of steps
x2 = np.arange( a, b + h2, h2)  # range of x
y2 = np.zeros(len(x2))
y2[0] = 1
for n in range(0, N2):
    y2[n + 1] = y2[n] + h2 * func(x2[n], y2[n])
plt.style.use('seaborn-poster')
plt.figure(figsize = (12, 8))
plt.plot(x, y, 'bo--', label='Approx: h=%s'%(h))
plt.plot(x2, y2,'ro--', label='Approx: h=%s'%(h2))
plt.plot(x2, np.exp(0.5 * x2 * x2), 'g', label='Exact')
plt.title('Approximate and Exact Solutions for Simple ODE')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid()
plt.legend(loc='lower right')
plt.show()

# As expected, cranking up the number of steps improves the accuracy of the solution. However, in practice, we often have a finite amount of numerical resources (e.g. CPU speed, number of nodes, memory size), so we can't always rely on the ability to simply decrease $h$. In those cases, we have to be smarter, and apply other techniques.

# ## Midpoint method
#
# In the lectures, we were introduced to a more accurate scheme, the Midpoint method. This essentially estimates the gradient of the tangent at the midpoint between steps $x_n+\frac{1}{2}h$, which lies halfway between $x_n$ and $x_{n+1}$. Using Euler's method, we then determine $y_{n+1}=y_n+hk_2,$ where
#
# $$
# \begin{align}
# k_1&=f(x_n,y_n), \\
# k_2&=f(x_n+\frac{1}{2}h, y_n+\frac{1}{2}hk_1).
# \end{align}
# $$
#
# This looks a bit weird, but all this means is that we start to evaluate the function not only at the grid point values, but also now at different locations in $x$ and $y$.

# Let's go ahead and implement this, in exactly the same manner as the previous method:

# In[9]:

y3 = np.zeros(len(x))
y3[0] = 1
for n in range(0, N):
    k1 = func(x[n],y[n])
    k2 = func(x[n] + 0.5 * h, y[n] + 0.5 * h * k1)
    y3[n + 1] = y3[n] + h*k2
plt.style.use('seaborn-poster')
plt.figure(figsize = (12, 8))
plt.plot(x, y, 'bo--', label='Euler: h=%s'%(h))
plt.plot(x, y3, 'o--', label='Midpoint: h=%s'%(h))
plt.plot(x, np.exp(0.5 * x * x), 'g', label='Exact')
plt.title('Euler and Midpoint methods: ODE solving')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid()
plt.legend(loc='lower right')
plt.show()

# We quickly see that the midpoint method is more accurate than the Euler method for the same step size. The trade-off is the additional calculation required to evaluate $k_1$ and $k_2$.

# ### Modified Euler (Euler-Trapezium Method)
#
# Our methods to solve ODE problems are gradually becoming more complicated in our search for better accuracy.
#
# The final method we will encounter in this worksheet is the Modified Euler (or Euler Trapezium) method. This is classed as a predictor-corrector method, which uses the average of gradients of tangents at the endpoints of each interval to improve accuracy. The derivation of this method is beyond the scope of this course, but can easily be found in textbooks or online. The Modified Euler method is also an example of a 2nd order member of the family of Runge Kutta methods, developed around 1900 by Carl Runge and Wilhelm Kutta. We will see some higher order examples of these methods later.
#
# The method uses Euler's method to predict $y_{n+1}$:
#
# $$
# y_{n+1}^p=y_n+h y_n',
# $$
#
# where $y_{n+1}^p$ is an initial prediction. We then use the derivative prediction, $y_{n+1}'^p$, from
#
# $$
# y_{n+1}'^p=f\left(x_{n+1}, y_{n+1}^p\right),
# $$
#
# to correct the value
#
# $$
# y_{n+1}^c=y_n+\frac{h}{2}\left(y_n'+y_{n+1}'^p\right).
# $$

# Once again, we can apply this solution to the ODE problem from earlier in the following way:

# In[10]:

y4 = np.zeros(len(x))
y4[0] = 1
for n in range(0, N):
    k1 = func(x[n], y[n])
    k2 = func(x[n + 1], y[n] + h * k1)
    y4[n + 1] = y4[n] + 0.5 * h * (k1 + k2)
plt.style.use('seaborn-poster')
plt.figure(figsize = (12, 8))
plt.plot(x, y, 'bo--', label='Euler: h=%s'%(h))
plt.plot(x, y4, 'mo--', label='Modified Euler: h=%s'%(h))
plt.plot(x, np.exp(0.5 * x * x), 'g', label='Exact')
plt.title('Euler and Modified Euler methods: ODE solving')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid()
plt.legend(loc='lower right')
plt.show()

# Once again, the Modified Euler does a much better job than the original Euler method, and (for this ODE) is comparable to the Midpoint method in accuracy.
#
# In the next worksheet we will explore a common higher order scheme, the RK4 algorithm, which is widely used in science and engineering (for example). This algorithm builds on some of the simple approaches used in these less accurate models.

# ## Further work
# Now its over to you:
#
# Try choosing a different worked example seen in the lectures, or indeed a tutorial question **once you have worked through the mathematical steps** and see if you can apply the schemes in this workbook to that problem.
#

# In[ ]: