import socket
from abc import ABC, abstractclassmethod
from dataclasses import dataclass, field

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication

from src.config.paths.paths import Paths
from src.packages.logging.logger import Logger

import json
from bs4 import BeautifulSoup

class EmailService:

    _log = Logger(console=True)

    def __init__(self) -> None:
        '''
            Default constructor.
        '''

        self._connection = None
        self._get_config_file()

    def __enter__(self):
        '''
            Gives the ability to connect to the mailbox using "with" keyword.
        '''

        self._connect()
        return self

    def __exit__(self, exception_type, exception_value, traceback) -> None:
        '''
            Closes safely the connection.
        '''

        if any((exception_type, exception_value, traceback)):
            self._log.critical('Disconnected from the mail service was triggered due to an unexpected exception.')
            self._log.critical(exception_type)
            self._log.critical(exception_value)
            self._log.critical(traceback)
        self._disconnect()

    def _get_config_file(self) -> None:
        '''
            Gets the configurations from the config file.
        '''

        try:
            with open(Paths.MAIL_CONFIG) as mail_config_file:
                self._config = json.load(mail_config_file)
        except Exception as exception_message:
            self._log.critical('Unexpected error occured while fetching mail configurations ...')
            self._log.critical(exception_message)

    def _connect(self) -> None:
        '''
            Establishes the connection.
        '''

        try:
            self._connection = smtplib.SMTP()
            self._connection.connect(self._config['connection'])
        except socket.error as smtplib_error:
            self._log.critical('Unexpected error occured, could not connect to the mail service ...')
            self._log.critical(smtplib_error)
        except Exception as exception_message:
            self._log.critical('Unexpected error occured, could not connect to the mail service ...')
            self._log.critical(exception_message)

    def _disconnect(self) -> None:
        '''
            Closes the connection.
        '''

        self._connection.quit()
        self._log.info('Disconnecting from the mail service ...')

    def send_mail(self, sender: str, recipients: str, message: str) -> None:
        '''
            Sends the emial to user.
        '''

        self._connection.sendmail(from_addr=sender, to_addrs=recipients, msg=message)

@dataclass
class Mail:
    '''
        Mail class.

        Arguments:
            subject: str
            sender: str
            recipients: list[str]
            body: dict[str, dict[str, str]]
            attachments: list[str]
    '''

    subject: str
    sender: str
    recipients: str | list[str]
    copies: str | list[str]
    body: dict[str, dict[str, str]] = field(default_factory=dict)
    attachments: list[str] = field(default_factory=list)

    _log = Logger(console=True)

    def __post_init__(self) -> None:
        '''
            Sets all necessary process after the object is created.
        '''

        self._message = MIMEMultipart()
        self._generate_mail()

    def _generate_mail(self) -> None:
        '''
            Generates the email.
        '''

        self._set_subject()
        self._set_sender()
        self._set_recipients()
        self._set_body()
        if self.attachments:
            self._set_attachments()

    def _set_subject(self) -> None:
        '''
            Sets the subject of the email.
        '''

        self._log.info(f'Adding subject "{self.subject}" to the email ...')
        self._message['Subject'] = self.subject

    def _set_sender(self) -> None:
        '''
            Sets the sender of the email.
        '''

        self._log.info(f'Adding sender "{self.sender}" to the email ...')
        self._message['From'] = self.sender

    def _set_recipients(self) -> None:
        '''
            Sets the recipients of the email.
        '''

        self._message['To'] = ', '.join(self.recipients) if isinstance(self.recipients, list) else self.recipients
        self._log.info(f'Adding recipients "{self._message["To"]}" to the email ...')
        self._message['Cc'] = ', '.join(self.copies) if isinstance(self.copies, list) else self.copies
        self._log.info(f'Adding CC "{self._message["Cc"]}" to the email ...')

    def _set_body(self) -> None:
        '''
            Sets the body of the email.
        '''

        self._log.info(f'Adding body of the email ...')
        try:
            with open(Paths.HTML_TEMPLATE) as template_file:
                default_template = template_file.read()
        except Exception as exception_message:
            self._log.critical('Unexpected error occured while opening default HTML email template ...')
            self._log.critical(exception_message)
            exit(-1)

        if self.body == '':
            self._log.warning('Body was not specified by user, will use default template ...')
            self.body = default_template
        else:
            if not isinstance(self.body, dict):
                self._log.warning('Given body:')
                self._log.warning(self.body)
                self._log.warning('Is not the correct format will use default template ...')
                self.body = default_template
            else:
                # TODO: add recursive function to loop through the elements and store them in the dict
                # then add users option in place of old ones
                # in case user would like to add new element that is not present in default template
                # add it in that order.
                # self._adjust_default_template(default_template=default_template)
                pass

        self._message.attach(MIMEText(self.body, 'html', _charset='utf-8'))

    def _adjust_default_template(self, default_template: str) -> None:
        '''
            Adjust default template to the one that user provided.
        '''

        soup = BeautifulSoup(default_template, 'html.parser')

        # TODO

    def _set_attachments(self) -> None:
        '''
            Sets the attachments of the email.
        '''

        self._log.info(f'Adding attachments to the email ...')
        if not isinstance(self.attachments, list):
            self._log.warning('Given attachments were not in format of list, will convert them into list ...')
            self.attachments = self.attachments.split() if ',' in self.attachments else [self.attachments]

        for attachment in self.attachments:
            try:
                with open(file=attachment, mode='rb') as file_to_attach:
                    self._message.attach(MIMEApplication(
                        file_to_attach.read(),
                        Name=attachment.split('/')[-1]
                    ))
            except Exception as exception_message:
                self._log.error('Unexpected error occured while adding attachment to the email ...')
                self._log.error(exception_message)

    def send(self) -> None:
        '''
            Sends email.
        '''

        with EmailService() as message_service:
            message_service.send_mail(
                sender=self.sender,
                recipients=self.recipients,
                message=self._message.as_string()
            )


import struct
import numpy as np

vecfile = open('speaker/speaker-average/sigma_w_nishi', 'rb')

vec = np.asarray(struct.unpack('f' * 16, vecfile.read()))

datafile = open('speaker/labeled-speaker/sigma_w_34', 'rb')

data = []

buf = datafile.read()

whole_len = int(len(buf) / 16)
norms = np.ndarray(shape=(whole_len, ))
count = 0
for i in range(0, len(buf), 64):
    d = np.asarray(struct.unpack('f' * 16, buf[i:i+64]))

    diff = vec - d
    norm = diff.dot(diff)
    norms[count] = norm
    count +=1

print("Mean: {}".format(np.mean(norms)))
print("STD : {}".format(np.std(norms)))
print("Max : {}".format(np.max(norms)))


__all__ = [
    "is_iterable", "is_valid_filename", "_is_rfft2_size_argument_valid",
    "add_weighted", "nb_add", "nb_subtract", "nb_multiply", "nb_power",
    "nb_divide", "nb_reciprocal", "bias_reciprocal", "elementwise_mean",
    "nb_outer", "outer_bias", "outer_bias_parallel", "lowpass_outer",
    "lowpass_outer_parallel", "highpass_outer", "highpass_outer_parallel",
    "transform", "transform_1D", "pad_reflect101", "normalize_uint8_parallel",
    "normalize_uint8"
]

import numpy as np

from cython import boundscheck, wraparound
from numba import njit, vectorize, prange
from img_type import ARR_8U2D, ARR_32F2D, ARR_1D

"""This document includes some basic function.
The operator with prefix 'nb_', e.g., nb_add and nb_multiply, is present for
restricting the output dtype.
"""

def is_iterable(obj: object) -> bool:
    """Check whether an object is iterable or not."""
    try:
        iter(obj)
    except:
        return False
    else:
        return True

@njit(
    "boolean(unicode_type)",
    nogil=True, cache=True, fastmath=True)
@wraparound(False)
def is_valid_filename(text: str) -> bool:
    """Check whether a text is valid as a filename."""
    if not text:
        return False
    keys = (
        "\\", "/", ":", "*", "?", "\"", "|", "<", ">"
    )
    for key in keys:
        if key in text:
            return False
    return True

# Basic operators for controling dtype.
@njit(nogil=True, cache=True, fastmath=True, parallel=True)
@wraparound(False)
def add_weighted(a, amount, b, amount2):
    return np.multiply(a, amount) + np.multiply(b, amount2)

__SIGNATURE_BASIC_OPERATORS = [
    "int16(uint8,int64)", "float32(uint8,float32)", "float32(uint8,float64)",
    "float32(float32,float32)", "float32(float32,float64)",
    "float32(float64,float64)",
]
@vectorize(
    __SIGNATURE_BASIC_OPERATORS,
    cache=True, fastmath=True)
@wraparound(False)
def nb_add(a, b):
    """Element-wise add two areguments."""
    return np.add(a, b)

@vectorize(
    __SIGNATURE_BASIC_OPERATORS,
    cache=True, fastmath=True)
@wraparound(False)
def nb_subtract(a, b):
    """Element-wise subtract two areguments."""
    return np.subtract(a, b)

__SIGNATURE_MULTIPLY = [
    "int16(uint8,int64)", "float32(uint8,float32)",
    "float32(uint8,float64)", "float32(float32,float32)",
    "float32(float64,float32)",
    "complex64(complex64,float32)", "complex128(complex128,float32)",
]
@vectorize(
    __SIGNATURE_MULTIPLY,
    cache=True, fastmath=True)
@wraparound(False)
def nb_multiply(a, b):
    """Element-wise multiply two areguments."""
    return np.multiply(a, b)

@vectorize(
    __SIGNATURE_BASIC_OPERATORS,
    cache=True, fastmath=True)
@wraparound(False)
def nb_power(base, exponent):
    """
    Firse argument(base) raised to the power of second argument(exponent),
    element-wise.
    """
    return np.power(base, exponent)

@vectorize(
    __SIGNATURE_BASIC_OPERATORS,
    cache=True, fastmath=True)
@wraparound(False)
def nb_divide(a, b):
    return np.divide(a, b)

__SIGNATURE_RECIPROCAL = [
    "float32(uint8)", "float32(int16)", "float32(float32)",
]
@vectorize(
    __SIGNATURE_RECIPROCAL,
    cache=True, fastmath=True)
@wraparound(False)
def nb_reciprocal(x):
    """Return the reciprocal of x, 1/x."""
    # reciprocal(x) is faster than divide(1, x). But, for large |x|,
    # reciprocal(x) might be zero, whereas divide(1, x) is not
    return np.divide(1, x)

__SIGNATURE_BIAS_RECIPROCAL = [
    "float32(uint8,float32)", "float32(int16,float32)",
    "float32(float32,float32)",
]
@vectorize(
    __SIGNATURE_BIAS_RECIPROCAL,
    cache=True, fastmath=True)
@wraparound(False)
def bias_reciprocal(x, bias):
    """Add a bias to x and then get the reciprocal."""
    return np.divide(1, np.add(x, bias))

__SIGNATURE_ELEMENTWISE_MEAN = [
    "float32(uint8,uint8)", "float32(float32,float32)",
]
@vectorize(
    __SIGNATURE_ELEMENTWISE_MEAN,
    cache=True, fastmath=True)
@wraparound(False)
def elementwise_mean(a, b):
    """Element-wise calculate mean of two areguments."""
    return np.multiply(0.5, np.add(a, b))

# Operations for filtering.
__SIGNATURE_OUTER = [
    "float32[:,:](float32[:],float32[:])",
]
@njit(
    __SIGNATURE_OUTER,
    nogil=True, cache=True, fastmath=True, parallel=True)
@wraparound(False)
def nb_outer(a, b):
    """
    Calculate the outer product of two 1-D array.

    This function is same as numpy.outer, but this is a parallel computation
    version with numba. numpy.outer is faster than nonparallel computation
    version.
    """
    output = np.empty((a.size, b.size), dtype=np.float32)
    for y in prange(a.size):
        output[y] = np.multiply(a[y], b)
    return output

