# [파이썬 S/W 문제해결 기본] 4일차 - 반복문자 지우기 (제출용) D2

T = int(input())
for tc in range(1,T+1):
    s = input()
    stk=[]
    tmp = ''
    for i in range(len(s)):
        # if s[i] == tmp: #연속문자 다 지우는줄 알았는데 두개씩만 지우네...
        #     pass
        if len(stk)==0:
            stk.append(s[i])
        elif s[i]==stk[-1]:
            tmp = stk[-1]
            stk.pop()
        else: stk.append(s[i])

    # print(stk)
    print(f'#{tc} {len(stk)}')


from openpyxl import load_workbook

file_name = 'excel_file.xlsx'
wb = load_workbook(filename=file_name)

# workbook is always created with one worksheet.
ws = wb.active

for row in ws.rows:
    print(row[0].value)


import re

WIND_REGEX = "\d* METAR.8EGLL \d*z [A-Z]*(\d{5}KT|VRB\d{2}KT).*="
WIND_EX_REGEX = "(\d{5}KT|VRB\d{2}KT)"
VARIABLE_WIND_REGEX = ".*VRB\d{2}KT"
VALID_WIND_REGEX = "\d{5}KT"
WIND_DIR_ONLY_REGEX = "(\d{3})\d{2}KT"
TAF_REGEX = ".*TAF.*"
COMMENT_REGEX = "\W*#.*"
METAR_CLOSE_REGEX = ".*="

def parse_to_array(text):
    lines = text.splitlines()
    metar_str = ""
    metars = []
    for line in lines:
        if re.search(TAF_REGEX, line):
            break
        if not re.search(COMMENT_REGEX, line):
            metar_str += line.strip()
        if re.search(METAR_CLOSE_REGEX, line):
            metars.append(metar_str)
            metar_str = ""
    return metars

def extract_wind_direction(metars):
    winds = []
    for metar in metars:
        if re.search(WIND_REGEX, metar):
            for token in metar.split():
                if re.match(WIND_EX_REGEX, token):
                    winds.append(re.match(WIND_EX_REGEX, token).group(1))
    return winds

def mine_wind_distribution(winds, wind_dist):
    for wind in winds:
        if re.search(VARIABLE_WIND_REGEX, wind):
            for i in range(8):
                wind_dist[i] += 1
        elif re.search(VALID_WIND_REGEX, wind):
            d = int(re.math(WIND_DIR_ONLY_REGEX, wind).group(1))
            dir_index = round(d / 45.0) % 8
            wind_dist[dir_index] += 1
    return wind_dist


# encoding=utf-8

# constant params
API_VERSION = '1.1'
API_HOST = 'http://api.twitter.com/' + API_VERSION

STREAM_HOST = 'http://stream.twitter.com/'
STREAM_VERSION = '2'

USERSTREAM_HOST = 'https://userstream.twitter.com/'
USERSTREAM_VERSION = '2'

SITESTREAM_HOST = 'https://sitestream.twitter.com/'
SITESTREAM_VERSION = '2b'


import logging

from assessment.models import (
    Assessment,
    ElementChangeLog,
    MasterEvaluationElement,
    MasterSection,
    get_last_assessment_created,
)
from django.utils.translation import gettext as _
from home.models import Organisation, UserResources
from sentry_sdk import capture_message

logger = logging.getLogger("monitoring")

def organisation_required_message(context):
    """
    I do not use the django method to display messages as the front is to 'ugly' and I have an easier way to do this
    as long as there is only one message to display
    :param context:
    :return:
    """
    context["message"] = {
        "alert-warning": _(
            "You first need to create your organisation before creating your evaluation."
        )
    }

def organisation_creation(request, form):
    """
    This function is used to create an organisation from a valid form.
    This is used in the CBV

    :param form: OrganisationCreationForm
    :param request: request POST

    :return: organisation
    """
    user = request.user
    name = form.cleaned_data.get("name")
    size = form.cleaned_data.get("size")
    country = form.cleaned_data.get("country")
    sector = form.cleaned_data.get("sector")
    # This method handles the fact that the membership is created for this orga as admin
    organisation = Organisation.create_organisation(
        name=name,
        size=size,
        country=country,
        sector=sector,
        created_by=user,
    )
    logger.info(
        f"[organisation_creation] A new organisation {organisation.name} has been created by "
        f"the user {user.email}"
    )
    return organisation

def manage_user_resource(request):
    """
    This function is used to check that the user has only one user_resource and create one if he doesn't have.
    Used in ProfileView

    :returns: user_resources
    """
    user = request.user
    user_resources = UserResources.objects.filter(user=user)
    if len(user_resources) == 1:
        return user_resources[0]
    # Case the user resource does not exist, which should not happen
    # but it does when you create super user with the shell
    elif len(user_resources) == 0:
        UserResources.create_user_resources(
            user=user
        )  # create user_resources so the user can access resources
        user_resources = UserResources.objects.get(user=user)
        return user_resources
    else:
        capture_message(
            f"[multiple_user_resources] The user {user.email} has multiple user resources, auto-cleaning"
        )
        while UserResources.objects.filter(user=user).count() > 1:
            UserResources.objects.filter(user=user)[-1].delete()
        return UserResources.objects.get(user=user)

def add_last_version_last_assessment_dictionary(dictionary):
    """
    Add the last assessment and the last version to the dictionary if they exist, else empty list for last_version
    This is used in the CBV, ProfileView for example with self.context as dictionary

    :param dictionary: dict
    :returns: dict
    """
    if isinstance(dictionary, dict):
        if get_last_assessment_created():
            dictionary[
                "last_version"
            ] = get_last_assessment_created().version  # get last version
            dictionary["last_assessment"] = get_last_assessment_created()
        else:
            dictionary["last_version"] = []
    return dictionary

def add_resources_dictionary(dictionary, user, user_resources):
    """
    Add the resources liked and the resources in the dictionary

    :param user:
    :param dictionary:
    :param user_resources:

    :return: the dictionary with new keys "resources" and "resources_liked"
    """
    if isinstance(dictionary, dict):
        query_resources_dict = user_resources.resources.all()
        dictionary["resources"] = query_resources_dict
        dictionary["resources_liked"] = user.userresources.resources.all()
    return dictionary

def get_all_change_logs():
    """
    Return a dictionary of the following structure:
    {
    "assessment_name.assessment_version":{
         "master section name": {
             "master evaluation element name": {
                  {
                    "edito":"bla bla",
                    "pastille":"New",
                  }
                  ...
             }
             ...
         }
         ...
      }
    ....
    }
    These change logs will be displayed on ProfileView
    """
    assessments = Assessment.objects.all()
    change_logs_dict = {}
    if assessments:
        for assessment in assessments:
            # check if there are any change logs for this version
            change_logs = ElementChangeLog.objects.filter(assessment=assessment)
            if change_logs:
                change_logs_dict[assessment] = {}
                master_sections = MasterSection.objects.filter(assessment=assessment)
                for master_section in master_sections:
                    change_logs_dict[assessment][master_section] = {}
                    master_evaluation_elements = MasterEvaluationElement.objects.filter(
                        master_section=master_section
                    )
                    for master_evaluation_element in master_evaluation_elements:
                        try:
                            change_log = ElementChangeLog.objects.get(
                                eval_element_numbering=master_evaluation_element.get_numbering(),
                                previous_assessment=assessment.previous_assessment,
                                assessment=assessment,
                            )
                        except (
                            ElementChangeLog.DoesNotExist,
                            ElementChangeLog.MultipleObjectsReturned,
                        ):
                            change_log = None

                        if change_log is not None:
                            if (
                                change_log.pastille != "Unchanged"
                                and change_log.pastille != "Inchangé"
                            ):
                                change_logs_dict[assessment][master_section][
                                    master_evaluation_element
                                ] = {}
                                change_logs_dict[assessment][master_section][
                                    master_evaluation_element
                                ] = change_log
                    if not change_logs_dict[assessment][master_section]:
                        # delete sections that contain unchanged elements only
                        del change_logs_dict[assessment][master_section]

    return change_logs_dict


import warnings
import dateutil.parser
from datamart.materializers.materializer_base import MaterializerBase
import importlib
import os
import sys
import json
from jsonschema import validate
from termcolor import colored
import typing
from pandas import DataFrame
import tempfile
from datamart.utilities.timeout import timeout

sys.path.append(os.path.join(os.path.dirname(__file__), '../materializers'))

class Utils:
    INDEX_SCHEMA = json.load(
        open(os.path.join(os.path.join(os.path.dirname(__file__), "../resources"), 'index_schema.json'), 'r'))

    TMP_FILE_DIR = tempfile.gettempdir()

    DEFAULT_DESCRIPTION = {
        "materialization": {
            "python_path": "default_materializer"
        },
        "variables": []
    }

    MATERIALIZATION_TIME_OUT = 300

    @staticmethod
    def date_validate(date_text: str) -> typing.Optional[str]:
        """Validate if a string is a valid date.

        Args:
            date_text: date string.

        Returns:
            string of valid date or None
        """

        try:
            this_datetime = dateutil.parser.parse(date_text)
        except ValueError:
            warnings.warn("Incorrect datatime format")
            return None
        return this_datetime.isoformat()

    @classmethod
    def temporal_coverage_validate(cls, coverage: dict) -> dict:
        """Validate if a string is a valid date.

        Args:
            coverage: dict of temporal_coverage.

        Returns:
            dict of temporal_coverage or True
        """
        if not coverage:
            return {
                'start': None,
                'end': None
            }
        if "start" in coverage and coverage["start"]:
            coverage['start'] = cls.date_validate(coverage['start'])
        else:
            coverage['start'] = None
        if "end" in coverage and coverage["end"]:
            coverage['end'] = cls.date_validate(coverage['end'])
        else:
            coverage['end'] = None
        return coverage

    @classmethod
    def load_materializer(cls, materializer_module: str) -> MaterializerBase:
        """Given the python path to the materializer_module, return a materializer instance.

        Args:
            materializer_module: Path to materializer_module file.

        Returns:
            materializer instance
        """

        module = importlib.import_module(materializer_module)
        md = module.__dict__
        lst = [
            md[c] for c in md if (
                    isinstance(md[c], type) and
                    issubclass(md[c], MaterializerBase
                               ) and
                    md[c].__module__ == module.__name__)
        ]
        try:
            materializer_class = lst[0]
        except:
            raise ValueError(colored("No materializer class found in {}".format(
                os.path.join(os.path.dirname(__file__), 'materializers', materializer_module))), 'red')

        materializer = materializer_class(tmp_file_dir=cls.TMP_FILE_DIR)
        return materializer

    @classmethod
    @timeout(seconds=MATERIALIZATION_TIME_OUT, error_message="Materialization times out")
    def materialize(cls,
                    metadata: dict,
                    constrains: dict = None) -> typing.Optional[DataFrame]:
        """Get the dataset with materializer.

       Args:
           metadata: metadata dict.
           variables:
           constrains:

       Returns:
            pandas dataframe
       """
        materializer = cls.load_materializer(materializer_module=metadata["materialization"]["python_path"])
        df = materializer.get(metadata=metadata, constrains=constrains)
        if isinstance(df, DataFrame):
            return df
        return None

    @classmethod
    def validate_schema(cls, description: dict) -> bool:
        """Validate dict against json schema.

        Args:
            description: description dict.

        Returns:
            boolean
        """
        try:
            validate(description, cls.INDEX_SCHEMA)
            return True
        except:
            print(colored("[INVALID SCHEMA] title: {}".format(description.get("title"))), 'red')
            raise ValueError("Invalid dataset description json according to index json schema")

    @staticmethod
    def test_print(func) -> typing.Callable:
        def __decorator(self):
            print("[Test]{}/{}".format(self.__class__.__name__, func.__name__))
            func(self)
            print(colored('.Done', 'red'))

        return __decorator

    @staticmethod
    def get_highlight_match_from_metadata(metadata: dict, fields: list) -> dict:
        """Get highlight match, highlight match is the string in some fields of doc which is matched by this query.

        Args:
            metadata: hitted result returned by es query
            fields: list of fields

        Returns:
            boolean
        """

        highlights = metadata.get("highlight", {})
        return {field: highlights[field] for field in fields if field in highlights}

    @staticmethod
    def get_offset_and_matched_queries_from_variable_metadata(metadata: dict) -> typing.Optional[typing.List[tuple]]:
        """Get offset of nested object got matched and which query string in matched.

        Args:
            metadata: hitted result returned by es query

        Returns:
            boolean
        """

        matched_queries_lst = metadata.get("inner_hits", {}).get("variables", {}).get("hits", {}).get("hits", [])
        if not matched_queries_lst:
            return None
        return [(matched_queries_lst[idx]["_nested"]["offset"], matched_queries_lst[idx]["matched_queries"])
                for idx in range(len(matched_queries_lst))]

    @classmethod
    def generate_metadata_from_dataframe(cls, data: DataFrame) -> dict:
        """Generate a default metadata just from the data, without the dataset schema

         Args:
             data: pandas Dataframe

         Returns:
              metadata dict
         """

        from datamart.profiler import Profiler
        from datamart.metadata.global_metadata import GlobalMetadata
        from datamart.metadata.variable_metadata import VariableMetadata

        profiler = Profiler()

        global_metadata = GlobalMetadata.construct_global(description=cls.DEFAULT_DESCRIPTION)
        for col_offset in range(data.shape[1]):
            variable_metadata = profiler.basic_profiler.basic_profiling_column(
                description={},
                variable_metadata=VariableMetadata.construct_variable(description={}),
                column=data.iloc[:, col_offset]
            )
            global_metadata.add_variable_metadata(variable_metadata)
        global_metadata = profiler.basic_profiler.basic_profiling_entire(global_metadata=global_metadata, data=data)

        return global_metadata.value

    @staticmethod
    def materialize_time_out_handler(signum, frame):
        """Materialization times out handler

         Args:
             signum
             frame

         Returns:

         """

        raise Exception(colored("Materialization times out", 'red'))


