# %%
#Mihai Cira - B. 29. Walmart Sales Forecast
#topic: analysis of the influence of holidays on sales

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
import seaborn as sns

from scipy import stats
import statsmodels.api as sm

from sklearn.preprocessing import MinMaxScaler

from sklearn import metrics
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

from sklearn.ensemble import RandomForestRegressor

# %%

# %%
#load the data
data = pd.read_csv('/kaggle/input/walmart-sales-forecast/train.csv')
stores = pd.read_csv('/kaggle/input/walmart-sales-forecast/stores.csv')
features = pd.read_csv('/kaggle/input/walmart-sales-forecast/features.csv')

# %%
#inspect the loaded data for the DATA table
data.shape
data.head()
data.info()
data.isna().sum()

# %%
#inspect the loaded data for the STORES table
stores.shape
stores.head()
stores.info()

# %%
#inspect the loaded data for the FEATURES table
features.shape
features.head()
features.info()
features.isna().sum()

# %%
# filling missing values
features['CPI'].fillna(features['CPI'].median(),inplace=True)
features['Unemployment'].fillna(features['Unemployment'].median(),inplace=True)

# %%
# Count unique values in the 'MarkDown1' column
features['MarkDown1'].value_counts().unique()

# %%
# Data cleaning for 'MarkDown' columns
from pandas.core.ops import flex_arith_method_FRAME
for i in range(1,6):
  features["MarkDown"+str(i)] = features["MarkDown"+str(i)].apply(lambda x: 0 if x<0 else x )
  features["MarkDown"+str(i)].fillna(value=0,inplace=True)

# %%
features.info()
features.head()

# %%
# Merge the 'data' dataframe ith the 'stores' and 'features'
data = pd.merge(data,stores,on='Store',how='left')
data = pd.merge(data,features,on=['Store','Date'],how='left')

data.head()

# %%
#convert 'date' column to datetime format, sort the dataframe based on 'date', set the 'date' as index
data['Date'] = pd.to_datetime(data['Date'],errors='coerce')
data.sort_values(by=['Date'],inplace=True)
data.set_index(data.Date, inplace=True)
data.head()

# %%
#check whether the column IsHoliday_x and IsHoliday_y are same or not
data['IsHoliday_x'].isin(data['IsHoliday_y']).all()

# %%
#Since this two columns are same so drop any one column and make another column as IsHoliday
data.drop(columns='IsHoliday_x',inplace=True)
data.rename(columns={"IsHoliday_y" : "IsHoliday"}, inplace=True)
data.info()

# %%
#exctracting the date values
data.head()
data['Year'] = data['Date'].dt.year
data['Month'] = data['Date'].dt.month
data['Week'] = data['Date'].dt.week
data.head()

# %%
#Outlier Detection and Abnormalities
#group the 'data' dataframe by 'Store' and 'Dept' columns and calculates various statistical measures (maximum, minimum, mean, median, and standard deviation) of the 'Weekly_Sales' column for each group.

agg_data = data.groupby(['Store', 'Dept']).Weekly_Sales.agg(['max', 'min', 'mean', 'median', 'std']).reset_index()
agg_data.head()

# %%
agg_data.isnull().sum()

# %%
#merge 'data' with 'agg_data' on 'Store' and 'Dept' using a left join, resulting in 'store_data'.
store_data = pd.merge(left=data,right=agg_data,on=['Store', 'Dept'],how ='left')
store_data.head(2)

# %%
#drop rows with missing values from 'store_data' and assigns the modified DataFrame back to 'data'
store_data.dropna(inplace=True)
data = store_data.copy()

# %%
#Converts the 'Date' column in the 'data' DataFrame to datetime format and sort the dataframe based on 'date' column
data['Date'] = pd.to_datetime(data['Date'],errors='coerce')
data.sort_values(by=['Date'],inplace=True)
data.set_index(data.Date, inplace=True)
data.head()

# %%
#calculate the total markdown value by summing the individual markdown columns
data['Total_MarkDown'] = data['MarkDown1']+data['MarkDown2']+data['MarkDown3']+data['MarkDown4']+data['MarkDown5']
data.drop(['MarkDown1','MarkDown2','MarkDown3','MarkDown4','MarkDown5'], axis = 1,inplace=True)
data.head()

# %%
#crate a new dataframe called data_numeric by selecting only the columns specified in the numeric_col list from 'data' dataframe
print(data.shape)
numeric_col = ['Weekly_Sales','Size','Temperature','Fuel_Price','CPI','Unemployment','Total_MarkDown']
data_numeric = data[numeric_col].copy()
print(data_numeric.head())

# %%
#filter the data
data = data[(np.abs(stats.zscore(data_numeric)) < 2.5).all(axis = 1)]
data.shape

# %%
data=data[data['Weekly_Sales']>=0]
data.shape

# %%
data['IsHoliday'] = data['IsHoliday'].astype('int')
data.head()

# %%
#Average Monthly Sales
plt.figure(figsize=(14,8))
sns.barplot(x='Month',y='Weekly_Sales',data=data)
plt.ylabel('Sales',fontsize=14)
plt.xlabel('Months',fontsize=14)
plt.title('Average Monthly Sales',fontsize=16)
plt.savefig('avg_monthly_sales.png')
plt.grid()

# %%
# Monthly Sales for Each Year
data_monthly = pd.crosstab(data["Year"], data["Month"], values=data["Weekly_Sales"],aggfunc='sum')
data_monthly

# %%
#creates a grid of subplots to display the monthly sales for each year. Line graphs are plotted for each month of each year using the 'data_monthly' dataframe.
fig, axes = plt.subplots(3,4,figsize=(16,8))
plt.suptitle('Monthly Sales for each Year', fontsize=18)
k=1
for i in range(3):
    for j in range(4):
      sns.lineplot(ax=axes[i,j],data=data_monthly[k])
      plt.subplots_adjust(wspace=0.4,hspace=0.32)
      plt.ylabel(k,fontsize=12)
      plt.xlabel('Years',fontsize=12)
      k+=1

plt.savefig('monthly_sales_every_year.png')
plt.show()

# %%
# Holiday Distribution
plt.figure(figsize=(8,8))
plt.pie(data['IsHoliday'].value_counts(),labels=['No Holiday','Holiday'],autopct='%0.2f%%')
plt.title("Pie chart distribution",fontsize=14)
plt.legend()
plt.savefig('holiday_distribution.png')
plt.show()

# %%
#One hot encoding
cat_col = ['Store','Dept','Type']
data_cat = data[cat_col].copy()
data_cat.head()

# %%
data_cat = pd.get_dummies(data_cat,columns=cat_col)
data_cat.head()

# %%
print(data.shape)
data = pd.concat([data, data_cat],axis=1)
print(data.shape)

# %%
data.drop(columns=cat_col,inplace=True)
data.drop(columns=['Date'],inplace=True)
data.shape

# %%
#performs min-max normalization on specified numerical columns of the dataframe
num_col = ['Weekly_Sales','Size','Temperature','Fuel_Price','CPI','Unemployment','Total_MarkDown','max','min','mean','median','std']

minmax_scale = MinMaxScaler(feature_range=(0, 1))
def normalization(df,col):
  for i in col:
    arr = df[i]
    arr = np.array(arr)
    df[i] = minmax_scale.fit_transform(arr.reshape(len(arr),1))
  return df

# %%
print(data.head())
data = normalization(data.copy(),num_col)
data.head()

# %%
# Finding Correlation between features
# generate a heatmap to visualize the correlation matrix between the selected numerical features in the dataset
plt.figure(figsize=(15,8))
corr = data[num_col].corr()
sns.heatmap(corr,vmax=1.0,annot=True)
plt.title('Correlation Matrix',fontsize=16)
plt.savefig('correlation_matrix.png')
plt.show()

# %%
# Feature Elimination
feature_col = data.columns.difference(['Weekly_Sales'])
feature_col

# %%
#train a random forest regressor model with 23 decision trees on the selected features and target variable.
radm_clf = RandomForestRegressor(oob_score=True,n_estimators=23)
radm_clf.fit(data[feature_col], data['Weekly_Sales'])

# %%
#computes the feature importance using a random forest regressor and creates a dataframe to store the rankings
indices = np.argsort(radm_clf.feature_importances_)[::-1]
feature_rank = pd.DataFrame(columns = ['rank', 'feature', 'importance'])

for f in range(data[feature_col].shape[1]):
    feature_rank.loc[f] = [f+1,
                           data[feature_col].columns[indices[f]],
                           radm_clf.feature_importances_[indices[f]]]

feature_rank

# %%
#selects the top 25 features from the feature_rank DataFrame and stores them in a list
x=feature_rank.loc[0:25,['feature']]
x=x['feature'].tolist()
print(x)

# %%
X = data[x]
Y = data['Weekly_Sales']

# %%
data = pd.concat([X,Y],axis=1)
data.head()

# %%
#building the model

#Splitting data into train and test data
X = data.drop(['Weekly_Sales'],axis=1)
Y = data.Weekly_Sales

# %%
#Split the data into training and testing sets
X_train,X_test,y_train,y_test = train_test_split(X,Y,test_size=0.20, random_state=50)

# %%
rf = RandomForestRegressor()
rf.fit(X_train, y_train)

# %%
y_pred = rf.predict(X_test)

