from typing import Any, Dict, Generator, Iterable, Optional, Union, Tuple, List

from aiosqlite.core import Connection
from aiosqlite.cursor import Cursor

from .utils import build_query, dict_factory

class MotorSqliteTable(object):
    def __init__(self, conn: Connection, table: str) -> None:
        self.db = None
        self.conn = conn
        self.table = table

        # SQL queries
        self.select_sql = f'SELECT * FROM {table} WHERE '
        self.update_sql = f'UPDATE {table} SET '
        self.insert_sql = f'INSERT INTO {table} '
        self.delete_sql = f'DELETE FROM {table} WHERE '
        self.drop_sql = f'DROP TABLE {table}'
        self.count_sql = f'SELECT COUNT(*) FROM {table}'

    async def _execute(
        self, query: str, values: Iterable[Any] = None
    ) -> Cursor:

        if self.db is None:
            self.db = await self.conn

        self.db.row_factory = dict_factory  # type: ignore
        cursor = await self.db.execute(query, values)
        await self.db.commit()

        return cursor

    async def find(
        self, _dict: Dict[str, Any]
    ) -> Generator[Dict[str, Union[str, int]], None, None]:
        if not _dict:
            cursor = await self._execute(self.select_sql.split(' WHERE')[0])
        else:
            query, values = build_query(self.select_sql, _dict)
            cursor = await self._execute(query, values)

        # async generator to keep it similar to motor api
        for res in await cursor.fetchall():
            yield res

    async def find_one(
        self, _dict: Dict[str, Any]
    ) -> Optional[Dict[str, Union[str, int]]]:

        try:
            return await self.find(_dict).__anext__()
        except StopAsyncIteration:
            return None

    def _update(
        self, _dict: Dict[str, Any], opts: Dict[str, Dict[str, Any]]
    ) -> Tuple[str, List[Any]]:
        query, values = build_query(self.update_sql, opts['$set'], ' = ?,')
        # such an ugly way to remove trailing `,` and add `WHERE`
        query = query[:-1] + ' WHERE '

        query, x = build_query(query, _dict)
        values += x

        return query, values

    async def update(
        self, _dict: Dict[str, Any], opts: Dict[str, Dict[str, Any]],
        one: bool = False
    ) -> int:
        query, values = self._update(_dict, opts)
        cursor = await self._execute(query, values)

        if one:
            query += ' LIMIT 1'

        return cursor.rowcount

    async def update_one(
        self, _dict: Dict[str, Any], opts: Dict[str, Dict[str, Any]]
    ) -> bool:
        return await self.update(_dict, opts, True) == 1

    async def insert(self, data: Dict[str, Any]) -> int:
        query = f"{self.insert_sql}({','.join(data.keys())}) VALUES ("
        query, values = build_query(query, data, '?,', False)

        # such an ugly way to remove trailing `,` and add `)`
        query = query[:-1] + ')'

        cursor = await self._execute(query, values)

        return cursor.rowcount

    async def insert_one(self, data: Dict[str, Any]) -> bool:
        return await self.insert(data) == 1

    async def execute_raw(
        self, sql: str, params: Iterable[Any] = None
    ) -> Cursor:
        return await self._execute(sql, params)

    async def drop_collection(self) -> Cursor:
        return await self._execute(self.drop_sql)

    async def delete(self, data: Dict[str, Any], one: bool = False) -> int:
        query, values = build_query(self.delete_sql, data)

        if one:
            query += ' LIMIT 1'

        cursor = await self._execute(query, values)

        return cursor.rowcount

    async def delete_one(self, data: Dict[str, Any]) -> bool:
        return await self.delete(data, True) == 1

    async def count_documents(self, _dict: Dict[str, Any] = None) -> int:
        query = self.count_sql
        values = None

        if _dict is not None:
            query += ' WHERE '
            query, values = build_query(query, _dict)

        cursor = await self._execute(query, values)

        return (await cursor.fetchone())['COUNT(*)']


"""ola"""
import json
import requests
from app import app

token_whats = app.config['API_KEY_WHATS']
verify_token = app.config['VERIFY_TOKEN']

def send_msg(phone_number_id, from_number, respons):
    """ola"""
    payload = json.dumps({
        'messaging_product': 'whatsapp',
        "recipient_type": "individual",
        'to': from_number,
        "type": "text",
        'text': {
            "preview_url": False,
            'body': respons}
    })
    headers = {
        'Content-Type': 'application/json'
    }

    requests.post(
        f'https://graph.facebook.com/v16.0/{phone_number_id}/messages?access_token={token_whats}',
        headers=headers,
        data=payload
    )

def verify_webhook(mode, token, challenge):
    """ola"""
    if mode and token:
        if mode == 'subscribe' and token == verify_token:
            print('WEBHOOK_VERIFIED')
            return challenge, 200
        else:
            return 'Verification token mismatch', 403
    else:
        return 'Bad request', 400


from __future__ import annotations

import logging
import operator
from typing import (Callable, Dict, Hashable, Iterable, List, Optional, Set,
                    Tuple, TypeVar)

from .enums import Amphipod
from .map import (FieldType, PossibleMove, get_allowed_moves, get_move_energy,
                  move)

logger = logging.getLogger(__name__)

IDType = TypeVar("IDType", bound=Hashable)

def dfs(
    *,
    starting_board: Dict[FieldType, Optional[Amphipod]],
    target_board: Dict[FieldType, Optional[Amphipod]],
    possible_moves: List[PossibleMove[FieldType]],
    board_hasher: Callable[[Dict[FieldType, Optional[Amphipod]]], IDType]
) -> Tuple[List[PossibleMove[FieldType]], int]:
    ALMOST_INFINITY = 2 ** 31
    best_energy = ALMOST_INFINITY
    no_moves: List[PossibleMove[FieldType]] = []

    score_sort_key = operator.itemgetter(1)

    cache: Dict[IDType, Tuple[List[PossibleMove[FieldType]], int]] = {}

    def get_moves(
        current_board: Dict[FieldType, Optional[Amphipod]], current_energy: int
    ) -> Iterable[Tuple[PossibleMove[FieldType], int]]:
        allowed_moves = get_allowed_moves(current_board, possible_moves)
        moves_with_energy = (
            (allowed_move, get_move_energy(current_board, allowed_move))
            for allowed_move in allowed_moves
        )
        promising_moves = (
            (possible_move, move_energy)
            for possible_move, move_energy in moves_with_energy
            if current_energy + move_energy < best_energy
        )
        # Explore the state space based on current move cost heuristic
        return sorted(promising_moves, key=score_sort_key)

    def recursive_search(
        current_board: Dict[FieldType, Optional[Amphipod]],
        current_energy: int,
        # moves_so_far: List[PossibleMove[FieldType]],
    ) -> Tuple[List[PossibleMove[FieldType]], int]:
        """Returns a tuple: (moves to terminal stage, energy to terminal stage)."""
        nonlocal best_energy
        if current_board == target_board:
            if current_energy < best_energy:
                logger.debug("Found best terminal state %d", current_energy)
                best_energy = current_energy
            return no_moves, 0

        current_board_id = board_hasher(current_board)
        if current_board_id in cache:
            return cache[current_board_id]

        results: List[Tuple[List[PossibleMove[FieldType]], int]] = []
        for possible_move, move_energy in get_moves(current_board, current_energy):
            possible_board = move(current_board, possible_move)
            result_moves, result_energy = recursive_search(
                possible_board,
                current_energy + move_energy,
                # moves_so_far + [possible_move],
            )
            results.append(
                ([possible_move] + result_moves, result_energy + move_energy)
            )
        if not results:
            # blind alley
            solution = no_moves, ALMOST_INFINITY
            cache[current_board_id] = solution
            return solution
        results.sort(key=score_sort_key)

        (best_possibility_moves, best_possibility_score), *other_possibilities = results
        if current_energy + best_possibility_score < best_energy:
            logger.debug("Found best state %d", current_energy + best_possibility_score)
            best_energy = current_energy + best_possibility_score

        solution = (best_possibility_moves, best_possibility_score)
        cache[current_board_id] = solution
        return solution

    return recursive_search(starting_board, 0)


from datetime import datetime
from datetime import date
import numpy as np
import pandas as pd
from pandas_datareader import data as wb
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import norm
import yfinance as yf
import math
from get_all_tickers import get_tickers as gt
dict = ['GOOG', 'AAPL','MSFT','AMZN','NVDA']
valid_Input = False

# User ticker selection
while not valid_Input:
    x = 0
    val = -1
    tickerSelection = ""
    for tickers in dict:
        tickerSelection += tickers + " [" + str(x) + "] || "
        x += 1
    tickerSelection += "\n"
    try:
        val = int(input("Please enter the number for the ticker you would like to track: \n" + tickerSelection))
    except:
        print("Please input integer only...")
    # print("Yo"val)
    if val >= 0 and val <= len(dict) -1:
        valid_Input = True
    else:
        print("Invalid input please try again \n\n")

# Ticker data pull
ticker =  dict[val]
start = datetime(2019, 1, 1)
end = date.today()
data = yf.download(ticker, start , end)['Adj Close']
data.plot(figsize=(15,6))
plt.show()

# Plot daily log returns
log_returns = np.log(1 + data.pct_change())
sns.distplot(log_returns.iloc[1:])
plt.xlabel("Daily Return")
plt.ylabel("Frequency")
plt.show()

# Probablitiy calculations
u = log_returns.mean()
var = log_returns.var()
drift = u - (0.5*var)
stdev = log_returns.std()

# CAGR and initializations
days = 50
number_of_trials = 3000
time_elapsed = (data.index[-1] - data.index[0]).days
total_growth = (data[-1] / data[1])
number_of_years = time_elapsed / 365.0
cagr = total_growth ** (1/number_of_years) - 1
closing_prices = []
number_of_trading_days = 252

# Daily return percentages for the next 50 days and Monte Carlo Sim
for i in range(number_of_trials):
    daily_return_percentages = np.random.normal(cagr/number_of_trading_days, stdev/math.sqrt(number_of_trading_days),number_of_trading_days)+1
    price_series = [data[-1]]

    for j in daily_return_percentages:
        price_series.append(price_series[-1] * j)
    closing_prices.append(price_series[-1])
    plt.plot(price_series)
plt.show()

# Histogram of 50 day returns
plt.hist(closing_prices,bins=40)
plt.show()

# Expected price
mean_end_price = round(np.mean(closing_prices),2)
print("Expected price: $", str(mean_end_price))

# Modified histogram (Top & bottom 10 percentiles)
top_ten = np.percentile(closing_prices,100-10)
bottom_ten = np.percentile(closing_prices,10)
plt.hist(closing_prices,bins=40)
plt.axvline(top_ten,color='r',linestyle='dashed',linewidth=2)
plt.axvline(bottom_ten,color='r',linestyle='dashed',linewidth=2)
plt.axvline(data[-1],color='g', linestyle='dashed',linewidth=2)
plt.show()


import unittest
import mock

from .const import SQLConstant

from . import parse

def mock_type_parser(lex):
    lex.expect('TYPE')
    return SQLConstant('TYPE')

def mock_parse_expression(lex):
    return SQLConstant(lex.parse_identifier())