__SIGNATURE_OUTER_BIAS = [
    "float32[:,:](float32[:],float32[:],float32)",
]
@njit(
    __SIGNATURE_OUTER_BIAS,
    nogil=True, cache=True, fastmath=True)
@wraparound(False)
def outer_bias(a, b, bias):
    """
    Calculate bias minus the outer product of two 1-D array, bias - outer(a,b).

    This function is present for calculate highpass filter which lowpass filter
    is separable.
    """
    output = np.empty((a.size, b.size), dtype=np.float32)
    for y, val in enumerate(a):
        output[y] = bias - np.multiply(val, b)
    return output

@njit(
    __SIGNATURE_OUTER_BIAS,
    nogil=True, cache=True, fastmath=True, parallel=True)
@wraparound(False)
def outer_bias_parallel(a, b, bias):
    """
    Calculate bias minus the outer product of a and b, bias - outer(a,b),
    with numba parallel computation.

    This function is present for calculate highpass filter which lowpass filter
    is separable.
    """
    output = np.empty((a.size, b.size), dtype=np.float32)
    for y in prange(a.size):
        output[y] = bias - np.multiply(a[y], b)
    return output

__SIGNATURE_LOWPASS_OUTER = [
    "complex128[:,:](complex128[:,:],float32[:],float32[:])",
    "complex64[:,:](complex64[:,:],float32[:],float32[:])",
    "uint8[:,:](uint8[:,:],float32[:],float32[:])",
]
@njit(
    __SIGNATURE_LOWPASS_OUTER,
    nogil=True, cache=True, fastmath=True)
@wraparound(False)
def lowpass_outer(fftimg, a, b):
    """
    Multiply a lowpass filter which is outer(a,b) to fftimg.
    """
    output = np.empty_like(fftimg)
    for y, row_fft in enumerate(fftimg):
        output[y] = np.multiply(np.multiply(a[y], b), row_fft)
    return output

@njit(
    __SIGNATURE_LOWPASS_OUTER,
    nogil=True, cache=True, fastmath=True, parallel=True)
@wraparound(False)
def lowpass_outer_parallel(fftimg, a, b):
    """
    Multiply a lowpass filter which is outer(a,b) to fftimg with numba parallel
    computation.
    """
    output = np.empty_like(fftimg)
    for y in prange(fftimg.shape[0]):
        output[y] = (a[y]*b) * fftimg[y]
    return output

__SIGNATURE_HIGHPASS_OUTER = [
    "complex128[:,:](complex128[:,:],float32[:],float32[:],float32)",
    "complex64[:,:](complex64[:,:],float32[:],float32[:],float32)",
    "float32[:,:](float32[:,:],float32[:],float32[:],float32)",
    "uint8[:,:](uint8[:,:],float32[:],float32[:],float32)",
]
@njit(
    __SIGNATURE_HIGHPASS_OUTER,
    nogil=True, cache=True, fastmath=True)
@wraparound(False)
def highpass_outer(fftimg, a, b, bias):
    """
    Multiply a highpass filter which the lowpass filter is outer(a,b),
    to fftimg.
    """
    output = np.empty_like(fftimg)
    for y, row_img in enumerate(fftimg):
        output[y] = (bias-a[y]*b) * row_img
    return output

@njit(
    __SIGNATURE_HIGHPASS_OUTER,
    nogil=True, cache=True, fastmath=True, parallel=True)
@wraparound(False)
def highpass_outer_parallel(fftimg, a, b, bias):
    """
    Multiply a highpass filter which the lowpass filter is outer(a,b),
    to fftimg with numba parallel computation.
    """
    ksize = fftimg.shape
    output = np.empty_like(fftimg)

    for y in prange(ksize[0]):
        output[y] = (bias-a[y]*b) * fftimg[y]
    return output

# # Norms
# _SIGNATURE_2_NORM = [
#     "float32[:,:](float32[:,:],float32[:,:])",
#     "float64[:,:](float64[:,:],float64[:,:])",
# ]
# @njit(
#     _SIGNATURE_2_NORM,
#     nogil=True, cache=True,  fastmath=True, parallel=True)
# @wraparound(False)
# def euclidean_norm(y, x):
#     """
#     Return the Euclidean norm (or, 2-norm) of in an 2-D space.

#     The first argument is the y-axis component, and the second argument is the
#     x-axis component.
#     """
#     return np.sqrt(np.add(np.power(y,2), np.power(x,2)))

# _SIGNATURE_P_NORM = [
#     "float32[:,:](float32[:,:],float32[:,:],float32)",
#     "float64[:,:](float64[:,:],float64[:,:],float32)",
# ]
# @njit(
#     _SIGNATURE_P_NORM,
#     nogil=True, cache=True, fastmath=True, parallel=True)
# @wraparound(False)
# def p_metric(y, x, p):
#     """
#     Return the p-norm of in an 2-D space.

#     The first argument is the y-axis component, and the second argument is the
#     x-axis component.
#     """
#     return np.power( np.add(np.power(y,p), np.power(x,p)), np.divide(1,p))

# _SIGNATURE_SUP_METRIC = [
#     "float64[:,:](float64[:,:],float64[:,:])",
# ]
# @njit(
#     _SIGNATURE_SUP_METRIC,
#     nogil=True, cache=True, fastmath=True, parallel=True)
# @wraparound(False)
# def sup_metric(y,x):
#     """
#     Return the sup-norm of in an 2-D space, that is, return y is y > x and
#     return x is x > y.

#     The first argument is the y-axis component, and the second argument is the
#     x-axis component.
#     """
#     return np.maximum(y, x)

# _SIGNATURE_DIST_MATRIX = [
#     "float32[:,:](UniTuple(uint16,2))"
# ]
# @njit(
#     _SIGNATURE_DIST_MATRIX,
#     nogil=True, cache=True, fastmath=True)
# @wraparound(False)
# def dist_matrix(size):
#     """
#     Return an 2-D matrix whose (y,x) entry is the dist between (y,x) and
#     center, where center = size//2.
#     """
#     output = np.empty(size, dtype=np.float32)
#     center_y = size[0] // 2
#     center_x = size[1] // 2

#     for y in range(size[0]):
#         dist_y = np.power(center_y-y, 2)
#         for x in range(size[1]):
#             output[y,x] = np.sqrt(dist_y + np.power(center_x-x, 2))
#     return output

# _SIGNATURE_DIST_SQUARE_MATRIX = [
#     "float32[:,:](UniTuple(uint16,2))"
# ]
# @njit(
#     _SIGNATURE_DIST_SQUARE_MATRIX,
#     nogil=True, cache=True, fastmath=True)
# @wraparound(False)
# def dist_square_matrix(size):
#     """
#     Return an 2-D matrix whose (y,x) entry is the square of dist between (y,x)
#     and center, where center = ceil((size-1) / 2).
#     """
#     output = np.empty(size, dtype=np.float32)
#     center_y = size[0] // 2
#     center_x = size[1] // 2

#     for y in range(size[0]):
#         dist_y = np.power(center_y-y, 2)
#         for x in range(size[1]):
#             output[y,x] = dist_y + np.power(center_x-x, 2)
#     return output

# Thansform uint8 data by table[data].
__SIGNATURE_INTENSITY_TRANSFORMATION = [
    "uint8[:,:](uint8[:,:],uint8[:])",
    "float32[:,:](uint8[:,:],float32[:])"
]
@njit(
    __SIGNATURE_INTENSITY_TRANSFORMATION,
    nogil=True, cache=True, fastmath=True
)
@wraparound(False)
def transform(
    img: ARR_8U2D, table: ARR_1D
) -> ARR_8U2D:
    """
    Transforms intensity of an image with a table:
        output = table[img].

    Parameters
    ----------
    img : ARR_8U2D
        An 8UC1 image.
    table : ARR_1D
        A 1-D array with length 256.

    Returns
    -------
    output : IMG_GRAY
        New image. The dtype depend on the dtype of table.
    """
    output = np.empty_like(img, dtype=table.dtype)
    for y, row in enumerate(img):
        for x, val in enumerate(row):
            output[y,x] = table[val]
    return output

__SIGNATURE_INTENSITY_TRANSFORMATION_1D = [
    "uint8[:](uint8[:],uint8[:])",
    "float32[:](uint8[:],float32[:])"
]
@njit(
    __SIGNATURE_INTENSITY_TRANSFORMATION_1D,
    nogil=True, cache=True, fastmath=True
)
@wraparound(False)
def transform_1D(
        seq: ARR_8U2D, table: ARR_1D
    ) -> ARR_8U2D:
    """Transforms intensity of a sequence with a table:
        output = table[seq].
    """
    output = np.empty_like(seq, dtype=table.dtype)
    for y, val in enumerate(seq):
        output[y] = table[val]
    return output

# Padding Border. See OpenCV BorderTypes:
# https://docs.opencv.org/4.x/d2/de8/group__core__array.html#ga209f2f4869e304c82d07739337eae7c5
@boundscheck(False)
@wraparound(False)
def pad_reflect101(img, ksize=(3,3)):
    """Padding Border with REFLECT101: dcb | abcdef | edc."""
    size = img.shape
    khalf_y = ksize[0]//2
    khalf_x = ksize[1]//2
    output = np.empty(
        (size[0]+ksize[0]-1,size[1]+ksize[1]-1), dtype=img.dtype)

    output[khalf_y:-khalf_y,khalf_x:size[1]+khalf_x] = img # Central=img
    output[khalf_y:-khalf_y,:khalf_x] = img[:,khalf_x:0:-1] # Left
    output[khalf_y:-khalf_y,size[1]+khalf_x:] = \
        img[:,size[1]-2:size[1]-khalf_x-2:-1] # Right

    output[:khalf_y,:] = output[ksize[0]-1:khalf_y:-1] # Top
    output[-1:-khalf_y-1:-1,:] = \
        output[size[0]-1:size[0]+khalf_y-1] # Bottom
    return output

# Normalize
__SIGNATURE_NORMALIZE_UINT8 = [
    "float32[:,:](uint8[:,:])",
]
@njit(
    __SIGNATURE_NORMALIZE_UINT8,
    nogil=True, cache=True, fastmath=True, parallel=True)
@wraparound(False)
def normalize_uint8_parallel(img: ARR_8U2D) -> ARR_32F2D:
    """Compress the range of values of an 8UC1 image to [0,1] with
    parallel=True.

    Parameters
    ----------
    img : ARR_8U2D
        An 8UC1 image.

    Returns
    -------
    output : ARR_32F2D
        Compressed image.
    """
    output = np.empty_like(img, dtype=np.float32)
    tabel = np.empty(256, dtype=np.float32)
    for i in range(256):
        tabel[i] = np.divide(i, 255)

    for y in prange(img.shape[0]):
        for x, val in enumerate(img[y]):
            output[y,x] = tabel[val]
    return output

@njit(
    __SIGNATURE_NORMALIZE_UINT8,
    nogil=True, cache=True, fastmath=True)
@wraparound(False)
def normalize_uint8(img: ARR_8U2D) -> ARR_32F2D:
    """Compress the range of values of an 8UC1 image to [0,1].

    Parameters
    ----------
    img : ARR_8U2D
        An 8UC1 image.

    Returns
    -------
    output : ARR_32F2D
        Compressed image.
    """
    output = np.empty_like(img, dtype=np.float32)
    tabel = np.empty(256, dtype=np.float32)
    for i in range(256):
        tabel[i] = np.divide(i, 255)

    for y, row in enumerate(img):
        for x, val in enumerate(row):
            output[y,x] = tabel[val]
    return output


from django.urls import path
from .import views

urlpatterns = [
    path( '', views.HomePageView.as_view() , name = 'homepageview'),
    path( 'login/', views.LoginView.as_view(), name = 'loginview'),
    path( 'sign_up/' , views.SignUpView, name = 'signup', ),

]


# Author: Ben Champion <bwc3252@rit.edu>
# Feel free to contact me if you have questions and/or something is horribly
# broken.

import numpy as np
import scipy.optimize as opt