import yfinance as yf
import pandas as pd
import pandas_datareader as pdr
#informacoes_bolsa = pd.read_csv('dados/dados_empresas_mercado.csv')

apple_df = pdr.get_data_alphavantage()
apple_df.head()

exit()
tickers_bolsa['ticker'] = tickers_bolsa['Código'].apply(lambda x: f'{x}.SA')

print(tickers_bolsa)

tickers_list = tickers_bolsa['ticker'].tolist()
dados_empreas = []

for ticker in tickers_list:
    try:
        ticker_info = yf.Ticker(ticker).info
        if ticker_info:
            dados_empreas.append(ticker_info)
    except Exception as e:
        print('Erro ao capturar dados do ticker: ', ticker)

dados_empresas_mercado = pd.DataFrame(data=dados_empreas)
dados_empresas_mercado.to_csv('dados/dados_empresas_mercado.csv')


from selenium.webdriver.support.select import Select
from settings import driver
filepath = r'C:\Users\vidya gowda\PycharmProjects\Selenium_DemoProject\html_files\Select_dropdown.html'
import time

driver.get(filepath)
driver.maximize_window()
element = driver.find_element_by_id("cars")
s = Select(element)
s.select_by_value("sub1")
time.sleep(2)
s.select_by_index(3)
time.sleep(2)
s.select_by_visible_text("Mysore")
time.sleep(2)

options = s.options
print(options)
print("all_options are")
for i in options:
    print(i.text)
print()
selected_options = s.all_selected_options
print(selected_options)
print("all_selected_options are")
for i in selected_options:
    print(i.text)
print()
first_selected = s.first_selected_option
print("first_selected_option : ",first_selected.text)
print()
print(s.is_multiple)
s.deselect_by_value("sub2")
time.sleep(2)
s.deselect_by_index(2)
time.sleep(2)
s.deselect_by_visible_text("Mumbai")
time.sleep(2)
driver.close()


#!/usr/bin/env python

#	This code test the Nystrom method
#	Given a symmetrick kernel matrix $K \in \mathbb{R}^{n \times n}$ and a submatrix L
#	$$K = \begin{bmatrix} A & B^T \\ B & C \end{bmatrix}, \quad L = \begin{bmatrix} A \\ B \end{bmatrix}$$
#	We define the eigenvalues of matrix $A \in \mathbb{R}^{q \times q}$ (where $q << n$) as
#	$$\sigma(A) = \{\sigma_1, \sigma_2, ..., \sigma_q\}$$
#	We define the eigenvectors of matrix $A$ as
#	$$V(A) = \{v_1, v_2, ..., v_q\}$$

#	The Nystrom method allows us to compute
#	1. The Entire $K$ matrix using $L$
#	2. The eigenvectors of $K$ using the eigenvectors of $A$
#	3. The inverse of $K$

#	The Nystrom method explained
#	1. We randomly sample q samples and use that to form the top left matrix A
#	2. We find $\sigma(A)$ and $V(A)$
#	3. We use the r most dominant eigenvectors of A to approximate the eigenfunctions $\phi$
#		If we define the feature map
#		$$\Psi_q = \begin{bmatrix} \psi(x_1)^T \\ \psi(x_2)^T \\ .. \\ \psi(x_q)^T \end{bmatrix}$$
#		Then the relationship between the eigenfunction $\phi_i$ and the eigenvector $v_i$ is
#		$$\phi_i = \frac{1}{\sqrt{\sigma_i}} \Psi_q^T v_i$$
#	4. Once we approximated the eigenfunction, we can use it to approximate $K$ via the following derivation
#	$$K = \Phi \Phi^T$$
#	$$K = \begin{bmatrix}  \phi_1(x_1) & \phi_2(x_1) & .. & \phi_r(x_1)\\  \phi_1(x_2) & \phi_2(x_2) & .. & \phi_r(x_2)\\ ... & ... & ... & ...\\ \phi_1(x_n) & \phi_2(x_n) & .. & \phi_r(x_n) \end{bmatrix} \begin{bmatrix}  \phi_1(x_1) & \phi_2(x_1) & .. & \phi_r(x_1)\\  \phi_1(x_2) & \phi_2(x_2) & .. & \phi_r(x_2)\\ ... & ... & ... & ...\\ \phi_1(x_n) & \phi_2(x_n) & .. & \phi_r(x_n) \end{bmatrix}^T $$
#	$$K = \begin{bmatrix} \frac{1}{\sqrt{\sigma_1}} \Psi_n \Psi_q^T v_1  & \frac{2}{\sqrt{\sigma_2}} \Psi_n \Psi_q^T v_2 & ... & \frac{1}{\sqrt{\sigma_r}} \Psi_n \Psi_q^T v_r\end{bmatrix} \begin{bmatrix}  \phi_1(x_1) & \phi_2(x_1) & .. & \phi_r(x_1)\\  \phi_1(x_2) & \phi_2(x_2) & .. & \phi_r(x_2)\\ ... & ... & ... & ...\\ \phi_1(x_n) & \phi_2(x_n) & .. & \phi_r(x_n) \end{bmatrix}^T $$
#	$$K =  (\Psi_n \Psi_q^T V \Sigma) (\Psi_n \Psi_q^T V \Sigma)^T$$
#	where
#	$$\Sigma = \begin{bmatrix} \frac{1}{\sqrt{\sigma_1}} & 0 & 0 & ... \\ 0 &  \frac{1}{\sqrt{\sigma_2}} & 0 & ... \\ 0 &  0 & \frac{1}{\sqrt{\sigma_2}} & ... \\ ... &  ... & ... & ... \end{bmatrix} $$
#	Therefore we have
#	$$K =  (LV\Sigma) (\Sigma V^TL^T)$$
#	$$K =  \Phi \Phi^T$$

import numpy as np
import matplotlib.pyplot as plt
import sklearn
from sklearn.utils import shuffle
from sklearn.kernel_approximation import Nystroem
from tools import *

#	Initialize all the setting
X = csv_load('../dataset/wine.csv', shuffle_samples=True)
q = 30				# size of submatrix A
n = X.shape[0]		# number of total samples
γ = get_rbf_γ(X)	# γ used for the gaussian kerenl

#	Use Nystrom to approximate the kernel
Xa = X[0:q, :]	# A will come from Xa
L = sklearn.metrics.pairwise.rbf_kernel(X, Y=Xa, gamma=γ)
A = L[0:q,:]
[σs,V] = np.linalg.eig(A)
V = V[:,0:10] # only keeping the largest eigenvectors
Σ = np.diag(1/(np.sqrt(σs[0:10])))
Φ = L.dot(V).dot(Σ)
ǩ = Φ.dot(Φ.T)

#	Use SKlearn to approximate the kernel
feature_map_nystroem = Nystroem(gamma=γ, random_state=1, n_components=q)
Φ2 = feature_map_nystroem.fit_transform(X)
K2 = Φ2.dot(Φ2.T)

#	Compute the actual kernel
K = sklearn.metrics.pairwise.rbf_kernel(X, gamma=γ)

#	Display the results
#	Notice that even though we only used 30 samples to approximate the entire 178 sample,
#	we still got really good approximation
a= 100; b= 107
print_two_matrices_side_by_side(K[a:b, a:b], ǩ[a:b, a:b], title1='Actual Kernel', title2='Nystrom Kernel')
print_two_matrices_side_by_side(K[a:b, a:b], ǩ[a:b, a:b], title1='Actual Kernel', title2='Sklearn Nystrom Kernel')

avg_error = mean_absolute_error(K, ǩ, (n*n))
avg_error2 = mean_absolute_error(K, K2, (n*n))

jupyter_print('The average absolute error with Nystrom of each element is %f'% avg_error)
jupyter_print('The average absolute error with Sklearn Nystrom of each element is %f\n\n'% avg_error2)

#	**How to use Nystrom to approximate eigenvectors of a large matrix**
#	The key insight is that given an approximation of the eigenfunction $\phi_i$, the corresponding eigenvector $u_i$ of the kernel matrix K is
#	$$u_i = \frac{1}{\sqrt{\sigma_i}} \Psi_n \phi_i$$

#	**Method**
#	1. use the eigenvectors $V$ of matrix A to approximate the eigenfunction
#	where $V = [v_1, v_2, ..., v_q]$, we get an expression for the eigenfunction
#	$$\phi_i = \frac{1}{\sqrt{\sigma_i}} \Psi_q^T v_i$$