class TestParser(unittest.TestCase):

    def assert_sql(self, val, fsql):
        expr = parse(val)
        self.assertEqual(str(expr), fsql)

        nsql = expr.as_sql(False)
        nexpr = parse(nsql)
        self.assertEqual(str(expr), str(nexpr))

    def test_basic_sql(self):
        self.assert_sql('/* x */ select E1 as x, E2, E3 as i',
                        'SELECT E1 AS x,E2,E3 AS i;')

    def test_select(self):
        self.assert_sql('/* x */ select EXPR as x, EXPR, EXPR as i',
                        'SELECT EXPR AS x,EXPR,EXPR AS i;')
        self.assert_sql('select EXPR as fieldA where EXPR group by fieldA',
                        'SELECT EXPR AS fieldA WHERE EXPR GROUP BY fieldA;')
        self.assert_sql('/* x */ select EXPR as x, EXPR, EXPR as i',
                        'SELECT EXPR AS x,EXPR,EXPR AS i;')
        self.assert_sql('SELECT EXPR UNION ALL SELECT EXPR',
                        'SELECT EXPR UNION ALL SELECT EXPR;')

    def test_withselect(self):
        self.assert_sql('with x as (select E1) select E2 as fieldA '
                        'where WEXPR group by fieldA;',
                        'WITH x AS(SELECT E1)SELECT E2 AS fieldA '
                        'WHERE WEXPR GROUP BY fieldA;')

    # TODO(scannell): Generate SQL for user defined functions as well!
    @mock.patch('sql_parser.types.SQLType.parse',
                mock.MagicMock(side_effect=mock_type_parser))
    def test_function(self):
        self.assert_sql('CREATE TEMPORARY FUNCTION TO_STRING(x TYPE) AS'
                        ' FExpr; SELECT F;',
                        'CREATE TEMPORARY FUNCTION TO_STRING(x TYPE)'
                        'AS FExpr;SELECT F;')
        self.assert_sql('CREATE TEMPORARY FUNCTION stuff(x TYPE) '
                        'RETURNS TYPE '
                        'LANGUAGE js AS """ javascript stuff """;'
                        'SELECT F;',
                        'CREATE TEMPORARY FUNCTION stuff(x TYPE)'
                        'RETURNS TYPE LANGUAGE '
                        'js AS \' javascript stuff \';SELECT F;')

    def test_order_by(self):
        self.assert_sql('SELECT x FROM t ORDER BY tx,tb ASC',
                        'SELECT x FROM t ORDER BY tx,tb ASC;')
        self.assert_sql('SELECT x FROM t ORDER BY tx DESC,tb ASC',
                        'SELECT x FROM t ORDER BY tx DESC,tb ASC;')

    def test_limit_offset(self):
        self.assert_sql('SELECT x FROM t LIMIT 10', 'SELECT x FROM t LIMIT 10;')
        self.assert_sql('SELECT x FROM t LIMIT 10 OFFSET 10',
                        'SELECT x FROM t LIMIT 10 OFFSET 10;')

    def test_query_table(self):
        self.assert_sql('SELECT E1 FROM(SELECT stuff FROM t)f',
                        'SELECT E1 FROM(SELECT stuff FROM t)AS f;')

    def test_sub_query(self):
        self.assert_sql('(SELECT F1 FROM stuff)ORDER BY F2',
                        '(SELECT F1 FROM stuff)ORDER BY F2;')
        self.assert_sql('(SELECT F1 FROM stuff ORDER BY f)ORDER BY F2',
                        '(SELECT F1 FROM stuff ORDER BY f)ORDER BY F2;')
        self.assert_sql('select F1 FROM stuff ORDER BY F2',
                        'SELECT F1 FROM stuff ORDER BY F2;')

    def test_having(self):
        self.assert_sql('SELECT Fx FROM t GROUP BY Tx HAVING E1',
                        'SELECT Fx FROM t GROUP BY Tx HAVING E1;')

    def test_select_type(self):
        self.assert_sql('SELECT DISTINCT f FROM t', 'SELECT DISTINCT f FROM t;')
        self.assert_sql('SELECT ALL f FROM t', 'SELECT ALL f FROM t;')

    def test_sets(self):
        self.assert_sql('SELECT x FROM t UNION ALL SELECT x FROM t',
                        'SELECT x FROM t UNION ALL SELECT x FROM t;')
        self.assert_sql('SELECT x FROM t UNION DISTINCT SELECT x FROM t',
                        'SELECT x FROM t UNION DISTINCT SELECT x FROM t;')
        self.assert_sql('SELECT x FROM t INTERSECT DISTINCT SELECT x FROM t',
                        'SELECT x FROM t INTERSECT DISTINCT SELECT x FROM t;')
        self.assert_sql('SELECT x FROM t EXCEPT DISTINCT SELECT x FROM t',
                        'SELECT x FROM t EXCEPT DISTINCT SELECT x FROM t;')

    # TODO(scannell)
    # Sub-query as table
    def test_select_expr(self):
        self.assert_sql('SELECT X FROM Y WHERE F IN (SELECT A FROM B)',
                        'SELECT X FROM Y WHERE F IN(SELECT A FROM B);')
        self.assert_sql('SELECT (SELECT 1)',
                        'SELECT(SELECT 1);')

    def test_join(self):
        self.assert_sql('SELECT X FROM Table Y JOIN Table X ON Expr1',
                        'SELECT X FROM Table AS Y INNER JOIN Table AS X '
                        'ON Expr1;')
        self.assert_sql('SELECT X FROM Table Y LEFT JOIN Table X',
                        'SELECT X FROM Table AS Y LEFT JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y INNER JOIN Table X',
                        'SELECT X FROM Table AS Y INNER JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y CROSS JOIN Table X',
                        'SELECT X FROM Table AS Y CROSS JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y, Table X',
                        'SELECT X FROM Table AS Y CROSS JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y LEFT OUTER JOIN Table X',
                        'SELECT X FROM Table AS Y LEFT OUTER JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y RIGHT OUTER JOIN Table X',
                        'SELECT X FROM Table AS Y RIGHT OUTER JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y RIGHT JOIN Table X',
                        'SELECT X FROM Table AS Y RIGHT JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y FULL OUTER JOIN Table X',
                        'SELECT X FROM Table AS Y FULL OUTER JOIN Table AS X;')
        self.assert_sql('SELECT X FROM Table Y FULL JOIN Table X',
                        'SELECT X FROM Table AS Y FULL JOIN Table AS X;')

    def test_truncate(self):
        self.assert_sql('TRUNCATE  TABLE /* b */ tt;',
                        'TRUNCATE TABLE tt;')
        self.assert_sql('truncate table td_address',
                        'TRUNCATE TABLE td_address;')

    def test_multiple(self):
        self.assert_sql('TRUNCATE  TABLE /* b */ tt;  SELECT  1;',
                        'TRUNCATE TABLE tt;SELECT 1;')

    def test_insert(self):
        self.assert_sql('INSERT INTO TABLE (a,b,c) SELECT 1,2;',
                        'INSERT INTO TABLE(a,b,c)SELECT 1,2;')
        self.assert_sql('INSERT INTO TABLE (SELECT 1,2);',
                        'INSERT INTO TABLE(SELECT 1,2);')

    def test_generate_stats(self):
        self.assert_sql('GENERATE   STATistics on blah;',
                        'GENERATE STATISTICS ON blah;')
        self.assert_sql('GROOM TABLE x RECORDS ALL;',
                        'GROOM TABLE x RECORDS ALL;')

    def test_update(self):
        self.assert_sql('UPDATE x SET f1=1, f2=a+b FROM y WHERE TRUE',
                        'UPDATE x SET f1=1,f2=a+b FROM y WHERE TRUE;')

    def test_trans(self):
        self.assert_sql('BEGIN;', 'BEGIN;')

    def test_sql_expr(self):
        self.assert_sql('select ((select 1) + 2)',
                        'SELECT((SELECT 1)+2);')

    def test_sql_delete(self):
        self.assert_sql('DELETE from y where true',
                        'DELETE FROM y WHERE TRUE;')

    def test_sql_create(self):
        self.assert_sql('CREATE OR replace TABLE y (i INT64, b BOOL) AS SELECT 1',
                        'CREATE OR REPLACE TABLE y(i INT64,b BOOL)AS SELECT 1;')


"""Deprecated"""

from enum import Enum

class FocusLevel(Enum):
    """Enum class for focus levels of the deepspeech model scorer."""
    POS_HIGH = 10.5
    POS_MEDIUM = 6.0
    POS_LOW = 1.0
    NEG_LOW = -1.0
    NEG_MEDIUM = -4.0
    NEG_HIGH = -9.0

def init_words_focus_assets():
    """Initialize the focus assets for the deepspeech model scorer."""
    regular_neg_lo_focus = [
        'lo', 'then', 'altaforte', 'generate', 'la',
        'stan', 'plate', 'his', 'her',
        'theology', 'once', 'me',
            # maybe not so much ->
        'and', 'leg', 'so', 'some', 'little', 'a',
        'simple', 'better', 'bade', 'matter', 'them',
    ]
    regular_pos_lo_focus = [
        'close',
        'hold',
        'turn', 'on', 'off',
        'rock', 'show', 'screen',
        'read',
        'measurement',
        'monitoring', 'heart',
        'stand',
        'start', 'recording',
        'sub',
        'gas',
        'red',
    ]

    regular_pos_med_focus = [
        'heads', 'up', 'display', 'show',
        'sample', 'rate', 'rock',
        # 'respiratory', 'note',
         'map', 'terrain',
        # 'green', 'blue',
        # 'pin',
        'read', 'condition', 'suit',
        'path',
        'checklist',
        'data',
        # 'take', 'photo',
        # 'audio',
        # 'toggle',
        'set', 'north',
        # 'finder',
        'geology', 'level',
    ]

    regular_pos_hi_focus = [
        'map', 'terrain', 'battery', 'oxygen', 'rate',
        'pin', 'road', 'tag', 'hide',
        'vitals',
    ]

    tagging_med_focus = [
        'measurement', 'rock', 'regolith', 'coordinates', 'sun',
        'shining', 'shine', 'visbility', 'outcrop', 'poor', 'optimal',
        'boulder', 'outskirts', 'crater', 'rim',
        'landslide', 'lava', 'flow', 'PSR', 'contacts', 'lithologies',
        'pick', 'hammer', 'tools', 'used', 'using', 'use',
        'fist-sized', 'fist', 'shape', 'dimension', 'measures', 'centimeters',
        'inches', 'chip off', 'chip', 'fragment', 'scoop', 'material',
        'range', 'appearance',
        'color', 'dark', 'gray', 'basalts', 'white', 'anorthosites', 'mottled',
        'breccias', 'black', 'green', 'glass', 'beads',
        'appearance' , 'texture', 'fine', 'grained', 'coarse',
        'vesiculated', 'coherent', 'brecciated', 'friable',
        'make out', 'variety', 'clasts', 'shiny', 'ilmenite',
        'opaque', 'phases', 'initial', 'geologic',
        'interpretation', 'origin', 'breccia', 'formed', 'impacts',
        'anorthosite', 'represents', 'Moon’s', 'primary', 'crust',
        'secondary', 'rock', 'over',
    ]

    tagging_hi_focus = [
        'volcanic', 'orange', 'exit'
    ]

    tmp = _remove_from([regular_pos_lo_focus,
                            regular_pos_med_focus,
                            regular_pos_hi_focus])
    regular_pos_lo_focus = tmp[0]
    regular_pos_med_focus = tmp[1]
    regular_pos_hi_focus = tmp[2]

    tmp = _remove_from([tagging_med_focus,
                            tagging_hi_focus,
                            regular_pos_lo_focus,
                            regular_pos_med_focus,
                            regular_pos_hi_focus])

    tagging_med_focus = tmp[0]
    tagging_hi_focus = tmp[1]

    return [
        regular_neg_lo_focus,
        regular_pos_lo_focus,
        regular_pos_med_focus,
        regular_pos_hi_focus,
        tagging_med_focus,
        tagging_hi_focus
    ]