def _sq_md(xdata, ydata, px, py, xerr, yerr):
    # Squared Mahalanobis distances between (xdata, ydata) and (px, py).
    # This is essentially a way of measuring distances between points that
    # accounts for uncertainties in each direction.
    return ((xdata - px) / xerr)**2 + ((ydata - py) / yerr)**2

def _residuals(f, xdata, ydata, xerr, yerr, params):
    # First, we need to find the values of x that, given our current params,
    # minimize the total squared Mahalanobis distances.
    res = opt.minimize(lambda x0 : np.sum(_sq_md(xdata, ydata, x0, f(x0, *params), xerr, yerr)),
            xdata, method="Powell")
    # With those x values, return those squared Mahalanobis distances as the
    # "residuals" needed by scipy.optimize.leastsq
    return _sq_md(xdata, ydata, res.x, f(res.x, *params), xerr, yerr)

def fit(f, xdata, ydata, xerr=None, yerr=None, p0=None):
    """
    Fits a (generally) nonlinear function f(x, *params) to data (xdata, ydata),
    accounting for uncertainties in both the independent and dependent
    variables.

    Parameters
    ----------
    f:
        Function of the form f(x, param_1, param_2, ..., param_M), where x is
        an array containing values of the independent variable and each param_i
        is a float.
    xdata:
        Array of size N containing independent variable.
    ydata:
        Array of size N containing dependent variable.
    xerr:
        Uncertainty in the independent variable, must be a float or an array of
        size N.
    yerr:
        Uncertainty in the dependent variable, must be a float or an array of
        size N.
    p0:
        Array of size M containing initial guesses for parameter values.

    Returns
    -------
    params:
        Array of size M containing parameter estimates.

    cov:
        Array with shape (M, M) with parameter covariances.
    """
    # refine the initial guess by fitting with only uncertainty in the
    # dependent variable
    p0, cov0 = opt.curve_fit(f, xdata, ydata, sigma=yerr, p0=p0)
    # if there is no uncertainty in the independent variable, go ahead and
    # return the result of this fit
    if xerr is None:
        return p0, cov0
    # minimize the sum of the squared residuals wrt. params
    res = opt.leastsq(lambda p : _residuals(f, xdata, ydata, xerr, yerr, p),
            p0, full_output=True)
    params, cov, infodict, errmsg, ier = res
    # The remainder of this is from:
    # https://github.com/scipy/scipy/blob/v1.4.1/scipy/optimize/minpack.py, in
    # the leastsq function. Note that "cost", however, is calculated according
    # to our modified cost function, not the standard least squares cost.
    cost = np.sum(_residuals(f, xdata, ydata, xerr, yerr, params))
    s_sq = cost / (ydata.size - p0.size)
    cov *= s_sq
    return params, cov

### test code, does not run when imported

if __name__ == "__main__":
    # imports needed for test
    import matplotlib.pyplot as plt

    # random seed (for consistency between tests)
    np.random.seed(1)

    def g(x, a, b):
        return a * x + b

    #
    # TEST PARAMETERS
    #
    n = 20          # number of data points
    a = 1.0
    b = 0.0
    sigma_x = 0.2   # uncertainty in x
    sigma_y = 0.2   # uncertainty in y
    llim = 0.0
    rlim = 5.0
    xplot = np.linspace(llim, rlim, 100)

    print("true parameters:")
    print("\ta = {0:.3f}".format(a))
    print("\tb = {0:.3f}".format(b))

    # generate some data
    x = np.linspace(llim, rlim, n)
    y = g(x, a, b) + np.random.normal(0.0, sigma_y, n)
    x += np.random.normal(0.0, sigma_x, n)
    xerr = np.ones(n) * sigma_x
    yerr = np.ones(n) * sigma_y

    # close-ish initial guess (used by both fitting implementations)
    p0=np.array([1.4, -0.2])

    # fit using both x and y uncertainty
    params, cov = fit(g, x, y, xerr=xerr, yerr=yerr, p0=p0)
    std_dev = np.sqrt(np.diag(cov)) # uncertainty in the fit parameters

    # plot and print the results of this fit
    plt.plot(xplot, g(xplot, *params), label="Using $\sigma_x$ and $\sigma_y$")
    print("\nour results:")
    print("\ta = {0:.3f} +/- {1:.3f}".format(params[0], std_dev[0]))
    print("\tb = {0:.3f} +/- {1:.3f}".format(params[1], std_dev[1]))

    # fit using standard scipy curve_fit
    params, cov = opt.curve_fit(g, x, y, sigma=yerr, p0=p0)
    std_dev = np.sqrt(np.diag(cov)) # uncertainty in the fit parameters

    # plot and print the results of this fit
    plt.plot(xplot, g(xplot, *params), label="Using only $\sigma_y$")
    print("\ncurve_fit's results:")
    print("\ta = {0:.3f} +/- {1:.3f}".format(params[0], std_dev[0]))
    print("\tb = {0:.3f} +/- {1:.3f}".format(params[1], std_dev[1]))

    res = opt.minimize(lambda x0 : np.sum(_sq_md(x, y, x0, g(x0, *params), xerr, yerr)),
            x, method="Powell")
    plt.scatter(res.x, g(res.x, *params), label="Points used to calculate $M_D$"
        + " in last iteration")

    # plot the data points, generate the legend, and display the plot
    plt.errorbar(x, y, xerr=xerr, yerr=yerr, fmt="none", color="black", capsize=2)
    plt.legend()
    plt.show()


from abc import ABC , abstractclassmethod,abstractproperty
from datetime import datetime

class Usuario(ABC):
  def __init__(self , nome, data_nascimento, cpf, endereco):
    self.nome = nome
    self.data_nascimento = data_nascimento
    self.cpf =  cpf
    self.endereco = endereco

  @abstractclassmethod
  def exibir_informacoes(self):
    pass

class ContaBancaria:
  def __init__(self, cliente):
    self.saldo = 0
    self. transacoes = []
    self.saques_realizados = 0
    self.cliente = cliente

  def depositar(self, amount):
    if amount > 0:
      self.saldo += amount
      self.transacoes.append(f"Depósito: + {amount}")
      return True
    return False

  def sacar(self, amount):
    if amount > 0 and self.saldo >= amount and self.saques_realizados < 3:
      self.saldo -= amount
      self.transacoes.append(f"Saque: - {amount:.2f}")
      self.saques_realizados += 1
      return True
    elif amount == 500:
      print("Limite máximo de R$500 por saques...")
    elif self.saques_realizados >=3:
      print("Limite máximo de  sasques diários atingido...")
    else:
      print("Saldo insuficiente...")
    return False

  def extrato(self):
    return self.transacoes

class Cliente(Usuario):
   def exibir_informcoes(self):
      print(f"Nome: {self.nome}")
      print(f"Data de nascimento: {self.data_nascimento}")
      print(f"CPF: {self.cpf}")
      print(f"Endereço: {self.endereco}")

   @classmethod
   def criar_cliente(cls):
      nome = input("Digite o nome do cliente:  ")
      data_nascimento = input("Digite a data de nascimento( dd/mm/aaaa")
      cpf = input("Digite o CPF: ")
      endereco = input("Digite o endereço (logradouro, no - bairro - cidade/sigla estado")
      return cls(nome, data_nascimento, cpf, endereco)

#Lista d eclientes
clientes =[]

def cadastrar_usuario():
  cliente = Cliente.criar_cliente()
  #Verificar se cpf Já Existe
  cpf_existente = any(c.cpf== cliente.cpf for c in clientes)
  if cpf_existente:
    print("CPf já cadastrado. Não é possivel cadastrar o mesmo Cpf")
    return

  clientes.append(cliente)
  print("Usuário cadastro com suceso!")

cliente_index = (clientes[0])# Assumindo o primeiro cliente da lista
conta_usuario = ContaBancaria(cliente_index)

def criar_conta_corrente():
  cpf = input("Digite o CPF do cliente: ")

  for cliente in clientes:
    if cliente.cpf == cpf:
      conta = ContaBancaria(cliente)
      print("Conta corrente criada com sucesso!")
      return

  print("CPF não encontrado.Certifique-se de cadastrar o cliente...")

# Função para exibir o menu e lidar com as operações

def menu_usuario():
  print("1 - Depositar")
  print("2 - Sacar")
  print("3 - Exibir Extrato")
  print("4 - Exibir Saldo")
  print("0 - Sair")

  opcao = input("Ecolher um a opção: ")
  return opcao

# Interação com o usuário

while True:
    opcao = menu_usuario()

    if opcao == '1':
        valor = float(input("Digite o valor para depositar: "))
        conta_usuario.depositar(valor)
    elif opcao == '2':
        valor = float(input("Digite o valor para saque: "))
        conta_usuario.sacar(valor)
    elif opcao == '3':
        extrato = conta_usuario.extrato()
        for transacao in extrato:
            print(transacao)
    elif opcao == '4':
        print(f"Saldo atual: {conta_usuario.saldo:.2f}")
    elif opcao == '0':
        print("Saindo...")
        break
    else:
        print("Opção inválida. Por favor, escolha uma opção válida.")


class Solution:
    def maxKelements(self, nums: List[int], k: int) -> int:
        heap = []

        for num in nums:
            heapq.heappush(heap, (-num, num))

        score = 0
        for i in range(k):
            max_num, num = heapq.heappop(heap)
            score += num
            new_num = math.ceil(num /3)
            heapq.heappush(heap, (-new_num, new_num))

        return score


"""
===============
Annotation Node
===============

This module annotates a AST tree to identify JIT-compatible trees
"""
import ast
from ast import iter_fields
from .annotated_node import AnnotationNode

from ..dev import debug

from .. import core
from ..core import hypervector

from ..jit import HYPERVECTOR_TYPE, HYPERMATRIX_TYPE
from ..jit import HD_INT_ARRAY_TYPE, HD_FLOAT_ARRAY_TYPE
from ..jit import NP_INT_ARRAY_TYPE, NP_FLOAT_ARRAY_TYPE
from ..jit import PREPROC_FEATURES

TAG = "jit.ast_annotator"

def annotate_ast(ast_root, arg_variables, encode_func_vars):
    """ Run ASTAnnotator """
    # Create virtual nodes to give information for the variables
    vnodes = VirtualKnownNode.factory_virtual_nodes(
            variables=arg_variables)

    # Run the annotator
    visitor = ASTAnnotator(vnodes, arg_variables)
    annotation_root = visitor.visit(ast_root)

    # Traverse the tree to update the openhd_included
    update_openhd_included(annotation_root)

    annotation_root.print_debug()
    #assert(False)

    if encode_func_vars is not None:
        body_node = annotation_root.child[0].child[0].child[2]
        assert(body_node.item == "body")
        debug.thrifty_assert(
                body_node.transformable,
                "Encoding function should be run on CUDA " +
                "for full acceleration. Simplify your encode function.")

    return annotation_root

def is_hyper_type(item_type):
    return item_type in [HYPERVECTOR_TYPE, HYPERMATRIX_TYPE]

def update_openhd_included(aNode):
    aNode.openhd_included = is_hyper_type(aNode.item_type)

    if isinstance(aNode.child, list):
        for subchild in aNode.child:
            aNode.openhd_included |= update_openhd_included(subchild)
    elif aNode.child:
        aNode.openhd_included |= update_openhd_included(aNode.child)

    return aNode.openhd_included

class VirtualKnownNode(object):
    """
    Virtual node to handle each argument variable of the compiling function
    """
    def __init__(self, name, item_type):
        self.id = name
        self.item_type = item_type
        self.lineno = -1

    @staticmethod
    def factory_virtual_nodes(variables):
        ret = []
        for n, (t, val) in variables.items():
            vNode = VirtualKnownNode(n, t)
            ret.append(vNode)

        return ret