# %%
print("MAE" , metrics.mean_absolute_error(y_test, y_pred))
print("MSE" , metrics.mean_squared_error(y_test, y_pred))
print("RMSE" , np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
print("R2" , metrics.explained_variance_score(y_test, y_pred))

# %%
rf_df = pd.DataFrame({'Actual': y_test, 'Predicted': y_pred})
rf_df.head()

# %%
# generate a plot comparing the predicted values with the actual values for a subset of the test data
plt.figure(figsize=(15,8))
plt.title('Comparison between actual and predicted values',fontsize=16)
plt.plot(rf.predict(X_test[:100]), label="prediction", linewidth=3.0,color='blue')
plt.plot(y_test[:100].values, label="real_values", linewidth=3.0,color='red')
plt.legend(loc="best")
plt.show()


import logging
import threading
import time
import sys

from metro.data import Data
from renderers.main import MainRenderer

logger = logging.getLogger("busses")

try:
    from rgbmatrix import RGBMatrix, __version__
    emulated = False
except ImportError:
    from RGBMatrixEmulator import RGBMatrix, version
    emulated = True

"""
The data object has methods for saving the data

The MainRenderer handles creating the matrix from Data
    it does this by calling a render method, busses.render_busses

The __render_xxx functions update Data every N seconds, which MainRenderer
   receives via reference

"""

def main(matrix):
    logger.info("(%sx%s)", matrix.width, matrix.height)

    if emulated:
        logger.debug("rgbmatrix not installed, falling back to emulator!")
        logger.debug("Using RGBMatrixEmulator version %s", version.__version__)
    else:
        logger.debug("Using rgbmatrix version %s", __version__)

    data = Data()

    render = threading.Thread(target=__render_main, args=[matrix, data], name="render_thread", daemon=True)
    time.sleep(1)
    render.start()

    __refresh_busses()

def __refresh_busses(render_thread, data):
    logger.debug("Main has selected the busses to refresh")
    while render_thread.is_alive():
        time.sleep(30)
        data.update_stop_statuses()

def __render_main(matrix, data):
    MainRenderer(matrix, data).render()

if __name__ == "__main__":
    matrix = RGBMatrix()
    try:
        main(matrix)
    except:
        logger.exception("Untrapped error in main!")
        sys.exit(1)
    finally:
        matrix.Clear()


from airflow.models import DAG
from airflow.contrib.operators.spark_submit_operator import SparkSubmitOperator
from datetime import datetime

default_args = {
    'owner': 'airflow',
    'start_date': datetime(2019, 1, 1)
}

dag = DAG('stackoverflow_stats', default_args=default_args,
          schedule_interval='@daily')

SparkSubmitOperator(
    task_id='get-stats',
    application="/usr/local/airflow/jobs/stats.py",
    dag=dag,
    run_as_user='airflow',
    application_args=['--date', '{{ ds }}'],
    name='Stats DAG for {{ ds }}',
    num_executors=2,
    executor_memory='2g'
)


from django.db.models.signals import pre_delete, pre_save
from django.dispatch import receiver

from wingtel.usage.models import DataUsageRecord, UsageRecord, VoiceUsageRecord
from wingtel.usage.services import (
    CreateUpdateUsageRecordService,
    DeleteUsageRecordService,
)

@receiver(pre_save, sender=DataUsageRecord, dispatch_uid="data_usage_save_handler")
def date_usage_handler(instance, **kwargs):
    service = CreateUpdateUsageRecordService(instance, UsageRecord.USAGE_TYPES.data)
    service.aggregate_object()

@receiver(pre_save, sender=VoiceUsageRecord, dispatch_uid="voice_usage_save_handler")
def voice_usage_handler(instance, **kwargs):
    service = CreateUpdateUsageRecordService(instance, UsageRecord.USAGE_TYPES.voice)
    service.aggregate_object()

@receiver(pre_delete, sender=DataUsageRecord, dispatch_uid="data_usage_delete_handler")
def date_usage_delete_handler(instance, **kwargs):
    service = DeleteUsageRecordService(instance, UsageRecord.USAGE_TYPES.data)
    service.modify_aggregated_object()

@receiver(pre_delete, sender=VoiceUsageRecord, dispatch_uid="voice_usage_delete_handler")
def voice_usage_delete_handler(instance, **kwargs):
    service = DeleteUsageRecordService(instance, UsageRecord.USAGE_TYPES.voice)
    service.modify_aggregated_object()


import multiprocessing as mp

def returnable_standard_worker(f):
    '''
    A decorator that adds the outputs to a return object. This is specified in the process decorator.
    :param f:
    :return:
    '''
    def wrapper(q, lock, return_object):
        while True:
            _args = q.get()
            try:
                args, kwargs = _args
            except Exception as e:
                args = _args
                if args is not None:
                    raise TypeError
            if args is not None:
                rv = f(*args, **kwargs)
                return_object.append(rv)
            else:
                # print(mp.current_process().pid, ' exiting')
                break
    return wrapper


from bs4 import BeautifulSoup
import requests

URL = 'https://web.archive.org/web/20200518073855/https://www.empireonline.com/movies/features/best-movies-2/'

response = requests.get(URL)
movies_article = response.text

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

title_tags = soup.find_all(name='h3', class_='title')

movies = [tag.getText() for tag in title_tags]
movies = movies[::-1] # Reverse the order of the list

with open('movies.txt', 'w', encoding='utf8') as f:
    f.write('\n'.join(movies))


import hashlib

def jacobi_symbol(a, b):
    """
    Calculates the Jacobi symbol of a with respect to b.
    Assumes that b is an odd positive integer.
    """
    assert b % 2 == 1 and b > 0, "b must be an odd positive integer"

    result = 1
    while a != 0:
        while a % 2 == 0:
            a //= 2
            if b % 8 in [3, 5]:
                result = -result
        a, b = b, a
        if a % 4 == b % 4 == 3:
            result = -result
        a %= b
    if b == 1:
        return result
    else:
        return 0

def hashIdentity(identityString, MPK):
    hashNum = int(hashlib.sha256(
        identityString.encode('utf-8')).hexdigest(), 16) % MPK
    jacobiNumber = jacobi_symbol(hashNum % MPK, MPK)
    while (jacobiNumber != 1):
        hashNum = int(hashlib.sha256(
            str(hashNum).encode('utf-8')).hexdigest(), 16) % MPK
        jacobiNumber = jacobi_symbol(hashNum, MPK)
    return hashNum

def mod_inverse(a, m):
    """
    Calculates the modular inverse of a modulo m.

    Parameters:
    a (int): The number to calculate the inverse of.
    m (int): The modulus.

    Returns:
    The modular inverse of a modulo m, or None if it does not exist.
    """
    # Check if gcd(a, m) = 1, otherwise the inverse does not exist
    gcd, x, y = extended_euclidean_algorithm(a, m)
    if gcd != 1:
        return None

    # Calculate the inverse using the extended Euclidean algorithm
    return (x % m + m) % m

def extended_euclidean_algorithm(a, b):
    """
    Calculates the greatest common divisor (gcd) of a and b, as well as the
    Bezout coefficients x and y such that ax + by = gcd(a, b).

    Parameters:
    a (int): The first number.
    b (int): The second number.

    Returns:
    A tuple (gcd, x, y) such that ax + by = gcd(a, b).
    """
    # Base case
    if a == 0:
        return (b, 0, 1)

    # Recursive call
    gcd, x1, y1 = extended_euclidean_algorithm(b % a, a)

    # Calculate the Bezout coefficients
    x = y1 - (b // a) * x1
    y = x1

    return (gcd, x, y)

def bits_to_key(bit_sequence):
    byte_sequence = bytearray()
    for i in range(0, len(bit_sequence), 8):
        byte = bit_sequence[i:i+8]
        byte_value = int(byte, 2)
        byte_sequence.append(byte_value)

    key = bytes(byte_sequence)
    return key

if __name__ == "__main__":
    P = 4095731
    Q = 5895079
    a = 16999610644564
    b = 19953850441984
    print(jacobi2(a, P))
    print(jacobi2(a, P*Q))
    print(jacobi2(a, Q))
    print(jacobi2(b, P))
    print(jacobi2(b, P*Q))
    print(jacobi2(b, Q))


from setuptools import setup
import pathlib
import re

curdir = pathlib.Path(__file__).parent

README = (curdir / "README.md").read_text()
VERSION = (curdir / "tidyfasta" / "__init__.py").read_text()
version_number=re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", VERSION).group(1)

setup(
    name="tidyfasta",
    version=version_number,
    packages=['tidyfasta', 'tidyfasta.common'],
    url='https://github.com/maxhebditch/tidyfasta',
    license='MIT',
    author='Max Hebditch',
    author_email='max@maxhebditch.co.uk',
    description='Sanitise protein FASTA files / data',
    long_description=README,
    long_description_content_type="text/markdown",
    entry_points={
        "console_scripts": [
            "tidyfasta=tidyfasta.__main__:main",
        ]
    }
)


# 고득점KIT 스택/큐의 주식가격 문제
# https://school.programmers.co.kr/learn/courses/30/lessons/42584

# O(N^2)인데 왜 통과야?
def solution(prices):
    answer = []

    # prices[i]를 prices[i]의 뒤에 위치한 모든 가격들(prices[j])과 비교하여
    # 가격이 떨어지는 경우 break 후 answer에 기간(초) 추가
    for i in range(len(prices)):
        length = 0
        for j in range(i + 1, len(prices)):
            length += 1
            if prices[i] > prices[j]:
                break
        answer.append(length)

    return answer

# O(N) 풀이
def solution(prices):
    stack = []
    answer = [0] * len(prices)

    # prices의 각 가격 조회하면서
    # 직전 가격(stack[-1][1])보다 저렴한 가격인 경우일 때마다
    # 직전 가격의 떨어진 기간 계산
    for i in range(len(prices)):
        # stack이 빈 배열인 경우이거나
        # 조회된 가격이 직전 가격보다 비싼 경우 상승세이므로 while문 실행 X
        while stack != [] and stack[-1][1] > prices[i]:
            past, _ = stack.pop() # 직전 가격 제거
            answer[past] = i - past # past번째 가격의 떨어지지 않은 기간(초) 계산

        stack.append([i, prices[i]]) # 지속적으로 상승되는 경우이므로 stack에 가격 추가

    # 떨어지는 순간 없이 끝까지 상승세인 가격의 기간(초) 계산
    for i, _ in stack:
        answer[i] = len(prices) - 1 - i

    return answer

print(solution([1, 2, 3, 2, 3]))


from ghapi.all import GhApi
from doozerlib.pushd import Dir
from dockerfile_parse import DockerfileParser

class CommentOnPr:
    def __init__(self, distgit_dir: str, token: str):
        self.distgit_dir = distgit_dir
        self.token = token
        self.owner = None
        self.repo = None
        self.commit = None
        self.gh_client = None  # GhApi client
        self.pr_url = None
        self.pr_no = None

    def list_comments(self):
        """
        List the comments in a PR
        """
        # https://docs.github.com/rest/reference/issues#list-issue-comments
        return self.gh_client.issues.list_comments(issue_number=self.pr_no, per_page=100)

    def check_if_comment_exist(self, comment):
        """
        Check if the same comment already exists in the PR
        """
        issue_comments = self.list_comments()
        for issue_comment in issue_comments:
            if issue_comment["body"] == comment:
                return True
        return False

    def post_comment(self, comment):
        """
        Post the comment in the PR if the comment doesn't exist already
        """
        # https://docs.github.com/rest/reference/issues#create-an-issue-comment
        if not self.check_if_comment_exist(comment):
            self.gh_client.issues.create_comment(issue_number=self.pr_no, body=comment)
            return True
        return False

    def set_pr_from_commit(self):
        """
        Get the PR from the merge commit
        """
        # https://docs.github.com/rest/commits/commits#list-pull-requests-associated-with-a-commit
        prs = self.gh_client.repos.list_pull_requests_associated_with_commit(self.commit)
        if len(prs) == 1:
            # self._logger.info(f"PR from merge commit {sha}: {pull_url}")
            self.pr_url = prs[0]["html_url"]
            self.pr_no = prs[0]["number"]
            return
        raise Exception(f"Multiple PRs found for merge commit {self.commit}")

    def set_repo_details(self):
        """
        Get the owner, commit and repo from the dfp label
        """
        with Dir(self.distgit_dir):
            dfp = DockerfileParser(str(Dir.getpath().joinpath('Dockerfile')))

            # eg: "https://github.com/openshift/origin/commit/660e0c785a2c9b1fd5fad33cbcffd77a6d84ccb5"
            source_commit_url = dfp.labels["io.openshift.build.commit.url"]
            url_split = source_commit_url.split("/")
            commit = url_split[-1]  # eg: 660e0c785a2c9b1fd5fad33cbcffd77a6d84ccb5
            repo = url_split[-3]  # eg: origin
            owner = url_split[-4]  # eg: openshift

            self.owner = owner
            self.commit = commit
            self.repo = repo

    def set_github_client(self):
        """
        Set the gh client after the get_source_details function is run
        """
        self.gh_client = GhApi(owner=self.owner, repo=self.repo, token=self.token)


# 32.
# Guess the Number Game
from inputplus import input_int
import random

def ask_restart(count):
    print(f"You got it in {count} guesses! Play again?")
    again = str(input()).upper()
    if again == "N":
        print("Goodbye!")
    elif again == "Y":
        try_loop()
    else:
        return ask_restart(count)

def set_lvl():
    print("Pick a difficulty level (1, 2, or 3):")
    lvl = input()
    if lvl == "1":
        guess = random.randint(1, 10)
        return guess
    elif lvl == "2":
        guess = random.randint(1, 100)
        return guess
    elif lvl == "3":
        guess = random.randint(1, 1000)
        return guess
    else:
        return set_lvl()

def try_loop():
    count = 1
    guess = set_lvl()
    print("I have my number. What's your guess?")
    user_try = input_int()
    while user_try != guess:
        if user_try > guess:
            print("Too high. Guess again.")
            user_try = input_int()
        elif user_try < guess:
            print("Too low. Guess again.")
            user_try = input_int()
        count += 1
    else:
        ask_restart(count)

try_loop()


import sys
input = sys.stdin.readline
for _ in range(int(input())):
    M, N, x, y = map(int, input().split())
    ans = -1
    j = -N + y - x
    for i in range(M):
        j += N
        r, q = divmod(j, M)
        if q == 0:
            ans = M * r + x
            break;
    print(ans)


from ScopeFoundry import HardwareComponent
from collections import OrderedDict
from pipython import GCSDevice, pitools, GCSError, gcserror
from pipython.gcscommands import GCSCommands
from pipython.interfaces.piserial import PISerial
from pipython.gcsmessages import GCSMessages
from pipython import GCSDevice, pitools
import threading
import time
import sys

CONTROLLERNAME = 'C-413.2GA'
STAGES = ['V-524.1AA']  # connect stages to axes
REFMODES = ['FNL']  # reference the connected stages
default_axes = OrderedDict( [(1,'x')] )
# here we use only one axis but more axes can be included...
#self.pidevice=GCSDevice(CONTROLLERNAME)

class PIStage(HardwareComponent):

    def __init__(self, app, debug=False, name=None, axes=default_axes):
        # self.pidevice = GCSDevice(CONTROLLERNAME)
        self.axes = axes

        HardwareComponent.__init__(self, app, debug=debug, name=name)

    def setup(self):

        ax_name = default_axes[1]
        # some settings are related to the number of axes implemented

        self.settings.New("controller", dtype=str, initial="C-413.2GA", ro=True) # PI controller name
        self.settings.New("stage", dtype=str, initial="V-524.1AA", ro=True) # PI stage name
        self.settings.port = self.add_logged_quantity('port', dtype=str, initial='COM5')
        # Note: the M-405.CG stage has a travel range of 0-50mm and the reference switch is at 25mm

        self.settings.New("ref_mode", dtype=str, choices=[ "None", "FNL", "FPL", "FRF"], initial="None", ro=False) # reference mode for the stage
        # None don't reference the stage position
        # FNL reference the stage position by starting a movement to the Negative limit of the stage!
        # FPL reference the stage position by starting a movement to the Positive limit of the stage!
        # FRF reference the stage position by starting a movement to the Reference switch (center) of the stage!

        self.settings.New(ax_name + "_position", dtype=float,
                          unit='mm', si=False, spinbox_decimals=4,
                          ro=True)

        self.settings.New(ax_name + "_target", dtype=float,
                          unit='mm', si=False, spinbox_decimals=4,
                          ro=False)

        self.settings.New(ax_name + "_relative_target_step", dtype=float,
                          unit='mm', si=False, spinbox_decimals=4, initial=0,
                          ro=False)

        self.settings.New(ax_name + '_servo', dtype=bool, ro=False)

        self.settings.New(ax_name + "_velocity", dtype=float, vmin=0, vmax=0.6, ro=False,
                          si=False, spinbox_decimals=3, unit='mm/s')

        self.settings.New(ax_name + "_on_target", dtype=bool, ro=True)

        #===================================================================
        # self.rangemin = self.add_logged_quantity("rangemin", dtype=float, unit='mm', ro=True)
        # self.rangemax = self.add_logged_quantity("rangemax", dtype=float, unit='mm', ro=True)
        #===================================================================

        self.add_operation("move relative", self.move_relative)
        self.add_operation("stop", self.stop)
        self.add_operation("set home", self.set_home)
        self.add_operation("go home", self.go_home)
        self.add_operation("start reference", self.start_reference)

    def connect(self):
            S = self.settings
            self.pidevice = GCSDevice(CONTROLLERNAME)
            """Connect controller via first serial port with 115200 baud."""

            # Choose the interface according to your cabling.

            # self.pidevice.ConnectTCPIP(ipaddress='192.168.90.207')
            self.pidevice.ConnectUSB(serialnum='0119024343')
            # self.pidevice.ConnectRS232(comport=1, baudrate=115200)

            print('connected: {}'.format(self.pidevice.qIDN().strip()))

            # Show the version info which is helpful for PI support when there
            # are any issues.

            if self.pidevice.HasqVER():
                print('version info:\n{}'.format(self.pidevice.qVER().strip()))

            print('initialize connected stages...')
            pitools.startup(self.pidevice, stages=STAGES, refmodes=REFMODES)
            # Now we query the allowed motion range and current position of all
            # connected stages. GCS commands often return an (ordered) dictionary
            # with axes/channels as "keys" and the according values as "values".

            rangemin = self.pidevice.qTMN()
            rangemax = self.pidevice.qTMX()
            curpos = self.pidevice.qPOS()
            print(curpos)

            ax_name=default_axes[1]
            ax_num=1 ##to change if number of stages is >1

            lq = S.get_lq(ax_name + "_position")
            lq.connect_to_hardware(
                read_func=lambda n=ax_num: self.pidevice.qPOS()[str(n)]
            )
            lq.read_from_hardware()

            lq = S.get_lq(ax_name + "_target")
            # move the stage to the specified value
            # Note: the position must be referenced before using this command
            lq.connect_to_hardware(
                read_func=lambda n=ax_num: self.pidevice.qMOV()[str(n)],
                write_func=lambda new_target, n=ax_num: self.pidevice.MOV(n, new_target)
            )
            lq.read_from_hardware()

            lq = S.get_lq(ax_name + "_servo")
            lq.connect_to_hardware(
                read_func=lambda n=ax_num: self.pidevice.qSVO()[str(n)],
                write_func=lambda enable, n=ax_num: self.pidevice.SVO(n, enable)
            )
            lq.read_from_hardware()

            lq = S.get_lq(ax_name + "_on_target")
            lq.connect_to_hardware(
                read_func=lambda n=ax_num: self.pidevice.qONT()[str(n)],
            )
            lq.read_from_hardware()

            lq = S.get_lq(ax_name + "_velocity")
            lq.connect_to_hardware(
                read_func=lambda n=ax_num: self.pidevice.qVEL()[str(n)],
                write_func=lambda new_vel, n=ax_num: self.pidevice.VEL(n, new_vel)
            )
            lq.read_from_hardware()

            self.update_thread_interrupted = False
            self.update_thread = threading.Thread(target=self.update_thread_run)
            self.update_thread.start()

            #             self.pidevice.RON(self.pidevice.axes, False)
    #             self.pidevice.POS(self.pidevice.axes, self.pidevice.qPOS(self.pidevice.axes)['1'])

    # print(self.pidevice.IsMoving(self.pidevice.axes)['1'])

    # ===============================================================================
    #         self.rangemin.hardware_read_func = self.GetRangeMN
    #         self.rangemax.hardware_read_func = self.GetRangeMX
    #
    #         self.rangemin.read_from_hardware()
    #         self.rangemax.read_from_hardware()
    # ===============================================================================

    def disconnect(self):
        for i in self.axes:
            if hasattr(self, 'pidevice'):
                self.pidevice.SVO(i, False) # turn the servo OFF

            self.settings.disconnect_all_from_hardware()

            if hasattr(self, 'update_thread'):
                print("stopping")
                self.update_thread_interrupted = True
                self.update_thread.join(timeout=1.0)
                del self.update_thread

            if hasattr(self, 'pidevice'):
                print("closing")
                self.pidevice.close()
                del self.pidevice

    #===========================================================================
    # def GetRangeMN(self):
    #     return float(list(self.pidevice.qTMN().values())[0])
    # def GetRangeMX(self):
    #     return float(list(self.pidevice.qTMX().values())[0])
    #===========================================================================

    def update_thread_run(self):
        while not self.update_thread_interrupted:
            for ax_num, ax_name in self.axes.items():
                self.settings.get_lq(ax_name + "_position").read_from_hardware()
                self.settings.get_lq(ax_name + "_on_target").read_from_hardware()
            time.sleep(0.050)

    def move_relative(self):
        ''' move relative to the current position of the distance specified with
        the relative_target_step setting.'''
        # can be used even without any reference
        for i in self.axes:
            if hasattr(self, 'pidevice'):
                self.pidevice.MVR(i, self.settings.x_relative_target_step.val)

    def stop(self):
        '''stop any movement (including those for reference)'''

        if hasattr(self, 'pidevice'):
            try:
                self.pidevice.STP()
            except:
                pass # ignores any error

    def set_home(self):
        '''set the current position as 0'''

        if hasattr(self, 'pidevice'):
            for i in self.axes:
                self.pidevice.DFH(i)

    def go_home(self):
        '''start a movement to 0'''
        # Note: the position must be referenced before using this command
        if hasattr(self, 'pidevice'):
            for i in self.axes:
                print("succhia cazzi")
                self.pidevice.GOH(i)

    def start_reference(self):
        '''start a reference move with the mode specified with the ref_mode setting'''

        if hasattr(self, 'pidevice'):
            for i in self.axes:
                if self.settings.ref_mode.val == "FNL":
                    self.pidevice.FNL(i)
                if self.settings.ref_mode.val == "FPL":
                    self.pidevice.FPL(i)
                if self.settings.ref_mode.val == "FRF":
                    self.pidevice.FRF(i)
                if self.settings.ref_mode.val == "None":
                    # *Dangerous*
                    # reference the stage by assuming that the current position is at the center of the stage
                    self.pidevice.RON(i, False)
                    self.pidevice.POS(i, 25)
                    self.set_home()


import argparse
import os
import pickle
import re
from collections import defaultdict

import numpy as np

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("filename", nargs="+")
    args = parser.parse_args()

    datasets = defaultdict(list)
    for filename in args.filename:
        match = re.match(r"(.*)-(simcse|bert).pkl", filename)
        if match:
            datasets[match.group(1)].append(filename)

    for dataset_name, dataset_filenames in datasets.items():
        dataset_dict = {"text_embeddings": [], "text_columns": []}

        for filename in sorted(dataset_filenames):
            with open(filename, "rb") as fp:
                current_dict = pickle.load(fp)
            text_embeddings = current_dict.pop("text_embeddings")
            text_columns = current_dict.pop("text_columns")

            dataset_dict.update(current_dict)
            dataset_dict["text_embeddings"].append(text_embeddings)
            dataset_dict["text_columns"] += text_columns

            os.remove(filename)

        dataset_dict["text_embeddings"] = np.concatenate(
            dataset_dict["text_embeddings"], axis=1
        )
        with open(f"{dataset_name}.pkl", "wb") as fp:
            pickle.dump(dataset_dict, fp)


"""
Given numBottles full water bottles, you can exchange numExchange
empty water bottles for one full water bottle.

The operation of drinking a full water bottle turns it into an
empty bottle.

Return the maximum number of water bottles you can drink.

"""
class Solution(object):
    def numWaterBottles(self, numBottles, numExchange):
        """
        :type numBottles: int
        :type numExchange: int
        :rtype: int
        """
        empty_bottles = 0
        result = 0
        while numBottles > 0:
            # add current number of full bottles
            result += numBottles
            # add on empty bottles
            numBottles += empty_bottles
            empty_bottles = 0
            # get remaining bottles that are empty
            remainder = numBottles % numExchange
            # exchange bottles
            numBottles //= numExchange
            # add remaining empty bottles to carry over
            # to next iteration
            empty_bottles += remainder
        return result


import http.client
import hmac
import hashlib
import json
import time
from urllib.parse import urlencode
from typing import Union, Dict

class CBAuth:
    """
    Singleton class for Coinbase authentication.
    """

    _instance = None  # Class attribute to hold the singleton instance

    def __new__(cls):
        """
        Override the __new__ method to control the object creation process.
        :return: A single instance of CBAuth
        """
        if cls._instance is None:
            print("Creating CBAuth instance")
            cls._instance = super(CBAuth, cls).__new__(cls)
            cls._instance.init()
        return cls._instance

    def init(self):
        """
        Initialize the CBAuth instance with API credentials.
        """
        self.key = None
        self.secret = None

    def set_credentials(self, api_key, api_secret):
        """
        Update the API credentials used for authentication.
        :param api_key: The API Key for Coinbase API
        :param api_secret: The API Secret for Coinbase API
        """
        self.key = api_key
        self.secret = api_secret

    def __call__(self, method: str, path: str, body: Union[Dict, str] = '', params: Dict[str, str] = None) -> Dict:
        """
        Prepare and send an authenticated request to the Coinbase API.

        :param method: HTTP method (e.g., 'GET', 'POST')
        :param path: API endpoint path
        :param body: Request payload
        :param params: URL parameters
        :return: Response from the Coinbase API as a dictionary
        """
        path = self.add_query_params(path, params)
        body_encoded = self.prepare_body(body)
        headers = self.create_headers(method, path, body)
        return self.send_request(method, path, body_encoded, headers)

    def add_query_params(self, path, params):
        if params:
            query_params = urlencode(params)
            path = f'{path}?{query_params}'
        return path

    def prepare_body(self, body):
        return json.dumps(body).encode('utf-8') if body else b''

    def create_headers(self, method, path, body):
        timestamp = str(int(time.time()))
        message = timestamp + method.upper() + \
            path.split('?')[0] + (json.dumps(body) if body else '')
        signature = hmac.new(self.secret.encode(
            'utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()

        return {
            "Content-Type": "application/json",
            "CB-ACCESS-KEY": self.key,
            "CB-ACCESS-SIGN": signature,
            "CB-ACCESS-TIMESTAMP": timestamp
        }

    def send_request(self, method, path, body_encoded, headers):
        conn = http.client.HTTPSConnection("api.coinbase.com")
        try:
            conn.request(method, path, body_encoded, headers)
            res = conn.getresponse()
            data = res.read()

            if res.status == 401:
                print("Error: Unauthorized. Please check your API key and secret.")
                return None

            response_data = json.loads(data.decode("utf-8"))
            if 'error_details' in response_data and response_data['error_details'] == 'missing required scopes':
                print(
                    "Error: Missing Required Scopes. Please update your API Keys to include more permissions.")
                return None

            return response_data
        except json.JSONDecodeError:
            print("Error: Unable to decode JSON response. Raw response data:", data)
            return None
        finally:
            conn.close()


# -*- coding: utf-8 -*-
"""
Created on Thu May 14 23:50:46 2020

@author: user
"""

# A program that reads a sequence of numbers
# and counts how many numbers are even and how many are odd.
# The program terminates when zero is entered.

odd_numbers = 0
even_numbers = 0

number = int(input("enter a number or type 0 to stop: "))

#alternatif
#while number != 0:
while number:
    #if number % 2 == 1:
    if number % 2:
        odd_numbers += 1
    else:
        even_numbers +=1
    number = int(input("enter a number or type 0 to stop: "))

print("Odd numbers are", odd_numbers)
print("Even numbers are", even_numbers)

print("")

#using counter
counter = 5
while counter != 0:
    print("Inside the loop.", counter)
    counter -= 1
print("Outside the loop.", counter)


from asset_browser_utilities.core.cache.tool import get_current_operator_properties
from asset_browser_utilities.core.library.prop import LibraryType
from asset_browser_utilities.core.log.logger import Logger
from bpy.types import Operator, PropertyGroup
from bpy.props import PointerProperty, StringProperty

from asset_browser_utilities.core.operator.tool import BatchExecute, BatchFolderOperator
from asset_browser_utilities.core.filter.catalog import FilterCatalog
from asset_browser_utilities.module.catalog.tool import CatalogsHelper

class CatalogMoveFromAToBBatchExecute(BatchExecute):
    def execute_one_file_and_the_next_when_finished(self):
        op_props = get_current_operator_properties()
        helper = CatalogsHelper()
        uuid_from, tree_from, name_from = helper.catalog_info_from_uuid(op_props.catalog_from.catalog)
        uuid_to, tree_to, name_to = helper.catalog_info_from_uuid(op_props.catalog_to.catalog)
        if uuid_from != uuid_to:
            helper.ensure_catalog_exists(uuid_to, tree_to, name_to)
            for asset in self.assets:
                asset_data = asset.asset_data
                if asset_data.catalog_id == uuid_from:
                    asset_data.catalog_id = uuid_to
                    Logger.display(f"{repr(asset)} moved from catalog '{name_from}' to catalog '{name_to}'")
            self.save_file()
        self.execute_next_file()

class CatalogMoveFromAToBOperatorProperties(PropertyGroup):
    catalog_from: PointerProperty(type=FilterCatalog)
    catalog_to: PointerProperty(type=FilterCatalog)

    def init(self, from_current_file=False):
        for catalog in (self.catalog_from, self.catalog_to):
            catalog.active = True
            catalog.from_current_file = from_current_file
            catalog.catalog_filepath = str(CatalogsHelper().catalog_filepath)

    def draw(self, layout, context=None):
        box = layout.box()
        box.label(text="Move Assets :")
        for catalog, label in zip((self.catalog_from, self.catalog_to), ("FROM", "TO")):
            split = box.split(factor=0.35)
            split.label(text=label)
            split.prop(catalog, catalog.catalog_attribute, icon="ASSET_MANAGER", text="")

class ABU_OT_catalog_move_from_a_to_b(Operator, BatchFolderOperator):
    bl_idname = "abu.catalog_move_from_a_to_b"
    bl_label = "Batch Move Assets From One Catalog to Another One"

    operator_settings: PointerProperty(type=CatalogMoveFromAToBOperatorProperties)
    logic_class = CatalogMoveFromAToBBatchExecute

    def invoke(self, context, event):
        return self._invoke(context)


# -*- coding: utf-8 -*-
import sys
import argparse
import math
# http://braeunig.us/space/index.htm
# http://braeunig.us/space/comb.htm

# Gas constant (J/(K*kmol))
R = 8314.4598

def exhaustVelocity(chamberP, chamberT, k, molarM, exitP):
	v = math.sqrt((2*k/(k-1))*(R*chamberT/molarM)*(1.0-(exitP/chamberP)**((k-1.0)/k)))
	return v

def throatP(chamberP, k):
	p = chamberP*((1.0 + (k-1)/2.0)**(-k/(k-1.0)))
	return p

def throatT(chamberT, k):
	t = chamberT/(1.0 + (k-1.0)/2.0)
	return t

def MachNum(chamberP, k, exitP):
	Mn = math.sqrt( (2.0/(k-1.0))*((chamberP/exitP)**((k-1.0)/k) - 1.0) )
	return Mn

def exitArea(chamberP, chamberT, k, molarM, exitP, A_t):
	Mn = MachNum(chamberP, k, exitP)
	#print ("MachN: " + str(Mn))
	A_e = (A_t/Mn)*( (1.0 + (k-1.0)/2.0 * Mn*Mn)/((k+1.0)/2.0) )**((k+1.0)/(2.0*(k-1.0)))
	return A_e

def main():

	# Parse commandline
	cmdLine = argparse.ArgumentParser()

	cmdLine.add_argument("massFlow", type=float, help="Thrust chamber mass flow")
	cmdLine.add_argument("chamberPress", type=float, help="Thrust chamber pressure")
	cmdLine.add_argument("temp", type=float, help="Thrust chamber temperature")
	cmdLine.add_argument("k", type=float, help="Spesific heat ratio")
	cmdLine.add_argument("areaRatio", type=float, help="Nozzle area ratio")
	cmdLine.add_argument("molarMass", type=float, help="Exhaust molar mass")

	args = cmdLine.parse_args()

	lowerP = 0.0
	# exit pressure can't be higher than throat pressure
	upperP = throatP(args.chamberPress, args.k)
	testP = (lowerP+upperP)/2.0
	#print ("starting p: " + str(testP))

	# Throat parameters
	P_t = throatP(args.chamberPress, args.k)
	T_t = throatT(args.temp, args.k)
	A_t = args.massFlow/P_t * math.sqrt(R*T_t/(args.molarMass*args.k))

	# Loop to find correct pressure on nozzle-exit
	iters = 0;
	while (1):
		iters = iters + 1
		v_e = exhaustVelocity(args.chamberPress, args.temp, args.k, args.molarMass, testP)
		A_e = exitArea(args.chamberPress, args.temp, args.k, args.molarMass, testP, A_t)

		# Do a binary search to find correct exit-pressure for the given area ratio
		if (A_e > A_t*args.areaRatio):
			lowerP = testP
			testP = (upperP+testP)/2.0
		else:
			upperP = testP
			testP = (lowerP+testP)/2.0

		#print ("New p: " + str(testP) + " area ratio with prev iter: " + str(A_e/A_t))

		# Exit loop when acceptable precision is reached
		if (math.fabs(args.areaRatio-(A_e/A_t)) < 0.01):
			break

	#print ("Iterations: " + str(iters))

	thrust_m = v_e * args.massFlow
	thrust_s = thrust_m + (testP - 101325)*A_e
	thrust_v = thrust_m + testP*A_e

	# 80% bell-nozzle length
	# Length percentage is relative to 15 deg half-angle conical nozzle
	aRatio = A_e/A_t
	n_radius = math.sqrt(A_e/math.pi)
	n_length = n_radius/math.tan(math.radians(15))*(math.sqrt(aRatio)-1)/math.sqrt(aRatio)
	n_length = n_length*0.8

	print ("Exhaust velocity: {0:.1f}".format(v_e) + " m/s")
	print ("Throat: {0:.6f}".format(A_t) + " m² {0:.0f}".format(T_t) + " K")
	print ("Exit: {0:.6f}".format(A_e) + " m² Area ratio: {0:.3f}".format(aRatio))
	print ("Pressure at nozzle exit: {0:.0f}".format(testP) + " Pa")
	print ("\nThrust")
	print (" Sea level: {0:.0f}".format(thrust_s) + " N")
	print (" Vacuum:    {0:.0f}".format(thrust_v) + " N")

	print ("\nBell-nozzle 80%:\n Length:   {0:.3f}".format(n_length) + " m")
	print (" Diameter: {0:.3f}".format(n_radius*2) + " m")

if __name__ == '__main__':
        try:
                main()
        except KeyboardInterrupt:
                sys.exit(1)


import json
import os

import data
from constants import SC_EMOJI, UP_ARROW_EMOJI

items = []

class Item():
    def __init__(self, item_dict):
        self.name = item_dict["name"]
        self.cost = item_dict.get("cost", None)
        self.emoji = item_dict["emoji"]
        self.description = item_dict["description"]
        self.aliases = item_dict["aliases"]
        self.shop_item = item_dict.get("shop_item", False)
        self.type = item_dict.get("type", "standard")
        self.boost_bonus = item_dict.get("boost", {}).get("bonus", None)
        self.boost_max_bonus = item_dict.get("boost", {}).get("max_bonus", None)
        self.boost_category = item_dict.get("boost", {}).get("category", None)

    def can_be_in_shop(self):
        return self.shop_item

    def has_value(self):
        return not self.cost is None

    def is_booster(self):
        return not self.boost_bonus is None

    def get_shop_string(self):
        # Brackets combine the multiple lines into one
        return (
            f'{self.emoji} **{self.name}** ─ {SC_EMOJI}{self.cost}' +
            (f' ─ {UP_ARROW_EMOJI}**{self.boost_category.capitalize()}**' if self.boost_category else '') +
            f'\n{self.description}\n\n'
        )

def import_items():
    global items
    items = []
    items_data = data.get_items()
    for i in items_data:
        items.append(Item(i))

def get_by_name(name):
    global items
    for i in items:
        if name.lower() == i.name.lower() or name.lower() in i.aliases:
            return i
    return None

def get_player_boost(player_id, *categories):
    """Get a player boost for a specific category.
    An output of 0 means a 0% bonus, 1 means an 100% boost.
    """
    boost = 0
    for name, amount in data.get_data(player_id, "inv", default_val={}).items():
        item = get_by_name(name)
        if item.is_booster():
            if item.boost_category in categories:
                boost += get_boost_from_item_amount(item, amount)
    return boost

def get_boost_from_item_amount(item, amount):
    """Get the amount of boost a player gets from a specific item."""
    if not item.is_booster():
        return 0
    boost = min(amount * item.boost_bonus, item.boost_max_bonus)
    return boost

def get_player_boost_from_item(player_id, item):
    """Get the amount of boost a player gets from a specific item."""
    if not item.is_booster():
        return 0
    amount = data.get_data(player_id, "inv", item.name, default_val=0)
    boost = min(amount * item.boost_bonus, item.boost_max_bonus)
    return boost


'''
@Description:
@Author: Jack Huang
@Github: https://github.com/HuangJiaLian
@Date: 2019-10-10 21:57:24
@LastEditors: Jack Huang
@LastEditTime: 2019-10-10 22:34:46
'''

import pandas as pd
import os
import matplotlib.pyplot as plt

class logger:
    def __init__(self, logger_name, logger_path, col_names, restore=False):
        self.logger_name = logger_name
        if not os.path.exists(logger_path):
            os.makedirs(logger_path)
        self.logger_path = logger_path
        self.col_names = col_names
        self.save_path = os.path.join(self.logger_path, self.logger_name + '.csv')
        if restore:
            self.dataLogs = self.load()
        else:
            self.dataLogs = pd.DataFrame(columns=col_names)

    def add_row_data(self, one_row, saveFlag=False):
        last_index = len(self.dataLogs)
        self.dataLogs.loc[last_index] = one_row
        if saveFlag:
            self.save()

    def save(self):
        self.dataLogs.to_csv(self.save_path)

    def load(self):
        dataLogs_Saved = pd.read_csv(self.save_path)
        return dataLogs_Saved

    def plotToFile(self, title, showFlag = False):
        plt.clf()
        plt.title(title)
        ax = plt.gca()
        self.dataLogs.plot(kind='line', x = self.col_names[0], y=self.col_names[1], ax=ax)
        self.dataLogs.plot(kind='line', x = self.col_names[0], y=self.col_names[2], ax=ax)
        self.dataLogs.plot(kind='line', x = self.col_names[0], y=self.col_names[3], ax=ax)
        self.dataLogs.plot(kind='line', x = self.col_names[0], y=self.col_names[4], ax=ax)
        plt.savefig(os.path.join(self.logger_path, self.logger_name + '.png'))
        if showFlag:
            plt.show()

    def close(self):
        self.save()


from django.shortcuts import render
from django.views.generic import TemplateView
from gandhara.models import Users

class IndexView(TemplateView):
    template_name = 'index.html'

    def get_context_data(self):
        ctxt = super().get_context_data()
        ctxt['users'] = Users.objects.all()
        return ctxt

class AboutView(TemplateView):
    template_name = 'about.html'

    def get_context_data(self):
        ctxt = super().get_context_data()
        ctxt['num_services'] = 123456790
        ctxt['skills'] = [
            "Python",
            "C++",
            "JavaScript",
            "PHP",
        ]
        return ctxt


from fastapi import FastAPI,Query
from fastapi.middleware.cors import CORSMiddleware
import nest_asyncio
from pyngrok import ngrok
import uvicorn

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)

# Default Values
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
    return {"skip" : skip, "limit" : limit}

# Optional Parameters
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str = None):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}

# Accessing Boolean multiple values (i.e. yes,no,on,off,true,false,0,1)
@app.get("/getitem/{item_id}")
async def read_item(item_id: str, q: str = None, short: bool = False):
    item = {"item_id": item_id}
    if q:
        item.update({"q": q})
    if not short:
        item.update(
            {"description": "This is an amazing item that has a long description"}
        )
    return item

#Multiple path and query parameters
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
    user_id: int, item_id: str, q: str = None, short: bool = False
):
    item = {"item_id": item_id, "owner_id": user_id}
    if q:
        item.update({"q": q})
    if not short:
        item.update(
            {"description": "This is an amazing item that has a long description"}
        )
    return item