def _remove_from(lists):
    """Auxillary Function for init_words_focus_assets"""
    newListOfSets = []
    for i in range(len(lists)-1):
        othersSet = set()
        for other in lists[i+1:]:
            othersSet = othersSet.union(set(other))
        aSet = set(lists[i]).difference(othersSet)
        newListOfSets.append(aSet)
    newListOfSets.append(set(lists[-1]))
    return newListOfSets


from random import randint

from PyQt5 import QtWidgets, QtCore, QtGui

from src.games.button_shooter.form_widget import FormWidget
from src.database_managers.game_database_management import GameDatabaseManagement
from src.games.game import Game

class ButtonShooterWindow(Game, FormWidget, QtWidgets.QWidget):
    database_path = "src/databases/button_shooter.csv"
    header = ["unlocked_level", "hit_targets"]
    default_values = [1, 0]
    max_level = 20

    def __init__(self, username):
        QtWidgets.QWidget.__init__(self)
        self.countdown = None
        self.game_database = None
        self.hit_targets = None
        self.missed_targets = None
        self.required_targets = None
        self.selected_button = None
        self.selected_level = None
        self.timer = None
        self.data_to_update = {"hit_targets": "hit_targets"}
        self.initialize_database(username)

    def initialize_database(self, username):
        self.game_database = GameDatabaseManagement(self.database_path, username, self.header)
        self.game_database.initialize_user_account(self.default_values)

    def play_game(self, selected_level):
        self.initialize_game(selected_level)
        self.show()
        text = f"Required Targets: {self.required_targets}.\nDo you want to start?"
        user_decision = self.show_start_screen(text)
        if user_decision == QtWidgets.QMessageBox.AcceptRole:
            self.timer.start(1000)
            self.show_buttons()
        elif user_decision == QtWidgets.QMessageBox.RejectRole:
            self.emit_game_menu_signal()

    def initialize_game(self, selected_level):
        self.countdown = 100
        self.hit_targets = 0
        self.missed_targets = 0
        self.required_targets = self.get_required_target(selected_level)
        self.selected_level = selected_level
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.update_timer)
        FormWidget.__init__(self, self, self.selected_level, self.countdown)
        Game.__init__(self, self.game_database, self.selected_level)
        self.connect_buttons_to_game()

    @staticmethod
    def get_required_target(level):
        return level * 5 + 50

    def update_timer(self):
        self.countdown -= 1
        self.countdown_label.setText(f"Time left: {self.countdown}")
        if self.countdown == 0:
            self.timer.stop()
            self.end_the_game()

    def update_values(self):
        values = self.game_database.get_values()
        updated_values = {}
        for header_name, variable_name in self.data_to_update.items():
            value = getattr(self, variable_name)
            if value > values[header_name]:
                updated_values[header_name] = value
        self.game_database.update_values(updated_values)

    def end_the_game(self):
        self.update_values()
        if self.hit_targets >= self.required_targets:
            if self.selected_level == self.max_level:
                self.show_every_level_completed()
                self.emit_game_menu_signal()
            else:
                self.unlock_next_level(self.selected_level)
                text = f"Congratulation you won!\nYou hit {self.hit_targets} targets and you missed " \
                       f"{self.missed_targets} targets."
                user_decision = self.show_selection_for_next_game(text)
                self.game_database.save_user_data()
                if user_decision == QtWidgets.QMessageBox.AcceptRole:
                    self.emit_game_menu_signal()
                elif user_decision == QtWidgets.QMessageBox.RejectRole:
                    self.emit_play_next_level_signal()
        else:
            self.game_database.save_user_data()
            text = f"Unfortunately you lost! You only hit {self.hit_targets} targets of required " \
                   f"{self.required_targets} targets. You missed {self.missed_targets} targets."
            user_decision = self.show_losing_screen(text)
            if user_decision == QtWidgets.QMessageBox.DestructiveRole:
                self.emit_level_menu_signal()
            elif user_decision == QtWidgets.QMessageBox.AcceptRole:
                self.emit_game_menu_signal()
            elif user_decision == QtWidgets.QMessageBox.RejectRole:
                self.emit_play_level_again_signal()

    def connect_buttons_to_game(self):
        self.game_menu_button.clicked.connect(self.emit_game_menu_signal)
        self.level_selection_button.clicked.connect(self.emit_level_menu_signal)
        for button in self.button_list:
            button.clicked.connect(self.is_clicked_button_the_proper_target)

    def is_clicked_button_the_proper_target(self):
        if self.sender() is self.selected_button:
            self.change_button_background(self.selected_button, self.palette().color(QtGui.QPalette.Background))
            self.show_buttons()
            self.hit_targets += 1
            self.hit_target_label.setText(f"Hit Targets: {self.hit_targets}")
        else:
            self.change_button_background(self.selected_button, self.palette().color(QtGui.QPalette.Background))
            self.show_buttons()
            self.missed_targets += 1

    @staticmethod
    def change_button_background(button, color):
        button.setStyleSheet(f"background-color: {color}")

    def show_buttons(self):
        self.selected_button = self.button_list[randint(0, len(self.button_list)-1)]
        self.change_button_background(self.selected_button, "red")


from ninja import Router
from app_camera.models import CameraSetting
from app_event.models import ColorROIControl
from .util.detect import Color

router = Router()
color = Color()

@router.get('/result/{camera_id}', tags=['color', 'detect'])
def get_color_result(request, camera_id:int):
    from app_camera.apps import AppCameraConfig
    try:
        cam_name = CameraSetting.objects.get(id=camera_id).camera_name
    except:
        return {'is_saved' : False}

    try:
        roi_object = ColorROIControl.objects
        roi_control = roi_object.filter(camera_id=camera_id)
        if len(roi_control) == 0:
            raise f'query empty {roi_control}'
    except:
        roi_object.create(camera_id=camera_id, color_select='red')
        roi_object.create(camera_id=camera_id, color_select='green')
        roi_object.create(camera_id=camera_id, color_select='blue')
        roi_control = roi_object.filter(camera_id=camera_id)
    data = dict()

    for cam, rtsp, name in AppCameraConfig.camera_list:
        if name == cam_name:
            data = cam.q.get()

    if len(data) == 0 :
        print('color data missing')
        print(AppCameraConfig.camera_list)
        return {'is_saved' : False}

    is_alive = data['is_alive']
    if not is_alive:
        print(f'is_alive : {is_alive}')
        return {'is_saved' : False}

    image = data['image']
    roi_list = list()
    for roi in roi_control:
        roi_list.append((roi.color_x, roi.color_y, roi.color_w, roi.color_h))

    JSON = color.color(image, roi_list)

    # log save
    from app_log.models import ColorLog
    log = ColorLog.objects
    log.create(
        camera_id = camera_id,
        red = JSON['color']['red'],
        green = JSON['color']['green'],
        blue = JSON['color']['blue'],
    )

    return JSON


import pandas as pd
import pandas_flavor as pf
from typing import Union, Optional
from functools import partial
import plotly.graph_objects as go

from plotnine import *

from pytimetk.plot.plot_timeseries import plot_timeseries
from pytimetk.utils.plot_helpers import hex_to_rgba, rgba_to_hex, parse_rgba
from pytimetk.utils.checks import check_dataframe_or_groupby, check_date_column, check_anomalize_data
from pytimetk.plot.theme import theme_timetk