class ASTAnnotator(object):
    """
    Performs a static analysis to build the AnnotationNode tree
    """
    boolean_constants = frozenset(['True', 'False'])
    known_functions = {
            # Function : (type, jit_availability)

            # Built-in python function
            'range' : (int, True),
            'int' : (int, True),
            'float' : (float, True),
            'bool' : (bool, True),

            # OpenHD to declare
            'draw_random_hypervector': (HYPERVECTOR_TYPE, True),
            'draw_gaussian_hypervector': (HYPERVECTOR_TYPE, True),
            'hypervector' : (HYPERVECTOR_TYPE, True),
            'hypermatrix' : (HYPERMATRIX_TYPE, False), # cannot be run on CUDA

            # OpenHD built-in operators
            'flip' : (HYPERVECTOR_TYPE, True),
            'permute' : (HYPERVECTOR_TYPE, True),
            'shuffle' : (HYPERVECTOR_TYPE, True),

            # OpenHD built-in operators, but already compiled with AOT
            'search' : (HD_INT_ARRAY_TYPE, False),
            'cossim' : (HD_FLOAT_ARRAY_TYPE, False),
            'matmul' : (HYPERMATRIX_TYPE, False),

            # OpenHD built-in operators which can be both AOT or JIT
            # Note: currently we only supports AOT and hypermatrix type
            'cos' : (HYPERMATRIX_TYPE, False),
            'sign' : (HYPERMATRIX_TYPE, False),

            # Numpy function to array - TODO: better inference for int type
            'array': (NP_FLOAT_ARRAY_TYPE, False),
            'zeros': (NP_FLOAT_ARRAY_TYPE, False),
            'ones': (NP_FLOAT_ARRAY_TYPE, False),

            # Cuda natives
            '__syncthreads': (int, True),
            '__hd_return': (int, True),
            }

    def __init__(self, known_nodes, arg_variables):
        self.declared_types = dict() # variable -> (declared_node, type)

        for vnode in known_nodes:
            self.declared_types[vnode.id] = \
                    (vnode, vnode.item_type)

        self.arg_variables = arg_variables

    def visit(self, node):
        """Visit a node."""
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.generic_visit)
        ret = visitor(node)

        assert(not(isinstance(ret, list) and len(ret) == 0))
        return ret

    def generic_visit(self, node):
        """Called if no explicit visitor function exists for a node."""
        aNode = AnnotationNode(node)
        if len(node._fields) == 0:
            return aNode

        children = []
        for field, value in iter_fields(node):
            aNode_field = AnnotationNode(field)
            if isinstance(value, list):
                if len(value) == 0:
                    children.append(aNode_field)
                    continue

                sub_children = []
                for item in value:
                    if isinstance(item, ast.AST):
                        aNode_field_list_item = self.visit(item)
                    else:
                        aNode_field_list_item = AnnotationNode(item)

                    sub_children.append(aNode_field_list_item)

                aNode_field.set(sub_children)
            elif isinstance(value, ast.AST):
                aNode_field.set(self.visit(value))

            children.append(aNode_field)

        return aNode.set(children)

    def visit_Num(self, node):
        return AnnotationNode(node).set(None, True).set_type(type(node.n))

    def visit_Str(self, node):
        return AnnotationNode(node).set(None, False).set_type(type(node.s))

    def visit_List(self, node):
        return AnnotationNode(node).set(None, False).set_type(list)

    def visit_Tuple(self, node):
        aNode = AnnotationNode(node)
        return aNode.set([self.visit(e) for e in node.elts]).set_type(tuple)

    def visit_Assign(self, node):
        aNode = AnnotationNode(node)

        if len(node.targets) != 1 or not(
                isinstance(node.targets[0], ast.Name) or \
                        isinstance(node.targets[0], ast.Tuple)):

            aNode_target = self.visit(node.targets[0])
            aNode_value = self.visit(node.value)

            if isinstance(node.targets[0], ast.Subscript):
                if aNode_target.child[0].item_type == NP_FLOAT_ARRAY_TYPE or \
                        aNode_target.child[0].item_type == NP_INT_ARRAY_TYPE:
                    if aNode_target.child[0].item.id != PREPROC_FEATURES:
                        aNode_target.set_tran(False)

            return aNode.set([aNode_target, aNode_value])

        aNode_target = self.visit(node.targets[0])
        aNode_value = self.visit(node.value)

        # New hypervector assignment
        if aNode_target.item_type is None:
            if aNode_value.is_included_declare():
                aNode_target.alloc_hv = True
            elif aNode_value.item_type == HYPERVECTOR_TYPE:
                aNode_target.alloc_hv = True

        # Copy data from value
        aNode_target.set_tran(aNode_value.transformable)
        aNode_target.set_type(aNode_value.item_type)

        if isinstance(node.targets[0], ast.Tuple):
            debug.thrifty_assert(
                    isinstance(node.value, ast.Tuple),
                    "Assigning tuple with one value is not supported yet.")
            debug.thrifty_assert(
                    len(node.targets[0].elts) == len(node.value.elts),
                    "Tuple assignment must take the same # of elements")

            for subtarget, subvalue, subtarget_node in zip(
                    aNode_target.child, aNode_value.child,
                    node.targets[0].elts):

                assert(isinstance(subtarget.item, ast.Name))
                if subtarget.item_type is None and subvalue.is_included_declare():
                    subtarget.alloc_hv = True

                # Copy data from value
                subtarget.set_tran(subvalue.transformable)
                subtarget.set_type(subvalue.item_type)

                self.declared_types[subtarget.item.id] = (
                        subtarget.item, subtarget.item_type)

                if isinstance(subtarget_node, ast.Subscript):
                    if subtarget.child[0].item_type == NP_FLOAT_ARRAY_TYPE or \
                            subtarget.child[0].item_type == NP_INT_ARRAY_TYPE:
                        subtarget.set_tran(False)

        else:
            self.declared_types[node.targets[0].id] = (
                    node.targets[0],
                    aNode_target.item_type)

        return aNode.set([aNode_target, aNode_value])

    def visit_Name(self, node):
        aNode = AnnotationNode(node)

        if node.id in self.boolean_constants:
            aNode.set(None, True).set_type(bool)
        elif node.id in self.declared_types:
            declared_node, item_type = self.declared_types[node.id]
            assert(declared_node.lineno <= node.lineno)
            aNode.set(None, True).set_type(item_type)
        elif node.id in core.__CONTEXT__: # Try to find it in the context
            val = core.__CONTEXT__[node.id]

            # Update arg vars and declared_node
            item_type = type(val)
            if isinstance(val, hypervector.hypervector):
                item_type = HYPERVECTOR_TYPE
            elif isinstance(val, hypervector.hypermatrix):
                item_type = HYPERMATRIX_TYPE

            self.arg_variables[node.id] = (item_type, val)
            vnode = VirtualKnownNode(node.id, item_type)
            self.declared_types[vnode.id] = \
                    (vnode, vnode.item_type)

            # Do the same thing
            declared_node, item_type = self.declared_types[node.id]
            aNode.set(None, True).set_type(item_type)

        return aNode

    def visit_Subscript(self, node):
        aNode = AnnotationNode(node)
        if not isinstance(node.slice, ast.Index):
            return self.generic_visit(node)

        aNode_value = self.visit(node.value)
        aNode_slice = self.visit(node.slice)

        transformable = False
        item_type = None
        if aNode_value.item_type == HYPERMATRIX_TYPE:
            transformable = True
            item_type = HYPERVECTOR_TYPE
        elif aNode_value.item_type == NP_INT_ARRAY_TYPE:
            aNode_value.transformable = True # override
            transformable = True
            item_type = int
        elif aNode_value.item_type == NP_FLOAT_ARRAY_TYPE:
            aNode_value.transformable = True # override
            transformable = True
            item_type = float
        elif aNode_value.item_type == HD_INT_ARRAY_TYPE:
            aNode_value.transformable = True # override
            transformable = True
            item_type = int
        elif aNode_value.item_type == HD_FLOAT_ARRAY_TYPE:
            aNode_value.transformable = True # override
            transformable = True
            item_type = float

        aNode.set([aNode_value, aNode_slice], transformable)
        if item_type is not None:
            aNode.set_type(item_type)

        return aNode

    def visit_Index(self, node):
        aNode = AnnotationNode(node)
        return aNode.set([self.visit(node.value)])

    def visit_Constant(self, node):
        return AnnotationNode(node).set(None, True).set_type(type(node.n))

    @staticmethod
    def type_inference(item1, item2):
        # Return type
        def is_either_type(item1, item2, item_type):
            if item1.item_type == item_type:
                return True

            if item2.item_type == item_type:
                return True

            return False

        #type_order = [HYPERMATRIX_TYPE, HYPERVECTOR_TYPE, float, int, bool]
        if is_either_type(item1, item2, HYPERMATRIX_TYPE):
            if item1.item_type == item2.item_type:
                assert(item1.hx_size == item2.hx_size)
                return HYPERMATRIX_TYPE
            elif item1.item_type == HYPERMATRIX_TYPE:
                return HYPERMATRIX_TYPE
            else: # item2.item_type == HYPERMATRIX_TYPE
                return HYPERMATRIX_TYPE
        elif is_either_type(item1, item2, HYPERVECTOR_TYPE):
            return HYPERVECTOR_TYPE

        primitive_type_order = [float, int, bool]
        for item_type in primitive_type_order:
            if is_either_type(item1, item2, item_type):
                return item_type

        return None

    def visit_AugAssign(self, node):
        aNode = AnnotationNode(node)
        if isinstance(node.target, ast.Name):
            node_name = node.target.id
        elif isinstance(node.target, ast.Subscript):
            node_name = node.target.value.id
        else:
            assert(False)
            return self.generic_visit(node)

        if node_name not in self.declared_types: # in fact a syntax error
            return self.generic_visit(node)

        aNode_target = self.visit(node.target)
        aNode_value = self.visit(node.value)

        aNode_target.set_tran(aNode_value.transformable)

        # update the type of the target
        if isinstance(node.target, ast.Name):
            item_type = ASTAnnotator.type_inference(
                    aNode_target, aNode_value)
            aNode_target.set_type(item_type)
            self.declared_types[node_name] = (
                    self.declared_types[node_name][0],
                    aNode_target.item_type)
        elif isinstance(node.target, ast.Subscript):
            self.declared_types[node_name] = (
                    self.declared_types[node_name][0],
                    aNode_target.child[0].item_type)
        else:
            assert(False)

        return aNode.set([aNode_target, aNode_value])

    def visit_Call(self, node):
        aNode = AnnotationNode(node)

        fname = None
        if isinstance(node.func, ast.Attribute):
            fname = node.func.attr # TODO: package name - node.func.value.id
        elif isinstance(node.func, ast.Name):
            fname = node.func.id

        if fname in self.known_functions:
            item_type, jit_availability = self.known_functions[fname]
            if node.args:
                children_args = [self.visit(a) for a in node.args]
                aNode.set(children_args, jit_availability).set_type(item_type)
            else:
                # TODO: Check the arguments
                # Currently, it just tries to compile it
                # without guaranteeing the syntax correctness
                aNode.set(None, jit_availability).set_type(item_type)

            return aNode

        # Skip the validation for the function name
        #return self.generic_visit(node)
        return aNode.set([self.generic_visit(a) for a in node.args])

    def visit_BinOp(self, node):
        aNode = AnnotationNode(node)
        child = [self.visit(node.left), self.visit(node.right)]
        item_type = ASTAnnotator.type_inference(child[0], child[1])

        return aNode.set(child).set_type(item_type)

    def visit_BoolOp(self, node):
        aNode = AnnotationNode(node)
        child = [self.visit(v) for v in node.values]
        return aNode.set(child).set_type(bool)

    def visit_Compare(self, node):
        aNode = AnnotationNode(node)
        assert(len(node.comparators) == 1)
        assert(len(node.ops) == 1)

        left = self.visit(node.left)
        right = self.visit(node.comparators[0])

        return aNode.set([left, right])

    def visit_For(self, node):
        aNode = AnnotationNode(node)

        if not isinstance(node.target, ast.Name):
            return self.generic_visit(node)

        if not isinstance(node.iter, ast.Call):
            return self.generic_visit(node)

        if node.iter.func.id != "range":
            return self.generic_visit(node)

        target = self.visit(node.target)
        it = self.visit(node.iter)
        assert(it.item_type == int)

        target.set_tran(it.transformable)
        target.set_type(it.item_type)
        self.declared_types[node.target.id] = (node.target, target.item_type)

        aNode_body = AnnotationNode(node.body)
        body = aNode_body.set([self.visit(c) for c in node.body])

        aNode.set([target, it, body])

        return aNode

    def visit_While(self, node):
        aNode = AnnotationNode(node)

        test = self.visit(node.test)
        aNode_body = AnnotationNode(node.body)
        body = aNode_body.set([self.visit(c) for c in node.body])

        aNode.set([test, body])
        return aNode

    def visit_Break(self, node):
        aNode = AnnotationNode(node)
        return aNode.set(None, True)

    def visit_Continue(self, node):
        aNode = AnnotationNode(node)
        return aNode.set(None, True)

    def visit_If(self, node):
        aNode = AnnotationNode(node)

        test = self.visit(node.test)
        body = AnnotationNode(node.body)
        body.set([self.visit(c) for c in node.body])

        if len(node.orelse) == 0:
            aNode.set([test, body])
        else:
            orelse = AnnotationNode(node.orelse)
            orelse.set([self.visit(c) for c in node.orelse])
            aNode.set([test, body, orelse])

        return aNode