ngrok_tunnel = ngrok.connect(8000)
print('Public URL:', ngrok_tunnel.public_url)
nest_asyncio.apply()
uvicorn.run(app, port=8000)


import re
import datetime

from rest_framework.response import Response
"""Expresiones Regulares"""
patron_id_cliente=re.compile(r"C[0-9]{1,}")
patron_dir=re.compile(r"[A-Za-z]+ [0-9]+")
patron_nombre=re.compile(r"[A-Za-z]+")
patron_id_sol=re.compile(r"S[0-9]{1,}")
patron_legajo=re.compile(r"CH[0-9]{1,}")
patron_remito=re.compile(r"R[0-9]{1,}")
"""Fin ER"""

def validate_crear_solicitud(body, id_cliente, opcion):
    dir_body=body['direccion']
    nombre=body['nombre_2']
    exito=False
    if re.fullmatch(patron_dir, dir_body) and re.fullmatch(patron_id_cliente, id_cliente) \
        and opcion in (0,1) and re.fullmatch(patron_nombre, nombre):
        exito=True
    return exito

def validate_agregar_bultos(body, id_solicitud):
    tipos_validos=('CH', 'M', 'G')
    tipo_bulto=body['tipo']
    exito=False
    if re.fullmatch(patron_id_sol, id_solicitud) and tipo_bulto in tipos_validos:
        exito=True
    return exito