@pf.register_dataframe_method
def plot_anomalies(
    data: Union[pd.DataFrame, pd.core.groupby.generic.DataFrameGroupBy],
    date_column: str,

    facet_ncol: int = 1,
    facet_nrow: Optional[int] = None,
    facet_scales: str = "free_y",
    facet_dir: str = "h",

    line_color: str = "#2c3e50",
    line_size: Optional[float] = None,
    line_type: str = 'solid',
    line_alpha: float = 1.0,

    anom_color: str = "#E31A1C",
    anom_alpha: float = 1.0,
    anom_size: Optional[float] = None,

    ribbon_fill: str = "#646464",
    ribbon_alpha: float = 0.2,

    y_intercept: Optional[float] = None,
    y_intercept_color: str = "#2c3e50",
    x_intercept: Optional[str] = None,
    x_intercept_color: str = "#2c3e50",

    legend_show: bool = True,

    title: str = "Anomaly Plot",
    x_lab: str = "",
    y_lab: str = "",
    color_lab: str = "Legend",

    x_axis_date_labels: str = "%b %Y",
    base_size: float = 11,
    width: Optional[int] = None,
    height: Optional[int] = None,

    engine: str = 'plotly'
):
    '''
    The `plot_anomalies` function is used to plot anomalies in a time series
    data using either Plotly, Matplotlib, or Plotnine. See `anomalize()` for the
    function to prepare the data for plotting.

    Parameters
    ----------
    data : Union[pd.DataFrame, pd.core.groupby.generic.DataFrameGroupBy]
        The input data for the plot. It can be either a pandas DataFrame or a
        pandas DataFrameGroupBy object.
    date_column : str
        The `date_column` parameter is a string that specifies the name of the
        column in the dataframe that contains the dates for the plot.
    facet_ncol : int, optional
        The `facet_ncol` parameter determines the number of columns in the facet
        grid. It specifies how many subplots will be arranged horizontally in
        the plot.
    facet_nrow : int
        The `facet_nrow` parameter determines the number of rows in the facet
        grid. It specifies how many subplots will be arranged vertically in the
        grid.
    facet_scales : str, optional
        The `facet_scales` parameter determines the scaling of the y-axis in the
        facetted plots. It can take the following values:
        - "free_y": The y-axis scale will be free for each facet, but the x-axis
          scale will be fixed for all facets. This is the default value.
        - "free_x": The y-axis scale will be free for each facet, but the x-axis
          scale will be fixed for all facets.
        - "free": The y-axis scale will be free for each facet (subplot). This
          is the default value.

    facet_dir : str, optional
        The `facet_dir` parameter determines the direction in which the facets
        (subplots) are arranged. It can take two possible values:
        - "h": The facets will be arranged horizontally (in rows). This is the
          default value.
        - "v": The facets will be arranged vertically (in columns).
    line_color : str, optional
        The `line_color` parameter is used to specify the color of the lines in
        the time series plot. It accepts a string value representing a color
        code or name. The default value is "#2c3e50", which corresponds to a
        dark blue color.
    line_size : float
        The `line_size` parameter is used to specify the size of the lines in
        the time series plot. It determines the thickness of the lines.
    line_type : str, optional
        The `line_type` parameter is used to specify the type of line to be used
        in the time series plot.
    line_alpha : float
        The `line_alpha` parameter controls the transparency of the lines in the
        time series plot. It accepts a value between 0 and 1, where 0 means
        completely transparent (invisible) and 1 means completely opaque (solid).
    anom_color : str, optional
        The `anom_color` parameter is used to specify the color of the anomalies
        in the plot. It accepts a string value representing a color code or name.
        The default value is `#E31A1C`, which corresponds to a shade of red.
    anom_alpha : float
        The `anom_alpha` parameter controls the transparency (alpha) of the
        anomaly points in the plot. It accepts a float value between 0 and 1,
        where 0 means completely transparent and 1 means completely opaque.
    anom_size : Optional[float]
        The `anom_size` parameter is used to specify the size of the markers
        used to represent anomalies in the plot. It is an optional parameter,
        and if not provided, a default value will be used.
    ribbon_fill : str, optional
        The `ribbon_fill` parameter is used to specify the fill color of the
        ribbon that represents the range of anomalies in the plot. It accepts a
        string value representing a color code or name.
    ribbon_alpha : float
        The parameter `ribbon_alpha` controls the transparency of the ribbon
        fill in the plot. It accepts a float value between 0 and 1, where 0
        means completely transparent and 1 means completely opaque. A higher
        value will make the ribbon fill more visible, while a lower value will
        make it
    y_intercept : float
        The `y_intercept` parameter is used to add a horizontal line to the plot
        at a specific y-value. It can be set to a numeric value to specify the
        y-value of the intercept. If set to `None` (default), no y-intercept
        line will be added to the plot
    y_intercept_color : str, optional
        The `y_intercept_color` parameter is used to specify the color of the
        y-intercept line in the plot. It accepts a string value representing a
        color code or name. The default value is "#2c3e50", which corresponds to
        a dark blue color. You can change this value.
    x_intercept : str
        The `x_intercept` parameter is used to add a vertical line at a specific
        x-axis value on the plot. It is used to highlight a specific point or
        event in the time series data.
        - By default, it is set to `None`, which means no vertical line will be
          added.
        - You can use a date string to specify the x-axis value of the intercept.
          For example, "2020-01-01" would add a vertical line at the beginning
          of the year 2020.
    x_intercept_color : str, optional
        The `x_intercept_color` parameter is used to specify the color of the
        vertical line that represents the x-intercept in the plot. By default,
        it is set to "#2c3e50", which is a dark blue color. You can change this
        value to any valid color code.
    legend_show : bool, optional
        The `legend_show` parameter is a boolean indicating whether or not to
        show the legend in the plot. If set to True, the legend will be
        displayed. The default value is True.
    title : str, optional
        The title of the plot.
    x_lab : str
        The `x_lab` parameter is used to specify the label for the x-axis in the
        plot. It is a string that represents the label text.
    y_lab : str
        The `y_lab` parameter is used to specify the label for the y-axis in the
        plot. It is a string that represents the label for the y-axis.
    color_lab : str, optional
        The `color_lab` parameter is used to specify the label for the legend or
        color scale in the plot. It is used to provide a description of the
        colors used in the plot, typically when a color column is specified.
    x_axis_date_labels : str, optional
        The `x_axis_date_labels` parameter is used to specify the format of the
        date labels on the x-axis of the plot. It accepts a string representing
        the format of the date labels. For  example, "%b %Y" would display the
        month abbreviation and year (e.g., Jan 2020).
    base_size : float, optional
        The `base_size` parameter is used to set the base font size for the plot.
        It determines the size of the text elements such as axis labels, titles,
        and legends.
    width : int
        The `width` parameter is used to specify the width of the plot. It
        determines the horizontal size of the plot in pixels.
    height : int
        The `height` parameter is used to specify the height of the plot in
        pixels. It determines the vertical size of the plot when it is rendered.
    engine : str, optional
        The `engine` parameter specifies the plotting library to use for
        creating the time series plot. It can take one of the following values:

        - "plotly" (interactive): Use the plotly library to create the plot.
           This is the default value.
        - "plotnine" (static): Use the plotnine library to create the plot.
          This is the default value.
        - "matplotlib" (static): Use the matplotlib library to create the plot.

    Returns
    -------
        A plot object, depending on the specified `engine` parameter:
        - If `engine` is set to 'plotnine' or 'matplotlib', the function returns
          a plot object that can be further customized or displayed.
        - If `engine` is set to 'plotly', the function returns a plotly figure
          object.

    See Also
    --------
    `anomalize()`: The `anomalize()` function is used to prepare the data for
                   plotting anomalies in a time series data.

    Examples
    --------
    ```{python}
    # EXAMPLE 1: SINGLE TIME SERIES
    import pytimetk as tk
    import pandas as pd
    import numpy as np

    # Create a date range
    date_rng = pd.date_range(start='2021-01-01', end='2024-01-01', freq='MS')

    # Generate some random data with a few outliers
    np.random.seed(42)
    data = np.random.randn(len(date_rng)) * 10 + 25
    data[3] = 100  # outlier

    # Create a DataFrame
    df = pd.DataFrame(date_rng, columns=['date'])
    df['value'] = data

    # Anomalize the data
    anomalize_df = tk.anomalize(
        df, "date", "value",
        method = "twitter",
        iqr_alpha = 0.10,
        clean_alpha = 0.75,
        clean = "min_max",
    )
    ```

    ``` {python}
    # Visualize the anomaly bands, plotly engine
    (
         anomalize_df
            .plot_anomalies(
                date_column = "date",
                engine = "plotly",
            )
    )
    ```

    ``` {python}
    # Visualize the anomaly bands, plotly engine
    (
         anomalize_df
            .plot_anomalies(
                date_column = "date",
                engine = "plotnine",
            )
    )
    ```

    ``` {python}
    # EXAMPLE 2: MULTIPLE TIME SERIES
    import pytimetk as tk
    import pandas as pd

    df = tk.load_dataset("walmart_sales_weekly", parse_dates=["Date"])[["id", "Date", "Weekly_Sales"]]

    anomalize_df = (
        df
            .groupby('id')
            .anomalize(
                "Date", "Weekly_Sales",
                period = 52,
                trend = 52,
                threads = 1
            )
    )
    ```

    ``` {python}
    # Visualize the anomaly bands, plotly engine
    (
        anomalize_df
            .groupby(["id"])
            .plot_anomalies(
                date_column = "Date",
                facet_ncol = 2,
                width = 800,
                height = 800,
                engine = "plotly",
            )
    )
    ```

    ``` {python}
    # Visualize the anomaly bands, matplotlib engine
    (
        anomalize_df
            .groupby(["id"])
            .plot_anomalies(
                date_column = "Date",
                facet_ncol = 2,
                width = 800,
                height = 800,
                engine = "matplotlib",
            )
    )
    ```

    '''

    # Check data
    check_dataframe_or_groupby(data)
    check_date_column(data, date_column)

    # Check data was anomalized first
    check_anomalize_data(data)

    # Handle line_size
    if line_size is None:
        if engine == 'plotnine':
            line_size = 0.35
        elif engine == 'matplotlib':
            line_size = 0.35
        elif engine == 'plotly':
            line_size = 0.65

    if engine == 'plotly':

        if anom_size is None:
            anom_size = 6.0

        fig = _plot_anomalies_plotly(
            data = data,
            date_column = date_column,

            facet_ncol = facet_ncol,
            facet_nrow = facet_nrow,
            facet_scales = facet_scales,
            facet_dir = facet_dir,

            line_color = line_color,
            line_size = line_size,
            line_type = line_type,
            line_alpha = line_alpha,

            anom_color = anom_color,
            anom_alpha = anom_alpha,
            anom_size = anom_size,

            ribbon_fill = ribbon_fill,
            ribbon_alpha = ribbon_alpha,

            y_intercept = y_intercept,
            y_intercept_color = y_intercept_color,
            x_intercept = x_intercept,
            x_intercept_color = x_intercept_color,

            legend_show = legend_show,

            title = title,
            x_lab = x_lab,
            y_lab = y_lab,
            color_lab = color_lab,

            x_axis_date_labels = x_axis_date_labels,
            base_size = base_size,
            width = width,
            height = height,
        )

    else:

        if anom_size is None:
            anom_size = 1.0

        fig = _plot_anomalies_plotnine(
            data = data,
            date_column = date_column,

            facet_ncol = facet_ncol,
            facet_nrow = facet_nrow,
            facet_scales = facet_scales,
            facet_dir = facet_dir,

            line_color = line_color,
            line_size = line_size,
            line_type = line_type,
            line_alpha = line_alpha,

            anom_color = anom_color,
            anom_alpha = anom_alpha,
            anom_size = anom_size,

            ribbon_fill = ribbon_fill,
            ribbon_alpha = ribbon_alpha,

            y_intercept = y_intercept,
            y_intercept_color = y_intercept_color,
            x_intercept = x_intercept,
            x_intercept_color = x_intercept_color,

            legend_show = legend_show,

            title = title,
            x_lab = x_lab,
            y_lab = y_lab,
            color_lab = color_lab,

            x_axis_date_labels = x_axis_date_labels,
            base_size = base_size,
            width = width,
            height = height,
        )

        if engine == 'matplotlib':
            if width == None:
                width_size = 800 # in pixels for compat with plotly
            else:
                width_size = width

            if height == None:
                height_size = 600 # in pixels for compat with plotly
            else:
                height_size = height
            fig = fig + theme_timetk(height=height_size, width=width_size) # setting default figure size to prevent matplotlib sizing error
            fig = fig.draw()

    return fig

# Monkey patch the method to pandas groupby objects
pd.core.groupby.generic.DataFrameGroupBy.plot_anomalies = plot_anomalies