#	2. Next, we plug the eigenvector into the previous equation to get the eigenvector
#	$$u_i = \frac{1}{\sqrt{\sigma_i}} \Psi_n (\frac{1}{\sqrt{\sigma_i}} \Psi_q^T v_i)$$
#	$$u_i = \frac{1}{\sigma_i} \Psi_n \Psi_q^T v_i$$
#	$$u_i = \frac{1}{\sigma_i} L v_i$$
#	$$U = L V \begin{bmatrix} \frac{1}{\sigma_1} & 0 & 0 & ... \\ 0 & \frac{1}{\sigma_2} & 0 & ... \\  0 & 0 & \frac{1}{\sigma_3} & 0 & ... \\   \end{bmatrix}$$
#	$\Sigma$ is a diagonal matrix
#	$$\bar{U} = L V \Sigma$$

jupyter_print("We now approximate the eigenvector with only 30 samples")
Σ = np.diag(1/σs[0:10]) 	# notice that Σ here is defined slightly differently
Ū = L.dot(V).dot(Σ)			# approximate eigenvector of the larger matrix
[Λ, U] = np.linalg.eig(K)	# compute the "actual" eigenvectors

jupyter_print("Notice that the approximation is not that great unless you are using a large amount of samples. ")
jupyter_print("For this reason, it makes sense to combine random svd with nystrom to approximate the eigenvectors")
print_two_matrices_side_by_side(U[0:10, 0:3], Ū[0:10, 0:3], title1='Actual eigenvectors', title2='Approximated Eigenvectors')

avg_error = mean_absolute_error(U[:,0:10], Ū[:,0:10], (n*10))
jupyter_print('The average absolute error of each element is %f\n'% avg_error)

jupyter_print("Let's perform the nystrom eigenvector approximation, but with a lot more samples, q=150, instead of just 30 samples")
#	Initialize all the setting
q = 150				# size of submatrix A

#	Use Nystrom to approximate the kernel
Xa = X[0:q, :]	# A will come from Xa
L = sklearn.metrics.pairwise.rbf_kernel(X, Y=Xa, gamma=γ)
A = L[0:q,:]
[σs,V] = np.linalg.eig(A)
V = V[:,0:10] # only keeping the largest eigenvectors

Σ = np.diag(1/σs[0:10]) 	# notice that Σ here is defined slightly differently
Ū = L.dot(V).dot(Σ)			# approximate eigenvector of the larger matrix

jupyter_print("Notice how much more accurate the approximation becomes!!!")
print_two_matrices_side_by_side(U[0:10, 0:3], Ū[0:10, 0:3], title1='Actual eigenvectors', title2='Approximated Eigenvectors')

avg_error = mean_absolute_error(U[:,0:10], Ū[:,0:10], (n*10))
jupyter_print('The average absolute error of each element is %f\n'% avg_error)


# -*- coding: utf-8 -*-
from django.db import models
from django.core import validators
from django.contrib.auth import models as auth_models
from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
import csv
import uuid
import decimal
import datetime
from io import StringIO
from products import models as products_models

class Order(models.Model):
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        verbose_name=_("Order number"),
        editable=False
    )
    client = models.ForeignKey(
        auth_models.User,
        verbose_name=_("Client"),
        related_name="orders",
        on_delete=models.PROTECT
    )
    timestamp = models.DateTimeField(auto_now_add=True)
    products = models.ManyToManyField(
        products_models.Product,
        through='Item',
        through_fields=('order', 'product')
    )

    def get_csv(self):
        csvfile = StringIO()
        writer = csv.writer(csvfile)
        for item in self.items.all():
            writer.writerow([self.id, item.product.name, item.product.ean, item.quantity, self.client.username, datetime.datetime.strftime(self.timestamp, '%y-%m-%d %H:%M')])
        return csvfile.getvalue()

    def send_email(self):
        email_msg = EmailMultiAlternatives(
            "New order",
            render_to_string('order_email.txt', {'order': self}),
            settings.DEFAULT_FROM_EMAIL,
            [settings.ORDERS_EMAIL]
        )
        email_msg.attach_alternative(render_to_string('order_email.html', {'order': self}), "text/html")
        email_msg.attach('order_{0}.csv'.format(self.id), self.get_csv(), "text/csv")
        email_msg.send(fail_silently=False)

    @property
    def get_total_price(self):
        return sum(i.get_total_price for i in self.items.all())

    def __str__(self):
        return "{0} - {1} Total: {2}€".format(self.id, self.client.username, self.get_total_price)

    class Meta:
        ordering = ('-timestamp',)

class Item(models.Model):
    order = models.ForeignKey(
        Order,
        on_delete=models.CASCADE,
        related_name='items'
    )
    product = models.ForeignKey(
        products_models.Product,
        on_delete=models.PROTECT,
        related_name='orders'
    )
    quantity = models.PositiveIntegerField(
        default=1,
        validators=[validators.MinValueValidator(1)]
    )

    @property
    def get_total_price(self):
        return decimal.Decimal(self.quantity) * self.product.price

    def __str__(self):
        return "{0} - {1} x{2} Total:{3}€".format(self.order.id, self.product.name, self.quantity, self.get_total_price)


import sys
import datetime as dt
import pandas as pd
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *
import psycopg2
import sqlite3
from os.path import expanduser

def fetch_db_data(db, query):
    try:
        conn = sqlite3.connect(db)
        cursor = conn.cursor()
        cursor.execute(query)

        return cursor.fetchall()
    except Exception as e:
        sys.exit("Error reading the database: %s" %e)

def text_message_split(text_number, message):
    try:
        if text_number == '+8215778000':
            number = message[1]
            text_original = message[0].replace('\n', ' ')
            date = message[6]
            time = message[0].split('\n')[2]
            ammounts = int(message[0].split('\n')[5][:-1].replace(',', ''))
            place = message[0].split('\n')[6][1:-1]

        elif text_number == '+8215447200':
            number = message[1]
            text_original = message[0]
            date = message[6]
            time = message[0].split(" ")[3]
            ammounts = int(message[0].split(" ")[4][:-1].replace(',', ''))
            place = message[0].split(" ")[5]

        result = {'number': number, 'text_original': text_original, 'date': date, 'time': time, 'ammounts': ammounts, 'place': place}

        return result
    except Exception as e:
        sys.exit("Error : %s" %e)

class registerNumber(QDialog):
    def __init__(self, mainWindowLeft, mainWindowTop):
        super().__init__()

        dialogWidth = 300
        dialogHeight = 200
        dialogLeft = mainWindowLeft + 50
        dialogTop = mainWindowTop + 50

        self.setGeometry(dialogLeft, dialogTop, dialogWidth, dialogHeight)
        self.setWindowTitle("Register #")
        self.setModal(True)

        self.setupUI()

    def setupUI(self):

        self.label1 = QLabel('Number: ')
        self.label1.setMinimumWidth(50)
        self.numberEdit = QLineEdit(self)

        self.label2 = QLabel('Name: ')
        self.label2.setMinimumWidth(50)
        self.nameEdit = QLineEdit(self)

        layout1 = QHBoxLayout()
        layout1.addWidget(self.label1)
        layout1.addWidget(self.numberEdit)
        layout2 = QHBoxLayout()
        layout2.addWidget(self.label2)
        layout2.addWidget(self.nameEdit)

        self.insertButton = QPushButton('Register!', self)
        self.insertButton.setMaximumWidth(100)
        self.insertButton.clicked.connect(self.insertButtonClicked)
        self.exitButton = QPushButton('Exit', self)
        self.exitButton.setMaximumWidth(100)
        self.exitButton.clicked.connect(self.exitButtonClicked)

        layout3 = QHBoxLayout()
        layout3.addWidget(self.insertButton)
        layout3.addWidget(self.exitButton)

        layout = QVBoxLayout()
        layout.addLayout(layout1)
        layout.addLayout(layout2)
        layout.addLayout(layout3)

        self.setLayout(layout)

    def insertButtonClicked(self):
        db = psycopg2.connect(host = 'localhost', dbname = 'ledger', user = 'mato', port = 5432)
        cursor = db.cursor()

        number = self.numberEdit.text()
        name = self.nameEdit.text()

        try:
            query = f"""INSERT INTO MESSAGE.MESSAGE_ID (NUMBER, NAME) VALUES('{number}', '{name}');"""
            cursor.execute(query)
            db.commit()
            QMessageBox.about(self, 'commit status', 'Commit Success!')

            self.numberEdit.clear()
            self.nameEdit.clear()
        except (Exception, psycopg2.DatabaseError) as error:
            db.rollback()

            QMessageBox.about(self, 'commit status', f"Error in transaction, '{error}'")

    def exitButtonClicked(self):
        self.close()