class Estudiante:
    def __init__(self, nombre: str, apellido: str, edad: int, codigo: str):
        self.nombre = nombre
        self.apellido = apellido
        self.edad = edad
        self.codigo = codigo
        self.notas = []

    def imprimir_info(self):
        print("-" * 100)
        print("El nombre completo del estudiante es:", self.nombre, self.apellido)
        print("La edad del estudiante es:", self.edad)
        print("El código del estudiante es:", self.codigo)
        print("Las notas del estudiante son:", self.notas)
        self.calcular_promedio()
        print("El promedio del estudiante es:", self.promedio)
        print("El estudiante fue ", end = "")
        print("APROBADO") if self.es_aprobado() else print("REPROBADO")

    def agregar_nota(self, nota: float):
        self.notas.append(nota)

    def calcular_promedio(self):
        self.promedio = round(sum(self.notas)/len(self.notas), 1) if self.notas != [] else 0.0

    def es_aprobado(self):
        return self.promedio >= 3.0

est01 = Estudiante("Mao", "Moreno", 35, "01")
est02 = Estudiante("Ingrid", "Cárdenas", 25, "02")
est03 = Estudiante("Javier", "Romero", 34, "03")

est01.agregar_nota(5.0)
est01.agregar_nota(3.2)
est01.agregar_nota(1.0)
est01.imprimir_info()
est02.imprimir_info()
est03.imprimir_info()


# mixins.py

from django.core.exceptions import ImproperlyConfigured

class ClassNameMixin(object):

    def get_context_data(self, **kwargs):
        context = super(ClassNameMixin, self).get_context_data(**kwargs)
        try:
            context['object_type'] = self.class_name
        except AttributeError:
            raise ImproperlyConfigured(
                'Please set class_name to appropriate string.')

        return context


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 17 09:56:58 2016

@author: mike
"""

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from mpl_toolkits.mplot3d import Axes3D
import math

def update(xr,xv,xa,yr,yv,ya,zr,zv,za):
    vx = xv + xa * dt
    vy = yv + ya * dt
    vz = zv + za * dt

    px = xr + vx * dt
    py = yr + vy * dt
    pz = zr + vz * dt

    new = [px,vx,xa,py,vy,ya,pz,vz,za]
    return new

#Init matrices
dt = .01
A = np.eye(9)
P = np.eye(9)
H = np.zeros((6,9))
R = np.eye(6)
I = np.eye(9)
G = np.zeros((9,1))
A[0,1] = dt
A[0,2] = pow(dt,2)/2
A[1,2] = dt
A[3,4] = dt
A[3,5] = pow(dt,2)/2
A[4,5] = dt
A[6,7] = dt
A[6,8] = pow(dt,2)/2
A[7,8] = dt
P *= 100
H[0,0] = 1
H[1,2] = 1
H[2,3] = 1
H[3,5] = 1
H[4,6] = 1
H[5,8] = 1
G[0] = pow(dt,2)/2
G[1] = dt
G[2] = 1
G[3] = pow(dt,2)/2
G[4] = dt
G[5] = 1
G[6] = pow(dt,2)/2
G[7] = dt
G[8] = 1

sa = 0.1
Q = np.dot(G,G.T)
Q *= pow(sa,2)

#Synthetically Create Position Measurement for Ball
Hz = 50.0 # Frequency of Vision System
dt = 1.0/Hz
T = 12.0 # s measuremnt time
m = int(T/dt) # number of measurements

px0 = 0.0 # x Position Start
py0 = 0.0 # y Position Start
pz0 = 0.0 # z Position Start

px1 = 0
py1 = 100
pz1 = 0

px2 = 5
py2 = 115
pz2 = 0

px3 = px2
py3 = 180
py3 = 0

vx = 0
vy = 0
vz = 0

vmax = 20
amax = 3

regime = 0

Xr = [0]
Xv = [0]
Xa = [0]
Yr = [0]
Yv = [0]
Ya = [0]
Zr = [0]
Zv = [0]
Za = [0]

for i in range(1,int(m)):
    if i < 5:
        Ya.append(50)
    else:
        Ya.append(0)
    Yv.append(Ya[i]*dt+Yv[i-1])
    Yr.append(Yv[i]*dt+Yr[i-1])
    Xr.append(0)
    Xv.append(0)
    Xa.append(0)
    Zr.append(0)
    Zv.append(0)
    Za.append(0)

#result = [Xr[0], Xv[0], Xa[0], Yr[0], Yv[0], Ya[0], Zr[0], Zv[0], Za[0]]
#for i in range(1,int(m)):
#    #first portion
#    if regime == 0:
#        ax = 0
#        az = 0
#        if vy < vmax:
#            ay = amax
#        if vy > vmax:
#            ay = 0
#
#    if regime == 1:
#        if Xr[i-1] < px2/2:
#            ax = amax
#        if Xr[i-1] >= px2/2 and Xr[i-1] < px2:
#            ax = -amax
#        if  Xr[i-1] >= px2:
#            ax = 0
#        ay = 0
#        az = 0
#
#    if regime == 2:
#        ax = 0
#        ay = 0
#        az = 0
#        Xv[i-1] = 0
#
#    result = update(Xr[i-1],Xv[i-1],ax,Yr[i-1],Yv[i-1],ay,Zr[i-1],Zv[i-1],az)
#    Xr.append(result[0])
#    Xv.append(result[1])
#    Xa.append(result[2])
#    Yr.append(result[3])
#    Yv.append(result[4])
#    Ya.append(result[5])
#    Zr.append(result[6])
#    Zv.append(result[7])
#    Za.append(result[8])

#    if Yr[i] < py1:
#        regime = 0
#
#    if Xr[i] < px2 and Yr[i] >= py1:
#        regime = 1
#
#    if Xr[i] >= px3 and Xa[i] == 0:
#        regime = 2

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(111, projection='3d')
ax.plot(Xr, Yr, Zr, c='gray')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.title('Car Position (Actual)')

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(111, projection='3d')
ax.plot(Xv, Yv, Zv, c='gray')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.title('Car Velocity (Actual)')

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(111, projection='3d')
ax.plot(Xa, Ya, Za, c='gray')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.title('Car Acceleration (Actual)')

#Add Noise to the position measurement
spr = 1 # Sigma for position noise
Xmr = Xr #+ spr * (np.random.randn(m))
Ymr = Yr #+ spr * (np.random.randn(m))
Zmr = Zr

#fig = plt.figure(figsize=(16,9))
#ax = fig.add_subplot(111, projection='3d')
#ax.scatter(Xmr, Ymr, Zmr, c='gray')
#ax.set_xlabel('X')
#ax.set_ylabel('Y')
#ax.set_zlabel('Z')
#plt.title('Car Position (Sensor)')

#Add Noise to the position measurement
spa = .1 # Sigma for position noise
Xma = Xa #+ spa * (np.random.randn(m))
Yma = Ya #+ spa * (np.random.randn(m))
Zma = Za

#Make the measurement vector
measurements = np.vstack((Xmr,Xma,Ymr,Yma,Zmr,Zma))

#Make the initial state
x = np.matrix([Xmr[0], Xv[0], Xma[0], Ymr[0], Yv[0], Yma[0], Zmr[0], Zv[0], Zma[0]]).T

#Preallocation for plotting
xrf = []
xvf = []
xaf = []
yrf = []
yvf = []
yaf = []
zrf = []
zvf = []
zaf = []

#Run the Kalman Filter
for filterstep in range(int(m)):
    # Time Update (Prediction)
    # ========================
    # Project the state ahead
    x = np.dot(A,x)

    # Project the error covariance ahead
    P = np.add(np.dot(A,np.dot(P,A.T)),Q)

    # Measurement Update (Correction)
    # ===============================
    # Compute the Kalman Gain
    S = np.add(np.dot(H,np.dot(P,H.T)),R)
    K = np.dot(np.dot(P,H.T),np.linalg.pinv(S))

    # Update the estimate via z
    Z = measurements[:,filterstep].reshape(H.shape[0],1)
    y = np.subtract(Z,np.dot(H,x))                            # Innovation or Residual
    x = np.add(x,np.dot(K,y))

    # Update the error covariance
    P = np.dot(np.subtract(I,np.dot(K,H)),P)

    # Save states for Plotting
    xrf.append(float(x[0]))
    xvf.append(float(x[1]))
    xaf.append(float(x[2]))
    yrf.append(float(x[3]))
    yvf.append(float(x[4]))
    yaf.append(float(x[5]))
    zrf.append(float(x[6]))
    zvf.append(float(x[7]))
    zaf.append(float(x[8]))

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(111, projection='3d')
ax.plot(xrf, yrf, zrf, c='red')
ax.plot(Xr,Yr,Zr,c='blue')
ax.plot(Xmr,Ymr,Zmr,c='grey')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.title('Car Position (Kalman Filter)')


"""
ID: wrwwctb1
LANG: PYTHON3
TASK: shuttle
4.4.1