def _plot_anomalies_plotly(
    data: Union[pd.DataFrame, pd.core.groupby.generic.DataFrameGroupBy],
    date_column: str,

    facet_ncol: int = 1,
    facet_nrow: Optional[int] = None,
    facet_scales: str = "free_y",
    facet_dir: str = "h",

    line_color: str = "#2c3e50",
    line_size: float = None,
    line_type: str = 'solid',
    line_alpha: float = 1.0,

    anom_color: str = "#E31A1C",
    anom_alpha: float = 1.0,
    anom_size: Optional[float] = None,

    ribbon_fill: str = "#646464",
    ribbon_alpha: float = 0.2,

    y_intercept: Optional[float] = None,
    y_intercept_color: str = "#2c3e50",
    x_intercept: Optional[str] = None,
    x_intercept_color: str = "#2c3e50",

    legend_show: bool = True,

    title: str = "Anomaly Plot",
    x_lab: str = "",
    y_lab: str = "",
    color_lab: str = "Legend",

    x_axis_date_labels: str = "%b %Y",
    base_size: float = 11,
    width: Optional[int] = None,
    height: Optional[int] = None,

):

    # Plot Setup
    ribbon_color_rgba = hex_to_rgba(ribbon_fill, ribbon_alpha)
    ribbon_color_rgba_list = parse_rgba(ribbon_color_rgba)
    ribbon_color_hex = rgba_to_hex(*ribbon_color_rgba_list)

    preload_plot_timeseries = partial(
        plot_timeseries,

        date_column = date_column,
        value_column = "_val",
        color_column = "_var",

        color_palette = [
            line_color,
            ribbon_color_hex,
            ribbon_color_hex
        ],

        facet_ncol = facet_ncol,
        facet_nrow = facet_nrow,
        facet_scales = facet_scales,
        facet_dir = facet_dir,

        line_size = line_size,
        line_type = line_type,
        line_alpha = line_alpha,

        y_intercept = y_intercept,
        y_intercept_color = y_intercept_color,
        x_intercept = x_intercept,
        x_intercept_color = x_intercept_color,

        smooth = False,

        legend_show = legend_show,

        title = title,
        x_lab = x_lab,
        y_lab = y_lab,
        color_lab = color_lab,

        x_axis_date_labels = x_axis_date_labels,

        base_size = base_size,

        width = width,
        height = height,
    )

    # 1.0 Create Main Plot

    is_grouped = False
    if isinstance(data, pd.core.groupby.generic.DataFrameGroupBy):
        group_names = data.grouper.names
        data = data.obj.copy()
        is_grouped = True
    else:
        data = data.copy()
        data["_grp"] = "Time Series"
        group_names = ["_grp"]

    data[group_names] = data[group_names].astype(str)

    data_prepared = (
        data[[*group_names, date_column, "observed", "recomposed_l1", "recomposed_l2"]]
            .melt(
                id_vars = [*group_names, date_column],
                value_name='_val',
                var_name = '_var',
            )
            .groupby(group_names)
    )

    fig = preload_plot_timeseries(data_prepared)

    # 2.0 Add Ribbon

    # Create lists to store the indices of the traces
    indices_l1 = [i for i, trace in enumerate(fig.data) if trace.name == 'recomposed_l1']
    indices_l2 = [i for i, trace in enumerate(fig.data) if trace.name == 'recomposed_l2']

    # Transparent color for the line (line will be invisible)
    transparent_rgba = 'rgba(0,0,0,0)'

    # Iterate through the pairs of indices and update the fig.data list
    for i_l1, i_l2 in zip(indices_l1, indices_l2):
        # Ensure 'recomposed_l1' comes before 'recomposed_l2' in fig.data
        if i_l1 > i_l2:
            i_l1, i_l2 = i_l2, i_l1  # Swap the indices
            fig.data[i_l1], fig.data[i_l2] = fig.data[i_l2], fig.data[i_l1]  # Swap the traces

        # Update the 'recomposed_l1' and 'recomposed_l2' traces to have transparent lines
        fig.data[i_l1].update(line=dict(color=transparent_rgba))
        fig.data[i_l2].update(line=dict(color=transparent_rgba), fill='tonexty', fillcolor=ribbon_color_rgba)  # Adjust fill color as needed

    # 3.0 Add Anomaly Dots

    # Assuming fig.data contains your trace data and you know the number of subplots
    num_subplots = len(fig.layout.annotations)  # Assuming each subplot has one annotation

    def generate_triples(n):
        result = []
        for i in range(1, 3*n+1, 3):
            result.append([i, i+1, i+2])
        return result

    tripples = generate_triples(num_subplots)

    for tripple in tripples:

        observed_data = None
        recomposed_l2_data = None
        recomposed_l1_data = None

        for i in tripple:
            i-=1

            # Extract data for each subplot

            if fig.data[i].name == 'observed':
                observed_data = fig.data[i]
            elif fig.data[i].name == 'recomposed_l1':
                recomposed_l1_data = fig.data[i]
                fig.data[i].legendgroup = "bands"
                fig.data[i].showlegend = False
            elif fig.data[i].name == 'recomposed_l2':
                recomposed_l2_data = fig.data[i]
                fig.data[i].legendgroup = "bands"
                fig.data[i].showlegend = False

        # Ensure we have the data
        if observed_data and recomposed_l2_data and recomposed_l1_data:
            # Identify points where condition is met
            x_values = []
            y_values = []
            for x, y, y_l2, y_l1 in zip(observed_data.x, observed_data.y, recomposed_l2_data.y, recomposed_l1_data.y):
                if y > y_l2 or y < y_l1:
                    x_values.append(x)
                    y_values.append(y)

            # Add scatter plot with identified points to the correct subplot
            fig.add_trace(
                go.Scatter(
                    x=x_values,
                    y=y_values,
                    mode='markers',
                    marker=dict(
                        color=anom_color,
                        size=anom_size,
                        opacity=anom_alpha,
                    ),
                    name='anomalies',
                    legendgroup='anomalies',
                    xaxis=observed_data.xaxis,
                    yaxis=observed_data.yaxis
                )
            )

    # Remove duplicate legends in each legend group
    seen_legendgroups = set()
    for trace in fig.data:
        legendgroup = trace.legendgroup
        if legendgroup in seen_legendgroups:
            trace.showlegend = False
        else:
            seen_legendgroups.add(legendgroup)

    # Cleanup annotations if ungrouped
    if not is_grouped:
        fig['layout']['annotations'] = []

    # Apply Styling
    fig.update_annotations(font_size=base_size*0.8)
    fig.update_traces(hoverlabel=dict(font_size=base_size*0.8))

    return fig

def _plot_anomalies_plotnine(
    data: Union[pd.DataFrame, pd.core.groupby.generic.DataFrameGroupBy],
    date_column: str,

    facet_ncol: int = 1,
    facet_nrow: Optional[int] = None,
    facet_scales: str = "free_y",
    facet_dir: str = "h",

    line_color: str = "#2c3e50",
    line_size: float = None,
    line_type: str = 'solid',
    line_alpha: float = 1.0,

    anom_color: str = "#E31A1C",
    anom_alpha: float = 1.0,
    anom_size: Optional[float] = None,

    ribbon_fill: str = "#646464",
    ribbon_alpha: float = 0.2,

    y_intercept: Optional[float] = None,
    y_intercept_color: str = "#2c3e50",
    x_intercept: Optional[str] = None,
    x_intercept_color: str = "#2c3e50",

    legend_show: bool = True,

    title: str = "Anomaly Plot",
    x_lab: str = "",
    y_lab: str = "",
    color_lab: str = "Legend",

    x_axis_date_labels: str = "%b %Y",
    base_size: float = 11,
    width: Optional[int] = None,
    height: Optional[int] = None,

):

    if isinstance(data, pd.core.groupby.generic.DataFrameGroupBy):

        group_names = data.grouper.names

        data = data.obj.copy()

        if not isinstance(group_names, list):
            group_names = [group_names]

        data[group_names] = data[group_names].astype(str)

        data["_group_names"] =  data[group_names].agg(" | ".join, axis=1)

    else:
        group_names = None
        data = data.copy()

    # 1.0 Canvas
    if group_names is not None:
        g = ggplot(
                data = data,
                mapping = aes(
                    x = date_column,
                    y = "observed",
                    group = "_group_names",
                )
            )

    else:
        g = ggplot(
            data = data,
            mapping = aes(
                x = date_column,
                y = "observed",
            )
        )

    # 2.0 Add facets
    if group_names is not None:
       g = g + facet_wrap(
            "_group_names",
            ncol = facet_ncol,
            nrow = facet_nrow,
            scales = facet_scales,
            dir = facet_dir,
            shrink = True
        )

    # 3.0 Add Ribbons
    g = g + geom_ribbon(
        mapping = aes(
            ymin = "recomposed_l1",
            ymax = "recomposed_l2",
        ),
        alpha = ribbon_alpha,
        fill  = ribbon_fill,
    )

    # 4.0 Add Lines
    g = g + \
        geom_line(
            color    = line_color,
            size     = line_size,
            linetype = line_type,
            alpha    = line_alpha
        )

    # 5.0 Add Anomalies
    if anom_size is None:
        anom_size = 2.0

    g = g + \
        geom_point(
            mapping = aes(
                x = date_column,
                y = "observed",
            ),
            color = anom_color,
            size = anom_size,
            alpha = anom_alpha,
            data = data.query("observed > recomposed_l2 | observed < recomposed_l1"),
        )

    # Add a Y-Intercept if desired
    if y_intercept is not None:
        g = g \
            + geom_hline(
                yintercept = y_intercept,
                color = y_intercept_color
        )

    # Add a X-Intercept if desired
    if x_intercept is not None:
        g = g \
            + geom_vline(
                xintercept = x_intercept,
                color = x_intercept_color
        )

    # Add theme & labs
    g = g + labs(x = x_lab, y = y_lab, title = title, color = color_lab)

    # Add scale to X
    g = g + scale_x_datetime(date_labels = x_axis_date_labels)

    # Add theme
    g = g + \
        theme_timetk(base_size=base_size, width = width, height = height)

    if not legend_show:
        g = g + theme(legend_position='none')

    return g


from sys import argv
from time import sleep, time

import pandas as pd
import requests

def get_pushshift(subreddit_name=None, before=None, after=None, searchType='submission'):
    suffix=''
    if (before is not None):
        suffix += f'&before={before}'
    if (after is not None):
        suffix += f'&after={after}'
    if (subreddit_name is not None):
        suffix += f'&subreddit={subreddit_name}'

    url = f'https://api.pushshift.io/reddit/search/{searchType}?sort=desc&size=1500{suffix}'
    #print('loading '+url)
    r = requests.get(url)
    data = r.json()
    if len(data['data']) > 0:
        prev_end_date = data['data'][-1]['created_utc']
    else:
        prev_end_date = None
    return (data, prev_end_date)

if __name__=="__main__":
    try:
        subreddit_name = str(argv[1]).strip()
    except:
        print('error: please input the name of subreddit as a first argument. ex: python subreddit_downloader.py [subreddit name]')
        exit()

    # scraping posts
    """
    scraping columns:
        data.
            id, title, author, selftext, link_flair_text, created_utc, num_comments, url(attatched url), permalink
    """
    print(f"Scraping posts in subreddit '{subreddit_name}'")
    df_posts = pd.DataFrame([], columns=['id', 'title',  'author', 'selftext', 'link_flair_text', 'created_utc', 'num_comments', 'url', 'permalink'])

    prev_end_date = 9999999999
    while prev_end_date is not None:
        submissions, prev_end_date = get_pushshift(subreddit_name=subreddit_name, before=prev_end_date-1, after='5y', searchType='submission')
        if prev_end_date is not None:
            for post in submissions['data']:
                df_posts.loc[len(df_posts)] = [
                    post.get('id',''), post.get('title',''), post.get('author',''), post.get('selftext',''), post.get('link_flair_text',''),
                    post.get('created_utc',''), post.get('num_comments',''), post.get('url',''), post.get('permalink','')
                    ]
        sleep(1)

    # scraping comments
    """
    scraping columns:
        data.
            id, parent_id, author, body, created_utc, permalink
    """

    print(f"Scraping comments in subreddit '{subreddit_name}'")
    df_comments = pd.DataFrame([], columns=['id', 'parent_id', 'author', 'body', 'created_utc', 'permalink'])

    prev_end_date = 9999999999
    while prev_end_date is not None:
        comments, prev_end_date = get_pushshift(subreddit_name=subreddit_name, before=prev_end_date-1, after='5y', searchType='comment')
        if prev_end_date is not None:
            for comment in comments['data']:
                df_comments.loc[len(df_comments)] = [
                    comment['id'], comment['parent_id'], comment['author'], comment['body'], comment['created_utc'], comment['permalink']
                    ]
        sleep(1)

    # saving to excel files
    time_current = round(time())
    path_save = f"subreddit_{subreddit_name}_{time_current}.xlsx"

    with pd.ExcelWriter(path_save) as writer:
        df_posts.to_excel(writer, sheet_name='posts', index=False)
        df_comments.to_excel(writer, sheet_name='comments', index=False)
    print(f"File was saved with the name of '{path_save}'")


import json
from tornado.testing import AsyncHTTPTestCase, gen_test
from tornado.web import Application
from tornado.httpserver import HTTPRequest
from unittest import mock
import backend.app
from backend.app import make_app
import backend.handlers

class TestMainHandler(AsyncHTTPTestCase):
    def get_app(self):
        with mock.patch("backend.app.setup_db") as _:
            return make_app()

    def test_get_ok(self):
        resp = self.fetch("/")
        assert resp.code == 200