class messageDashboard(QDialog):
    def __init__(self):

        super().__init__()

        ag = QGuiApplication.primaryScreen().availableGeometry()
        mainWindowWidth = 900
        mainWindowHeight = 700
        mainWindowLeft = (ag.width() - mainWindowWidth) / 2
        mainWindowTop = (ag.height() - mainWindowHeight) / 2
        self.setGeometry(mainWindowLeft, mainWindowTop, mainWindowWidth, mainWindowHeight)
        self.setWindowTitle("Message Dashboard v1")

        self.setupUI()

    def setupUI(self):
        # wid = QWidget(self)
        # self.setCentralWidget(wid)

        # Title
        self.label1 = QLabel("Message Dashboard", self)
        font1 = self.label1.font()
        font1.setPointSize(30)
        self.label1.setFont(font1)

        hbox1 = QHBoxLayout()
        hbox1.addWidget(self.label1)

        # Register Number
        self.label2 = QLabel("Registered Number", self)
        font2 = self.label2.font()
        font2.setPointSize(20)
        self.label2.setFont(font2)
        self.label_duration = QLabel("Duration: ", self)
        self.label_duration.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.combo_duration = QComboBox(self)
        combo_list = [str(i) for i in range(1, 16)]
        self.combo_duration.addItems(combo_list)
        self.combo_duration.setCurrentIndex(4)
        self.combo_duration.setMaximumWidth(100)
        # self.combo_duration.set

        self.numberTable = QTableWidget(self)
        self.numberTable.setColumnCount(3)
        self.numberTable.setColumnWidth(0, 40)
        self.numberTable.setHorizontalHeaderLabels(('No', 'Number', 'Name'))
        # self.numberTable.setMaximumHeight(100)
        self.numberTable.setEditTriggers(QAbstractItemView.EditTrigger(0)) # 수정 못하게 막기
        self.numberTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) # row 선택
        self.numberTable.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) # 한개만 선택

        self.numbershowButton = QPushButton("Show", self)
        self.numbershowButton.setMaximumWidth(100)
        self.numbershowButton.clicked.connect(self.numbershowButtonClicked)
        self.registerButton = QPushButton("Register", self)
        self.registerButton.setMaximumWidth(100)
        self.registerButton.clicked.connect(self.registerButtonClicked)
        self.getButton = QPushButton('Get', self)
        self.getButton.setMaximumWidth(100)
        self.getButton.clicked.connect(self.getButtonClicked)

        hbox2 = QVBoxLayout()
        hbox2_0 = QHBoxLayout()
        hbox2_0.addWidget(self.label2)
        hbox2_0.addWidget(self.label_duration)
        hbox2_0.addWidget(self.combo_duration)
        hbox2_1 = QHBoxLayout()
        hbox2_1_1 = QVBoxLayout()
        hbox2_1_1.addWidget(self.numberTable)
        hbox2_1_2 = QVBoxLayout()
        hbox2_1_2.addWidget(self.numbershowButton)
        hbox2_1_2.addWidget(self.registerButton)
        hbox2_1_2.addWidget(self.getButton)
        hbox2_1.addLayout(hbox2_1_1)
        hbox2_1.addLayout(hbox2_1_2)
        hbox2.addLayout(hbox2_0)
        hbox2.addLayout(hbox2_1)

        # 문자 가져오기
        self.label3 = QLabel("Text Message Table", self)
        font3 = self.label3.font()
        font3.setPointSize(20)
        self.label3.setFont(font3)
        # self.numberText = QText
        self.textTable = QTableWidget(self)
        self.textTable.setColumnCount(7)
        self.textTable.setHorizontalHeaderLabels(('Id', 'Number', 'Date', 'Text', 'Ammounts', 'place', 'check'))
        self.textTable.setMinimumHeight(350)
        self.textTable.setEditTriggers(QAbstractItemView.EditTrigger(0)) # 수정 못하게 막기
        # self.textTable.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) # 다중선택 가능
        self.textTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) # row 선택
        self.textTable.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) # 한개만 선택
        # self.textTable.doubleClicked.connect(self.textTableDoubleClicked)

        self.textshowButton = QPushButton("Show", self)
        self.textshowButton.setMaximumWidth(100)
        self.deleteButton = QPushButton("Delete", self)
        self.deleteButton.setMaximumWidth(100)

        self.textshowButton.clicked.connect(self.textshowButtonClicked)
        self.deleteButton.clicked.connect(self.deleteButtonClicked)

        hbox3 = QVBoxLayout()
        hbox3.addWidget(self.label3)
        hbox3_1 = QHBoxLayout()
        hbox3_1.addWidget(self.textTable)
        hbox3_2 = QVBoxLayout()
        hbox3_2.addWidget(self.textshowButton)
        hbox3_2.addWidget(self.deleteButton)
        hbox3_1.addLayout(hbox3_2)
        hbox3.addLayout(hbox3_1)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)
        vbox.addLayout(hbox3)

        # wid.setLayout(vbox)
        self.setLayout(vbox)

    def numbershowButtonClicked(self):
        db = psycopg2.connect(host = 'localhost', dbname = 'ledger', user = 'mato', port = 5432)
        cursor = db.cursor()

        query = """SELECT * FROM MESSAGE.MESSAGE_ID;"""
        cursor.execute(query)
        self.results = cursor.fetchall()
        self.numberTable.setRowCount(len(self.results))

        i = 0
        for row in self.results:
            j = 0
            for col in row:
                item = QTableWidgetItem(str(col))
                self.numberTable.setItem(i, j, item)
                j += 1
            i += 1

    def registerButtonClicked(self):
        sg = self.geometry()
        registerdialog = registerNumber(sg.left(), sg.top())
        registerdialog.exec()

    def getButtonClicked(self):
        if len(self.numberTable.selectedItems()) == 0:
            QMessageBox.about(self, 'Error!', 'Please Select One Row!')
        else:
            text_number = self.numberTable.selectedItems()[1].text()
            duration_day = self.combo_duration.currentText()

            db_path = expanduser("~") + "/Library/Messages/chat.db"
            query = f"""SELECT text, handle.id, datetime((date / 1000000000) + 978307200, 'unixepoch', 'localtime'),
                    handle.service, message.destination_caller_id, message.is_from_me, date((date/1000000000)+978307200, 'unixepoch', 'localtime')
                    FROM message JOIN handle ON message.handle_id = handle.ROWID
                    WHERE handle.id == '{text_number}' and message.is_from_me == 0 and
                    date((date/1000000000)+978307200, 'unixepoch', 'localtime') >= date('now', 'localtime', '-{duration_day} days')
                    ORDER BY date asc"""

            rval = fetch_db_data(db_path, query)

            db = psycopg2.connect(host = 'localhost', dbname = 'ledger', user = 'mato', port = 5432)
            cursor = db.cursor()

            for i in rval:
                try:
                    result = text_message_split(text_number, i)
                    # number = i[1]
                    # text_original = i[0]
                    # date = i[6]
                    # time = i[0].split(" ")[3]
                    # ammounts = int(i[0].split(" ")[4][:-1].replace(',', ''))
                    # place = i[0].split(" ")[5]
                    query = f"""INSERT INTO message.text_message (number, text_original, date, time, ammounts, place)
                                VALUES('{result['number']}', '{result['text_original']}', '{result['date']}', '{result['time']}', {int(result['ammounts'])}, '{result['place']}')
                                ON CONFLICT (TEXT_ORIGINAL)
                                DO NOTHING;"""
                    cursor.execute(query)
                    cursor.execute("COMMIT")
                    print("Insert Sucess!! ======================")
                except Exception as e:
                    QMessageBox.about(self, 'Fail!', f'{e}')

            QMessageBox.about(self, 'Good!', 'Success!!')

        # print(len(self.numberTable.selectedItems()))

            # print(self.numberTable.selectedItems()) #  아무것도 선택하지 않으면 empty list 반환
            # print(self.numberTable.selectedItems()[1].text())

    def textshowButtonClicked(self):
        db = psycopg2.connect(host = 'localhost', dbname = 'ledger', user = 'mato', port = 5432)
        cursor = db.cursor()

        query = """SELECT ID, NUMBER, TEXT_ORIGINAL, DATE, AMMOUNTS, PLACE, SAVE_CHECK FROM MESSAGE.TEXT_MESSAGE
                    WHERE SAVE_CHECK = FALSE
                    ORDER BY ID DESC;"""
        cursor.execute(query)
        self.results = cursor.fetchall()
        self.textTable.setRowCount(len(self.results))

        i = 0
        for row in self.results:
            j = 0
            for col in row:
                item = QTableWidgetItem(str(col))
                # item.setTextAlignment(Qt.AlignmentFlag.AlignCenter, Qt.AlignmentFlag.AlignVCenter)
                self.textTable.setItem(i, j, item)
                j += 1
            i += 1

        self.textTable.resizeColumnsToContents()
        self.textTable.resizeRowsToContents()

    # def textTableDoubleClicked(self):
    #     print(self.textTable.selectedIndexes()[1].row())

    def deleteButtonClicked(self):
        if len(self.textTable.selectedItems()) == 0:
            QMessageBox.about(self, 'Error!', 'Please Select One Row!')
        else:
            message = QMessageBox(self)
            message.setWindowTitle('Warning!')
            message.setText('Are you Sure to Delete!?')
            message.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
            message.setIcon(QMessageBox.Icon.Question)
            reply = message.exec()

            if reply == QMessageBox.StandardButton.Yes:
                message_id = int(self.textTable.selectedItems()[0].text())

                db = psycopg2.connect(host = 'localhost', dbname = 'ledger', user = 'mato', port = 5432)
                cursor = db.cursor()
                try:
                    query = f"""DELETE FROM MESSAGE.TEXT_MESSAGE WHERE ID = {message_id};"""
                    cursor.execute(query)
                    cursor.execute('COMMIT;')
                    QMessageBox.about(self, 'Delete!', 'Deleted From Table!')
                except (Exception, psycopg2.DatabaseError) as error:
                    db.rollback()

                    QMessageBox.about(self, 'commit status', f"Error in transaction, '{error}'")
            else:
                print('N')

        # row = self.textTable.rowCount()
        # print(self.textTable.selectedIndexes()[0].row())
        # for i in range(row):
            # print(self.textTable.indexAt(i, 3))
            # print(self.textTable.item(i, 2).text())
            # print(self.textTable.item(i, 1).text())
            # if self.textTable.item(i, 2).checkState() == 2:
            #     number = self.textTable.item(i, 0).text()
            #     date = self.textTable.item(i, 1).text()
            #     print(number, date)

# app = QApplication(sys.argv)
# window = messageDashboard()
# window.show()
# app.exec()


# import the necessary packages
from imutils.video import VideoStream
from imutils.video import FPS
import imutils
import time
import cv2

vs = VideoStream(src=0).start()
# vs = VideoStream(usePiCamera=True).start()
time.sleep(2.0)

# start the FPS counter
fps = FPS().start()

print('[INFO]press "q" to close VideoStream')
# loop over frames from the video file stream
while True:
    # grab the frame from the threaded video stream and resize it
    # to 500px (to speedup processing)
    frame = vs.read()
    frame = imutils.resize(frame, width=500)

    # display the image to our screen
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF

    # if the `q` key was pressed, break from the loop
    if key == ord("q"):
        break

    # update the FPS counter
    fps.update()