def validate_remitos_chofer_estado(body):
    legajo=body['legajo']
    estado=body['estado']
    estados_validos=['asignado', 'en_circulacion', 'en_viaje', 'rendido', 'segunda_entrega',\
        'pendiente', 'pagado']
    exito=False
    if re.fullmatch(patron_legajo, legajo) and estado in estados_validos:
        exito=True
    return exito

def validate_asociar_sol_remito(body):
    medios_validos=['OR', 'DES', 'CC']
    nro_remito=body['remito']
    id_solicitud=body['sol']
    medio_pago=body['m_pago']
    exito=False
    if re.fullmatch(patron_remito, nro_remito) and re.fullmatch(patron_id_sol, id_solicitud)\
        and medio_pago in medios_validos:
        exito=True
    return exito

def validate_viaje_fecha(fecha):
    format="%Y-%m-%d"
    exito=True
    try:
        datetime.datetime.strptime(fecha, format)
    except ValueError:
        exito=False
    return exito

def validate_asoc_remito_viaje(body):
    remitos=body['remitos']
    exito=True
    index=0
    while exito is True and index < len(remitos):
        if re.fullmatch(patron_remito, remitos[index]) is None:
            exito=False
        index+=1

    return exito


# error handling with loops
# users can end up entering all kind of weirdness