@mock.patch("backend.handlers.frequency_analysis")
@mock.patch("backend.handlers.CELERY")
class TestAnalysisHandler(AsyncHTTPTestCase):
    def get_app(self):
        with mock.patch("backend.app.setup_db") as _:
            return make_app()

    def test_get_missing_task_id(self, *_):
        resp = self.fetch("/tasks")
        assert resp.code == 404

    def test_get_task_not_ready(self, celery_mock, *_):
        task_mock = mock.MagicMock()
        task_mock.ready.return_value = False
        celery_mock.AsyncResult.return_value = task_mock

        resp = self.fetch("/tasks/some-task")
        assert resp.code == 204

    def test_get_task_ready(self, celery_mock, *_):
        task_mock = mock.MagicMock()
        task_mock.ready.return_value = True
        dummy_result = {"done": True}
        task_mock.result = dummy_result
        celery_mock.AsyncResult.return_value = task_mock

        resp = self.fetch("/tasks/some-task")
        assert resp.code == 200
        assert json.loads(resp.body) == dummy_result

    def test_get_task_failure(self, celery_mock, *_):
        task_mock = mock.MagicMock()
        task_mock.ready.return_value = True
        task_mock.result = Exception()
        celery_mock.AsyncResult.return_value = task_mock

        resp = self.fetch("/tasks/some-task")
        assert resp.code == 400

    def test_post_task_malformed_payload(self, *_):
        payload = ["this is", "sparta"]

        resp = self.fetch("/tasks", method="POST", body=json.dumps(payload))
        assert resp.code == 400

    def test_post_task(self, celery_mock, freq_analysis_mock):
        mocked_task = mock.MagicMock()
        mocked_task.id = "dummy-task"
        freq_analysis_mock.delay.return_value = mocked_task

        payload = {"url": "http://dummy.com"}
        resp = self.fetch("/tasks", method="POST", body=json.dumps(payload))
        assert resp.code == 202
        resp_body = json.loads(resp.body)
        assert resp_body["id"] == "dummy-task"

@mock.patch("backend.handlers.Token")
class TestTokensHandler(AsyncHTTPTestCase):
    def get_app(self):
        with mock.patch("backend.app.setup_db") as _:
            return make_app()

    def test_get_(self, mocked_token):
        tokens = [
            {"token": "sparta", "frequency": 2},
            {"token": "tomato", "frequency": 14},
        ]
        total = 10
        mocked_token.get_page.return_value = (tokens, total)

        resp = self.fetch("/tokens?size=2&page1")
        assert resp.code == 200
        resp_body = json.loads(resp.body)
        assert list(  # There's an element with with token==tomato in the list
            filter(lambda x: x["token"] == "tomato", resp_body["tokens"])
        )
        assert resp_body["total"] == total


""" Currency update """
import requests
import xmltodict
from model import currency
import config
from util import backup

_EXECUTED_TODAY = False

def execute(run_backup: bool = True):
    """ Runs currency update """
    global _EXECUTED_TODAY

    if _EXECUTED_TODAY:
        return

    _EXECUTED_TODAY = True

    try:
        # Backup
        if run_backup:
            backup.execute()

        # Currencies from TCMB
        resp = requests.get(config.CONSTANTS["CURRENCY_CONV_URL"], verify=False, timeout=5)
        resp_as_dict = xmltodict.parse(resp.text)

        # Gold conversion
        gold_resp = requests.get(config.CONSTANTS["CURRENY_GOLD_URL"], verify=False, timeout=5)
        pos1 = gold_resp.text.find('name:"Altın",buying:gj,selling:') + 31
        pos2 = gold_resp.text.find(",", pos1)
        gold_price_txt = gold_resp.text[pos1:pos2]
        gold_price = float(gold_price_txt)
        dgc_dict = {
            "@CrossOrder": "0",
            "@Kod": "DGC",
            "@CurrencyCode": "DGC",
            "Unit": "1",
            "Isim": "Gold",
            "CurrencyName": "Gold",
            "ForexBuying": gold_price,
            "ForexSelling": gold_price,
            "BanknoteBuying": gold_price,
            "BanknoteSelling": gold_price
        }
        resp_as_dict["Tarih_Date"]["Currency"].append(dgc_dict)

        # Save
        currency.save_currency_conv(resp_as_dict)

    except Exception as error:
        print(f"Currency update error: {str(error)}")


import itertools
import json
import logging
from recordtype import recordtype
import requests

Department = recordtype('Department', ['id', 'name', 'superdepartment'])
Office = recordtype('Office', ['id', 'city', 'country', 'address'])
Employee = recordtype('Employee', ['id', 'first', 'last', 'manager', 'department', 'office'])

class Collection:

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

    def add(self, elem):
        self._data[elem.id] = elem

    def adds(self, elems):
        self._data.update([(e.id, e) for e in elems])

    def get(self, limit, offset, expand=None):
        self._validate_params(limit, offset)
        max = len(self._data) if limit > len(self._data) else limit
        return [self._data.get(k) for k in range(offset + 1, max + 1)]

    def get_by_id(self, id, expand=None):
        return self._data.get(id)

    def _validate_params(self, limit, offset):
        if offset < 0:
            raise RuntimeError('Offset must be a positive number')
        if limit < 1 or limit > 1000:
            raise RuntimeError('Limit must be higher than 0 and less than 1000')

class EmployeesCollection(Collection):

    _URL = 'https://rfy56yfcwk.execute-api.us-west-1.amazonaws.com/bigcorp/employees'

    _IDS_PER_REQUEST = 100

    def __init__(self):
        super().__init__()
        self._pending_managers = set([])

    def _chunked_iterable(self, iterable, size):
        """Iterates iterable in chunks of size"""
        i = iter(iterable)
        while True:
            chunk = tuple(itertools.islice(i, size))
            if not chunk:
                break
            yield chunk

    def _get(self, params):
        return requests.get(self._URL, params).json()

    def _get_by_id(self, params):
        logging.info('Retrieving: {}'.format(str(['id={}'.format(p) for i, p in params])))
        return self._get(params)

    def _build_employee(self, json):
        office = Offices.get_by_id(json['office'])
        department = Departments.get_by_id(json['department'])
        return Employee(json['id'], json['first'], json['last'], json['manager'], department, office)

    def _process_employees(self, employees):
        """Build employees objects from json dict and store them"""
        for e in employees:
            employee = self._build_employee(e)
            manager = super().get_by_id(e['manager'])
            if e['manager'] and not manager:
                logging.info('Manager {} not found in collection'.format(e['manager']))
                self._pending_managers.add(e['manager'])
            else:
                employee.manager = manager
            super().add(employee)

    def _clear_collection(self):
        self._data = {}
        self._pending_managers = set([])

    def _process_response(self, data, expand):
        """Store the employees from json dict and retrieve pending managers"""
        self._clear_collection()
        self._process_employees(data)
        manager_count = max([sum(1 for m in e.split('.') if m == 'manager') for e in expand], default=0)
        self._retrieve_pending(manager_count+1)

    def get(self, limit, offset, expand=[]):
        self._validate_params(limit, offset)
        data = self._get({'limit': limit, 'offset': offset})
        self._process_response(data, expand)
        max = len(self._data) if limit > len(self._data) else limit + offset
        return [self._data.get(k) for k in range(offset + 1, max + 1)]

    def get_by_id(self, id, expand=[]):
        data = self._get_by_id([('id', id)])
        self._process_response(data, expand)
        return super().get_by_id(id)

    def _update_pending_managers(self, managers):
        """Update manager objects on stored employees"""
        for manager in managers:
            obj = super().get_by_id(manager)
            for k, v in self._data.items():
                if v.manager == manager:
                    v.manager = obj

    def _retrieve_pending(self, levels=0):
        """Retrieve pending managers using _pending_managers"""
        if levels:
            pending_managers = list(self._pending_managers)
            logging.info('Pending managers to retrieve: {} - level {}'.format(len(pending_managers), levels))
            self._pending_managers = set([])
            for chunks in self._chunked_iterable(pending_managers, self._IDS_PER_REQUEST):
                params = [('id', manager) for manager in chunks]
                data = self._get_by_id(params)
                self._process_employees(data)
                self._update_pending_managers(chunks)

            if self._pending_managers and levels > 0:
                self._retrieve_pending(levels-1)

def _load_data_from_files():
    departments = Collection()
    offices = Collection()

    with open('data/departments.json') as file:
        for d in json.load(file):
            superdepartment = departments.get_by_id(d['superdepartment'])
            new_department = Department(d['id'], d['name'], superdepartment)
            departments.add(new_department)

    with open('data/offices.json') as file:
        offices.adds(json.load(file, object_hook=lambda o: Office(**o)))

    return departments, offices

Departments, Offices = _load_data_from_files()
Employees = EmployeesCollection()


import tkinter as tk
from tkinter import ttk
from typing import Any, Collection, NamedTuple, Sequence

from .style import _Style

def _set_vscrollbar_style(bg: str, slider: str, active_slider: str) -> None:
    """flat, no arrow, bg=color of slider"""
    style = ttk.Style()
    style.theme_use("clam")
    style.layout(
        "Vertical.TScrollbar",
        [
            (
                "Vertical.Scrollbar.trough",
                {
                    "children": [("Vertical.Scrollbar.thumb", {"expand": "1", "sticky": "nswe"})],
                    "sticky": "ns",
                },
            )
        ],
    )
    style.configure(
        "Vertical.TScrollbar",
        background=slider,
        gripcount=0,
        bordercolor=bg,
        troughcolor=bg,
        relief="flat",
        lightcolor=bg,
        darkcolor=bg,
    )
    style.map("Vertical.TScrollbar", background=[("active", active_slider)])

class _Border(NamedTuple):
    bg: str
    size: float
    relief: str

def _get_border(border: _Border, **kwargs: Any) -> dict[str, Any]:
    return dict(highlightbackground=border.bg, highlightthickness=border.size, highlightcolor=border.bg, **kwargs)

# pylint: disable=too-many-ancestors
class _AutoScrollbar(ttk.Scrollbar):
    def set(self, first, last) -> None:
        if float(first) <= 0.0 and float(last) >= 1.0:
            self.grid_remove()
        else:
            self.grid()
        super().set(first, last)

class _VscrollCanvas(tk.Canvas):
    """
    Canvas with an automatic vertical scrollbar
    use _VAutoScrollableCanvas.frame to populate it
    """

    def __init__(self, master: tk.BaseWidget, **kwargs) -> None:
        super().__init__(master, bd=0, highlightthickness=0, **kwargs)  # w/o border
        # set the vertical scrollbar
        vscrollbar = _AutoScrollbar(master, orient="vertical")
        self.config(yscrollcommand=vscrollbar.set, yscrollincrement="2")
        vscrollbar.config(command=self.yview)
        self.scrollbar = vscrollbar
        # position everything
        vscrollbar.grid(row=0, column=1, sticky=tk.NS)
        self.grid(row=0, column=0, sticky=tk.NSEW)
        # Making the canvas expandable
        self.master.grid_rowconfigure(0, weight=1)
        self.master.grid_columnconfigure(0, weight=1)
        # creating the frame attached to self
        self.frame = tk.Frame(self, bg=self["bg"])
        self.create_window(0, 0, anchor=tk.NW, window=self.frame)
        # set the scroll region when the frame content changes
        self.frame.bind("<Configure>", self._on_configure)
        # bind the mousewheel
        self.bind_all("<MouseWheel>", self._on_mousewheel)

    def _on_configure(self, _) -> None:
        w, h = self.frame.winfo_reqwidth(), self.frame.winfo_reqheight()
        self.config(scrollregion=(0, 0, w, h), width=w, height=h)

    def _on_mousewheel(self, event: tk.Event) -> None:
        self.yview_scroll(int(-1 * (event.delta / 12)), "units")