# stop the timer and display FPS information
fps.stop()
print("[INFO] elasped time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))

# do a bit of cleanup
cv2.destroyAllWindows()
vs.stop()


import sys
import json
import pandas as pd
import numpy as np
import collections
from pathlib import Path

output = {}
########## function definitions needed for rainfall status forecasting #############

def filterData(data):
  filteredRainFall=[]
  for i in range(len(data)):
    rain_fall=float(data[i])
    if(rain_fall== 0):
      rainFallStatus="NoRain"
    elif(rain_fall<50):
      rainFallStatus="LightRain"
    else:
      rainFallStatus="HeavyRain"
    filteredRainFall.append(rainFallStatus)
  return filteredRainFall

def ChangeStatusFunction(data):
  changeStatusArray=[]
  noRainCount=0
  lightRainCount=0
  heavyRainCount=0

  for i in range(len(data)-1):
    if(data[i]=="NoRain"):
      if(data[i+1]=="NoRain"):
        changeStatus="NN"
      elif(data[i+1]=="LightRain"):
        changeStatus="NL"
      elif(data[i+1]=="HeavyRain"):
        changeStatus="NH"
      noRainCount=noRainCount+1
    elif(data[i]=="HeavyRain"):
      if(data[i+1]=="NoRain"):
        changeStatus="HN"
      elif(data[i+1]=="LightRain"):
        changeStatus="HL"
      elif(data[i+1]=="HeavyRain"):
        changeStatus="HH"
      heavyRainCount=heavyRainCount+1
    elif(data[i]=="LightRain"):
      if(data[i+1]=="NoRain"):
        changeStatus="LN"
      elif(data[i+1]=="LightRain"):
        changeStatus="LL"
      elif(data[i+1]=="HeavyRain"):
        changeStatus="LH"
      lightRainCount=lightRainCount+1
    changeStatusArray.append(changeStatus)
  return changeStatusArray

def CalculateTransitionMatrix(data,list):
  changeStatusArray_numpy = np.array(data)
  counted_array = collections.Counter(changeStatusArray_numpy)

  NNCount = counted_array['NN']
  NLCount = counted_array['NL']
  NHCount = counted_array['NH']

  LNCount = counted_array['LN']
  LLCount = counted_array['LL']
  LHCount = counted_array['LH']

  HNCount = counted_array['HN']
  HLCount = counted_array['HL']
  HHCount = counted_array['HH']
  list.pop()
  NoRainCount = list.count('NoRain')
  LightRainCount = list.count('LightRain')
  HeavyRainCount = list.count('HeavyRain')
  transitionMatrix=[]

  if((NoRainCount)==0 or(LightRainCount)==0 or(HeavyRainCount)==0):
      print("invalid history data input")
  else:
      transitionMatrix=[
                        [NNCount/NoRainCount,NLCount/NoRainCount,NHCount/NoRainCount],
  [LNCount/LightRainCount,LLCount/LightRainCount,LHCount/LightRainCount],
  [HNCount/HeavyRainCount,HLCount/HeavyRainCount,HHCount/HeavyRainCount]]
  return transitionMatrix

def RainFallForecast(days,current,transitionMatrix):
    transitionName = [["NN","NL","NH"],["LN","LL","LH"],["HN","HL","HH"]]
    # Choose the starting state
    states = current
    rainStatusToday = str(states)
    # Shall store the sequence of states taken. So, this only has the starting state for now.
    rainStatusList = [rainStatusToday]
    i = 0
    # To calculate the probability of the rainStatusList
    prob = 1
    while i != days:
        if rainStatusToday == "NoRain":
            change = np.random.choice(transitionName[0],replace=True,p=transitionMatrix[0])
            if change == "NN":
                prob = prob * transitionMatrix[0][0]
                rainStatusList.append("NoRain")
                pass
            elif change == "NL":
                prob = prob * transitionMatrix[0][1]
                rainStatusToday = "LightRain"
                rainStatusList.append("LightRain")
            elif change== "NH":
                prob = prob * transitionMatrix[0][2]
                rainStatusToday = "HeavyRain"
                rainStatusList.append("HeavyRain")

        elif rainStatusToday == "LightRain":
            change = np.random.choice(transitionName[1],replace=True,p=transitionMatrix[1])
            if change == "LN":
                prob = prob * transitionMatrix[1][0]
                rainStatusToday = "NoRain"
                rainStatusList.append("NoRain")
                pass
            elif change == "LL":
                prob = prob * transitionMatrix[1][1]
                rainStatusToday = "LightRain"
                rainStatusList.append("LightRain")
            elif change == "LH":
                prob = prob * transitionMatrix[1][2]
                rainStatusToday = "HeavyRain"
                rainStatusList.append("HeavyRain")

        elif rainStatusToday == "HeavyRain":
            change = np.random.choice(transitionName[2],replace=True,p=transitionMatrix[2])
            if change == "HN":
                prob = prob * transitionMatrix[2][0]
                rainStatusToday = "NoRain"
                rainStatusList.append("NoRain")
                pass
            elif change == "HL":
                prob = prob * transitionMatrix[2][1]
                rainStatusToday = "LightRain"
                rainStatusList.append("LightRain")
            elif change == "HH":
                prob = prob * transitionMatrix[2][2]
                rainStatusToday = "HeavyRain"
                rainStatusList.append("HeavyRain")
        i += 1
    return [rainStatusList, prob ]

def accuracy(original_data, predicted_data):
  simillar_count = 0
  for i in range(0,len(original_data)):
    if(original_data[i] == predicted_data[i]):
      simillar_count += 1
  return (simillar_count/len(original_data)*100)

####################################################################

########## function definitions needed for flood status forecasting #############
def filterDataWL(data):
  floodStatusArray=[]
  for i in range(len(data)):
    water_level=float(data[i])
    if(water_level<2.75):
      floodStatus="NoFlood"
    elif(water_level<3.75):
      floodStatus="Critical"
    elif(water_level<4):
      floodStatus="Warning"
    else:
      floodStatus="Flood"
    floodStatusArray.append(floodStatus)
  return floodStatusArray

def floodStatus_forecast(days, current, transitionMatrix):
    transitionName = [["NN","NC","NW","NF"],["CN","CC","CW","CF"],["WN","WC","WW","WF"],["FN","FC","FW","FF"]]
    # Choose the starting state
    floodStatusToday = str(current)
    #floodStatusToday = "Warning"
    # Shall store the sequence of states taken. So, this only has the starting state for now.
    floodStatusList = [floodStatusToday]
    i = 0
    # To calculate the probability of the floodStatusList
    prob = 1
    while i != days:
        if floodStatusToday == "NoFlood":
            change = np.random.choice(transitionName[0],replace=True,p=transitionMatrix[0])
            if change == "NN":
                prob = prob * transitionMatrix[0][0]
                floodStatusList.append("NoFlood")
                pass
            elif change == "NC":
                prob = prob * transitionMatrix[0][1]
                floodStatusToday = "Critical"
                floodStatusList.append("Critical")
            elif change== "NW":
                prob = prob * transitionMatrix[0][2]
                floodStatusToday = "Warning"
                floodStatusList.append("Warning")
            else:
                prob = prob * transitionMatrix[0][3]
                floodStatusToday = "Flood"
                floodStatusList.append("Flood")

        elif floodStatusToday == "Critical":
            change = np.random.choice(transitionName[1],replace=True,p=transitionMatrix[1])
            if change == "CN":
                prob = prob * transitionMatrix[1][0]
                floodStatusToday = "NoFlood"
                floodStatusList.append("NoFlood")
                pass
            elif change == "CC":
                prob = prob * transitionMatrix[1][1]
                floodStatusToday = "Critical"
                floodStatusList.append("Critical")
            elif change == "CW":
                prob = prob * transitionMatrix[1][2]
                floodStatusToday = "Warning"
                floodStatusList.append("Warning")
            else:
                prob = prob * transitionMatrix[1][3]
                floodStatusToday = "Flood"
                floodStatusList.append("Flood")
        elif floodStatusToday == "Warning":
            change = np.random.choice(transitionName[2],replace=True,p=transitionMatrix[2])
            if change == "WN":
                prob = prob * transitionMatrix[2][0]
                floodStatusToday = "NoFlood"
                floodStatusList.append("NoFlood")
                pass
            elif change == "WC":
                prob = prob * transitionMatrix[2][1]
                floodStatusToday = "Critical"
                floodStatusList.append("Critical")
            elif change == "WW":
                prob = prob * transitionMatrix[2][2]
                floodStatusToday = "Warning"
                floodStatusList.append("Warning")
            else:
                prob = prob * transitionMatrix[2][3]
                floodStatusToday = "Flood"
                floodStatusList.append("Flood")
        else:
            change = np.random.choice(transitionName[3],replace=True,p=transitionMatrix[3])
            if change == "FN":
                prob = prob * transitionMatrix[3][0]
                floodStatusToday = "NoFlood"
                floodStatusList.append("NoFlood")
                pass
            elif change == "FC":
                prob = prob * transitionMatrix[3][1]
                floodStatusToday = "Critical"
                floodStatusList.append("Critical")
            elif change == "FW":
                prob = prob * transitionMatrix[3][2]
                floodStatusToday = "Warning"
                floodStatusList.append("Warning")
            else:
                prob = prob * transitionMatrix[3][3]
                floodStatusToday = "Flood"
                floodStatusList.append("Flood")
        i += 1
    return [floodStatusList, prob]

##############################################################################

##################### Rainfall forecasting Calculations #####################################
sample_data_RF = pd.read_excel(str(
    Path(__file__).resolve().parents[1]/"markov"/"data"/"data.xlsx"),usecols='B:D')
date = pd.read_excel(str(
    Path(__file__).resolve().parents[1]/"markov"/"data"/"data.xlsx"),usecols='A')

columns = list(sample_data_RF)

days_to_forecast_RF= json.loads(sys.argv[1])
current_states_RF = json.loads(sys.argv[2])

for i in columns:
  temp_output={}
  #filter data for Batalagoda
  filtered_data = filterData(sample_data_RF[i])
  #changed status array
  change_status_array = ChangeStatusFunction(filtered_data)
  #calculate transition matrix
  t_matrix = CalculateTransitionMatrix(change_status_array,filtered_data)

  res_rain = RainFallForecast(int(days_to_forecast_RF[i]),str(current_states_RF[i]),t_matrix)
  calc_acc= RainFallForecast(len(filtered_data),filtered_data[0],t_matrix)

  acc= accuracy(filtered_data,calc_acc[0])

  temp_output["transitionMatrix"] = t_matrix
  temp_output['days']=int(days_to_forecast_RF[i])
  temp_output['startState']=res_rain[0][0]
  temp_output['endState']=res_rain[0][-1]
  temp_output['possibleStates']=res_rain[0]
  temp_output['probability']=res_rain[1]
  temp_output['accuracy']=acc
  output[i]=temp_output
#################################################################################################

##################### Flood status forecasting Calculations #####################################
sample_data_WL = pd.read_excel(str(
    Path(__file__).resolve().parents[1]/"markov"/"data"/"data.xlsx"),usecols='E')
columns_WL = list(sample_data_WL)
days_to_forecast_FLOOD = sys.argv[3]
current_states_FlOOD = sys.argv[4]

for col in columns_WL:
  floodStatusArray = filterDataWL(sample_data_WL[col])

  changeStatusArray=[]
  noFloodCount=0
  warningCount=0
  criticalCount=0
  floodCount=0

  for i in range(len(floodStatusArray)-1):
    if(floodStatusArray[i]=="NoFlood"):
      if(floodStatusArray[i+1]=="NoFlood"):
        changeStatus="NN"
      elif(floodStatusArray[i+1]=="Critical"):
        changeStatus="NC"
      elif(floodStatusArray[i+1]=="Warning"):
        changeStatus="NW"
      elif(floodStatusArray[i+1]=="Flood"):
        changeStatus="NF"
      noFloodCount=noFloodCount+1
    elif(floodStatusArray[i]=="Warning"):
      if(floodStatusArray[i+1]=="NoFlood"):
        changeStatus="WN"
      elif(floodStatusArray[i+1]=="Critical"):
        changeStatus="WC"
      elif(floodStatusArray[i+1]=="Warning"):
        changeStatus="WW"
      elif(floodStatusArray[i+1]=="Flood"):
        changeStatus="WF"
      warningCount=warningCount+1
    elif(floodStatusArray[i]=="Critical"):
      if(floodStatusArray[i+1]=="NoFlood"):
        changeStatus="CN"
      elif(floodStatusArray[i+1]=="Critical"):
        changeStatus="CC"
      elif(floodStatusArray[i+1]=="Warning"):
        changeStatus="CW"
      elif(floodStatusArray[i+1]=="Flood"):
        changeStatus="CF"
      criticalCount=criticalCount+1
    elif(floodStatusArray[i]=="Flood"):
      if(floodStatusArray[i+1]=="NoFlood"):
        changeStatus="FN"
      elif(floodStatusArray[i+1]=="Critical"):
        changeStatus="FC"
      elif(floodStatusArray[i+1]=="Warning"):
        changeStatus="FW"
      elif(floodStatusArray[i+1]=="Flood"):
        changeStatus="FF"
      floodCount=floodCount+1
    changeStatusArray.append(changeStatus)

  #calculation
  NNCount=0
  NCCount=0
  NWCount=0
  NFCount=0

  CNCount=0
  CCCount=0
  CWCount=0
  CFCount=0

  WNCount=0
  WCCount=0
  WWCount=0
  WFCount=0

  FNCount=0
  FCCount=0
  FWCount=0
  FFCount=0

  for j in range(len(changeStatusArray)):
    if(changeStatusArray[j]=="NN"):
      NNCount=NNCount+1
    elif(changeStatusArray[j]=="NC"):
      NCCount=NCCount+1
    elif(changeStatusArray[j]=="NW"):
      NWCount=NWCount+1
    elif(changeStatusArray[j]=="NF"):
      NFCount=NFCount+1
    elif(changeStatusArray[j]=="CN"):
      CNCount=CNCount+1
    elif(changeStatusArray[j]=="CC"):
      CCCount=CCCount+1
    elif(changeStatusArray[j]=="CW"):
      CWCount=CWCount+1
    elif(changeStatusArray[j]=="CF"):
      CFCount=CFCount+1
    elif(changeStatusArray[j]=="WN"):
      WNCount=WNCount+1
    elif(changeStatusArray[j]=="WC"):
      WCCount=WCCount+1
    elif(changeStatusArray[j]=="WW"):
      WWCount=WWCount+1
    elif(changeStatusArray[j]=="WF"):
      WFCount=WFCount+1
    elif(changeStatusArray[j]=="FN"):
      FNCount=FNCount+1
    elif(changeStatusArray[j]=="FC"):
      FCCount=FCCount+1
    elif(changeStatusArray[j]=="FW"):
      FWCount=FWCount+1
    elif(changeStatusArray[j]=="FF"):
      FFCount=FFCount+1

  transitionMetrix=[]

  if((NNCount+NCCount+NWCount+NFCount)==0 or(CNCount+CCCount+CWCount+CFCount)==0 or(WNCount+WCCount+WWCount+WFCount)==0 or(FNCount+FCCount+FWCount+FFCount)==0):
      print("invalid history data input")
  else:
      transitionMetrix=[
                        [NNCount/(NNCount+NCCount+NWCount+NFCount),NCCount/(NNCount+NCCount+NWCount+NFCount),NWCount/(NNCount+NCCount+NWCount+NFCount),NFCount/(NNCount+NCCount+NWCount+NFCount)],
  [CNCount/(CNCount+CCCount+CWCount+CFCount),CCCount/(CNCount+CCCount+CWCount+CFCount),CWCount/(CNCount+CCCount+CWCount+CFCount),CFCount/(CNCount+CCCount+CWCount+CFCount)],
  [WNCount/(WNCount+WCCount+WWCount+WFCount),WCCount/(WNCount+WCCount+WWCount+WFCount),WWCount/(WNCount+WCCount+WWCount+WFCount),WFCount/(WNCount+WCCount+WWCount+WFCount)],
  [FNCount/(FNCount+FCCount+FWCount+FFCount),FCCount/(FNCount+FCCount+FWCount+FFCount),FWCount/(FNCount+FCCount+FWCount+FFCount),FFCount/(FNCount+FCCount+FWCount+FFCount)]]

  flood_res = floodStatus_forecast(int(days_to_forecast_FLOOD), str(current_states_FlOOD),transitionMetrix)
  calc_acc = floodStatus_forecast(len(floodStatusArray),floodStatusArray[0],transitionMetrix)
  acc = accuracy(floodStatusArray,calc_acc[0] )
  temp_output={}
  temp_output["transitionMatrix"] = transitionMetrix
  temp_output['days']=int(days_to_forecast_FLOOD)
  temp_output['startState']=flood_res[0][0]
  temp_output['possibleStates']=flood_res[0]
  temp_output['endState']=flood_res[0][-1]
  temp_output
  temp_output['probability']=flood_res[1]
  temp_output['accuracy']=acc
  output['flood_' + col]=temp_output

#########################################################################################################
print(json.dumps(output))


def calculate_fibonacci(n):

    n1,n2 = 0,1
    count = 1
    if n<0:
        print("Numeros mayores que cero")
    elif n==0:
        print(0)
    elif n==1:
        print(0,1)
    else:
        while count < n:
            print(n1)
            nth = n1 + n2
            n1=n2
            n2=nth
            count +=1

def fibo(n):
    a,b = 0,1

    while a < n:
        print(a, end=' ')
        a,b = b, a+b
    #print()


from robot_manager import RobotManager
from robomaster import robot
from navigation.path_planner import PathPlanner
from utils.transformations import robot2world, world2robot
import time
import numpy as np

def calculate_angle(angle):
    new_angle = angle % 360
    new_angle = (new_angle + 360) % 360
    if new_angle > 180:
        new_angle -= 360

    return new_angle

if __name__ == '__main__':
    ep_robot = robot.Robot()
    ep_robot.initialize(conn_type="ap")
    manager = RobotManager(ep_robot)
    time.sleep(1)
    path_planner = PathPlanner()

    start_location = (1117, 203, 0)
    goal_location = (2616, 1117, 0)
    path = path_planner.get_optimal_path(start_location, goal_location)
    print(path)
    curr_global_pose = np.array(start_location, dtype=float)
    for global_waypoint in path:
        pre_movement_odom = manager.get_odometry()
        robot_waypoint = world2robot(tuple(curr_global_pose), global_waypoint)
        manager.move_base(round(robot_waypoint[0] / 1000, 3), round(-robot_waypoint[1] / 1000, 3), calculate_angle(robot_waypoint[2]))
        time.sleep(1)
        post_movement_odom = manager.get_odometry()
        odom_delta = post_movement_odom - pre_movement_odom
        curr_global_pose += np.array((odom_delta[0] * 1000, -odom_delta[1] * 1000, odom_delta[2]))

    manager.shutdown()


import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
import cairo

from DBBGUI.utils_resources import DLBox, LabelsWindow, ImageResourcesBox, DrawingResource

import os
from os import listdir

class ResourcesPanel():
    def __init__(self, window):
        self.window = window
        self.screen = window.get_screen()
        monitor_geo = self.screen.get_monitor_geometry(0)
        self.screen_width = monitor_geo.width
        self.screen_height = monitor_geo.height

        self.image_folder = None

        box_width = int(self.screen_width * 0.178)
        self.resource_box = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
        self.resource_box.set_name('MENU_BOX')
        self.resource_box.set_size_request(box_width, 0)

        self.panelManager()

    def panelManager(self):
        spacing = int(self.screen_height * 0.01)

        self.DLFiles = DLBox.DeepLearningBox(self.window)
        DLFilesBox = self.DLFiles.return_netBox()
        self.resource_box.pack_start(DLFilesBox, False, False, 0)

        self.LWindow = LabelsWindow.LabelClasses(self.window)
        LWindowBox = self.LWindow.return_NetLabelsBox()
        self.resource_box.pack_start(LWindowBox, False, False, spacing)

        self.IResources = ImageResourcesBox.ImageResources(self.window)
        IRBox = self.IResources.return_image_resources_box()
        self.resource_box.pack_start(IRBox, False, False, 0)

        self.DLFiles.wrap_LWindow(self.LWindow)

    def set_drawingResource(self, darea, dw, dh):
        self.drawingImage = DrawingResource.DrawingEvents(darea, dw, dh)
        self.DLFiles.wrap_drawing(self.drawingImage)
        self.drawingImage.generate_working_colors(self.LWindow.get_workingLabels())
        self.drawingImage.wrap_LabelsWindow(self.LWindow)
        self.LWindow.wrap_drawing(self.drawingImage)

    def  update_Imagefiles_resources(self, path):
        self.image_folder = path
        self.IResources.update_files(path)
        self.imageIterator = bi_iterator(path)
        filename, index = self.imageIterator.current()
        self.IResources.set_view_cursor(index)
        self.drawingImage.set_drawing(filename)

    def next_image(self):
        if self.image_folder is not None:
            filename, index = self.imageIterator.next()
            if filename is not "None":
                self.IResources.set_view_cursor(index)
                self.drawingImage.set_drawing(filename)

    def prev_image(self):
        if self.image_folder is not None:
            filename, index = self.imageIterator.prev()
            if filename is not "None":
                self.IResources.set_view_cursor(index)
                self.drawingImage.set_drawing(filename)

    def create_rectbox(self):
        self.drawingImage.create_rectbox()

    def delete_rectbox(self):
        self.drawingImage.clear_current_rectangle()

    def save_detections(self):
        self.drawingImage.save_detections()

    def set_save_folder(self, save_path):
        self.drawingImage.set_save_folder(save_path)

    def return_resource_box(self):
        return self.resource_box

class bi_iterator():
    def __init__(self, path):
        self.collection = []
        files = listdir(path)
        for file_ in files:
            self.collection.append(path+'/'+file_)
        self.index = 0
        self.max_index = len(self.collection)
        self.current_index = 0

    def next(self):
        if self.current_index < self.max_index-1:
            self.current_index += 1
        else:
            return "None", self.current_index
        return self.collection[self.current_index], self.current_index

    def prev(self):
        if self.current_index > 0:
            self.current_index -= 1
        else:
            return "None", self.current_index
        return self.collection[self.current_index], self.current_index

    def current(self):
        return self.collection[self.current_index], self.current_index


# https://adventofcode.com/2022/day/22

with open('input.txt', 'r') as f:
    data = f.read().split('\n')

# manually parse input (who needs regex!)
turns = [idx for idx, char in enumerate(data[-2]) if char in ['L', 'R']]
pointer = 0
route = []
while turns:
    next_turn = turns.pop(0)
    route.append(data[-2][pointer:next_turn])
    route.append(data[-2][next_turn])
    pointer = next_turn + 1
route.append(data[-2][pointer:])

# pad board so every row has same width
board = data[:-3]
width = max([len(i) for i in board])
height = len(board)
board = [i + ' '*(width-len(i)) for i in board]

# map directions and turns
turning = {'R': {'E': 'S', 'S': 'W', 'W': 'N', 'N': 'E'},
           'L': {'E': 'N', 'S': 'E', 'W': 'S', 'N': 'W'}}
yx_map = {'E': (0, 1), 'S': (1, 0), 'W': (0, -1), 'N': (-1, 0)}
direction_value = {'E': 0, 'S': 1, 'W': 2, 'N': 3}

def starting_state():
    # creates initial state
    y0, x0 = (0, 50)
    direction = 'E'
    return y0, x0, direction

# these dict tells us how to wrap around the board
ywrap = {height: 0, -1: height-1}
xwrap = {width: 0, -1: width-1}

def next_pos_safe(y0, x0, yd, xd):
    # makes a single step, wrapping round if necessary
    y1, x1 = y0 + yd, x0 + xd
    y1 = ywrap.get(y1, y1)
    x1 = xwrap.get(x1, x1)
    return y1, x1

def final_score(y0, x0, direction):
    return ((1000*(y0+1)) + (4 * (x0+1)) + direction_value[direction])

def wrap_around_basic(y1, x1, yd, xd, next):
    # part 1 wrap around function
    while next == ' ':
        y1, x1 = next_pos_safe(y1, x1, yd, xd)
        next = board[y1][x1]
    return y1, x1, next

def wrap_around_cube(y1, x1, direction0):
    # part 2 wrap around function (relies on hardcoded cube_map)
    # fine the position and direction we end up at
    y1, x1, direction1 = cube_map[y1, x1, direction0]
    # update the deltas and next space
    yd, xd = yx_map[direction1]
    next = board[y1][x1]
    if next == '.':  # only update if its a free space
        direction0 = direction1
    return y1, x1, yd, xd, next, direction0

###################################
####### HARD CODED CUBE MAP ######
###################################

fr, to = [], []
# [2,0] -> [1,1]
fr.extend([(99, x, 'N') for x in range(0, 50)])
to.extend([(y, 50, 'E') for y in range(50, 100)])
# [2,0] <- [1,1]
fr.extend([(y, 49, 'W') for y in range(50, 100)])
to.extend([(100, x, 'S') for x in range(0, 50)])
# [3,0] -> [2,1]
fr.extend([(y, 50, 'E') for y in range(150, 200)])
to.extend([(149, x, 'N') for x in range(50, 100)])
# [3,0] <- [2,1]
fr.extend([(150, x, 'S') for x in range(50, 100)])
to.extend([(y, 49, 'W') for y in range(150, 200)])
# [1,1] -> [0,2]
fr.extend([(y, 100, 'E') for y in range(50, 100)])
to.extend([(49, x, 'N') for x in range(100, 150)])
# [1,1] <- [0,2]
fr.extend([(50, x, 'S') for x in range(100, 150)])
to.extend([(y, 99, 'W') for y in range(50, 100)])
# [2,1] -> [0,2] (REVERSED)
fr.extend([(y, 100, 'E') for y in range(100, 150)])
to.extend([(y, 149, 'W') for y in range(49, -1, -1)])
# [2,2] <- [0,2]
fr.extend([(y, 0, 'E') for y in range(0, 50)])
to.extend([(y, 99, 'W') for y in range(149, 99, -1)])
# [2,0] -> [0,1] (REVERSED)
fr.extend([(y, 149, 'W') for y in range(100, 150)])
to.extend([(y, 50, 'E') for y in range(49, -1, -1)])
# [2,0] <- [0,1]
fr.extend([(y, 49, 'W') for y in range(0, 50)])
to.extend([(y, 0, 'E') for y in range(149, 99, -1)])
# [3,0] -> [0,1]
fr.extend([(y, 149, 'W') for y in range(150, 200)])
to.extend([(0, x, 'S') for x in range(50, 100)])
# [3,0] <- [0,1]
fr.extend([(199, x, 'N') for x in range(50, 100)])
to.extend([(y, 0, 'E') for y in range(150, 200)])
# [3,0] -> [0,2]
fr.extend([(0, x, 'S') for x in range(0, 50)])
to.extend([(0, x, 'S') for x in range(100, 150)])
# [3,0] <- [0,2]
fr.extend([(199, x, 'N') for x in range(100, 150)])
to.extend([(199, x, 'N') for x in range(0, 50)])
# make the final map
cube_map = {f: t for f, t in zip(fr, to)}

# MAIN TASK
def tread_path(method='basic'):

    # create starting state
    y0, x0, direction = starting_state()

    # loop through route
    for step in route:

        if step.isdigit():  # if we've moving...
            yd, xd = yx_map[direction]  # the direction we're moving

            for n in range(int(step)):
                # next pos avoid edge of board
                y1, x1 = next_pos_safe(y0, x0, yd, xd)
                next = board[y1][x1]  # next space

                # if we need to wrap around
                if next == ' ':
                    if method == 'basic':  # part 1 method
                        y1, x1, next = wrap_around_basic(y1, x1, yd, xd, next)
                    else:  # part 2 method
                        y1, x1, yd, xd, next, direction = wrap_around_cube(
                            y1, x1, direction)

                if next == '.':  # if free, then move forward
                    y0, x0 = y1, x1
                elif next == '#':  # else we have to stop walking
                    break

        else:  # if we're turning
            direction = turning[step][direction]

    return final_score(y0, x0, direction)

# part 1 answer
print(tread_path(method='basic'))
# part 2 answer
print(tread_path(method='cube'))


import html

import htmlgenerator as hg
from django.conf import settings
from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils.html import strip_tags
from django.utils.translation import get_language
from django.utils.translation import gettext_lazy as _

from .components.notification import ToastNotification
from .components.shell_header import ShellHeader
from .components.sidenav import SideNav

_static = staticfiles_storage.url

def default_page_layout(menu, *content, hidemenus=False):
    return hg.HTML(
        hg.HEAD(
            hg.TITLE(
                hg.F(
                    lambda c: html.unescape(
                        strip_tags(
                            hg.render(
                                hg.BaseElement(
                                    c.get("pagetitle", settings.PLATFORMNAME)
                                ),
                                c,
                            )
                        )
                    )
                    + " | "
                    + strip_tags(settings.PLATFORMNAME)
                )
            ),
            hg.LINK(rel="shortcut icon", href=_static("logo.png")),
            hg.LINK(
                rel="stylesheet",
                type="text/css",
                href=hg.If(
                    settings.DEBUG,
                    _static("css/basxbread-main.css"),  # generate with "make css"
                    _static("css/basxbread-main.min.css"),  # generate with "make css"
                ),
                media="all",
            ),
        ),
        hg.BODY(
            hg.If(
                not hidemenus,
                hg.BaseElement(
                    ShellHeader(
                        settings.PLATFORMNAME,
                        hg.C("global_preferences.general__organizationname"),
                        searchbar=hg.If(
                            hg.C("request.user.is_authenticated"),
                            hg.C("SEARCHBAR"),
                            "",
                        ),
                    ),
                    hg.If(
                        hg.C("request.user.is_authenticated"),
                        SideNav(menu),
                    ),
                ),
            ),
            hg.DIV(
                hg.Iterator(
                    hg.C("messages"),
                    "message",
                    ToastNotification(
                        message=hg.F(lambda c: _(c["message"].tags.capitalize())),
                        details=hg.C("message.message"),
                        kind=hg.C("message.level_tag"),
                        hidetimestamp=True,
                        autoremove=5.0,
                    ),
                ),
                style="position: fixed; right: 0; z-index: 999",
            ),
            hg.DIV(*content, _class="bx--content"),
            hg.If(
                settings.DEBUG,
                hg.BaseElement(
                    hg.SCRIPT(src=_static("js/bliss.js")),
                    hg.SCRIPT(src=_static("js/htmx.js")),
                    hg.SCRIPT(src=_static("js/main.js")),
                    hg.SCRIPT(
                        src=_static("design/carbon_design/js/carbon-components.js")
                    ),
                ),
                hg.SCRIPT(
                    src=_static("js/basxbread.min.js")
                ),  # generate with "make js"
            ),
            hg.SCRIPT("CarbonComponents.watch(document);"),
        ),
        doctype=True,
        _class="no-js",
        lang=get_language(),
    )


import os
import sys
import subprocess
from functools import partial

import pymel.core as pm

import mgear

from mgear.maya import synoptic, skin, rigbits, attribute, shifter
from mgear.maya.simpleRig import simpleRigTool, simpleRig_legacy
from mgear.maya.animbits import softTweaks, crankTool
from mgear.maya.rigbits import (
    postSpring,
    rope,
    proxySlicer,
    utils,
    channelWrangler,
    eye_rigger,
    lips_rigger,
    rbf_manager_ui
)

from mGear_guidesTemplates import (
    guideUI,
    buildFromSelection,
    bipedGuide,
    quadrupedGuide,
    updateGuide
)
from mGear_mocapTools import (
    importSkeletonBiped,
    characterizeBiped,
    bakeMocap,
)

def openFile(file, *args):
    if sys.platform.startswith("darwin"):
        subprocess.call(("open", file))
    elif os.name == "nt":
        os.startfile(file)
    elif os.name == "posix":
        subprocess.call(("xdg-open", file))
    else:
        raise OSError("Unsupported platform '%s'" % os.name)

def createMenu():
    if pm.menu("mGear", exists=True):
        pm.deleteUI("mGear")

    mGearM = pm.menu("mGear",
                     parent="MayaWindow",
                     tearOff=True,
                     allowOptionBoxes=True,
                     label="mGear")

    # Rigging systems
    # Shifter rigging builder
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Shifter")
    pm.menuItem(label="Guides UI", command=guideUI)
    pm.menuItem(divider=True)
    pm.menuItem(label="Build From Selection",
                command=buildFromSelection)
    pm.menuItem(divider=True)
    pm.menuItem(label="Import Biped Guide",
                command=bipedGuide)
    pm.menuItem(label="Import Quadruped Guide",
                command=quadrupedGuide)
    pm.menuItem(divider=True)
    pm.menuItem(label="Update Guide", command=updateGuide)
    pm.menuItem(divider=True)
    pm.menuItem(label="Reload Components", command=shifter.reloadComponents)

    pm.setParent(mGearM, menu=True)
    pm.menuItem(divider=True)

    # Simple rig
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Simple Rig")
    pm.menuItem(label="Simple Rig Tool", command=simpleRigTool.openSimpleRigUI)
    pm.menuItem(divider=True)
    pm.menuItem(subMenu=True, tearOff=True, label="simpleRig Legacy")
    pm.menuItem(label="Simple Rig Generate",
                command=partial(simpleRig_legacy.simpleRig, "rig", False))
    pm.menuItem(divider=True)
    pm.menuItem(label="Create Root", command=simpleRig_legacy.createRoot)
    pm.menuItem(divider=True)
    pm.menuItem(label="Set User Pivot",
                command=simpleRig_legacy.setUserRigPivot)
    pm.menuItem(label="Add To User Pivot",
                command=simpleRig_legacy.addToUserPivot)
    pm.menuItem(label="Select Objects In User Pivot",
                command=simpleRig_legacy.selectObjectInUserRootPivot)
    pm.setParent(mGearM, menu=True)
    pm.menuItem(divider=True)

    # Rigging Tools
    riggingM = pm.menuItem(parent="mGear",
                           subMenu=True,
                           tearOff=True,
                           label="Rigging")
    pm.menuItem(label="Add NPO", command=rigbits.addNPO)
    pm.menuItem(subMenu=True, tearOff=True, label="Gimmick Joints")
    pm.menuItem(label="Add Joint",
                command=rigbits.addJnt)
    pm.menuItem(divider=True)
    pm.menuItem(label="Add Blended Joint",
                command=rigbits.addBlendedJoint)
    pm.menuItem(label="Add Support Joint",
                command=rigbits.addSupportJoint)
    pm.setParent(riggingM, menu=True)
    pm.menuItem(divider=True)
    pm.menuItem(label="Replace Shape",
                command=rigbits.replaceShape)
    pm.menuItem(divider=True)
    pm.menuItem(label="Match All Transform", command=rigbits.matchWorldXform)
    pm.menuItem(label="Match Pos with BBox", command=rigbits.matchPosfromBBox)
    pm.menuItem(label="Align Ref Axis", command=rigbits.alignToPointsLoop)

    pm.setParent(riggingM, menu=True)
    pm.menuItem(divider=True)
    pm.menuItem(subMenu=True, tearOff=True, label="CTL as Parent")
    pm.menuItem(label="Square", command=partial(rigbits.createCTL, "square"))
    pm.menuItem(label="Circle", command=partial(rigbits.createCTL, "circle"))
    pm.menuItem(label="Cube", command=partial(rigbits.createCTL, "cube"))
    pm.menuItem(label="Diamond", command=partial(rigbits.createCTL, "diamond"))
    pm.menuItem(label="Sphere", command=partial(rigbits.createCTL, "sphere"))
    pm.menuItem(label="Cross", command=partial(rigbits.createCTL, "cross"))
    pm.menuItem(label="Cross Arrow",
                command=partial(rigbits.createCTL, "crossarrow"))
    pm.menuItem(label="Pyramid",
                command=partial(rigbits.createCTL, "pyramid"))
    pm.menuItem(label="Cube With Peak",
                command=partial(rigbits.createCTL, "cubewithpeak"))
    pm.setParent(riggingM, menu=True)
    pm.menuItem(subMenu=True, tearOff=True, label="CTL as Child")
    pm.menuItem(label="Square",
                command=partial(rigbits.createCTL, "square", True))
    pm.menuItem(label="Circle",
                command=partial(rigbits.createCTL, "circle", True))
    pm.menuItem(label="Cube",
                command=partial(rigbits.createCTL, "cube", True))
    pm.menuItem(label="Diamond",
                command=partial(rigbits.createCTL, "diamond", True))
    pm.menuItem(label="Sphere",
                command=partial(rigbits.createCTL, "sphere", True))
    pm.menuItem(label="Cross",
                command=partial(rigbits.createCTL, "cross", True))
    pm.menuItem(label="Cross Arrow",
                command=partial(rigbits.createCTL, "crossarrow", True))
    pm.menuItem(label="Pyramid",
                command=partial(rigbits.createCTL, "pyramid", True))
    pm.menuItem(label="Cube With Peak",
                command=partial(rigbits.createCTL, "cubewithpeak", True))
    pm.setParent(riggingM, menu=True)
    pm.menuItem(divider=True)
    pm.menuItem(label="Duplicate symmetrical",
                command=rigbits.duplicateSym)
    pm.menuItem(divider=True)
    pm.menuItem(label="RBF Manager",
                command=rbf_manager_ui.show)
    pm.menuItem(label="Space Jumper",
                command=rigbits.spaceJump)
    pm.menuItem(label="Interpolated Transform",
                command=rigbits.createInterpolateTransform)

    pm.menuItem(subMenu=True, tearOff=True, label="Connect local SRT")
    pm.menuItem(label="connect SRT",
                command=partial(rigbits.connectLocalTransform, None, 1, 1, 1))
    pm.menuItem(label="connect T",
                command=partial(rigbits.connectLocalTransform, None, 0, 0, 1))
    pm.menuItem(label="connect R",
                command=partial(rigbits.connectLocalTransform, None, 0, 1, 0))
    pm.menuItem(label="connect S",
                command=partial(rigbits.connectLocalTransform, None, 1, 0, 0))
    pm.setParent(riggingM, menu=True)
    pm.menuItem(divider=True)
    pm.menuItem(label="Spring", command=postSpring.spring_UI)
    pm.menuItem(label="Rope", command=rope.rope_UI)
    pm.menuItem(divider=True)
    pm.menuItem(label="Channel Wrangler",
                command=channelWrangler.openChannelWrangler)
    pm.menuItem(divider=True)
    pm.menuItem(label="FACIAL: Eye Rigger",
                command=eye_rigger.showEyeRigUI)
    pm.menuItem(label="FACIAL: Lips Rigger",
                command=lips_rigger.showLipRigUI)

    # skinning tools
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Skinning")
    pm.menuItem(label="Copy Skin", command=partial(skin.skinCopy, None, None))
    pm.menuItem(label="Select Skin Deformers", command=skin.selectDeformers)
    pm.menuItem(divider=True)
    pm.menuItem(label="Import Skin", command=skin.importSkin)
    pm.menuItem(label="Import Skin Pack", command=skin.importSkinPack)
    pm.menuItem(divider=True)
    pm.menuItem(label="Export Skin", command=skin.exportSkin)
    pm.menuItem(label="Export Skin Pack", command=skin.exportSkinPack)
    pm.menuItem(divider=True)
    pm.menuItem(label="Get Names in gSkin File",
                command=skin.getObjsFromSkinFile)

    # Modeling tools
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Modeling")
    pm.menuItem(label="Proxy Slicer",
                command=partial(proxySlicer.slice))
    pm.menuItem(label="Proxy Slicer Parenting",
                command=partial(proxySlicer.slice, True))

    # Animation Tools
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Animation")
    pm.menuItem(label="Synoptic", command=synoptic.open)
    pm.menuItem(divider=True)
    pm.menuItem(label="Soft Tweaks", command=softTweaks.openSoftTweakManager)
    pm.menuItem(label="Crank: Shot Sculpt", command=crankTool.openUI)
    pm.menuItem(divider=True)
    pm.menuItem(label="Import Mocap Skeleton Biped",
                command=importSkeletonBiped)
    pm.menuItem(label="Characterize Biped",
                command=characterizeBiped)
    pm.menuItem(label="Bake Mocap Biped", command=bakeMocap)
    pm.menuItem(divider=True)
    pm.menuItem(label="Smart Reset Attribute/SRT",
                command=attribute.smart_reset)

    # util Tools
    pm.setParent(mGearM, menu=True)
    pm.menuItem(divider=True)
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Utilities")
    pm.menuItem(label="Reload", command=partial(mgear.reloadModule, "mgear"))
    pm.menuItem(divider=True)
    pm.menuItem(label="Compile PyQt ui", command=utils.ui2py)
    pm.menuItem(divider=True)
    pm.menuItem(label="Create mGear Hotkeys",
                command=utils.createHotkeys)

    # Help
    pm.setParent(mGearM, menu=True)
    pm.menuItem(divider=True)
    pm.menuItem(parent="mGear", subMenu=True, tearOff=True, label="Help")
    pm.menuItem(label="Web",
                command=partial(
                    openFile,
                    "http://www.mgear-framework.com/"))
    pm.menuItem(label="Forum",
                command=partial(
                    openFile,
                    "http://forum.mgear-framework.com/"))
    pm.menuItem(divider=True)
    pm.menuItem(label="About", command=mgear.maya.aboutMgear)

# Backwards compatibiltiy
CreateMenu = createMenu


from pytorch_lightning.loggers import WandbLogger
import sys
import argparse
import torch
import yaml
from data.datamodule import StaticDataModule
from models.modules.autoencoders.big_ae import BigAE
from collections import OrderedDict

import numpy as np
import os
import json

def angular_error(gt, pred):
    gt = np.pad(gt, [[0,0],[0,1],[0,0],[0,0]], 'constant', constant_values=1)
    pred = np.pad(pred, [[0,0],[0,1],[0,0],[0,0]], 'constant', constant_values=1)

    # u, v = pred[0], pred[1]
    # u_gt, v_gt = gt[0], gt[1]
    # AE = np.arccos((1. + u*u_gt + v*v_gt)/(np.sqrt(1.+u**2+v**2)*np.sqrt(1.+u_gt**2+v_gt**2)))
    AE = np.arccos(np.sum(gt*pred, axis=1)/(np.linalg.norm(gt, axis=1) * np.linalg.norm(pred, axis=1)))
    return AE

def endpoint_error(gt, pred):
    EE = np.linalg.norm(gt-pred, axis=1)
    return EE

class BigAEfixed(BigAE):

    def __init__(self, config):
        super(BigAEfixed, self).__init__(config)
        checkpoint = torch.load(config["checkpoint_ae"], map_location='cpu')
        new_state_dict = OrderedDict()
        for key, value in checkpoint['state_dict'].items():
            # if key[3:] == 'ae.':
            new_key = key[3:]  # trimming keys by removing "ae."
            new_state_dict[new_key] = value
        m, u = self.load_state_dict(new_state_dict, strict=False)
        assert len(m) == 0, "BigAE state_dict is missing pretrained params"
        del checkpoint

    def train(self, mode: bool):
        """ avoid pytorch lighting auto set trian mode """
        return super().train(False)

    def state_dict(self, destination, prefix, keep_vars):
        """ avoid pytorch lighting auto save params """
        destination = OrderedDict()
        destination._metadata = OrderedDict()
        return destination

def parse():
    parser = argparse.ArgumentParser('Fully Connected VAE')
    parser.add_argument('-g', '--gpus', type=int, required=True, help='gpu/gpus to run on e.g. -g 5')
    parser.add_argument('-c', '--config', default='config/BigGanAE.yaml', metavar='config_path')
    parser.add_argument('-b', '--batch_size', type=int, default=10)
    parser.add_argument('--offline', action='store_true', help='dont sync wandb logger')
    return parser.parse_args()

if __name__ == '__main__':
    np.random.seed(42)
    torch.manual_seed(42)
    torch.cuda.manual_seed(42)

    args = parse()
    batch_size = args.batch_size
    gpus = args.gpus
    device = f"cuda:0"
    os.environ["CUDA_VISIBLE_DEVICES"] = str(gpus)
    os.environ["WANDB_DIR"] = "/export/compvis-nfs/user/daltmann/scratch/FCAEModel"
    print('gpu: ', str(gpus))
    config_path = args.config
    with open(config_path, 'r') as stream:
        config = yaml.safe_load(stream, Loader=yaml.FullLoader)

    datakeys = ['flow']
    config['data']['batch_size'] = batch_size
    config["architecture"]["in_size"] = config["data"]["spatial_size"][0]
    datamod = StaticDataModule(config['data'], datakeys=datakeys)

    # Logger
    wandb_logger = WandbLogger(project='Fully Connected BigGAN Autoencoder',
                               offline=args.offline,
                               notes=str(sys.argv),
                               name='evaluation'
                               )
    datamod.setup()

    z_dim_list = [256, 512, 1024]
    R = {}
    loaders = {'validation': datamod.val_dataloader(),
               'train': datamod.train_dataloader()}

    for loader_name, loader in loaders.items():
        for z_dim in z_dim_list:
            config['architecture']['z_dim'] = z_dim
            path = f"scratch/FCAEModel_{z_dim}.ckpt"
            config['architecture']['checkpoint_ae'] = path
            R[path] = {'angular_error': {'2.5'  : [],
                                          '5.0' : [],
                                          '10.0': []},
                       'endpoint_error': {'0.5' : [],
                                          '1.0' : [],
                                          '2.0' : []},
                       'endpoint_error_normalized': {'0.5': [],
                                          '1.0': [],
                                          '2.0': []}
                       }
            model = BigAEfixed
            model = model(config['architecture']).eval()
            model.to(device)
            for batch in loader:
                flow = batch['flow'].to(device)
                rec, _, _ = model(flow)
                rec_cpu = np.array(rec.detach().cpu())
                batch_cpu = np.array(flow.detach().cpu())

                AE = angular_error(batch_cpu, rec_cpu)
                EE = endpoint_error(batch_cpu, rec_cpu)
                normalization = np.minimum(np.linalg.norm(rec_cpu,axis=1), np.linalg.norm(batch_cpu, axis=1))
                normalization = np.minimum(normalization, np.zeros_like(normalization) + 1e-4)
                NEE = EE/normalization
                R_temp = AE[AE>2.5*np.pi/180]
                R[path]['angular_error']['2.5'] += [R_temp.size/AE.size]
                R_temp = R_temp[R_temp>5.*np.pi/180]
                R[path]['angular_error']['5.0'] += [R_temp.size/AE.size]
                R_temp = R_temp[R_temp>10.*np.pi/180]
                R[path]['angular_error']['10.0'] += [R_temp.size/AE.size]

                R_temp = EE[EE>0.5]
                R[path]['endpoint_error']['0.5'] += [R_temp.size/EE.size]
                R_temp = R_temp[R_temp>1.0]
                R[path]['endpoint_error']['1.0'] += [R_temp.size/EE.size]
                R_temp = R_temp[R_temp>2.0]
                R[path]['endpoint_error']['2.0'] += [R_temp.size/EE.size]

                R_temp = NEE[NEE>0.5]
                R[path]['endpoint_error_normalized']['0.5'] += [R_temp.size/NEE.size]
                R_temp = R_temp[R_temp>1.0]
                R[path]['endpoint_error_normalized']['1.0'] += [R_temp.size/NEE.size]
                R_temp = R_temp[R_temp>2.0]
                R[path]['endpoint_error_normalized']['2.0'] += [R_temp.size/NEE.size]
            print(path)
            print(np.mean(R[path]['endpoint_error']['2.0']))

        with open(f'scratch/FCAE_eval_{loader_name}.json', 'w+') as f:
            json.dump(R, f, indent=4)
    wandb_logger.experiment.log(R)