while True:
    try:
        user_input = input("Please enter a whole integer ")
        number = int(user_input) # possible error here jumps to except then
        print("Cool you entered", number)
        # we only get to this break if we did not get any errors in previous
        # 3 lines
        break
    except ValueError: # it is recommend that specific Errors are handled
        print(f"Sorry {user_input} is not a valid integer!")
    # we could add other error types here

# now we can do whatever we want with our number - it is guaranteed to be integer
print("normal work with integer", number)

# we can nest our loops

total = 0
while total < 1_000_000:
    # entering inner loop for input validation
    while True:
        try:
            user_input = input("Please enter a whole integer ")
            number = int(user_input) # possible error here jumps to except then
            # we could do it for floats as well, or some other types
            # we could ask for more inputs here as well
            print("Cool you entered", number)
            # we only get to this break if we did not get any errors in previous
            # 3 lines
            break
        except ValueError: # it is recommend that specific Errors are handled
            print(f"Sorry {user_input} is not a valid integer!")
    # we are out of inner loop with guaranteed integer
    total += number
    print("Total is ", total)
    ready_to_quit = input("Do you want to quit? (y/n)")
    if ready_to_quit == "y":
        print("I am tired ready to go home")
        break
    # now we go back to start of outer loop here

print("After work our total collected is", total)


from fastapi import APIRouter, UploadFile, File
from PIL import Image
from io import BytesIO
from schemas.item import Item
from src.embedding.embedding import get_single_image_embedding
from database import index
from constants import DIMENSIONS

router = APIRouter(prefix="/items", tags=["items"])

# Retrieve all clothing items in database
@router.get("/", response_model=list[Item])
def get_items() -> list[Item]:
    try:
        items = index.query(
            vector=[0] * DIMENSIONS,
            top_k=1000,
            include_values=False,
            include_metadata=True,
        )

        return items.matches
    except Exception as e:
        print(e)

# Add clothing image, description, tags, and embeddings to database
@router.post("/", status_code=201)
def add_item(item: Item):
    # Converts Pydantic Item model into Python dictionary
    item_dict = item.model_dump(exclude={"score"})
    try:
        index.upsert(vectors=[item_dict])
    except Exception as e:
        print(e)

@router.post("/similar", response_model=list[Item])
async def similar_items(file: UploadFile = File(...)) -> list[Item]:
    try:
        contents = await file.read()
        with Image.open(BytesIO(contents)).convert("RGB") as image:
            embeddings = get_single_image_embedding(image)[0].tolist()
        items = index.query(
            vector=embeddings,
            top_k=1000,
            include_values=False,
            include_metadata=True,
        )

        return items.matches
    except Exception as e:
        print(e)


from nova.openstack.common import log as logging
from nova.virt import netutils as base_netutils
from oslo.config import cfg
from paxes_nova import logcall
from nova.network import model

CONF = cfg.CONF

injection_ops = [
    cfg.StrOpt('no_v4subnet_method_inject', default='dhcp',
               help='The network configuration method to inject in the VMs '
                    'network template if the VIF does not have a IPv4 subnet.'
                    'Valid values are (None, dhcp)'),

    cfg.StrOpt('no_v6subnet_method_inject', default=None,
               help='The network configuration method to inject in the VMs '
                    'network template if the VIF does not have a IPv6 subnet.'
                    'Valid values are (None, dhcp, auto)'),
]

CONF.register_opts(injection_ops)
CONF.import_opt('use_ipv6', 'nova.netconf')
CONF.import_opt('injected_network_template', 'nova.virt.disk.api')
LOG = logging.getLogger(__name__)

@logcall
def get_injected_network_template(network_info, use_ipv6=CONF.use_ipv6,
                                  template=CONF.injected_network_template):
    """Returns a rendered network template for the given network_info.

    :param network_info:
        :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
    :param use_ipv6: If False, do not return IPv6 template information
        even if an IPv6 subnet is present in network_info.
    :param template: Path to the interfaces template file.
    """
    if not (network_info and template):
        return

    nets = []
    ifc_num = -1

    for vif in network_info:
        if not vif['network']:
            continue

        network = vif['network']
        if not network.get_meta('injected'):
            continue

        subnet_v4 = None
        subnet_v6 = None
        methodv4 = None
        methodv6 = None

        if vif['network']['subnets']:
            subnet_v4 = base_netutils._get_first_network(network, 4)
            subnet_v6 = base_netutils._get_first_network(network, 6)
        # NOTE(bnemec): The template only supports a single subnet per
        # interface and I'm not sure how/if that can be fixed, so this
        # code only takes the first subnet of the appropriate type.

        ifc_num += 1

        address = None
        netmask = None
        gateway = ''
        broadcast = None
        dns = None
        if subnet_v4:
            if subnet_v4.get_meta('dhcp_server') is not None:
                methodv4 = 'dhcp'
            elif subnet_v4['ips']:
                methodv4 = 'static'
                ip = subnet_v4['ips'][0]
                address = ip['address']
                netmask = model.get_netmask(ip, subnet_v4)
                if subnet_v4['gateway']:
                    gateway = subnet_v4['gateway']['address']
                broadcast = str(subnet_v4.as_netaddr().broadcast)
                dns = ' '.join([i['address'] for i in subnet_v4['dns']])
        else:
            # The VIF doesn't have a v4 subnet.  Check CONF values.
            if CONF.no_v4subnet_method_inject and \
                    CONF.no_v4subnet_method_inject.lower() == 'dhcp':
                methodv4 = 'dhcp'

        address_v6 = None
        gateway_v6 = ''
        netmask_v6 = None
        if subnet_v6:
            if subnet_v6.get_meta('dhcp_server') is not None:
                methodv6 = 'dhcp'
            elif subnet_v6['ips']:
                methodv6 = 'static'
                ip_v6 = subnet_v6['ips'][0]
                address_v6 = ip_v6['address']
                netmask_v6 = model.get_netmask(ip_v6, subnet_v6)
                if subnet_v6['gateway']:
                    gateway_v6 = subnet_v6['gateway']['address']

        elif use_ipv6:
            # request to use ipv6 but no subnet defined.
            # Check the CONF value for the method to use.
            if CONF.no_v6subnet_method_inject:
                if CONF.no_v6subnet_method_inject.lower() == 'dhcp':
                    methodv6 = 'dhcp'
                elif CONF.no_v6subnet_method_inject.lower() == 'auto':
                    methodv6 = 'auto'

        net_info = {'name': 'eth%d' % ifc_num,
                    'methodv4': methodv4,
                    'methodv6': methodv6,
                    'address': address,
                    'netmask': netmask,
                    'gateway': gateway,
                    'broadcast': broadcast,
                    'dns': dns,
                    'address_v6': address_v6,
                    'gateway_v6': gateway_v6,
                    'netmask_v6': netmask_v6,
                    }
        nets.append(net_info)

    if not nets:
        return

    return base_netutils.build_template(template, nets, use_ipv6)