key idea
all answers have the same length so dfs suffices
"""

import os, sys, re, time
from collections import Counter, deque, defaultdict
from queue import Queue
from copy import copy, deepcopy
from itertools import combinations, permutations, accumulate, \
                      combinations_with_replacement
from functools import lru_cache, cmp_to_key, partial as functools_partial
from heapq import heappush, heappop, nlargest, nsmallest
from bisect import bisect_left, bisect_right
from math import ceil, floor, factorial, gcd, modf, log, log2, log10, sqrt, \
         pi, sin, cos, tan, asin, acos, atan, atan2, hypot, erf, erfc, inf, nan
# sys.setrecursionlimit(5782)
# python -m cProfile -s time ha.py

print = functools_partial(print, flush=True)
input = lambda: fin.readline().strip('\n')

def printwrite(string):
    print(string)
    fout.write(str(string) + '\n')

filename = 'shuttle'
fin = open(filename + '.in', 'r')
fout = open(filename + '.out', 'w')

def can(state, i):
    if state[i] == 0:
        if i+1 < NN and state[i+1] == None:
            return '0move'
        if i+2 < NN and state[i+2] == None:
            return '0jump'
    if state[i] == 1:
        if i-1 >= 0 and state[i-1] == None:
            return '1move'
        if i-2 >= 0 and state[i-2] == None:
            return '1jump'
    return None

def forward(state, i, moveJump):
    if moveJump == '0move':
        state[i] = None
        state[i+1] = 0
        return
    if moveJump == '0jump':
        state[i] = None
        state[i+2] = 0
        return
    if moveJump == '1move':
        state[i] = None
        state[i-1] = 1
        return
    if moveJump == '1jump':
        state[i] = None
        state[i-2] = 1
        return

def backward(state, i, moveJump):
    if moveJump == '0move':
        state[i] = 0
        state[i+1] = None
        return
    if moveJump == '0jump':
        state[i] = 0
        state[i+2] = None
        return
    if moveJump == '1move':
        state[i] = 1
        state[i-1] = None
        return
    if moveJump == '1jump':
        state[i] = 1
        state[i-2] = None
        return

def dfs(state, choices):
    if state == final:
        return True
    for i in range(NN):
        moveJump = can(state, i)
        if moveJump:
            choices.append(i)
            forward(state, i, moveJump)
            if dfs(state, choices):
                return True
            del choices[-1]
            backward(state, i, moveJump)
    return False

tt = time.time()

# constants
N = int(input())
NN = 2 * N + 1
final = [1] * N + [None] + [0] * N

# search. it seems there are only two solutions of equal lengths
state = [0] * N + [None] + [1] * N
choices = []
dfs(state, choices)

# output
choices = [c+1 for c in choices]

out = []
i = 0
while i < len(choices):
    out.append(' '.join(map(str, choices[i:i+20])))
    i += 20
out = '\n'.join(out)
printwrite(out)

print(time.time()-tt)

fin.close()
fout.close()
del print, input


# -*- coding: utf-8 -*-
# Screaming Strike startup file
# Copyright (C) 2019 Yukio Nozawa <personal@nyanchangames.com>
# License: GPL V2.0 (See copying.txt for details)
# See SsAppMain.py for application entry point
import sys
import shutil
import os
import platform_utils.paths
import traceback
if platform_utils.paths.is_mac and platform_utils.paths.is_frozen():
    os.chdir(platform_utils.paths.embedded_data_path())
if platform_utils.paths.is_windows:
    genpy_path = os.path.join(os.environ["temp"], "gen_py")
    if os.path.isdir(genpy_path):
        shutil.rmtree(genpy_path)
    # end rebuilding
# end win
import buildSettings
from ssAppMain import *

def main():
    app = ssAppMain()
    app.initialize()
    app.run()

def exchandler(type, exc, tb):
    f = open("data/errorLog.txt", "w")
    f.writelines(traceback.format_exception(type, exc, tb))
    f.close()
    dialog.dialog("Error", "An error occured. Please send error-log.txt, found in the data directory of wherever you are running the game, to the developer.")
    sys.exit()

#global schope
sys.excepthook = exchandler
if __name__ == "__main__":
    main()


from flask import Flask, redirect, render_template
import urllib.request
import pandas as pd
import plotly.offline as pyo
import json
import plotly
import plotly.graph_objects as go
import math
import numpy as np
import os
import time
from datetime import date
# To merge geojson data and cases data:
import geopandas as gpd

# To create custom maps:
import folium

# To fetch District level data link:
from bs4 import BeautifulSoup
import re
import requests

# pyo.init_notebook_mode()

app = Flask(__name__)
app.debug = True

# Load Data:
#my_path='/home/ubuntu/CovidApp/COVID_19_Flask/'
my_path='/run/media/nisarg/Drive2/AWS - Nisarg Covid Instance/Elastic_IP/CovidApp/COVID_19_Flask/'
def load_wiki_data():
    ## Set Working Directory:
    os.chdir(my_path)

    df_wiki = pd.read_csv("output/covid-wiki-data.csv")
    df_wiki.fillna("0", inplace=True)
    df_wiki['Recovered'] = df_wiki['Recovered'].fillna(0.0).astype(float).astype(int)

    return df_wiki

def load_india_data():
    ## Set Working Directory:
    os.chdir(my_path)

    df_india = pd.read_csv('output/India_confirmed_cases.csv')
    df_india.fillna("0", inplace=True)
    gps=gpd.read_file("output/Updated_Geojson_data.geojson")

    return df_india, gps

def load_global_conf_data():
    ## Set Working Directory:
    os.chdir(my_path)

    df_conf = pd.read_csv("output/group-covid-Confirmed.csv")

    return df_conf

def load_global_death_data():
    ## Set Working Directory:
    os.chdir(my_path)

    df_death = pd.read_csv("output/group-covid-Death.csv")

    return df_death

def load_global_recover_data():
    ## Set Working Directory:
    os.chdir(my_path)

    df_recovered = pd.read_csv("output/group-covid-Recovered.csv")

    return df_recovered

## Plotting Confirmed cases:

def world_map():

    df_wiki=load_wiki_data()

    # Generate a list for color gradient display
    colorList = []
    color=[]
    remaining=0

    for comfirmed, recovered, deaths in zip(df_wiki["Confirmed_Cases"], df_wiki["Recovered"].fillna(0), df_wiki["Deaths"]):
        remaining = int(comfirmed) - int(deaths) - int(recovered)
        colorList.append(math.ceil(remaining))

    df_wiki['Active']=colorList

    del remaining
    df_remain = pd.DataFrame({'Remaining':np.array(colorList)})

    [color.append('#d7191c') if i>0 else '#1a9622' for i in colorList]

    # To deal with negative values:
    tmp1=df_remain.copy()
    l=[]
    [l.append(i) if i>0 else l.append(0) for i in tmp1['Remaining'].values]

    fig = go.Figure(data=go.Scattergeo(
            lon = df_wiki['Long'],
            lat = df_wiki['Lat'],
            #text = df_conf['Province/State'].fillna("")+df_conf['Country/Region']+"
"+"Confirmed Cases: "+df_conf.iloc[:,-1].astype(str)+"
Death Cases: "+df_death.iloc[:,-1].astype(str)+"
Recovered Cases: "+df_recovered.iloc[:,-1].astype(str)+"
Remaining Cases: "+df_remain.iloc[:,-1].astype(str),
            text = df_wiki['Locations']+"
"+"Confirmed Cases: "+df_wiki['Confirmed_Cases'].astype(str)+"
Death Cases: "+df_wiki['Deaths'].astype(str)+"
Recovered Cases: "+df_wiki['Recovered'].astype(str)+"
Active Cases: "+df_remain.iloc[:,-1].astype(str),
            mode = 'markers',
            #showlegend=True,

            #marker_color = df_conf.iloc[:,-1],
            marker=go.scattergeo.Marker(
                #color=['#d7191c' if i>0 else '#1a9622' for i in colorList],
                #colorscale = 'Magenta',
                cmin = 0,
                color = df_remain.iloc[:,-1],
                cmax = (df_remain.iloc[:,-1].max()),
                colorbar_title="Active Patients:",

                colorbar=dict(lenmode='pixels', len=300),

                #size=[i**(1/3) for i in df_conf.iloc[:,-1]],
                size=[i**(1/3) for i in l],
                sizemin=1,
                sizemode='area',
                sizeref=2.*max([math.sqrt(i) for i in l])/(100.**2)
                        #org 2.*max([math.sqrt(max(i,0)) for i in df_conf.iloc[:,-1]])/(100.**2)
                        #2.*max([math.sqrt(max(i,0)) for i in df_conf.iloc[:,-1]])/(100.**2)
            )
            ))

    fig.update_layout(
            #title = 'CORONAVIRUS Spread',
            #paper_bgcolor='#cbd2d3',
            autosize=True,
            #width=800,
            #height=500,
            margin=dict(
                l=10,
                r=10,
                b=10,
                t=75,
                pad=0
                ),
            geo = go.layout.Geo(
                #bgcolor='#cbd2d3',
                resolution=110,
                scope='world',
                showland = True,
                countrywidth = 0.5,
                subunitwidth = 0.5,
                showsubunits = True,
                showcountries = True,
                showframe=True,
                #subunitcolor = "rgb(225, 225, 0)",
                projection = dict(
                type = 'orthographic',
                #scale=1
                #scale=1
                ),
                lonaxis_range = [ -400.0, 400.0 ], # Y-axis
                lataxis_range = [ -400.0, 400.0 ], # X-axis
                #center=dict(lon=78.66,
                #            lat=22.35)
            )

        )

    del df_remain

    pyo.plot(fig,filename = 'output/World.html', auto_open=False)

    return fig, df_wiki.drop(["Lat","Long"], axis=1)

## Plotting India Specific Case Data:

def india_map():

    df_india, gps = load_india_data()

    # Bins for the confirmed cases:
    bins=[0,1,100,501,1001,3001]
    my_map3 = folium.Map(#location = [22.9734, 78.6569],
                         location = [19.7515, 75.7139],
                         max_zoom=8,
                         min_zoom=3,
                         zoom_start = 4,
                         #width=700,
                         #height=450
                        )

    folium.Choropleth(
        geo_data=gps,
        data=df_india,
        name='Total cases in India (including Foreign National)',
        columns=['State/UT', 'Total Confirmed cases (including Foreign National)'],
        key_on='feature.properties.NAME',
        bins=bins,
        fill_color='YlGn',
        fill_opacity=1,
        legend_name='Confirmed Cases',
        highlight=True
    ).add_to(my_map3).geojson.add_child(folium.GeoJsonTooltip(
        fields=['NAME','Total Confirmed cases (including Foreign National)','Cured/Discharged/Migrated','Death'],
        aliases=['State/UT','Confirmed Cases','Recovered Cases','Death Cases'],
        localize=True
        )
    )

    df_india["Active"]=df_india['Total Confirmed cases (including Foreign National)']-df_india['Cured/Discharged/Migrated']-df_india['Death']

    #title_html = '''
    #
Total cases in India (including Foreign National)

    #
Hover over the State/UT to check the respective case counts
'''
    #my_map3.get_root().html.add_child(folium.Element(title_html))

    my_map3.save('output/India.html')
    my_map3.save('templates/India.html')
    return my_map3, df_india

## Line plot for total confirmed cases for 20 most affected Countries in last 3 days:

def most_affected_20():

    df_gc = load_global_conf_data()

    df_gc = df_gc.sort_values(by=df_gc.columns[-1],ascending=False)

    bar_fig = go.Figure()

    bar_fig.add_trace(go.Bar(
        x=df_gc["Country/Region"][:20].values,
        y=df_gc.iloc[:,-1].values,
        #mode = 'lines+markers',
        name = df_gc.columns[-1],
        marker_color='rgb(55, 83, 109)'
        ))

    bar_fig.add_trace(go.Bar(
        x=df_gc["Country/Region"][:20].values,
        y=df_gc.iloc[:,-2].values,
        #mode = 'lines+markers',
        name = df_gc.columns[-2],
        marker_color='rgb(26, 118, 255)'
        ))

    bar_fig.add_trace(go.Bar(
        x=df_gc["Country/Region"][:20].values,
        y=df_gc.iloc[:,-3].values,
        #mode = 'lines+markers',
        name = df_gc.columns[-3],
        marker_color='rgb(0, 175, 90)'
        ))

    bar_fig.update_layout(
        title='Total Confirmed cases',
        # width=680,
        # height=500,
        xaxis=dict(
            title='Top 20 countries with most confirmed cases',
            titlefont_size=14,
            tickfont_size=12,
            tickangle = -45
        ),
        yaxis=dict(
            title='Confirmed Cases (In thousands)',
            titlefont_size=14,
            tickfont_size=12,
        ),
        legend=dict(
            #x=0.9,
            #y=0.9,
            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)'
        ),

        barmode='group',
        bargap=0.15, # gap between bars of adjacent location coordinates.
        bargroupgap=0.1 # gap between bars of the same location coordinate.
    )

    pyo.plot(bar_fig, filename="output/Top_20_Conf_Cases.html", auto_open=False)
    return bar_fig

## Line plot for total death cases for top 20 Countries in last 3 days:

def most_death_20():

    #df_gd = df_death.groupby('Country/Region')[df_death.columns[4:]].sum().reset_index()
    df_gd = load_global_death_data()
    df_gd = df_gd.sort_values(by=df_gd.columns[-1],ascending=False)

    bar_fig3 = go.Figure()

    bar_fig3.add_trace(go.Bar(
        x=df_gd["Country/Region"][:20].values,
        y=df_gd.iloc[:,-1].values,
        #mode = 'lines+markers',
        name = df_gd.columns[-1],
        marker_color='rgb(220,20,60)'
        ))

    bar_fig3.add_trace(go.Bar(
        x=df_gd["Country/Region"][:20].values,
        y=df_gd.iloc[:,-2].values,
        #mode = 'lines+markers',
        name = df_gd.columns[-2],
        marker_color='rgb(178,34,34)'
        ))

    bar_fig3.add_trace(go.Bar(
        x=df_gd["Country/Region"][:20].values,
        y=df_gd.iloc[:,-3].values,
        #mode = 'lines+markers',
        name = df_gd.columns[-3],
        marker_color='rgb(219,112,147)'
        ))

    bar_fig3.update_layout(
        title='Total Death cases',
        # width=680,
        # height=500,
        xaxis=dict(
            title='20 countries with most Death cases',
            titlefont_size=14,
            tickfont_size=12,
            tickangle=-45
        ),
        yaxis=dict(
            title='Death Cases (In thousands)',
            titlefont_size=14,
            tickfont_size=12,
        ),
        legend=dict(
            #x=0.9,
            #y=0.9,
            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)'
        ),

        barmode='group',
        bargap=0.15, # gap between bars of adjacent location coordinates.
        bargroupgap=0.1 # gap between bars of the same location coordinate.
    )

    pyo.plot(bar_fig3, filename="output/Top_20_Death_Cases.html", auto_open=False)
    return bar_fig3

## Line plot for total recovery cases for top 20 Countries in last 3 days:

def most_recover_20():

    df_gr = load_global_recover_data()
    df_gr = df_gr.sort_values(by=df_gr.columns[-1],ascending=False)

    bar_fig5 = go.Figure()

    bar_fig5.add_trace(go.Bar(
        x=df_gr["Country/Region"][:20].values,
        y=df_gr.iloc[:,-1].values,
        #mode = 'lines+markers',
        name = df_gr.columns[-1],
        marker_color='rgb(137,178,174)'
        ))

    bar_fig5.add_trace(go.Bar(
        x=df_gr["Country/Region"][:20].values,
        y=df_gr.iloc[:,-2].values,
        #mode = 'lines+markers',
        name = df_gr.columns[-2],
        marker_color='rgb(91,129,142)'
        ))

    bar_fig5.add_trace(go.Bar(
        x=df_gr["Country/Region"][:20].values,
        y=df_gr.iloc[:,-3].values,
        #mode = 'lines+markers',
        name = df_gr.columns[-3],
        marker_color='rgb(35,66,87)'
        ))

    bar_fig5.update_layout(
        title='Total Recovered cases',
        # width=680,
        # height=500,
        xaxis=dict(
            title='20 countries with most Recovered cases',
            titlefont_size=14,
            tickfont_size=12,
            tickangle=-45
        ),
        yaxis=dict(
            title='Recovered Cases (In thousands)',
            titlefont_size=14,
            tickfont_size=12,
        ),
        legend=dict(
            #x=0.9,
            #y=0.9,
            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)',
            #title = 'hi'
        ),
        barmode='group',
        bargap=0.15, # gap between bars of adjacent location coordinates.
        bargroupgap=0.1 # gap between bars of the same location coordinate.
    )

    pyo.plot(bar_fig5, filename="output/Top_20_Recovered_Cases.html", auto_open=False)
    return bar_fig5

## Line plot for total confirmed cases in India:

def india_conf():

    df_gc = load_global_conf_data()

    df_gc = df_gc.sort_values(by=df_gc.columns[-1],ascending=False)

    bar_fig2 = go.Figure()

    bar_fig2.add_trace(go.Scatter(
        x=df_gc[df_gc["Country/Region"]=='India'].iloc[:,1:].columns,
        y=df_gc[df_gc["Country/Region"]=='India'].iloc[:,1:].sum(),
        mode = 'lines+markers',
        name = "Growth rate (Linear)",
        marker_color='rgb(55, 83, 109)',
        fill= 'tozeroy',
        showlegend=True
        ))

    bar_fig2.update_layout(
        #title='Total confirmed cases: India',
        legend_orientation="h",
        # width=680,
        # height=500,
        xaxis=dict(
            title='Date (mm/dd/yy)',
            titlefont_size=14,
            tickfont_size=12,
            tickangle=-45
        ),
        yaxis=dict(
            title='Confirmed Cases',
            titlefont_size=14,
            tickfont_size=12,
        ),
        legend=dict(
            #x=0.2,
            #y=0.9,
            x=0,
            y=1.2,

            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)'
        )
    )

    pyo.plot(bar_fig2, filename="output/India_Conf_Cases.html", auto_open=False)
    return bar_fig2

## Line plot for total death cases in India:

def india_death():

    df_gd = load_global_death_data()
    df_gd = df_gd.sort_values(by=df_gd.columns[-1],ascending=False)

    bar_fig4 = go.Figure()

    bar_fig4.add_trace(go.Scatter(
        x=df_gd[df_gd["Country/Region"]=='India'].iloc[:,1:].columns,
        y=df_gd[df_gd["Country/Region"]=='India'].iloc[:,1:].sum(),
        mode = 'lines+markers',
        name = "Death rate (Linear)",
        marker_color='rgb(205,92,92)',
        fill= 'tozeroy',
        showlegend=True
        ))

    bar_fig4.update_layout(
        #title='Total Death cases: India',
        legend_orientation="h",
        # width=680,
        # height=500,
        xaxis=dict(
            title='Date (mm/dd/yy)',
            titlefont_size=14,
            tickfont_size=12,
            tickangle=-45
        ),
        yaxis=dict(
            title='Death Cases',
            titlefont_size=14,
            tickfont_size=12,
        ),
        legend=dict(
            #x=0.2,
            #y=0.9,
            x=0,
            y=1.2,

            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)'
        )
    )

    pyo.plot(bar_fig4, filename="output/India_Death_Cases.html", auto_open=False)
    return bar_fig4

## Line plot for total recovery cases for India:
def india_recovery():

    df_gr = load_global_recover_data()
    df_gr = df_gr.sort_values(by=df_gr.columns[-1],ascending=False)

    bar_fig6 = go.Figure()

    bar_fig6.add_trace(go.Scatter(
        x=df_gr[df_gr["Country/Region"]=='India'].iloc[:,1:].columns,
        y=df_gr[df_gr["Country/Region"]=='India'].iloc[:,1:].sum(),
        mode = 'lines+markers',
        name = "Recovery rate (Linear)",
        marker_color='rgb(102,178,178)',
        fill = 'tozeroy',
        showlegend=True
        ))

    bar_fig6.update_layout(
        #title='Total Recovered cases: India',
        legend_orientation="h",
        # width=680,
        # height=500,
        xaxis=dict(
            title='Date (mm/dd/yy)',
            titlefont_size=14,
            tickfont_size=12,
            tickangle=-45
        ),
        yaxis=dict(
            title='Recovered Cases',
            titlefont_size=14,
            tickfont_size=12,
        ),
        legend=dict(
            #x=0.2,
            #y=0.9,
            x=0,
            y=1.2,

            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)'
        )
    )

    pyo.plot(bar_fig6, filename="output/India_Recovered_Cases.html", auto_open=False)
    return bar_fig6

## Exponential growth plot for India:
def india_conf_exponential():

    df_gc = load_global_conf_data()

    df_gc = df_gc.sort_values(by=df_gc.columns[-1],ascending=False)

    labels = (df_gc[df_gc["Country/Region"]=='India'].iloc[:,-1:1:-1].columns)

    bar_fig7 = go.Figure()

    bar_fig7.add_trace(go.Scatter(
        #x=(df_gc[df_gc["Country/Region"]=='India'].iloc[:,-1:-8:-1].sum()).values,
        x=(df_gc[df_gc["Country/Region"]=='India'].iloc[:,-1:1:-1].sum()).values,

        #y=df_gc[df_gc["Country/Region"]=='India'].iloc[:,-1:-8:-1] - df_gc[df_gc["Country/Region"]=='India'].iloc[:,-2:-9:-1],
        #y=(df_gc[df_gc["Country/Region"]=='India'].iloc[:,-1:-8:-1].sum()).values - (df_gc[df_gc["Country/Region"]=='India'].iloc[:,-2:-9:-1].sum()).values,
        y=(df_gc[df_gc["Country/Region"]=='India'].iloc[:,-1:1:-1].sum()).values - (df_gc[df_gc["Country/Region"]=='India'].iloc[:,-2:0:-1].sum()).values,
        mode = 'lines+markers',
        name = "Logarithmic Growth rate",
        marker_color='rgb(55, 83, 109)',
        fill= 'tozeroy',

        #hovertemplate =
        text = ['Date (mm/dd/yy): '+i for i in labels],
        hovertemplate =
            '%{text}'
            '
New Cases: %{y}
'
            '<extra></extra>',
        showlegend=True
        ))

    bar_fig7.update_layout(
        title='Exponential Growth Rate',
        # width=680,
        # height=500,
        legend_orientation="h",
        autosize=True,
        xaxis=dict(
            title='Total Confirmed Cases (Log Scale)',
            titlefont_size=14,
            tickfont_size=12,
            tickangle=-45,
            type="log",
            rangeslider=dict(
                visible=True
            ),
        ),
        yaxis=dict(
            title='New Cases (Log Scale)',
            titlefont_size=14,
            tickfont_size=12,
            type="log"
        ),

        #xaxis_type="log", yaxis_type="log",

        legend=dict(
            #x=0.2,
            #y=0.9,

            bgcolor='rgba(255, 255, 255, 0)',
            bordercolor='rgba(255, 255, 255, 0)'
        )
    )

    pyo.plot(bar_fig7, filename="output/India_Conf_log_Cases.html", auto_open=False)
    return bar_fig7

def get_district_link():
    response = requests.get('https://www.mohfw.gov.in/index.html')
    pattern = re.compile('District Reportings')
    soup = BeautifulSoup(response.content, "html.parser")
    ul = soup.find_all("ul", {"class":"nav clearfix"})
    district_link=ul[0].find('a', text=pattern).get('href')

    return district_link

def get_last_updated_time():
    return time.strftime('%b %d, %Y %H:%M', time.localtime(os.path.getmtime("Data_Scrap.log")))

# def serve_layout():
#     return html.Div(children=[
#         html.H1('Dashboard hi'),
#         html.Div(className='Maps', children=[
#                 html.Div(className='world_map', children=[
#                 dcc.Graph(id='world_map',figure=world_map())]
#                 ),
#                 html.Iframe(className='india_map',srcDoc=open(india_map(), 'r').read(), width='40%', height='500')
#             ]),
#         html.Div(className='Line_plots_conf', children=[
#                 html.Div(className='most_conf', children=[
#                 dcc.Graph(id='most_conf',figure=most_affected_20())]
#                 ),
#                 html.Div(className='most_death', children=[
#                 dcc.Graph(id='most_death',figure=most_death_20())]
#                 ),
#                 html.Div(className='most_recover', children=[
#                 dcc.Graph(id='most_recover',figure=most_recover_20())]
#                 )
#             ]),
#         ])

@app.route('/')
def main():

    # Display World Map and its table:
    world_data = world_map()
    wmJSON = json.dumps(world_data[0], cls=plotly.utils.PlotlyJSONEncoder)
    world_table = world_data[1].to_dict('records')

    # Display India's Map and its table:
    india_data = india_map()[1]
    india_table = india_data.to_dict('records')

    # Display Confirmed cases of 20 Most Affected countries:
    conf_20_most = most_affected_20()
    c20JSON = json.dumps(conf_20_most, cls=plotly.utils.PlotlyJSONEncoder)

    # Display Death cases of 20 Most Affected countries:
    death_20_most = most_death_20()
    d20JSON = json.dumps(death_20_most, cls=plotly.utils.PlotlyJSONEncoder)

    # Display Recovered cases of 20 Most Affected countries:
    recovered_20_most = most_recover_20()
    r20JSON = json.dumps(recovered_20_most, cls=plotly.utils.PlotlyJSONEncoder)

    # Display Confirmed cases of India:
    conf_ind = india_conf()
    cindJSON = json.dumps(conf_ind, cls=plotly.utils.PlotlyJSONEncoder)

    # Display Death cases of India:
    death_ind = india_death()
    dindJSON = json.dumps(death_ind, cls=plotly.utils.PlotlyJSONEncoder)

    # Display Recovered cases of India:
    recovery_ind = india_recovery()
    rindJSON = json.dumps(recovery_ind, cls=plotly.utils.PlotlyJSONEncoder)

    # Display Recovered cases of India:
    conf_ind_expo = india_conf_exponential()
    cindexpJSON = json.dumps(conf_ind_expo, cls=plotly.utils.PlotlyJSONEncoder)

    # Display global counts:
    world_conf = load_global_conf_data().iloc[:,-1].sum()
    world_death = load_global_death_data().iloc[:,-1].sum()
    world_recovered = load_global_recover_data().iloc[:,-1].sum()
    world_days = (date.today() - date(2019, 12, 31)).days

    wc24h = load_global_conf_data().iloc[:,-1].sum() - load_global_conf_data().iloc[:,-2].sum()
    wd24h = load_global_death_data().iloc[:,-1].sum() - load_global_death_data().iloc[:,-2].sum()
    wr24h = load_global_recover_data().iloc[:,-1].sum() - load_global_recover_data().iloc[:,-2].sum()

    # Get district wise list of cases:
    dist_link = get_district_link()

    # Get last updated time of data:
    last_update = get_last_updated_time()

    return render_template('index.html', a=wmJSON, world_table=world_table, india_table=india_table, c20lplot=c20JSON,
    d20lplot=d20JSON, r20lplot=r20JSON, cindlplot=cindJSON, dindlplot=dindJSON, rindlplot=rindJSON, cindexpplot=cindexpJSON,
    wconf=world_conf, wdeath=world_death, wrecoverd=world_recovered, wdays=world_days, wc24h=wc24h, wd24h=wd24h, wr24h=wr24h,
    dist_link=dist_link, last_update = last_update)

# Display India's Map
@app.route('/map')
def map():
    #return send_file(url_for('templates', filename = 'India.html'))
    #return india_map()[0].get_root().render()
    return india_map()[0]._repr_html_()


from alpaca_trade_api.rest import APIError

def validate_backend():
    from backend.settings import BACKEND_ALPACA_ID, BACKEND_ALPACA_KEY
    from backend.tradingbot.apimanagers import AlpacaManager
    from django.core.exceptions import ValidationError
    backendapi = AlpacaManager(BACKEND_ALPACA_ID, BACKEND_ALPACA_KEY)
    if not backendapi.validate_api()[0]:
        raise ValidationError(backendapi.validate_api()[1])
    return backendapi

def sync_database_company_stock(ticker):
    """
        check if company/stock for this ticker exist and sync to database if not already exists
        returns the stock and company
    """
    from backend.tradingbot.models import Company, Stock
    if not Company.objects.filter(ticker=ticker).exists():
        # add Company
        company = Company(name=ticker, ticker=ticker)
        company.save()
        # add Stock
        stock = Stock(company=company)
        stock.save()
        print(f"added {ticker} to Company and Stock")
    else:
        company = Company.objects.get(ticker=ticker)
        stock = Stock.objects.get(company=company)
    return stock, company

def sync_stock_instance(user, portfolio, stock):
    """
        create a stock instance and
        add it to the database
    """
    from backend.tradingbot.models import StockInstance
    if not StockInstance.objects.filter(user=user, portfolio=portfolio, stock=stock).exists():
        # add Stock Instance
        stock_instance = StockInstance(user=user, portfolio=portfolio, stock=stock, quantity=0)
        stock_instance.save()
        print(f"added {stock} to Watchlist")
    else:
        stock_instance = StockInstance.objects.get(user=user, portfolio=portfolio, stock=stock)
    return stock_instance

def sync_alpaca(user):  # noqa: C901
    """
        sync user related database data with Alpaca
        this is a simplified, incomplete version.
    """
    user_details = {}
    # check if user has credential
    if not hasattr(user, 'credential'):
        return

    from backend.tradingbot.apimanagers import AlpacaManager
    api = AlpacaManager(user.credential.alpaca_id, user.credential.alpaca_key)

    # check if api is valid
    if not api.validate_api()[0]:
        print(api.validate_api()[1])
        return

    # get account information
    account = api.get_account()
    user_details['equity'] = str(round(float(account.equity), 2))
    user_details['buy_power'] = str(round(float(account.buying_power), 2))
    user_details['cash'] = str(round(float(account.cash), 2))
    user_details['currency'] = account.currency
    user_details['long_portfolio_value'] = str(round(float(account.long_market_value), 2))
    user_details['short_portfolio_value'] = str(round(float(account.short_market_value), 2))
    user_details['portfolio_percent_change'] = str(round((float(account.portfolio_value) - float(account.last_equity))
                                                         / float(account.last_equity), 2))
    user_details['portfolio_dollar_change'] = str(round((float(account.portfolio_value) - float(account.last_equity))))

    if (float(account.portfolio_value) - float(account.last_equity)) >= 0:
        user_details['portfolio_change_direction'] = "positive"
    elif (float(account.portfolio_value) - float(account.last_equity)) < 0:
        user_details['portfolio_change_direction'] = "negative"
    else:
        user_details['portfolio_change_direction'] = "error"

    user_details['portfolio_percent_change'] = str(
        round((float(account.portfolio_value) - float(account.last_equity)) / float(account.last_equity), 2))
    # get portfolio information
    portfolio = api.get_positions()

    # non-user specific synchronization. e.g. add new company, new stock if it didn't exist
    for position in portfolio:
        sync_database_company_stock(position.symbol)

    from backend.tradingbot.models import StockInstance, Stock, Company, Portfolio
    from django.db.models import Q
    if not hasattr(user, 'portfolio'):
        new_portfolio = Portfolio(name='default-1', user=user, cash=0)
        new_portfolio.save()
    # delete all instances except those with 0 quantity
    if StockInstance.objects.filter(~Q(quantity=0.00), user=user, portfolio=user.portfolio).exists():
        StockInstance.objects.filter(~Q(quantity=0.00), user=user, portfolio=user.portfolio).delete()
    for position in portfolio:
        company = Company.objects.get(ticker=position.symbol)
        stock = Stock.objects.get(company=company)
        # delete 0 quantity stock if that stock appears
        if StockInstance.objects.filter(stock=stock, user=user, portfolio=user.portfolio).exists():
            StockInstance.objects.filter(stock=stock, user=user, portfolio=user.portfolio).delete()
        instance = StockInstance(stock=stock, portfolio=user.portfolio, quantity=position.qty, user=user)
        instance.save()

    # 2) synchronizes order status (To be completed)
    alpaca_open_orders = api.api.list_orders(status='open', nested=True)
    from backend.tradingbot.models import Order
    from backend.tradingbot.apiutility import create_local_order
    local_open_orders = Order.objects.filter(user=user, status='A')
    for order in local_open_orders.iterator():
        client_order_id = str(order.order_number)
        try:
            if order.client_order_id == '':
                alpaca_order = api.api.get_order_by_client_order_id(client_order_id)
            else:
                alpaca_order = api.api.get_order_by_client_order_id(order.client_order_id)
        except APIError:
            print(f"Order ID {client_order_id} deleted as no matching order found in Alpaca")
            order.delete()
            continue
        if alpaca_order.status == 'accepted':
            order.status = 'A'
        elif alpaca_order.status == 'new':
            order.status = 'N'
        elif alpaca_order.status == 'filled':
            order.status = 'F'
            order.filled_avg_price = float(alpaca_order.filled_avg_price)
            order.filled_timestamp = alpaca_order.filled_at.to_pydatetime()
            order.filled_quantity = float(alpaca_order.filled_qty)
        else:  # order closed, either cancelled or completed
            order.status = 'C'
        order.save()

    usable_cash = float(account.cash)
    for order in alpaca_open_orders:
        # get usable trading cash
        if order.order_type == 'market' and order.side == 'buy':
            backendapi = validate_backend()
            _, price = backendapi.get_price(order.symbol)
            # print(f"{order.symbol}, {price}")
            usable_cash -= float(price) * float(order.qty)
        # sync open orders to database if not already exist
        if not order.client_order_id.isnumeric() or \
                not Order.objects.filter(user=user, order_number=order.client_order_id).exists():
            if not Order.objects.filter(user=user, client_order_id=order.client_order_id).exists():
                create_local_order(user=user, ticker=order.symbol, quantity=float(order.qty),
                                   order_type=order.order_type, transaction_type=order.side, status="A",
                                   client_order_id=order.client_order_id)

    # iterate through all 0 qty stock and add to portfolio
    display_portfolio = []
    for position in portfolio:
        display_portfolio.append(position)
    if StockInstance.objects.filter(quantity=0.00, user=user, portfolio=user.portfolio).exists():
        backend_api = validate_backend()
        from alpaca_trade_api import TimeFrame
        import datetime
        from datetime import timedelta
        start = (datetime.datetime.now(datetime.timezone.utc) - timedelta(days=5)).strftime('%Y-%m-%d')
        end = (datetime.datetime.now(datetime.timezone.utc) - timedelta(days=1)).strftime('%Y-%m-%d')
        for stock_instance in StockInstance.objects.filter(quantity=0.00, user=user, portfolio=user.portfolio):
            ticker = str(stock_instance.stock)
            _, price = backend_api.get_price(ticker)
            bar_prices, _ = backend_api.get_bar(symbol=ticker, timestep=TimeFrame.Day, start=start,
                                                end=end, price_type="close", adjustment='all')
            cur_price = price
            last_day_price = bar_prices[0]
            display_dict = {
                'asset_class': 'NA',
                'asset_id': 'NA',
                'asset_marginable': True,
                'avg_entry_price': '0',
                'change_today': str((cur_price-last_day_price)/last_day_price),
                'cost_basis': '0',
                'current_price': str(cur_price),
                'exchange': 'NA',
                'lastday_price': last_day_price,
                'market_value': '0',
                'qty': '0.00',
                'qty_available': '0',
                'side': 'NA',
                'symbol': ticker,
                'unrealized_intraday_pl': '0',
                'unrealized_intraday_plpc': '0',
                'unrealized_pl': '0',
                'unrealized_plpc': '0'
            }
            display_portfolio.append(display_dict)

    user_details['usable_cash'] = str(round(usable_cash, 2))
    user_details['portfolio'] = portfolio
    user_details['display_portfolio'] = display_portfolio
    user_details['orders'] = [order.display_order() for order in
                              Order.objects.filter(user=user).order_by('-timestamp').iterator()]

    # 3) check if user has a portfolio and update portfolio cash
    if not hasattr(user, 'portfolio'):
        from .models import Portfolio
        port = Portfolio(user=user, cash=float(user_details['usable_cash']), name='default-1')
        port.save()
        print("created portfolio default-1 for user")
    else:
        user.portfolio.cash = float(user_details['usable_cash'])
        user.portfolio.save()
    user_details['strategy'] = user.portfolio.get_strategy_display()

    return user_details


import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient

client = MongoClient('localhost', 27017)
db = client.dbsparta

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://finance.naver.com/marketindex/exchangeList.naver', headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
trs = soup.select(
    '#_cs_foreigninfo > div > div.api_cs_wrap > div > div.c_rate > div.rate_table_bx._table > table > tbody > tr')

def return_value(address, addition):
    res = requests.get(address + addition)
    soup = BeautifulSoup(res.content, 'html.parser')

    frame = soup.find('iframe', id="frame_ex1")
    frameaddr = address + frame['src']

    res1 = requests.get(frameaddr)
    frame_soup = BeautifulSoup(res1.content, 'html.parser')
    items = frame_soup.select('body > div > table > tbody > tr')

    for item in items:
        name = item.select('td')[0].text.replace("\n", "")
        name = name.replace("\t", "")
        print(name + "\t" + item.select('td')[1].text)

baseaddress = 'https://finance.naver.com'
info = '/marketindex/?tabSel=exchange#tab_section'
return_value(baseaddress, info)