class _Button(tk.Button):
    """
    button with a colored border
    with a mouseover color
    """

    def __init__(self, master: tk.BaseWidget, bg: str, mouseover: str, border: _Border, **kwargs: Any) -> None:
        # create a frame for the border, note: do not pack
        self._frame = tk.Frame(master, bg=bg, **_get_border(border))
        active = dict(activebackground=bg, activeforeground=kwargs.get("fg", "white"))
        # create the button
        super().__init__(self._frame, bg=bg, bd=0, **active, **kwargs)
        super().pack(fill="both", expand=True)
        # handle mouseover
        self.bind("<Enter>", lambda _: self.config(bg=mouseover), add="+")
        self.bind("<Leave>", lambda _: self.config(bg=bg), add="+")

    def grid(self, **kwargs) -> None:
        self._frame.grid(**kwargs)

    def grid_remove(self) -> None:
        self._frame.grid_remove()

    def pack(self, **kwargs) -> None:
        self._frame.pack(**kwargs)

class _ListView(tk.Frame):
    """
    List view with styled content and auto scroll
    Note: set_headers should be called before set_rows
    """

    # pylint: disable=too-many-arguments
    def __init__(self, master: tk.BaseWidget, bg_headers: str, bg_rows: str, bg_separator: str, pad: int) -> None:
        super().__init__(master)
        # headers
        self._frame_headers = tk.Frame(self, bg=bg_headers)
        self._frame_headers.pack(fill="both", expand=True)
        # headers separator
        sep = tk.Frame(self, bg=bg_separator)
        sep.pack(fill="both", expand=True)
        # rows
        frame_rows = tk.Frame(self)
        canvas = _VscrollCanvas(frame_rows, bg=bg_rows)
        self._frame_rows = canvas.frame
        frame_rows.pack(fill="both", expand=True)
        # for use later
        self._bg_headers = bg_headers
        self._bg_rows = bg_rows
        self._bg_separator = bg_separator
        self._pad = pad
        self._widths = []

    @staticmethod
    def _clear(what: tk.BaseWidget) -> None:
        for widget in what.winfo_children():
            widget.destroy()

    def set_headers(self, headers: Collection[_Style]) -> None:
        self._clear(self._frame_headers)
        pad = self._pad
        n_column = len(headers)
        self._widths = [0] * n_column
        for column, text in enumerate(headers):
            label = tk.Label(self._frame_headers, bg=self._bg_headers, **text.to_tk)
            label.grid(row=0, column=column, ipadx=pad, ipady=pad, sticky=tk.NSEW)
            self._widths[column] = max(label.winfo_reqwidth() + pad * 2, self._widths[column])
        self.set_column_widths()

    def set_rows(self, rows: Sequence[Collection[_Style]]) -> None:
        self._clear(self._frame_rows)
        pad = self._pad
        n_column = len(self._widths)
        for row, row_content in enumerate(rows):
            assert len(row_content) == n_column
            for column, text in enumerate(row_content):
                label = tk.Label(self._frame_rows, bg=self._bg_rows, **text.to_tk)
                label.grid(row=row * 2, column=column, ipadx=pad, ipady=pad, sticky=tk.NSEW)
                self._widths[column] = max(label.winfo_reqwidth() + pad * 2, self._widths[column])
                # row separator
                if row != len(rows) - 1:
                    sep = tk.Frame(self._frame_rows, bg=self._bg_separator)
                    sep.grid(row=row * 2 + 1, column=0, columnspan=n_column, sticky=tk.EW)
        if not rows:
            self._frame_rows.config(height=1)
        self.set_column_widths()

    def set_column_widths(self) -> None:
        for column, width in enumerate(self._widths):
            self._frame_headers.columnconfigure(column, minsize=width)
            self._frame_rows.columnconfigure(column, minsize=width)


import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self, imsize):
        super(Net, self).__init__()
        self.downsample = ((imsize-4)/2)
        if self.downsample % 2 == 0:
            self.finalsize = int((self.downsample-4)/2)
        else:
            self.finalsize = int((self.downsample - 5) / 2)
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * self.finalsize * self.finalsize, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 2)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * self.finalsize * self.finalsize)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

if __name__ == '__main__':
    net = Net()


import sys
import urllib.parse as urlparse

import backoff
from airbyte_cdk.logger import AirbyteLogger
from facebook_business.exceptions import FacebookRequestError
from requests.status_codes import codes as status_codes

logger = AirbyteLogger()

class InstagramAPIException(Exception):
    """General class for all API errors"""

class InstagramExpectedError(InstagramAPIException):
    """Error that we expect to happen, we should continue reading without retrying failed query"""

def retry_pattern(backoff_type, exception, **wait_gen_kwargs):
    def log_retry_attempt(details):
        _, exc, _ = sys.exc_info()
        logger.info(str(exc))
        logger.info(f"Caught retryable error after {details['tries']} tries. Waiting {details['wait']} more seconds then retrying...")

    def should_retry_api_error(exc: FacebookRequestError):
        # Retryable OAuth Error Codes
        if exc.api_error_type() == "OAuthException" and exc.api_error_code() in (1, 2, 4, 17, 341, 368):
            return True

        # Rate Limiting Error Codes
        if exc.api_error_code() in (4, 17, 32, 613):
            return True

        if (
            exc.http_status() == status_codes.INTERNAL_SERVER_ERROR
            and exc.api_error_code() == 1
            and exc.api_error_message() == "Please reduce the amount of data you're asking for, then retry your request"
        ):
            return True

        if (
            exc.http_status() == status_codes.INTERNAL_SERVER_ERROR
            and exc.api_error_code() == 1
            and exc.api_error_message() == "An unknown error occurred"
        ):
            return True

        if exc.http_status() == status_codes.TOO_MANY_REQUESTS:
            return True

        if (
            exc.api_error_type() == "OAuthException"
            and exc.api_error_code() == 10
            and exc.api_error_message() == "(#10) Not enough viewers for the media to show insights"
        ):
            return True

        # Issue 4028, Sometimes an error about the Rate Limit is returned with a 400 HTTP code
        if exc.http_status() == status_codes.BAD_REQUEST and exc.api_error_code() == 100 and exc.api_error_subcode() == 33:
            return True

        if exc.api_transient_error():
            return True

        # The media was posted before the most recent time that the user's account
        # was converted to a business account from a personal account.
        if exc.api_error_type() == "OAuthException" and exc.api_error_code() == 100 and exc.api_error_subcode() == 2108006:
            return False

        return False

    return backoff.on_exception(
        backoff_type,
        exception,
        jitter=None,
        on_backoff=log_retry_attempt,
        giveup=lambda exc: not should_retry_api_error(exc),
        **wait_gen_kwargs,
    )

def remove_params_from_url(url, params):
    parsed = urlparse.urlparse(url)
    query = urlparse.parse_qs(parsed.query, keep_blank_values=True)
    filtered = dict((k, v) for k, v in query.items() if k not in params)
    return urlparse.urlunparse(
        [parsed.scheme, parsed.netloc, parsed.path, parsed.params, urlparse.urlencode(filtered, doseq=True), parsed.fragment]
    )


'''
< 피자판매 >
https://www.acmicpc.net/problem/2632
문제)
- 두 종류 피자 A, B
- 각 종류의 피자는 다양한 크기의 여러 개의 피자 조각으로 구성
- 각 조각에 쓰여진 숫자는 피자조각의 크기를 나타냄
- 한종류의 피자를 2조각 이상 판매할 때는 연속된 조각들을 잘라서 판매
- 이때 판매한 피자조각의 크기 합이 주문한 크기가 되어야 한다.
입력)
- 1     : 손님이 구매하고자 하는 피자크기 2,000,000 이하 자연수
- 2     : A, B 피자조각의 개수 m, n
- 3[m]  : 미리잘린 A피자 크기
- 4[m]  : 미리잘린 B피자 크기
출력)
- 1     : 피자를 판매하는 방법의 가지 수를 나타내는 정수\
    없는경우 숫자 0 출력
'''
import sys
from collections import defaultdict
input = sys.stdin.readline

X = int(input())
n, m = map(int, input().split())

def go(x, psum, _dict):
    for i in range(1, x + 1): # interval
        # for j in range(i, x + i):
        #     tmp = psum[j] - psum[j - i]
        for j in range(x):
            tmp = psum[j + i] - psum[j]
            _dict[tmp] += 1
            if i == x: break
    return

A = [0]
B = [0]
psum_A = [0]
psum_B = [0]
for i in range(1, n + 1):
    tmp = int(input())
    A.append(tmp)
    psum_A.append(psum_A[i - 1] + A[i])
for i in range(n + 1, 2*n + 1):
    psum_A.append(psum_A[i - 1] + A[i - n])
for i in range(1, m + 1):
    tmp = int(input())
    B.append(tmp)
    psum_B.append(psum_B[i - 1] + B[i])
for i in range(m + 1, 2*m + 1):
    psum_B.append(psum_B[i - 1] + B[i - m])

dict_A = defaultdict(int)
dict_B = defaultdict(int)

# dict_A = defaultdict(int)
# dict_B = defaultdict(int)

go(n, psum_A, dict_A)
go(m, psum_B, dict_B)

ret = 0
for i in range(1, X):
    ret += dict_A[X - i] * dict_B[i]
ret += dict_A[X]
ret += dict_B[X]
print(ret)
'''
원형 고리 구조!!! (시작과 끝이 이어짐)
    - 선형적인 자료구조를 두개 이어붙이자!
    - 누적합도 원형구조를 고려해서 계산
<풀이법>
1. A, B 각각의 조각에 있어서 누적합배열 psum_A, psum_B을 정의
    - 초기값 [0]
2. 누적합 배열에 원소를 넣을때 for문 2회씩 : Index기준
    1) 1 ~ n, 1 ~ m
    2) n+1 ~ 2*n, m+1 ~ 2*m
3. 해쉬맵(dict or map)을 선언 - 파이썬 defaultdict
4. 전구간에서 interval을 기준으로 전구간을 체크해서 dict update
    key : 합
    value : 수
5. 원하는 수 X를 기준으로 결과에
    1) + dict_A[X]
    2) + dict_B[X]
    3) 전구간 for문  += dict_A[X - i] * dict_B[i]

'''


import numpy as np
import pandas as pd
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import MinMaxScaler
from datetime import timedelta

from pogodynka.models import (
    TimeStamp,
    Prediction,
    PredictionType,
    Location,
)
from django.conf import settings
import datetime
import pytz