def monkey_patch_netutils_get_injected_network_template():
    LOG.debug('Monkey patching nova.virt.netutils'
              '.get_injected_network_template(...)')

    base_netutils.get_injected_network_template = get_injected_network_template


#*****************************************************************************************
# Author: Luis Ernesto Colchado Soncco
# Email: luis.colchado@ucsp.edu.pe / luisernesto.200892@gmail.com
# Description: Generate Interpolation maps
#******************************************************************************************
import os, sys

from tqdm import tqdm

currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from datetime import timedelta
import shapefile as shp
from Models.Attention.ANN import ANN
from Models.Attention.attetion_layer import GraphAttentionLayer
from Models.commom import *
from Models.cGANSL.generator import Generator
import Models.IDW as IDW
import warnings
from tqdm import tqdm
warnings.filterwarnings('ignore')

try:
    DATASOURCE = sys.argv[1]
    if not DATASOURCE in ['BE', 'SP']:
        raise 'Invalid parameters: 1: BE or SP'
except Exception as e:
    raise 'Not found parameters: input DATASOURCE: SP or BE'

class setting:
    z_dim = 100
    if DATASOURCE == 'BE':
        dir_results = '../Experimentation/Results/BE'
        stations = ['Aotizhongxin', 'Dingling', 'Changping', 'Dongsi', 'Gucheng', 'Guanyuan',  'Huairou',
                   'Nongzhanguan', 'Shunyi', 'Wanliu', 'Wanshouxigong', 'Tiantan']
        knn = 3
        dir_cgansl = f"../Models/cGANSL/Backup/cgansl_advloss1_spl2_knn{knn}"
        sel_test_station = "Shunyi"
        selected_features = ['temp', 'pres', 'dewp', 'wd', 'ws', 'ndvi', 'ntl', 'dem']
        dir_cgansl_r2 = f"../Experimentation/Results/{DATASOURCE}/cgansl_advloss1_spl2_knn{knn}"
        dir_data = f"../Preprocessing/Results/data_train.csv"
        start_date = pd.to_datetime("2015-02-01")
        end_date = pd.to_datetime("2015-02-01")
        condition_features = ['temp', 'pres', 'dewp', 'wd', 'ws', 'ndvi', 'ntl', 'dem']
        dir_shp = "shp/beijing.shp"
        name_city = 'Beijing'
    else:
        dir_results = '../Experimentation/Results/SP'
        stations = ['Osasco', 'Pico do Jaraguá', 'Marg.Tietê-Pte Remédios',
                  'Cid.Universitária-USP-Ipen', 'Pinheiros', 'Parelheiros', 'Ibirapuera',
             'Congonhas', 'Santana', 'Parque D.Pedro II', 'Itaim Paulista']
        knn = 10
        dir_cgansl = f"../Models/cGANSL/Backup/cgansl_advloss0.2_spl0.8_knn{knn}"
        sel_test_station = "Itaim Paulista"
        selected_features = ['temp', 'press', 'hum', 'wd', 'ndvi', 'ntl', 'dem']
        dir_cgansl_r2 = f"../Experimentation/Results/{DATASOURCE}/cgansl_advloss0.2_spl0.8_knn{knn}"
        dir_data = f"../Preprocessing/Results/data_train_sp_.csv"
        start_date = pd.to_datetime("2018-07-01")
        end_date = pd.to_datetime("2018-07-01")#pd.to_datetime("2018-08-31")
        condition_features = ['temp', 'press', 'hum', 'wd', 'ndvi', 'ntl', 'dem']
        dir_shp = "shp/sao_paulo.shp"
        name_city = 'Sao Paulo'
    z_dim = 100
    dir_cgansl_r2 = f"{dir_cgansl_r2}/station_{sel_test_station}/r2_test.npy"
    dir_attention_r2 = f"../Experimentation/Results/{DATASOURCE}/attention_kernel_affine/station_{sel_test_station}/r2_test.npy"
    dir_attention = f"../Models/Attention/Backup/{DATASOURCE}_affine"
    for_normalization = selected_features+['PM25']
    input_g = len(selected_features)+(knn*2)+z_dim
    hidden_g = 100
    output_g = 1
    f_sig = torch.nn.Sigmoid()
    f_relu = torch.nn.LeakyReLU()
    idw_cols_train = ['lat', 'lon', 'PM25']
    idw_cols_test = ['lat', 'lon']
    p = 1

def load_map_shp(shp_path):
    sf = shp.Reader(shp_path, encoding='latin-1')
    return sf

def load_attention(epoch):
    number_features = len(setting.selected_features)
    dir_attention = f"{setting.dir_attention}/attention_layer_{epoch}.pt"
    attention_layer = GraphAttentionLayer(number_features, number_features, k=10,
                                    no_feature_transformation=True, graph=None,
                                    kernel="affine", nonlinearity_1=None, nonlinearity_2=nn.LeakyReLU())
    attention_layer.load_state_dict(torch.load(dir_attention))
    attention_layer.eval()

    dir_ann = f"{setting.dir_attention}/ann_{epoch}.pt"
    ann = ANN(number_features*2+1, 1)
    ann.load_state_dict(torch.load(dir_ann))
    ann.eval()
    return attention_layer, ann

def load_cgansl_g(epoch):
    dir_g = f"{setting.dir_cgansl}/g_model_{DATASOURCE}_{epoch}_{setting.sel_test_station}.pt"
    g = Generator(setting.input_g, setting.hidden_g, setting.output_g, f=setting.f_sig, f_relu=setting.f_relu)
    g.load_state_dict(torch.load(dir_g))
    g.eval()
    return g

def preparing_data(labeled_data, data_, name_model='attention'):
    if name_model == 'attention':
        data_ = data_[~data_['ntl'].isna()]
        scaled_data = data_.copy()
        scalers = {}
        for column in setting.for_normalization:
            scaled, scaler = normalization_interpolation(labeled_data, data_, column)
            scaled_data[column] = scaled.reshape(len(data_), 1)
            scalers.update({column: scaler})
        return scaled_data, scalers

def interpolation(x_tensor, name_model, model, ann=None):
    if name_model == 'attention':
        _, output_atten, _ = model(x_tensor)
        pm25_inter = ann(output_atten, DATASOURCE=DATASOURCE)
    elif name_model == 'cgansl':
        pm25_inter = model(x_tensor)
    return pm25_inter.detach().numpy()[-1]

if __name__ == '__main__':

    values_knn = []
    distance_features = []
    for k in range(setting.knn):
        values_knn.append(f'pm25_{k}')
        distance_features.append(f'dist_{k}')
    features_training = setting.condition_features+values_knn+distance_features

    data_frame = pd.read_csv(setting.dir_data)
    r2_attention = np.load(setting.dir_attention_r2)
    r2_cgansl = np.load(setting.dir_cgansl_r2)

    sf = load_map_shp(setting.dir_shp)

    max_atten_index = 500 if DATASOURCE == 'BE' else 328 #SP328#np.argmax(r2_attention)
    max_cgan_index = np.argmax(r2_cgansl)

    print(max_cgan_index)
    for j, v in enumerate(r2_cgansl):
        print(j, v)

    atten_layer, ann_model = load_attention(max_atten_index)
    cgansl_model = load_cgansl_g(max_cgan_index)

    labeled_data = data_frame[~data_frame['PM25'].isna()]
    lat_min, lat_max = min(data_frame['lat']), max(data_frame['lat'])
    lon_min, lon_max = min(data_frame['lon']), max(data_frame['lon'])

    scaled_data, scalers = preparing_data(labeled_data, data_frame, 'attention')
    scaled_data_cgansl, scalers_cgansl = preparing_data(data_frame, data_frame, 'attention')

    d_ = setting.start_date
    with tqdm(total=(setting.end_date - setting.start_date).days) as bar:
        while d_ <= setting.end_date:

            curr_data = scaled_data[scaled_data['current_date']==str(d_.date())]
            lab_data = curr_data[~curr_data['PM25'].isna()]
            unlab_data = curr_data[curr_data['PM25'].isna()]
            lab_data = lab_data[~lab_data['ntl'].isna()]
            x_lab = lab_data[setting.for_normalization].values

            curr_data_cgansl = scaled_data_cgansl[scaled_data_cgansl['current_date']==str(d_.date())]
            lab_data_cgansl = curr_data_cgansl[~curr_data_cgansl['PM25'].isna()]
            unlab_data_cgansl = curr_data_cgansl[curr_data_cgansl['PM25'].isna()]

            idw_train = lab_data[setting.idw_cols_train]
            idw_tree = IDW.tree(idw_train.values[:, :2], idw_train.values[:, -1])
            for i in unlab_data.index:
                x_unlab = unlab_data[setting.for_normalization].loc[i].values
                concat_data =  np.concatenate([x_lab, x_unlab.reshape(1, len(x_unlab))])
                x_tensor = to_tensor(concat_data, is_cuda=False)
                pm25 = interpolation(x_tensor=x_tensor, name_model='attention',
                                    model=atten_layer, ann=ann_model)
                pm25 = scalers['PM25'].inverse_transform(pm25.reshape(-1, 1))
                unlab_data.loc[i, "PM25_atten"] = pm25[-1]

                x_unlab_cgansl = unlab_data_cgansl[features_training].loc[i].values
                z = np.random.rand(1, setting.z_dim)
                x_unlab_cgansl = x_unlab_cgansl.reshape(1, len(x_unlab_cgansl))
                concat_cgansl = np.concatenate([z, x_unlab_cgansl], axis=1)
                x_tensor = to_tensor(concat_cgansl, is_cuda=False)
                pm25 = interpolation(x_tensor=x_tensor, name_model='cgansl',
                                    model=cgansl_model)
                pm25 = scalers_cgansl['PM25'].inverse_transform(pm25.reshape(-1, 1))
                unlab_data.loc[i, "PM25_cgansl"] = pm25[-1]

                x_test = unlab_data[setting.idw_cols_test].loc[i].values.reshape(1, 2)
                z = idw_tree(x_test, k=setting.knn, p=setting.p)
                pm25_idw = scalers['PM25'].inverse_transform(z.reshape(-1, 1))
                unlab_data.loc[i, "PM25_IDW"] = pm25_idw[-1]

            fig, axs = plt.subplots(figsize=(25, 15), nrows=2, ncols=3)
            fig.suptitle(f'{setting.name_city} ({str(d_.date())})', fontsize=22)
            x = []
            y = []
            for shape in sf.shapeRecords():
                for i in shape.shape.points[:]:
                    if i[0] <= lon_max and i[0] >= lon_min and i[1] <= lat_max and i[1] >= lat_min:
                        y.append(i[0])
                        x.append(i[1])
            axs[0, 0].plot(x,y, c='black', linewidth=0.02)
            axs[0, 1].plot(x, y, c='black', linewidth=0.02)
            axs[0, 2].plot(x, y, c='black', linewidth=0.02)
            axs[1, 0].plot(x, y, c='black', linewidth=0.02)
            axs[1, 1].plot(x, y, c='black', linewidth=0.02)

            lab_data['PM25_inv'] = scalers['PM25'].inverse_transform(lab_data['PM25'].values.reshape(-1, 1))

            im = axs[0, 0].scatter(x=lab_data['lat'], y=lab_data['lon'], c=lab_data['PM25_inv'], marker='s', s=150, cmap='YlOrBr')
            ax = axs[0, 0]
            ax.set_title('PM2.5 by AQM stations', fontsize=18)
            fig.colorbar(im, ax=ax)

            im = axs[0, 1].scatter(x=unlab_data['lat'], y=unlab_data['lon'], c=unlab_data['PM25_IDW'], marker='s', s=150, cmap='YlOrBr')
            ax = axs[0, 1]
            ax.set_title('PM2.5 by Inverse Distance Weighting', fontsize=18)
            fig.colorbar(im, ax=ax)

            im = axs[0, 2].scatter(x=unlab_data['lat'], y=unlab_data['lon'], c=unlab_data['PM25_atten'], marker='s', s=150, cmap='YlOrBr')
            ax = axs[0, 2]
            ax.set_title('PM2.5 by Neural Network with Attention Layer', fontsize=18)
            fig.colorbar(im, ax=ax)

            im = axs[1, 0].scatter(x=unlab_data['lat'], y=unlab_data['lon'], c=unlab_data['PM25_cgansl'], marker='s', s=150, cmap='YlOrBr')
            ax = axs[1, 0]
            ax.set_title('PM2.5 by Generative Model (cGAN & Spatial Learning)', fontsize=18)
            fig.colorbar(im, ax=ax)

            im = axs[1, 1].scatter(x=curr_data['lat'], y=curr_data['lon'], c=curr_data['ndvi'], marker='s', s=150, cmap='YlGn')
            ax = axs[1, 1]
            ax.set_title('NDVI (Vegetation Index)', fontsize=18)
            fig.colorbar(im, ax=ax)

            im = axs[1, 2].scatter(x=curr_data['lat'], y=curr_data['lon'], c=curr_data['ntl'], marker='s', s=150, cmap='gray')
            ax = axs[1, 2]
            ax.set_title('NTL (~Population Level)', fontsize=18)
            fig.colorbar(im, ax=ax)

            plt.savefig(f"maps/{DATASOURCE}/inter_{DATASOURCE}_{d_}", bbox_inches = 'tight')
            bar.update(1)
            d_ += timedelta(days=1)