class Data:
    def __init__(self, prediction_name):
        self.prediction_name = prediction_name
        self.prediction_type = PredictionType.objects.get(name=prediction_name)
        self.meteo_location = Location.objects.get(name='meteo')
        self.politechnika_location = Location.objects.get(name='politechnika')
        self._prepare_learning_data()

    def _prepare_learning_data(self):
        from_date = datetime.datetime(2019, 9, 1, tzinfo=pytz.utc)
        to_date = datetime.datetime(2019, 9, 6, tzinfo=pytz.utc)

        from_date = datetime.datetime.combine(from_date, datetime.time.min)
        to_date = datetime.datetime.combine(to_date, datetime.time.max)

        timezone = pytz.timezone(settings.TIME_ZONE)

        from_date = timezone.localize(from_date)
        to_date = timezone.localize(to_date)

        hours = TimeStamp.objects.filter(date__range=(from_date, to_date)).order_by('date')

        preds = []
        for hour in hours:
            prediction = Prediction.objects.filter(type=self.prediction_type, location=self.meteo_location, date=hour).order_by('-when').first()
            preds.append((hour.date,prediction.value))

        df1 = pd.DataFrame(preds, columns=["date", "value"])
        df1['Datetime'] = pd.to_datetime(df1['date'])
        df1 = df1.set_index('Datetime')
        df1 = df1.drop(['date'], axis=1)

        preds = []
        for hour in hours:
            prediction = Prediction.objects.filter(type=self.prediction_type, location=self.politechnika_location, date=hour).order_by('-when').first()
            preds.append((hour.date,prediction.value))

        df2 = pd.DataFrame(preds, columns=["date", "value"])
        df2['Datetime'] = pd.to_datetime(df2['date'])
        df2 = df2.set_index('Datetime')
        df2 = df2.drop(['date'], axis=1)
        #
        #
        # end_base = df1.index[-1]
        # new_dates = []
        # for _ in range(3):
        #     for i in range(df1.shape[0]):
        #         end_base += timedelta(hours=1)
        #         new_dates.append(pd.to_datetime(end_base))
        #
        # new_values = []
        # bias = 0
        # bias2 = 0
        # for _ in range(3):
        #     for i in range(df1.shape[0]):
        #         bias += (np.random.random_sample() - 0.5) / 5
        #         bias2 += (np.random.random_sample() - 0.5) / 1
        #         new_values.append(df1['value'][i] + df1['value'][i]/3 + bias  - np.random.random_sample())
        # n = pd.DataFrame(new_values,
        #                               index=new_dates,
        #                  columns=['value'])
        # df1 = df1.append(n)

        # df2 = df1.copy()
        # df2['value'] += np.random.randint(-10,-5,df1.shape[0])/9

        df1.columns = ["meteo"]
        df2.columns = ["politechnika"]

        ax = df1.plot()
        df2.plot(ax=ax)

        df1['politechnika'] = df2['politechnika']

        shift_days = 1
        warump_days = 6

        warump_steps = warump_days * 24
        shift_steps = shift_days * 24 # 144,  hours

        df = df1.copy()

        politechnika = df["politechnika"].values[:-168]
        meteo = df["meteo"].values[24:-144]
        data_in = [politechnika, meteo]
        data_out = df["politechnika"].values[144:-24]

        in_scaler = MinMaxScaler()
        out_scaler = MinMaxScaler()

        self.tscv = TimeSeriesSplit((len(data_out)-1)//4)
        self.in_scaled = in_scaler.fit_transform(data_in)
        self.out_scaled = out_scaler.fit_transform(data_out.reshape(-1,1)).flatten()

    def get_train_test_data(self):
        print("GETTING DATA")
        for train_index, test_index in self.tscv.split(self.out_scaled[:-168]):
            # print("TRAIN:", train_index, "TEST:", test_index)
            X_train = []
            y_train = []

            for i in train_index:
                train_start = i
                train_end = i + 168
                _X_train = np.array(self.in_scaled)[:, train_start: train_end]
                _y_train = self.out_scaled[i]

                X_train.append(_X_train)
                y_train.append(_y_train)
            X_train = np.array(X_train)
            y_train = np.array(y_train)

            X_test = []
            y_test = []
            for i in test_index:
                test_start = i
                test_end = i + 168
                _X_test = np.array(self.in_scaled)[:,test_start: test_end]
                _y_test = np.array(self.out_scaled[i])
                X_test.append(_X_test)
                y_test.append(_y_test)

            # for a in X_test:
            #     print(a.shape)
            X_test = np.array(X_test)
            y_test = np.array(y_test)
            # print("RETURNING TRAIN", X_train.shape, y_train.shape)
            # print("RETURNING TEST", X_test.shape, y_test.shape)
            yield X_train, y_train, X_test, y_test


from typing import Callable
from types import ModuleType

class Node:

    def __init__(self):
        self._content = None
        self.children = []
        self.decomposed = False
        self.children_decomposed = False

    def render(self):
        raise NotImplementedError

    def __add__(self, other):
        return JointNode([self, other])

    def __radd__(self, other):
        if isinstance(other, str):
            self.children.append(other)
            return self
        else:
            raise NotImplementedError

class JointNode(Node):
    def __init__(self, nodes):
        super().__init__()
        self.children = nodes
        self.decomposed = True

    def render(self):
        return self.children

    def __add__(self, other):
        if isinstance(other, JointNode):
            self.children.extend(other.children)
            return self
        else:
            self.children.append(node(other))
            return self

    def __radd__(self, other):
        if isinstance(other, str):
            self.children.insert(0, other)
            return self
        else:
            raise NotImplementedError

class StringNode(Node):
    def __init__(self, string):
        super().__init__()
        self._content: str = string
        self.children = None
        self.decomposed = True

    def render(self):
        return self._content

class SectionNode(Node):
    def __init__(self, content_dict: dict):
        super().__init__()
        self._content: content_dict = content_dict
        self.decomposed = True

    def render(self):
        return self._content

class FunctionNode(Node):
    def __init__(self, func: Callable):
        super().__init__()
        self._content: Callable = func
        self.decomposed = True

    def render(self):
        res = self._content()
        if isinstance(res, dict):
            return SectionNode(res)
        else:
            return res

class ModuleNode(FunctionNode):
    def __init__(self, module: ModuleType):
        if not hasattr(module, "main"):
            raise ValueError("Module must have a main function to be used as a node")
        super().__init__(module.main)

def node(src=None):
    if src is None:
        return JointNode([])
    if isinstance(src, str):
        return StringNode(src)
    if isinstance(src, dict):
        return SectionNode(src)
    if isinstance(src, list):
        return JointNode([node(s) for s in src])
    if isinstance(src, Node):
        return src
    if isinstance(src, Callable):
        return FunctionNode(src)
    # check if it is a module
    if isinstance(src, ModuleType):
        return ModuleNode(src)
    raise NotImplementedError


from collections import deque
from statistics import median

with open('in/10.txt') as f:
    lines = f.read().splitlines()

pairs = {
    '(': ')',
    '[': ']',
    '{': '}',
    '<': '>',
}

# Part 1

points = {
    ')': 3,
    ']': 57,
    '}': 1197,
    '>': 25137
}

total = 0
for line in lines:
    s = deque()
    for c in line:
        if c in pairs:
            s.append(c)
        else:
            if len(s) == 0 or pairs[s[-1]] != c:
                total += points[c]
                break
            s.pop()

print(total)

# Part 2

points = {
    '(': 1,
    '[': 2,
    '{': 3,
    '<': 4,
}
scores = []
for line in lines:
    s = deque()
    for c in line:
        if c in pairs:
            s.append(c)
        else:
            if len(s) == 0 or pairs[s[-1]] != c:
                break
            s.pop()
    else:
        score = 0
        while len(s):
            curr = s.pop()
            score *= 5
            score += points[curr]
        scores.append(score)

print(median(scores))


# For running the code, these packages are required:
# pip install torch
# pip install prettytable
#
# To run the code issue this command:
# python synthetic_v1.py
#
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import time
import yaml
import lightning.pytorch as pl
import torch.distributed as dist
from torch.utils.data import DataLoader

from sciml_bench.core.runtime import RuntimeIn, RuntimeOut

class RegressionDataset(torch.utils.data.Dataset):

    def __init__(self, num_samples, input_size):
        super().__init__()
        X = torch.randn(num_samples, input_size)
        y = X ** 2 + 15 * np.sin(X) **3
        y_t = torch.sum(y, dim=1)
        self._x = X
        self._y = y_t

    def __len__(self):
        return self._x.shape[0]

    def __getitem__(self, index):
        x = self._x[index]
        y = self._y[index]
        return x, y

class SyntheticRegression(pl.LightningModule):

    def __init__(self, input_size, hidden_size, output_size):
        super().__init__()

        self.net = torch.nn.Sequential(
            nn.Linear(input_size, hidden_size),  # input -> hidden layer
            nn.Sigmoid(),
            nn.Linear(hidden_size, hidden_size),
            nn.Sigmoid(),
            nn.Linear(hidden_size, hidden_size),
            nn.Sigmoid(),
            nn.Linear(hidden_size, hidden_size),
            nn.Sigmoid(),
            nn.Linear(hidden_size, output_size), # hidden -> output layer
            nn.Sigmoid()
        )

    def training_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self.net(x).flatten()
        loss = F.mse_loss(y_hat, y)
        self.log("train_loss", loss)
        return loss

    def configure_optimizers(self):
        optimizer = torch.optim.SGD(self.parameters(), lr=0.001)
        return optimizer

def sciml_bench_training(params_in: RuntimeIn, params_out: RuntimeOut):
    default_args = {
        'input_size': 784,
        'batch_size': 128,
        'num_samples': 1024000,
        'hidden_size': 3000,
        'epochs': 1,
    }

    if 'LOCAL_RANK' in os.environ:
        # Running with torchrun
        dist.init_process_group(backend="nccl", init_method='env://')
    else:
        # Running without torchrun - force to use a single process
        dist.init_process_group(backend="nccl", rank=0, world_size=1, store=dist.HashStore())
        os.environ['LOCAL_RANK'] = '0'

    local_rank = int(os.environ["LOCAL_RANK"])
    params_out.activate(rank=dist.get_rank(), local_rank=local_rank)

    # Log top level process
    log = params_out.log.console
    log.begin(f'Running benchmark synthetic_regression in training mode')

    # Parse input arguments against default ones
    with log.subproc('Parsing input arguments'):
        args = params_in.bench_args.try_get_dict(default_args=default_args)

    num_samples = args['num_samples']
    batch_size = args['batch_size']

    with log.subproc("Creating dataset"):
        dataset = RegressionDataset(num_samples, args['input_size'])
        dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

    log.message(f"Number of samples: {num_samples}")
    log.message(f"Total number of batches: {len(dataloader)}, {num_samples/batch_size}")

    model = SyntheticRegression(
        args['input_size'],
        args['hidden_size'],
        output_size=1
    )

    with log.subproc('Training model'):
        trainer = pl.Trainer(accelerator='gpu', devices=dist.get_world_size(), strategy="ddp", max_epochs=args['epochs'], default_root_dir=params_in.output_dir)
        start_time = time.time()
        trainer.fit(model, dataloader)
        end_time = time.time()

    time_taken = end_time - start_time

    metrics = {}
    metrics['time'] = time_taken
    metrics_file = params_in.output_dir / 'metrics.yml'
    with log.subproc('Saving training metrics to a file'):
        with open(metrics_file, 'w') as handle:
            yaml.dump(metrics, handle)


# load general packages and functions
import datetime

# load GraphINVENT-specific functions
import util
from parameters.constants import constants
from Workflow import Workflow

# suppress minor warnings
util.suppress_warnings()

# defines and runs the job

def main():
    """
    Defines the type of job (preprocessing, training, generation, or testing),
    writes the job parameters (for future reference), and runs the job.
    """
    # fix date/time
    _ = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    workflow = Workflow(constants=constants)

    job_type = constants.job_type
    print(f"* Run mode: '{job_type}'", flush=True)

    if job_type == "preprocess":
        # write preprocessing parameters
        util.write_preprocessing_parameters(params=constants)

        # preprocess all datasets
        workflow.preprocess_phase()

    elif job_type == "train":
        # write training parameters
        util.write_job_parameters(params=constants)

        # train model and generate graphs
        workflow.training_phase()

    elif job_type == "generate":
        # write generation parameters
        util.write_job_parameters(params=constants)

        # generate molecules only
        workflow.generation_phase()

    elif job_type == "test":
        # write testing parameters
        util.write_job_parameters(params=constants)

        # evaluate best model using the test set data
        workflow.testing_phase()

    else:
        raise NotImplementedError("Not a valid `job_type`.")

if __name__ == "__main__":
    main()