from abc import ABC, abstractmethod
from ..common_imports import *
from typing import Literal
from ..core.config import parse_output_idx, Config
from ..core.model import Ref
from ..core.wrapping import unwrap
from ..core.tps import ListType, StructType, DictType, SetType
from .weaver import (
    ValNode,
    CallNode,
    StructOrientations,
    get_items,
    get_elts,
    get_elt_and_struct,
    get_idx,
    is_key,
    is_idx,
    traverse_all,
    get_vq_orientation,
)
from .graphs import InducedSubgraph, get_canonical_order
from ..ui.viz import (
    Node,
    Edge,
    SOLARIZED_LIGHT,
    to_dot_string,
    write_output,
    HTMLBuilder,
    Cell,
)
import textwrap

class ValueLoaderABC(ABC):
    @abstractmethod
    def load_value(self, full_uid: str) -> Any:
        raise NotImplementedError

class GraphPrinter:
    def __init__(
        self,
        vqs: Set[ValNode],
        fqs: Set[CallNode],
        value_loader: Optional[ValueLoaderABC] = None,
        names: Optional[Dict[ValNode, str]] = None,
    ):
        self.vqs = vqs
        self.fqs = fqs
        self.value_loader = value_loader
        self.g = InducedSubgraph(vqs=vqs, fqs=fqs)
        self.v_labels, self.f_labels, self.vq_to_node = self.g.canonicalize()
        self.full_topsort, self.sources, self.sinks = self.g.topsort(
            canonical_labels={**self.v_labels, **self.f_labels}
        )
        if names is None:
            names = get_names(
                hints={}, canonical_order=get_canonical_order(vqs=vqs, fqs=fqs)
            )
        self.names = names

    def get_struct_comment(
        self,
        vq: ValNode,
        elt_names: Tuple[str, ...],
        idx_names: Optional[Tuple[str, ...]] = None,
    ) -> str:
        id_to_name = {"__list__": "list", "__dict__": "dict", "__set__": "set"}
        if len(elt_names) == 1:
            s = f"{self.names[vq]} will match any {id_to_name[vq.tp.struct_id]} containing a match for {elt_names[0]}"
            if idx_names is not None:
                s += f" at index {idx_names[0]}"
        else:
            s = f'{self.names[vq]} will match any {id_to_name[vq.tp.struct_id]} containing matches for each of {", ".join(elt_names)}'
            if idx_names is not None:
                s += f" at indices {', '.join(idx_names)}"
        return s

    def get_source_comment(self, vq: ValNode) -> str:
        if is_idx(vq=vq):
            return "index into list"
        elif is_key(vq=vq):
            return "key into dict"
        else:
            return "input to computation; can match anything"

    def get_construct_computation_rhs(self, node: ValNode) -> str:
        """
        Given a constructive struct, return
            [name_0, ..., name_1] for lists
            {key_0: name_0, ..., key_n: name_n} for dicts
            {name_0, ..., name_n} for sets
        """
        if isinstance(node.tp, (ListType, DictType)):
            idxs_and_elts = list(get_items(vq=node).values())
            idxs = [x[0] for x in idxs_and_elts]
            idx_values = [
                unwrap(self.value_loader.load_value(full_uid=idx.constraint[0]))
                for idx in idxs
            ]
            elts = [x[1] for x in idxs_and_elts]
            elt_names = tuple([str(self.names[elt]) for elt in elts])
            idx_value_to_elt_name = {
                idx_value: elt_name
                for idx_value, elt_name in zip(idx_values, elt_names)
            }
            if isinstance(node.tp, ListType):
                assert sorted(idx_value_to_elt_name.keys()) == list(range(len(elts)))
                rhs = f'[{", ".join([idx_value_to_elt_name[i] for i in range(len(elts))])}]'
            elif isinstance(node.tp, DictType):
                elt_strings = [
                    f"'{idx_value}': {elt_name}"
                    for idx_value, elt_name in idx_value_to_elt_name.items()
                ]
                rhs = f'{", ".join(elt_strings)}'
                rhs = f"{{{rhs}}}"
            else:
                raise RuntimeError
        elif isinstance(node.tp, SetType):
            elts = list(get_elts(vq=node).values())
            elt_names = tuple([str(self.names[elt]) for elt in elts])
            rhs = f'{", ".join(elt_names)}'
            rhs = f"{{{rhs}}}"
        else:
            raise ValueError
        return rhs

    def get_construct_query_rhs(self, node: ValNode) -> str:
        if isinstance(node.tp, (ListType, DictType)):
            idxs_and_elts = list(get_items(vq=node).values())
            idxs = [x[0] for x in idxs_and_elts]
            elts = [x[1] for x in idxs_and_elts]
            elt_names = tuple([str(self.names[elt]) for elt in elts])
            idx_names = tuple(
                [str(self.names[idx]) if idx is not None else "?" for idx in idxs]
            )
            comment = self.get_struct_comment(
                vq=node, elt_names=elt_names, idx_names=idx_names
            )
            if isinstance(node.tp, ListType):
                rhs = f'ListQ(elts=[{", ".join(elt_names)}], idxs=[{", ".join(idx_names)}]) # {comment}'
            elif isinstance(node.tp, DictType):
                elt_strings = [
                    f"{idx_name}: {elt_name}"
                    for idx_name, elt_name in zip(idx_names, elt_names)
                ]
                rhs = f'DictQ(dct={{..., {", ".join(elt_strings)}, ...}}) # {comment}'
            else:
                raise RuntimeError
        elif isinstance(node.tp, SetType):
            elts = list(get_elts(vq=node).values())
            elt_names = tuple([str(self.names[elt]) for elt in elts])
            comment = self.get_struct_comment(vq=node, elt_names=elt_names)
            rhs = f'SetQ(elts=[{", ".join(elt_names)}]) # {comment}'
        else:
            raise ValueError
        return rhs

    def get_op_computation_line(self, node: CallNode) -> str:
        full_returns = node.returns_interp
        full_returns_names = [
            self.names[vq] if vq is not None else "_" for vq in full_returns
        ]
        lhs = ", ".join(full_returns_names)
        # rhs
        args_dict = {arg_name: self.names[vq] for arg_name, vq in node.inputs.items()}
        for inp_name in node.func_op.sig.input_names:
            if inp_name not in args_dict:
                raise RuntimeError
        args_string = ", ".join([f"{k}={v}" for k, v in args_dict.items()])
        rhs = f"{node.func_op.sig.ui_name}({args_string})"
        if len(lhs) == 0:
            return rhs
        else:
            return f"{lhs} = {rhs}"

    def get_op_query_line(self, node: CallNode) -> str:
        # lhs
        full_returns = node.returns_interp
        full_returns_names = [
            self.names[vq] if vq is not None else "_" for vq in full_returns
        ]
        lhs = ", ".join(full_returns_names)
        # rhs
        args_dict = {arg_name: self.names[vq] for arg_name, vq in node.inputs.items()}
        for inp_name in node.func_op.sig.input_names:
            if inp_name not in args_dict:
                args_dict[inp_name] = "Q()"
        args_string = ", ".join([f"{k}={v}" for k, v in args_dict.items()])
        rhs = f"{node.func_op.sig.ui_name}({args_string})"
        if len(lhs) == 0:
            return rhs
        else:
            return f"{lhs} = {rhs}"

    def get_destruct_computation_line(self, node: CallNode) -> str:
        elt, struct = get_elt_and_struct(fq=node)
        idx = get_idx(fq=node)
        lhs = f"{self.names[elt]}"
        if idx is None:
            raise NotImplementedError
        # inline the index value if the index is a source in the graph
        if idx in self.sources:
            assert idx.constraint is not None
            assert len(idx.constraint) == 1
            idx_ref = self.value_loader.load_value(full_uid=idx.constraint[0])
            idx_value = unwrap(idx_ref)
            if isinstance(idx_value, int):
                rhs = f"{self.names[struct]}[{idx_value}]"
            elif isinstance(idx_value, str):
                rhs = f"{self.names[struct]}['{idx_value}']"
            else:
                raise RuntimeError
        else:
            rhs = f"{self.names[struct]}[{self.names[idx]}]"
        return f"{lhs} = {rhs}"

    def get_destruct_query_line(self, node: CallNode) -> str:
        elt, struct = get_elt_and_struct(fq=node)
        idx = get_idx(fq=node)
        lhs = f"{self.names[elt]}"
        if idx is None:
            idx_label = "?"
        else:
            idx_label = self.names[idx]
        rhs = f"{self.names[struct]}[{idx_label}] # {self.names[elt]} will match any element of a match for {self.names[struct]} at index matching {idx_label}"
        return f"{lhs} = {rhs}"

    def print_computational_graph(self, show_sources_as: str = "values") -> str:
        res = []
        for node in self.full_topsort:
            if isinstance(node, ValNode):
                if node in self.sources:
                    if is_idx(node) or is_key(node):
                        # exclude indices/keys if they are sources (we will inline them)
                        continue
                    assert node.constraint is not None
                    assert len(node.constraint) == 1
                    if show_sources_as == "values":
                        ref = self.value_loader.load_value(full_uid=node.constraint[0])
                        value = unwrap(ref)
                        rep = textwrap.shorten(str(value), 25)
                        res.append(f"{self.names[node]} = {rep}")
                    elif show_sources_as == "uids":
                        uid, causal_uid = Ref.parse_full_uid(
                            full_uid=node.constraint[0]
                        )
                        res.append(
                            f"{self.names[node]} = Ref(uid={uid}, causal_uid={causal_uid})"
                        )
                    else:
                        raise ValueError
                elif isinstance(node.tp, StructType):
                    if get_vq_orientation(node) != StructOrientations.construct:
                        continue
                    rhs = self.get_construct_computation_rhs(node=node)
                    lhs = f"{self.names[node]}"
                    line = f"{lhs} = {rhs}"
                    res.append(line)
            elif isinstance(node, CallNode):
                if not node.func_op.is_builtin:
                    res.append(self.get_op_computation_line(node=node))
                else:
                    if node.orientation == StructOrientations.destruct:
                        res.append(self.get_destruct_computation_line(node=node))
        return "\n".join(res)

    def print_query_graph(
        self,
        selection: Optional[List[ValNode]],
        pprint: bool = False,
    ):
        res = []
        for node in self.full_topsort:
            if isinstance(node, ValNode):
                if node in self.sources:
                    comment = self.get_source_comment(vq=node)
                    res.append(f"{self.names[node]} = Q() # {comment}")
                elif isinstance(node.tp, StructType):
                    if get_vq_orientation(node) != StructOrientations.construct:
                        continue
                    rhs = self.get_construct_query_rhs(node=node)
                    lhs = f"{self.names[node]}"
                    line = f"{lhs} = {rhs}"
                    res.append(line)
            elif isinstance(node, CallNode):
                if not node.func_op.is_builtin:
                    res.append(self.get_op_query_line(node=node))
                else:
                    if node.orientation == StructOrientations.destruct:
                        res.append(self.get_destruct_query_line(node=node))
        if selection is not None:
            res.append(
                f"result = storage.df({', '.join([self.names[vq] for vq in selection])})"
            )
        res = [textwrap.indent(line, "    ") for line in res]
        if Config.has_rich and pprint:
            from rich.syntax import Syntax

            highlighted = Syntax(
                "\n".join(res),
                "python",
                theme="solarized-light",
                line_numbers=False,
            )
            # return Panel.fit(highlighted, title="Computational Graph")
            return highlighted
        else:
            return "\n".join(res)

def print_graph(
    vqs: Set[ValNode],
    fqs: Set[CallNode],
    names: Dict[ValNode, str],
    selection: Optional[List[ValNode]],
    pprint: bool = False,
):
    printer = GraphPrinter(vqs=vqs, fqs=fqs, names=names)
    s = printer.print_query_graph(selection=selection)
    if Config.has_rich and pprint:
        rich.print(s)
    else:
        print(s)

def graph_to_dot(
    vqs: List[ValNode],
    fqs: List[CallNode],
    names: Dict[ValNode, str],
    layout: Literal["computational", "bipartite"] = "computational",
    memoization_tables: Optional[Dict[CallNode, pd.DataFrame]] = None,
) -> str:
    # should work for subgraphs
    assert set(vqs) <= set(names.keys())
    nodes = {}  # val/op query -> Node obj
    edges = []
    col_names = []
    counter = 0
    for vq in vqs:
        if names[vq] is not None:
            col_names.append(names[vq])
        else:
            col_names.append(f"unnamed_{counter}")
            counter += 1
    for vq, col_name in zip(vqs, col_names):
        html_label = HTMLBuilder()
        if hasattr(vq, "_hidden_message"):
            msg = vq._hidden_message
            text = f"{col_name} ({msg})"
        else:
            text = str(col_name)
        html_label.add_row(
            cells=[
                Cell(
                    text=text,
                    port=None,
                    bold=True,
                    bgcolor=SOLARIZED_LIGHT["orange"],
                    font_color=SOLARIZED_LIGHT["base3"],
                )
            ]
        )
        node = Node(
            internal_name=str(id(vq)),
            # label=str(val_query.column_name),
            label=html_label.to_html_like_label(),
            color=SOLARIZED_LIGHT["blue"],
            shape="plain",
        )
        nodes[vq] = node
    for func_query in fqs:
        html_label = HTMLBuilder()
        func_preview = (
            f'{func_query.displayname}({", ".join(func_query.inputs.keys())})'
        )
        if hasattr(func_query, "_hidden_message"):
            msg = func_query._hidden_message
            func_preview = f"{func_preview} ({msg})"
        title_cell = Cell(
            text=func_preview,
            port=None,
            bgcolor=SOLARIZED_LIGHT["blue"],
            bold=True,
            font_color=SOLARIZED_LIGHT["base3"],
        )
        input_cells = []
        output_cells = []
        for input_name in func_query.inputs.keys():
            input_cells.append(Cell(text=input_name, port=input_name, bold=True))
            # html_label.add_row(elts=[Cell(text=input_name, port=input_name)])
        for output_idx in range(len(func_query.outputs)):
            output_cells.append(
                Cell(
                    text=f"output_{output_idx}", port=f"output_{output_idx}", bold=True
                )
            )
        if layout == "bipartite":
            html_label.add_row(cells=[title_cell])
            if len(input_cells + output_cells) > 0:
                html_label.add_row(cells=input_cells + output_cells)
            if memoization_tables is not None:
                column_names = [cell.text for cell in input_cells + output_cells]
                port_names = [cell.port for cell in input_cells + output_cells]
                # remove ports from the table column cells
                for cell in input_cells + output_cells:
                    cell.port = None
                df = memoization_tables[func_query][column_names]
                rows = list(df.head().itertuples(index=False))
                for tup in rows:
                    html_label.add_row(
                        cells=[
                            Cell(text=textwrap.shorten(str(x), 25), bold=True)
                            for x in tup
                        ]
                    )
                # add port names to the cells in the *last* row
                for cell, port_name in zip(html_label.rows[-1], port_names):
                    cell.port = port_name
        elif layout == "computational":
            if len(input_cells) > 0:
                html_label.add_row(input_cells)
            html_label.add_row([title_cell])
            if len(output_cells) > 0:
                html_label.add_row(output_cells)
        else:
            raise ValueError(f"Unknown layout: {layout}")
        node = Node(
            internal_name=str(id(func_query)),
            # label=str(func_query.func_op.sig.ui_name),
            label=html_label.to_html_like_label(),
            color=SOLARIZED_LIGHT["red"],
            shape="plain",
        )
        nodes[func_query] = node
        for input_name, vq in func_query.inputs.items():
            if not vq in nodes:
                continue
            if layout == "bipartite":
                edges.append(
                    Edge(
                        target_node=nodes[vq],
                        source_node=nodes[func_query],
                        source_port=input_name,
                        arrowtail="none",
                        arrowhead="none",
                    )
                )
            elif layout == "computational":
                edges.append(
                    Edge(
                        source_node=nodes[vq],
                        target_node=nodes[func_query],
                        target_port=input_name,
                    )
                )
            else:
                raise ValueError(f"Unknown layout: {layout}")
        for output_name, vq in func_query.outputs.items():
            if not vq in nodes:
                continue
            edges.append(
                Edge(
                    source_node=nodes[func_query],
                    target_node=nodes[vq],
                    source_port=output_name,
                    arrowtail="none" if layout == "bipartite" else None,
                    arrowhead="none" if layout == "bipartite" else None,
                )
            )
    return to_dot_string(nodes=list(nodes.values()), edges=edges, groups=[])

def visualize_graph(
    vqs: Set[ValNode],
    fqs: Set[CallNode],
    names: Optional[Dict[ValNode, str]],
    layout: Literal["computational", "bipartite"] = "computational",
    memoization_tables: Optional[Dict[CallNode, pd.DataFrame]] = None,
    output_path: Optional[Path] = None,
    show_how: Literal["none", "browser", "inline", "open"] = "none",
):
    if names is None:
        names = get_names(
            hints={}, canonical_order=get_canonical_order(vqs=vqs, fqs=fqs)
        )
    dot_string = graph_to_dot(
        vqs=list(vqs),
        fqs=list(fqs),
        names=names,
        layout=layout,
        memoization_tables=memoization_tables,
    )
    if output_path is None:
        tempfile_obj, output_name = tempfile.mkstemp(suffix=".svg")
        output_path = Path(output_name)
    write_output(
        output_path=output_path,
        dot_string=dot_string,
        output_ext="svg",
        show_how=show_how,
    )
    return output_path

def show(*vqs: ValNode):
    vqs, fqs = traverse_all(vqs=list(vqs), direction="both")
    visualize_graph(vqs=vqs, fqs=fqs, show_how="browser")

def extract_names_from_scope(scope: Dict[str, Any]) -> Dict[ValNode, str]:
    """
    Heuristic to get deterministic name for all ValQueries we can find in the
    scope.
    """
    names_per_vq = defaultdict(list)
    for k, v in scope.items():
        if k.startswith("_"):
            continue
        if isinstance(v, Ref) and v._query is not None:
            names_per_vq[v.query].append(k)
        elif isinstance(v, ValNode):
            names_per_vq[v].append(k)
    res = {}
    for vq, names_per_vq in names_per_vq.items():
        if len(names_per_vq) > 1:
            logger.warning(
                f"Found multiple names for {vq}: {names_per_vq}, choosing {sorted(names_per_vq)[0]}"
            )
        res[vq] = names_per_vq[0]
    return res

def get_names(
    hints: Dict[ValNode, str], canonical_order: List[ValNode]
) -> Dict[ValNode, str]:
    """
    Get names for the given oredered list of ValQueries, using the following
    priority:
        - from the object itself;
        - the hints;
        - a_{counter} for the rest.
    """
    counter = Count()
    idx_counter = Count()
    key_counter = Count()
    existing_names = set(hints.values()) | {
        vq.name for vq in canonical_order if vq.name is not None
    }
    res = {}
    for vq in canonical_order:
        if vq.name is not None:
            res[vq] = vq.name
        elif vq in hints:
            res[vq] = hints[vq]
        else:
            if is_key(vq):
                c, prefix = key_counter, "key"
            elif is_idx(vq):
                c, prefix = idx_counter, "idx"
            else:
                c, prefix = counter, "a"
            while f"{prefix}{c.i}" in existing_names:
                c.i += 1
            res[vq] = f"{prefix}{c.i}"
            c.i += 1
    return res

class Count:
    def __init__(self):
        self.i = 0


import argparse
import os
import signal

from .run import run

def main():
    run(**parse_args())

def parse_args():
    parser = argparse.ArgumentParser(
        prog="dump_lammps",
        description="Create lammps data/dump from simulation trajectory",
    )
    parser.add_argument(
        "--phase",
        dest="phasename",
        metavar="<phase>",
        type=str,
        help="simulation phase to dump (initialization | relaxation | simulation)",
    )
    parser.add_argument(
        "--steps",
        dest="step_range",
        metavar="[<start>:]<end>",
        type=str,
        help="step range to dump",
    )
    parser.add_argument(
        "--apart",
        dest="apart",
        metavar="<factor>",
        type=float,
        help="scale the center of mass of each chain by this factor",
    )
    parser.add_argument(
        "--smooth",
        dest="smooth_window",
        metavar="<window>",
        type=int,
        default=None,
        help="apply gaussian smoothing of given window size",
    )
    parser.add_argument(
        "trajfile", metavar="<trajectory>", type=str, help="input hdf5 file",
    )
    parser.add_argument(
        "datafile", metavar="<data>", type=str, help="output LAMMPS data file",
    )
    parser.add_argument(
        "dumpfile", metavar="<dump>", type=str, help="output LAMMPS dump file",
    )
    args = parser.parse_args()

    args.step_range = parse_step_range(args.step_range)

    return vars(args)

def parse_step_range(arg):
    if arg is None:
        return None
    fragments = arg.split(":")
    if len(fragments) == 1:
        return 0, int(arg)
    if len(fragments) == 2:
        return int(fragments[0]), int(fragments[1])
    raise Exception("invalid step range")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        os.kill(os.getpid(), signal.SIGINT)
    except BrokenPipeError:
        signal.signal(signal.SIGPIPE, signal.SIG_DFL)
        os.kill(os.getpid(), signal.SIGPIPE)