import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

DATA_TYPES = {
    "SEQN": "str",
    "RIDAGEYR": "float",
    "RIAGENDR": "category",
    "RIDRETH3": "category",
    "BMXBMI": "float",
    "SMQ020": "category",
    "ALQ151": "category",
    "SLD010H": "float",
    "MCQ160A": "category",
    "MCQ160L": "category",
    "OSQ060": "category",
    "OSQ150": "category",
    "BPQ020": "category",
}

COLUMN_NAMES_REPLACED = {
    "SEQN": "ID",
    "RIDAGEYR": "Age",
    "RIAGENDR": "Gender",
    "RIDRETH3": "Race",
    "BMXBMI": "BMI",
    "SMQ020": "Smoking",
    "ALQ151": "Heavy Drinking",
    "SLD010H": "Sleep Duration (Hours)",
    "MCQ160A": "Arthritis",
    "MCQ160L": "Liver Condition",
    "OSQ060": "Osteoporosis",
    "OSQ150": "Parental Osteoporosis",
#     "BPQ020": "High blood Pressure",
}

GENERAL_CODE_REPLACED = {
    1: "Yes",
    2: "No",
    7: "Refused",
    9: "Don't know"
}

DEMOGRAPHIC_REPLACED = {
    "RIAGENDR": {1: "Male", 2: "Female"},
    "RIDRETH3": {
        1: "Mexican American",
        2: "Other Hispanic",
        3: "Non-Hispanic White",
        4: "Non-Hispanic Black",
        6: "Non-Hispanic Asian",
        7: "Other Race - Including Multi-Racial",
    },
}

THRESHOLD_DICT = {
    'Age':  {
        0: "40-44",
        45: "45-49",
        50: "50-54",
        55: "55-59",
        60: "60-64",
        65: "65-69",
        70: "70-74",
        75: "75-79",
        80: "80+",
    },
    'BMI': {
        0: "Underweight",
        18.5: "Healthy Weight",
        25: "Overweight",
        30: "Obesity"
    },
    'Sleep Duration (Hours)': {
        0: "Less than 7 Hours",
        7: "7-9 Hours",
        9: "More than 9 Hours",
    }
}

def import_data(file,
                folder1='data/2013-2014-NHANES',
                folder2='data/2017-2020-NHANES',
                split=False):
    data1 = pd.read_sas(f"{folder1}/{file}")
    data2 = pd.read_sas(f"{folder2}/{file}")
    if not split:
        data = pd.concat([data1, data2])
        return data
    else:
        return data1, data2

def data_replace(data, general_replace=False, replace_dict=""):
    # replace values according to the replace_dict,
    # which contains specific values for specific columns
    if replace_dict and not general_replace:
        for var, value_dict in replace_dict.items():
            for value, new_value in value_dict.items():
                if var in data.columns:
                    data[var] = data[var].replace(value, new_value)
    # replace values of all columns, according to GENERAL_CODE_REPLACED
    else:
        for var in data.columns:
            for value, new_value in GENERAL_CODE_REPLACED.items():
                data[var] = data[var].replace(value, new_value)

    return data

def change_data_types(data):
    for var, data_type in DATA_TYPES.items():
        if var in data.columns:
            data[var] = data[var].astype(data_type)

    return data

def rename_columns(data):
    data = data.rename(columns=COLUMN_NAMES_REPLACED)

    return data

def common_clean(data):
    """Common process for data cleaning

    Args:
        data: the pandas dataframe
    Returns:
        a clean dataframe without missing values and duplicates
    """
    # fill in missing values
    data = data.fillna(0)
    # remove white spaces from field names
    data.columns = data.columns.str.strip()
    # remove duplicates
    data = data.drop_duplicates()
    # changes data types
    data = change_data_types(data)
    # rename columns
    data = rename_columns(data)

    return data

def clean_demographic(data):
    """Method for cleaning the demographics data, including
       replacing certain values and changing data types

    Args:
        data: the pandas dataframe of demographics
    Returns:
        a clean dataframe
    """
    df = data.copy()

    # replace values
    df = data_replace(df, replace_dict=DEMOGRAPHIC_REPLACED)
    # fill in missing values an remove duplicates
    df = common_clean(df)

    return df

def clean_bmi(data):
    """Method for cleaning the BMI data, including
       replacing certain values and changing data types

    Args:
        data: the pandas dataframe of demographics
    Returns:
        a clean dataframe
    """
    df = data.copy()
    # drop missing values
    df = df.dropna()
    df = common_clean(df)

    return df

def clean_sleep(data):
    df = data.copy()
    # only keep rows with valid sleep hours, 77 means Refused
    df = df.query("SLD010H < 24")
    df = common_clean(df)

    return df

def number_to_category(data, var_col, new_var_col):
    """Group by a specific variable based on pre-defined dictionary

    Args:
        data: the dataframe with var_col
        var_col: the numeric variable to be grouped
        new_var_col: the new variable with data type string
    Returns:
        a dataframe in which the numeric variable has been converted to strings
    """
    df = data.copy()
    if var_col in THRESHOLD_DICT:
        # find the corresponding dict
        threshold_dict = THRESHOLD_DICT[var_col]
        for threshold_value, new_value in threshold_dict.items():
            df.loc[df[var_col] >= threshold_value, new_var_col] = new_value

    return df

def clean_variable(data, col_list):
    """Method for cleaning the target data, including
       removing data with unsure values other than Yes/No

    Args:
        data: the pandas dataframe of the target
    Returns:
        a clean dataframe
    """

    df = data.copy()

    # only keep rows answered "Yes" or "No" in the target_col
    for target_col in col_list:
        df = df.query(f"{target_col}==1 | {target_col}==2")

    # replace positive answers with "Yes" and negative answers with "No"
    df = data_replace(df, general_replace=True)
    df = common_clean(df)

    return df

def get_percentage(data, var_col, target_col):
    df = data.copy()
    df = df.groupby([var_col, target_col]).size().unstack(fill_value=0).reset_index()
    # get percentage of each group
    df['Percentage'] = round(df["Yes"]/(df["Yes"]+df["No"])*100, 2)

    return df

def barplot_percentage(data, var_col, target_col, order='', title=''):
    """Grouping the dataset by var_col and target_col
       and return the target's percentage in each age group

    Args:
        var_col: the variable with Yes/No values
        target_col: the target variable name
    Returns:
        a dataframe displays percentage of the target variable
    """

    # get percentage
    df = get_percentage(data, var_col, target_col)

    # barplot with percentage as annotation
    if order:
        ax = sns.barplot(data=df, x=var_col, y="Percentage", order=order)
    else:
        ax = sns.barplot(data=df, x=var_col, y="Percentage")
    for p in ax.patches:
        x = p.get_width()*0.5 + p.get_x()
        y = p.get_height()+0.1
        ax.annotate(p.get_height(), (x, y), ha='center', color="black", fontsize=10, weight="bold")
    ax.set_title(title, pad=20)

    return ax

def move_legend(ax, new_loc, **kws):
    old_legend = ax.legend_
    handles = old_legend.legendHandles
    labels = [t.get_text() for t in old_legend.get_texts()]
    title = old_legend.get_title().get_text()
    ax.legend(handles, labels, loc=new_loc, title=None, **kws)

def compare_distribution(data, compare_col, var_list):
    """Plot histgrams or boxplots for multiple variables

    Args:
        data: the input pandas dataframe
        compare_col: the variable that distinguished the data to be compared
        var_list: the list of variables
    """

    nrow = int(np.ceil(len(var_list)/2))
    ncol = 2
    i = 1
    for var in var_list:
        plt.subplot(nrow, ncol, int(i))
        plt.gca().set_title(f"{var}",
                    fontsize=12, weight='bold')
        # histplot for each categorical variable
        if data[var].dtype.name == 'category':
            unique_compare_values = data[compare_col].unique().tolist()
            for compare_value in unique_compare_values:
                selected_data = data[data[compare_col]==compare_value]
                ax = sns.histplot(
                data=selected_data,
                x=compare_col,
                discrete=True,
                stat='percent',
                hue=var,
                multiple='dodge',
                shrink=.6
                )

            move_legend(ax, "center left", bbox_to_anchor=(1, 0.5), fontsize=9, ncol=1, frameon=False)
        else:
            sns.boxplot(data=data, x=var, y=compare_col, palette="Set2")

        # increase subplot number
        i += 1

    plt.tight_layout()

def countplot_by_category(data, category):
    df_counts = data[category].value_counts()
    ax = sns.countplot(data=data, y=category, order=df_counts.index)
    total = df_counts.sum()
    # add percentage annotation
    for p in ax.patches:
        percentage = '{:.1f}%'.format(100 * p.get_width()/total)
        x = p.get_width()*0.5
        y = p.get_y() + p.get_height()/1.5
        ax.annotate(percentage, (x, y), ha='center', color="white", fontsize=8, weight="bold")

def multi_countplot(data, var_list):
    nrow = int(np.ceil(len(var_list)/2))
    ncol = 2
    i = 1
    for var in var_list:
        plt.subplot(nrow, ncol, int(i))
        plt.gca().set_title(f"{var}",
                    fontsize=14, weight='bold')
        if data[var].dtype.name == 'category':
            countplot_by_category(data, category=var)
        else:
            sns.histplot(data=data, x=var)
        i += 1
    plt.tight_layout()

def plot_by_gender(data, gender, var_col, target_col,
                   annotate_x=0, annotate_y=0, annotate_text='',
                   order='', x_tick_rotation=0):
    data_gender = data.query(f"Gender == '{gender}'")

    ax = barplot_percentage(data_gender, var_col, target_col, order, title=f"Prevalence of {target_col}, {gender}")
    ax.axhline(y=annotate_y, color='red', alpha=0.5, linestyle='dashed')
    ax.text(annotate_x, annotate_y*1.06, f"{gender} {target_col}: {annotate_text}%", fontsize=10)
    ax.set_xticklabels(ax.get_xticklabels(), rotation=x_tick_rotation, horizontalalignment='right')

    return ax

def plot_both_gender(data, var_col, target_col,
                     text_male, text_female,
                     annotate_x_male, annotate_x_female,
                     annotate_y_male, annotate_y_female,
                     order='', x_tick_rotation=0
                    ):
    plt.subplot(121)
    ax = plot_by_gender(data, 'Male', var_col, target_col,
                        annotate_x_male, annotate_y_male,
                        text_male, order, x_tick_rotation)
    plt.subplot(122)
    ax = plot_by_gender(data, 'Female', var_col, target_col,
                        annotate_x_female, annotate_y_female,
                        text_female, order, x_tick_rotation)
    plt.tight_layout()

# group by var_col and target_col, for a specific gender
def gender_groupby(data, gender, var_col, target_col):
    df = (
            data.query(f"Gender=='{gender}'")
            .groupby([var_col, target_col])
            .size()
            .unstack()
            .transpose()
         )

    return df

def get_pr_ci(data, group1_name, group2_name):
    """Get 95% confidence interval for the prevalence ratio of group1/group2

    Args:
        data: the dataframe with numbers of outcome/no outcome
        separated by groups
        group1_name: the group name of the numerator of the prevalence ratio
        group2_name: the group name of the denominator of the prevalence ratio
    Returns:
        95% confidence interval and whether it's statistically significant
        for the prevalence ratio of group1/group2
    """

    # number of outcome in group 1
    group1_outcome = data.loc['Yes', group1_name]
    # number of without outcome in group 1
    group1_no_outcome = data.loc['No', group1_name]
    # number of outcome in group 2
    group2_outcome = data.loc['Yes', group2_name]
    # number of without outcome in group 2
    group2_no_outcome = data.loc['No', group2_name]
    # PR of group 1, group 2
    prev1 = group1_outcome/(group1_outcome + group1_no_outcome)
    prev2 = group2_outcome/(group2_outcome + group2_no_outcome)
    # log of PR
    ln_pr = np.log(prev1/prev2)
    # standard error of ln(PR)
    se_ln_pr = np.sqrt(1/group1_outcome - 1/(group1_outcome+group1_no_outcome)
                      + 1/group2_outcome - 1/(group2_outcome+group2_no_outcome))
    lower_ci = round(np.exp(ln_pr - 1.96*se_ln_pr), 3)
    upper_ci = round(np.exp(ln_pr + 1.96*se_ln_pr), 3)
    # 95% confidence interval for the prevalence ratio
    ci_pr = (lower_ci, upper_ci)
    pr = round(prev1/prev2, 3)
    pr_ci_str = str(pr) + ' ' + str(ci_pr)
    significant = 'Yes' if upper_ci < 1 or lower_ci > 1 else 'No'

    # return prevalence ratio with 95% CI
    return pr_ci_str, significant

def get_prevalence_ratios(data, reference_group):
    """Get Prevalence Ratios (95% CI)

    Args:
        data: the dataframe contains reference group and other groups
        reference_group: the reference group name (the denominator of the prevalence ratio)
    Returns:
        a dataframe shows 95% CI for the prevalence ratio of
        all the other groups vs. the reference group
    """

    group_list = [x for x in data.columns.values if x!= reference_group]
    predictors = [reference_group] + group_list
    ci_list = [get_pr_ci(data, x, reference_group) for x in group_list]
    significants = [c[1] for c in ci_list]
    confidence_intervals = ['ref'] + [c[0] for c in ci_list]
    df_pr =  pd.DataFrame({
        'Predictor': predictors,
        'PR (95% CI)': confidence_intervals,
        'Statistically Significant': [''] + significants
    })

    return df_pr


from typing import List
import torch
import torch.nn as nn
import torch.nn.functional as F
import mgail.common as common

class Policy(nn.Module):
    def __init__(self, in_dim: int, out_dim: int, size: List[int], do_keep_prob: float) -> None:
        super().__init__()

        self.arch_params = {
            'in_dim': in_dim,
            'out_dim': out_dim,
            'n_hidden_0': size[0],
            'n_hidden_1': size[1],
            'do_keep_prob': do_keep_prob
        }

        self.model = nn.Sequential(
            nn.Linear(self.arch_params['in_dim'], self.arch_params['n_hidden_0']),
            nn.ReLU(inplace=True),
            nn.Linear(self.arch_params['n_hidden_0'], self.arch_params['n_hidden_1']),
            nn.ReLU(inplace=True)
        )
        self.fc_out = nn.Linear(self.arch_params['n_hidden_1'], out_dim)

        self.model.apply(common.init_weights)
        self.fc_out.apply(common.init_weights)

    def forward(self, state: torch.Tensor, do_keep_prob: float = None) -> torch.Tensor:
        bs = state.size(0)

        x = self.model(state.view(bs, -1))

        do_keep_prob = self.arch_params['do_keep_prob'] if do_keep_prob == None else do_keep_prob
        x = F.dropout(x, p=1.0-do_keep_prob, training=self.training)

        x = self.fc_out(x)
        return x

class StateIndependentPolicy(nn.Module):

    def __init__(self, state_shape, action_shape, hidden_units=(64, 64),
                 hidden_activation=nn.Tanh()):
        super().__init__()

        self.net = common.build_mlp(
            input_dim=state_shape[0],
            output_dim=action_shape[0],
            hidden_units=hidden_units,
            hidden_activation=hidden_activation
        )
        self.log_stds = nn.Parameter(torch.zeros(1, action_shape[0]))

    def forward(self, states):
        return torch.tanh(self.net(states))

    def sample(self, states):
        return common.reparameterize(self.net(states), self.log_stds)

    def evaluate_log_pi(self, states, actions):
        return common.evaluate_lop_pi(self.net(states), self.log_stds, actions)

class StateDependentPolicy(nn.Module):

    def __init__(self, state_shape, action_shape, hidden_units=(256, 256),
                 hidden_activation=nn.ReLU(inplace=True)):
        super().__init__()

        self.net = common.build_mlp(
            input_dim=state_shape[0],
            output_dim=2 * action_shape[0],
            hidden_units=hidden_units,
            hidden_activation=hidden_activation
        )

    def forward(self, states):
        return torch.tanh(self.net(states).chunk(2, dim=-1)[0])

    def sample(self, states):
        means, log_stds = self.net(states).chunk(2, dim=-1)
        return common.reparameterize(means, log_stds.clamp_(-20, 2))


'''
Let's say we want to create an app that allows us to add, update
and delete employees from a database, as well as able to grab
employee info from that databases.

SQLite Documentation:  https://www.sqlite.org/docs.html

'''
import sqlite3
from employee import Employee  # can import module since it's in same directory

# Need a connection object that represents our DB.
# It'll either create the file and/or connect to the DB. .db looks like gibberish
'''
SUPER HANDY. You can connect to a database stored in memory:

conn = sqlite3.connect(':memory:')

This gives us a DB that lives in RAM and that's useful for testing if you want a
fresh clean database for EVERY RUN. So, basically it's kinda like "w" write mode
where it'll completely overwrite what was previously there. The benefit of all
this is that you don't have to COMMENT EVERYTHING OUT. It won't insert multiple
values or get errors when trying to create tables that already exist, etc.

This is nice when you're doing your testing. And when you're ready you can just
pass in a file and your database will be stored like we did earlier (employee.db)

'''
#conn = sqlite3.connect('employee.db')
conn = sqlite3.connect(':memory:')

# Let's create a cursor so we can execute SQL commands using execute method
c = conn.cursor()

# SQLite Datatypes available:(NULL, INTEGER, REAL (float), TEXT, BLOB)
# Use """ """ for multi-line SQL and single pair for one line.
c.execute("""CREATE TABLE employees (
            first text,
            last text,
            pay integer
            )""")

'''
Let's prototype a basic application where we use our created table to
insert/add employees, get employee info by name, update pay, delete employee, etc.
This is a more PYTHONIC way of doing thing.

TRICK/TIP CONTEXT MANAGERS:
It's a pain to commit() after every time you insert, update, or delete. If you know
about CONTEXT MANAGERS using the WITH statment, then we're in luck. CONTEXT MANAGERS
allow us to configure a SETUP and TEARDOWN of resources automatically. In SQLite,
connection objects can be used as context managers that automatically commit or
roll back transactions. So transactions will auto commit unless there's an exceptions,
in which case it will be automatically rolled back.

*NOTE* use WITH statments for INSERT, UPDATE, DELETE since these have to be committed.

with conn:
    c.execute(...)

After you create all your functions for the application, you can basically delete/remove
all of the previous code (except conn.close()).
'''
def insert_emp(emp):
    with conn:
        c.execute("INSERT INTO employees VALUES (:first, :last, :pay)",
                {'first': emp.first, 'last': emp.last, 'pay': emp.pay})

def get_emps_by_name(lastname):
    c.execute("SELECT * FROM employees WHERE last=:last", {'last': lastname})
    return c.fetchall()

def update_pay(emp, pay):
    with conn:
        c.execute("UPDATE employees SET pay=:pay WHERE last=:last AND first=:first",
                {'first': emp.first, 'last': emp.last, 'pay': pay})

def remove_emp(emp):
    with conn:
        c.execute("DELETE FROM employees WHERE first=:first AND last=:last",
                {'first': emp.first, 'last': emp.last})

emp_1 = Employee('John', 'Doe', 80000)  # These are just Python objs but
emp_2 = Employee('Jane', 'Doe', 90000)  # haven't been inserted into DB yet

insert_emp(emp_1)
insert_emp(emp_2)

emps = get_emps_by_name('Doe')
print(emps)  # [('John', 'Doe', 80000), ('Jane', 'Doe', 90000)]

update_pay(emp_2, 95000)
remove_emp(emp_1)

emps = get_emps_by_name('Doe')
print(emps)

conn.close()

 # ================ BELOW was done before adding ABOVE functions ===========
 # === Can add, delete, update, etc. now by just using the new functions ===

'''
After you create some Python Class objects (emp_1, emp), they still haven't
been added to the DB yet. You might be tempted to use string formatting but
this is BAD PRACTICE when using DBs in Python. Example:

"... VALUES ('{}', '{}', {})".format(emp_1.first, emp_1.last, emp_1.pay)

If you're accepting values from an enduser (website, etc.) then this is vulnerable
to SQL injection attacks. Basically, there are values that I could set these
variables to equal to that could break the entire database. The reason for that
is that they are not properly escaped.

There are two different ways to do this:
1. Use "?" instead of {} and add your values to the second argument of the execute() method.
Note that the second argument MUST be in a TUPLE regardless if it's just one value.
In the case that it's just a single value, to make a TUPLE you add a ',' ('Schafer',).
The ? is a DB API placeholder and you don't need quotes anymore.

c.execute("INSERT INTO employees VALUES (?, ?, ?)", (emp_1.first, emp_1.last, emp_1.pay))

2. COREY'S PREFERRED METHOD. Instead of ?, we use :column. We still pass a second argument
to execute() but this time it's a DICT.

c.execute("INSERT INTO employees VALUES (:first, :last, :pay)",
    {'first': emp_2.first, 'last': emp_2.last, 'pay': emp_2.pay})

'''
# Now that we have the table, let's comment it out and start adding data
#c.execute("INSERT INTO employees VALUES ('Mary', 'Schafer', 70000)")

# Added "Mary" so this is just being explicit to commit() again before query
#conn.commit()

# c.execute("INSERT INTO employees VALUES (?, ?, ?)", (emp_1.first, emp_1.last, emp_1.pay))

# conn.commit()

# c.execute("INSERT INTO employees VALUES (:first, :last, :pay)",
#     {'first': emp_2.first, 'last': emp_2last, 'pay': emp_2.pay})

# conn.commit()

'''
At first we've been typing in the values directly into SELECT statements.
However, the way you'll mostly use this with Python is that you'll have
some variables in your code that you will insert into your SQL query.

Example - Let's start using the Employee Class
def __init__(self, first, last, pay)

After importing the module: from employee import Employee
We can add some code using that class up above our INSERT statement.

'''
# Let's query the DB to see if Corey was added (see above for how to use Python)
#c.execute("SELECT * FROM employees WHERE last='Schafer'")

# Using ? placeholder and tuple
# c.execute("SELECT * FROM employees WHERE last=?", ('Schafer',))  # Note the comma!

# print(c.fetchall())

# # Using :column placeholder and dictionary
# c.execute("SELECT * FROM employees WHERE last=:last", {'last': 'Doe'})

# print(c.fetchall())
# Note that the SELECT statement is going to provide results we can iterate through.
#print(c.fetchone()) # It will get the next row in results or None. Sort of like next()
#c.fetchmany(5)  # Returns a LIST of n rows.
#print(c.fetchall())  # Returns a LIST of remaining rows or empty list.

# Let's do a couple more things before running this (create table code)
# commit() - commits the current transaction. If you're not seeing anyting in the DB,
# then good chance you haven't committed.
# conn.commit()

# It's also good practice to close your DB connection using conn.close()
# conn.close()

# After you run it, it's good. If you try to run again you get this error:
# sqlite3.OperationalError: table employees already exists

# import sqlite3
# from employee import Employee

# conn = sqlite3.connect(':memory:')

# c = conn.cursor()

# c.execute("""CREATE TABLE employees (
#             first text,
#             last text,
#             pay integer
#             )""")

# def insert_emp(emp):
#     with conn:
#         c.execute("INSERT INTO employees VALUES (:first, :last, :pay)", {'first': emp.first, 'last': emp.last, 'pay': emp.pay})

# def get_emps_by_name(lastname):
#     c.execute("SELECT * FROM employees WHERE last=:last", {'last': lastname})
#     return c.fetchall()

# def update_pay(emp, pay):
#     with conn:
#         c.execute("""UPDATE employees SET pay = :pay
#                     WHERE first = :first AND last = :last""",
#                   {'first': emp.first, 'last': emp.last, 'pay': pay})

# def remove_emp(emp):
#     with conn:
#         c.execute("DELETE from employees WHERE first = :first AND last = :last",
#                   {'first': emp.first, 'last': emp.last})

# emp_1 = Employee('John', 'Doe', 80000)
# emp = Employee('Jane', 'Doe', 90000)

# insert_emp(emp_1)
# insert_emp(emp)

# emps = get_emps_by_name('Doe')
# print(emps)

# update_pay(emp, 95000)
# remove_emp(emp_1)

# emps = get_emps_by_name('Doe')
# print(emps)

# conn.close()


import cv2 as cv
import numpy as np

#bins = np.arange(256).reshape(256,1)

'''
원본 이미지에 마스크를 씌우고 그 부분에서의 밝기에 대한 histogram을 찾는 함수. (밝기에 대한 분포도)

    param 1 : 히스토그램을 구할 원본이미지
    param 2~4: (x1,y1) 부터 (x2, y2) 의 이미지 마스크를 씌워서 구분함
'''
def mask_histogram(img, x1,y1,x2,y2):
    h = np.zeros((img.shape[0], 256), dtype=np.uint8)   # 히스토그램을 만들 공간 생성.

    # mask생성
    mask = np.zeros(img.shape[:2], np.uint8)
    mask[y1:y2, x1:x2] = 255                 # [세로 x 가로]의 픽셀을 255으로 마스크 씌움

    # 이미지에 mask가 적용된 결과
    masked_img = cv.bitwise_and(img, img, mask=mask)

    # Histogram: [이미지], [ 채널: [0]그래이스케일/ 컬러 경우, BGR채널[0][1][2] ], 전체이미지 : None , x축범위, 막대의 갯수
    hist_item = cv.calcHist([img], [0], mask, [256], [0, 256])

    cv.normalize(hist_item, hist_item, 0, 255, cv.NORM_MINMAX)
    hist = np.int32(np.around(hist_item))
    for x, y in enumerate(hist):
        cv.line(h, (x, 0 + 10), (x, y + 10), (255, 255, 255))

    cv.line(h, (0, 0 + 10), (0, 5), (255, 255, 255))
    cv.line(h, (255, 0 + 10), (255, 5), (255, 255, 255))
    y = np.flipud(h)

    result = np.hstack((masked_img, y))  # 이미지와 히스토그램을 붙이기

    # histogram의 y값, 마스크씌운 이미지를 리턴.
    return hist_item, result

def draw_histogram(img):
    h = np.zeros((img.shape[0], 256), dtype=np.uint8)

    # Histogram: [이미지], [ 채널: [0]그래이스케일/ 컬러 경우, BGR채널[0][1][2] ], 전체이미지 : None , x축범위, 막대의 갯수
    hist_item = cv.calcHist([img], [0], None, [256], [0, 256])

    cv.normalize(hist_item,hist_item,0,255,cv.NORM_MINMAX)
    hist = np.int32(np.around(hist_item))
    for x, y in enumerate(hist):
        cv.line(h, (x, 0+10), (x, y+10), (255, 255, 255))

    cv.line(h, (0, 0 + 10), (0, 5), (255, 255, 255) )
    cv.line(h, (255, 0 + 10), (255, 5), (255, 255, 255))
    y = np.flipud(h)

    result = np.hstack((img, y))  # 이미지와 히스토그램을 붙이기

    # histogram의 y값, 마스크씌운 이미지를 리턴.
    return hist_item, result

img = cv.imread('./img/15000.bmp', cv.IMREAD_COLOR)
img = cv.resize(img,(1260,960))
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# 원본 이미지에 대한 히스토그램
# result1 = draw_histogram(gray)
# cv.imshow('result1', result1)

# 위에서부터 아래로 차례대로 스캔하면서 감.
for i in range(0, 550, 10):
    for j in range(0, 560, 10):
        # 마스크 씌운 이미지의 히스토그램
        y, result2 = mask_histogram(gray, 103+j,40+i,665+j,490+i)
        cv.imshow('result2', result2)
        print(y)
        cv.waitKey(100)
        #cv.destroyAllWindows()


import matplotlib.pyplot as plt
from ui.signal_winding_view import SignalWindingView
from ui.phasor_winding_view import PhasorWindingView
from ui.uiutil import UiUtil
from ui.interactive_view.frequency_slider import FrequencySlider

class InteractiveView(object):

    def __init__(self, initialFrequency, callback):
        self.initialFrequency = initialFrequency
        self.callback = callback

        self.fig = plt.figure(figsize=(13, 10))
        self.fig.patch.set_facecolor(UiUtil.BACKGROUND_COLOR)
        plt.subplots_adjust(top=0.96)

        # add signal winding view
        ax = self.fig.add_subplot(2, 1, 1)
        UiUtil.setAxisColors(ax)
        self.signal_winding_view = SignalWindingView(ax)
        plt.xlim(-1, 1)
        plt.ylim(-1, 1)

        # add phasor winding view
        ax = self.fig.add_subplot(2, 1, 2)
        UiUtil.setAxisColors(ax)
        self.phasor_winding_view = PhasorWindingView(ax)
        self.slider = FrequencySlider(self._slider_callback, initialFrequency)

    def show(self, frequencyAmplitude):
        self.phasor_winding_view.show(frequencyAmplitude)

    def _slider_callback(self, val):
        wrap = self.callback(val)
        centroid = wrap.mean()
        self.signal_winding_view.update(wrap, centroid)
        self.phasor_winding_view.update([val, centroid[0]])


import random
from PIL import Image, ImageDraw
import PIL

from TuringMachine import TuringMachine
from CellularAutomata import CellularAutomata

# TuringMachine (states, starting_state, alphabet, transition_function, halting_state, tape_length, random_start, pointer))
# CellularAutomata (rule_number, already_in_base, neighborhood, states, random_first_row, width)

class CursedImage():
    def __init__(self, ca_width=200, tm_width=35, scale=1, generations=100):
        # Side length for each cell (10 -> each cell will be 10 pixels wide)
        self.scale = scale
        self.generations = generations

        self.tm_width = scale * tm_width
        self.ca_width = scale * ca_width
        # Setting parameters for image
        # Pixel buffer on left side, inbetween, and on right side of image
        self.buffer = 3 * scale
        self.image_width = scale * (ca_width + tm_width + (3 * self.buffer))
        self.height = (self.scale * self.generations) + (2 * self.buffer)
        # x-value
        self.ca_start = self.tm_width + (2 * self.buffer)

        # Create Actual Image as CursedImage.im
        self.im = Image.new("RGB", (self.image_width, self.height), "#000000")

    # Pass the entire Turing Machine
    def create_image(self, tm, ca):
        image = ImageDraw.Draw(self.im)

        for gen in range(self.generations):
            # Draw Turing Machine State
            for index, cell in enumerate(tm.tape):
                x = self.buffer + self.scale * index
                y = self.buffer + self.scale * gen
                image.rectangle([x, y, x+self.scale, y+self.scale],
                    fill = tm.colors[cell])
            #print(ca.curr_row)
            for index, cell in enumerate(ca.curr_row):
                x = self.ca_start + self.scale * index
                y = self.buffer + self.scale * gen
                image.rectangle([x, y, x+self.scale, y+self.scale],
                    fill = ca.colors[cell])

            # Advance Generations
            tm.advance_generation()
            # Use the new tape values as the values for the Cellular Automata
            ca.advance_generation(rule_values=tm.tape)
            #ca.advance_generation()

def main():
    # Creat
    ca = CellularAutomata(rule_number=None, already_in_base=True, neighborhood=3, states=('0', '1', '2'),
        random_first_row=False, width=500)
    # Almost nothing is defined because we're just using the default (2,3) Turing Machine
    tm = TuringMachine(tape_length=len(ca.rule_input_keys),
        halting_state=None, random_start=True)

    image = CursedImage(tm_width=tm.tape_length, scale=10, generations=500, ca_width=ca.width)
    image.create_image(tm=tm, ca=ca)
    image.im.show()

main()


from typing import Optional, Type, Union

import mutagen
import mutagen.flac

from . import generic as _generic
from krautcat.audio.metadata import Metadata
from krautcat.audio.metadata.types import Date
from krautcat.audio.file.audio.generic import TagsBackend as GenericTagsBackend
from krautcat.gstreamer import ElementFLACDecoder, ElementFLACEncoder, ElementFLACParser

class TagsBackend(GenericTagsBackend):
    def __init__(self, audio_file: "AudioFileFLAC") -> None:
        super().__init__(audio_file)

        self._mutagen_file = mutagen.flac.FLAC(audio_file.path)

    def load_tags(self) -> Metadata:
        mutagen_tags = self._mutagen_file.tags

        def _get_tag(name: str, default: Optional[str] = None) -> Union[str, None]:
            tag_values = mutagen_tags.get(name, default)
            if isinstance(tag_values, list):
                return tag_values[0]
            else:
                return tag_values

        track_name = _get_tag("title")
        artist = _get_tag("artist")
        album = _get_tag("album")

        date = Date(_get_tag("date", None))

        track_number_field = _get_tag("tracknumber", 0)
        track_number = 0
        total_tracks = 0
        if isinstance(track_number_field, str) and "/" in track_number_field:
            track_tag = track_number_field.split("/")
            track_number = track_tag[0]
            total_tracks = track_tag[1]
        else:
            track_number = int(track_number_field)
            total_tracks = int(_get_tag("tracktotal") or 0)

        disc_number_field = _get_tag("discnumber", None)
        disc_number = 0
        if isinstance(disc_number_field, str) and (
                "/" in disc_number_field
                or " / " in disc_number_field
                or ";" in disc_number_field):
            if "/" in disc_number_field:
                disc_tag = disc_number_field.split("/")
            elif " / " in disc_number_field:
                disc_tag = disc_number_field.split(" / ")
            elif ";" in disc_number_field:
                disc_tag = disc_number_field.split(";")
            disc_number = disc_tag[0]
            total_discs = disc_tag[1]
        elif disc_number_field is not None:
            disc_number = int(disc_number_field)
        else:
            disc_number = None

        total_discs_field = _get_tag("disctotal", None)
        total_discs = 0
        if total_discs_field is None or total_discs_field == "":
            total_discs = None
        else:
            total_discs = int(total_discs_field)

        krautcat_tags = Metadata(artist=artist,
                                 track_name=track_name,
                                 track_number=track_number,
                                 album=album,
                                 date=date,
                                 tracks_total=total_tracks,
                                 disc_number=disc_number,
                                 discs_total=total_discs)
        return krautcat_tags

    def save_tags(self, metadata: Metadata) -> bool:
        self._mutagen_file.tags["TITLE"] = metadata.track_name
        self._mutagen_file.tags["ALBUM"] = metadata.album
        self._mutagen_file.tags["ARTIST"] = metadata.artist

        if metadata.track_number is not None:
            self._mutagen_file.tags["TRACKNUMBER"] = str(metadata.track_number)
        if metadata.total_tracks is not None:
            self._mutagen_file.tags["TRACKTOTAL"] = str(metadata.total_tracks)

        if metadata.date is not None:
            self._mutagen_file.tags["DATE"] = str(metadata.date)

        if metadata.disc_number is not None:
            self._mutagen_file.tags["DISCNUMBER"] = str(metadata.disc_number)
        if metadata.total_discs is not None:
            self._mutagen_file.tags["DISCTOTAL"] = str(metadata.total_discs)

        self._mutagen_file.save()

class AudioFileFLAC(_generic.AudioFile):
    EXTENSION = "flac"
    SUFFIX = EXTENSION

    def __init__(self, path, open=True):
        super().__init__(path)

        self._tags_backend = TagsBackend(self)

        if open:
            self.metadata = self._tags_backend.load_tags()
        else:
            self.metadata = None

    def load_tags(self) -> None:
        self.metadata = self._tags_backend.load_tags()
        print(self.metadata)

    def save_tags(self):
        if self.metadata is not None:
            self._tags_backend.save_tags(self.metadata)
        else:
            raise ValueError()

    @property
    def gst_parser(self) -> Type[ElementFLACParser]:
        return ElementFLACParser

    @property
    def gst_decoder(self) -> Type[ElementFLACDecoder]:
        return ElementFLACDecoder

    @property
    def gst_encoder(self) -> Type[ElementFLACEncoder]:
        return ElementFLACEncoder


import pytest

from thoth.adviser.boots import SolvedSoftwareEnvironmentBoot
from thoth.adviser.context import Context
from thoth.adviser.enums import RecommendationType
from thoth.adviser.exceptions import NotAcceptable
from thoth.adviser.pipeline_builder import PipelineBuilderContext
from ..base import AdviserUnitTestCase

from thoth.common import get_justification_link as jl

class TestSolvedSoftwareEnvironmentBoot(AdviserUnitTestCase):
    """Test solved software environment boot."""

    UNIT_TESTED = SolvedSoftwareEnvironmentBoot

    def test_verify_multiple_should_include(self, builder_context: PipelineBuilderContext) -> None:
        """Verify multiple should_include calls do not loop endlessly."""
        builder_context.recommendation_type = RecommendationType.LATEST
        builder_context.project.runtime_environment.should_receive("is_fully_specified").with_args().and_return(True)
        self.verify_multiple_should_include(builder_context)

    def test_should_include(self, builder_context: PipelineBuilderContext) -> None:
        """Test registering this unit if supplied software environment is fully specified."""
        builder_context.project.runtime_environment.should_receive("is_fully_specified").with_args().and_return(
            True
        ).twice()
        assert list(SolvedSoftwareEnvironmentBoot.should_include(builder_context)) == [{}]
        builder_context.add_unit(SolvedSoftwareEnvironmentBoot())
        assert list(SolvedSoftwareEnvironmentBoot.should_include(builder_context)) == []

    def test_should_include_not_fully_specified(self, builder_context: PipelineBuilderContext) -> None:
        """Test not registering this unit if supplied software environment is not fully specified."""
        builder_context.project.runtime_environment.should_receive("is_fully_specified").with_args().and_return(
            False
        ).once()
        assert list(SolvedSoftwareEnvironmentBoot.should_include(builder_context)) == []

    def test_run(self, context: Context) -> None:
        """Test if the given software environment is solved."""
        context.project.runtime_environment.operating_system.name = "fedora"
        context.project.runtime_environment.operating_system.version = "32"
        context.project.runtime_environment.python_version = "3.8"
        context.graph.should_receive("solved_software_environment_exists").with_args(
            os_name="fedora", os_version="32", python_version="3.8"
        ).and_return(True).once()

        assert not context.stack_info

        unit = SolvedSoftwareEnvironmentBoot()
        with unit.assigned_context(context):
            assert unit.run() is None

        assert not context.stack_info, "No stack information should be supplied"

    def test_run_error(self, context: Context) -> None:
        """Test raising no exception raised if the given software environment is not solved."""
        context.project.runtime_environment.operating_system.name = "fedora"
        context.project.runtime_environment.operating_system.version = "32"
        context.project.runtime_environment.python_version = "3.8"
        context.graph.should_receive("solved_software_environment_exists").with_args(
            os_name="fedora", os_version="32", python_version="3.8"
        ).and_return(False).once()

        assert not context.stack_info

        solvers_configured = SolvedSoftwareEnvironmentBoot._THOTH_ADVISER_DEPLOYMENT_CONFIGURED_SOLVERS
        try:
            SolvedSoftwareEnvironmentBoot._THOTH_ADVISER_DEPLOYMENT_CONFIGURED_SOLVERS = "\nsolver-ubi-8-py39\n"
            unit = SolvedSoftwareEnvironmentBoot()
            with pytest.raises(NotAcceptable):
                with unit.assigned_context(context):
                    unit.run()
        finally:
            SolvedSoftwareEnvironmentBoot._THOTH_ADVISER_DEPLOYMENT_CONFIGURED_SOLVERS = solvers_configured

        assert context.stack_info == [
            {
                "type": "ERROR",
                "message": "No observations found for 'fedora' in version '32' using Python '3.8'",
                "link": jl("solved_sw_env"),
            },
            {
                "message": "Consider using 'ubi' in version '8' with Python 3.9",
                "type": "ERROR",
                "link": jl("solved_sw_env"),
            },
        ]


# Cron testing (not for deployment)

from pymongo import MongoClient
import scraper
import jobbankapply

# connect to mongodb and get users
client = MongoClient('mongodb://localhost:27017')
db = client['jabberDatabase']
users = db.users

# find users who signed up for cron job
users = db.users.find({'cron': { "$exists": True} })

# for users who have signed up for cron job
for u in users:
    print(u['cron']['cron_job'])

    # Scrape for jobs that user is interested in
    test = scraper.scrape()
    jobs = []
    employer = []
    links = []
    jobs, employer, links, count = test.search(u['cron']['cron_job'], u['cron']['cron_loc'], True)
    j = jobbankapply.apply(links)
    emails, jobs, employer = j.run()

    # print fetched data and email recruiters on behalf of user
    print(emails)
    print(jobs)
    print(employer)
    j.email(emails, jobs, employer, u['cv'], u['resume'], u['id'])


from django.contrib.auth import login as auth_login, authenticate
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
import json
from django.db.models import Q
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse

from core.forms import SignUpForm, ItemForm, UserItemForm, ProfileForm, UpdateScoreForm, UpdateInterestForm
from core.models import Item, User_Item, Profile, Tag, Following, Update, User_Tag, Favorite_User_Tag, Notification
from core.utils import update_user_item, \
    get_latest_items, get_comparison_list, get_or_create_tag, \
    get_latest_users, update_user_tag, get_arranged_ratings, get_ratings, get_comparisons

def home(request):
    latest_items = get_latest_items(5)
    newest_users = get_latest_users(5)

    if request.user.is_authenticated:
        user = request.user
        updates = user.get_all_updates()

        return render(request, 'home.html', {'latest_items': latest_items,
                                             'updates': updates,
                                             'newest_users': newest_users, })

    return render(request, 'home.html', {'latest_items': latest_items,
                                         'newest_users': newest_users, })

def about(request):
    latest_items = get_latest_items(5)
    return render(request, 'about.html', {'latest_items': latest_items})

def signup(request):
    if request.user.is_authenticated:
        return redirect('home')

    else:
        if request.method == 'POST':
            form = SignUpForm(request.POST)
            if form.is_valid():
                form.save()
                form.login(request)
                return redirect('home')
        else:
            form = SignUpForm()

        return render(request, 'signup.html', {'form': form})

def user(request, username):
    user = get_object_or_404(User, username=username)

    tag_name = request.GET.get('tag', '')
    updates = user.get_updates_by_tag_name(tag_name)

    updates.sort(key=lambda x: x.timestamp, reverse=True)

    latest_items = get_latest_items(5)

    return render(request, 'home.html', {'user': user,
                                         'updates': updates,
                                         'latest_items': latest_items})

def users(request):
    user = request.user
    users = User.objects.all()
    return render(request, 'user_list.html', {'users': users,
                                              'user': user})

def user_created_item_list(request, username):
    user = get_object_or_404(User, username=username)

    item_list = Item.objects.filter(creator=user)

    return render(request, 'user_created_item_list.html', {'item_list': item_list,
                                                           'user': user, })

def item(request, item_id):
    item = get_object_or_404(Item, id=item_id)
    latest_items = get_latest_items(5)

    if request.user.is_authenticated:
        user = request.user
        user_item = User_Item.objects.filter(user=user, item=item).first

        if request.method == 'POST':
            form = UserItemForm(request.POST)
            if form.is_valid():
                return update_user_item(user, item, form)

        else:
            form = UserItemForm()

        return render(request, 'item.html', {'item': item,
                                             'form': form,
                                             'user_item': user_item,
                                             'latest_items': latest_items})

    else:
        return render(request, 'item.html', {'item': item,
                                             'latest_items': latest_items})

def items(request):
    if request.GET.get('tag'):
        tag_name = request.GET.get('tag')
        tag = get_object_or_404(Tag, name=tag_name)
        items = tag.items.all().order_by('-avg_rating')
    else:
        items = Item.objects.all().order_by('-avg_rating')
    return render(request, 'items.html', {'items': items})

def rating_list(request, username):
    user = get_object_or_404(User, username=username)

    tag_name = request.GET.get('tag', '')
    order = request.GET.get('order', 'score')
    sort = request.GET.get('sort', 'desc')

    if request.user.is_authenticated and request.user != user:
        comparisons = get_comparisons(user, request.user, tag_name, order, sort)
        return render(request, 'ratings.html', {'user': user,
                                                'comparisons': comparisons})
    else:
        ratings = get_ratings(user, tag_name, order, sort)
        return render(request, 'ratings.html', {'user': user,
                                                'rating_list': ratings,
                                                })

@login_required
def create_item(request):
    if request.method == 'POST':
        form = ItemForm(request.POST, request.FILES)
        if form.is_valid():
            user = request.user
            if request.POST.get('is_official') == 'on':
                is_official = True
            else:
                is_official = False

            item = form.save(commit=False)
            item.creator = user
            item.is_official = is_official
            item.save()

            raw_tags = request.POST.get('tags').split(';')
            for raw_tag in raw_tags:
                if raw_tag != "" and raw_tag != " ":
                    tag_name = raw_tag.lower().strip()
                    tag = get_or_create_tag(tag_name)
                    item.tags.add(tag)

            message = "created: "
            user_item = user.get_or_create_user_item(item)
            Update.objects.create(user=user,
                                  message=message,
                                  is_visible=True,
                                  interaction=user_item)

            return redirect('item', item.id)
        else:
            return render(request, 'edit_item.html', {'form': form})
    else:
        form = ItemForm()
    return render(request, 'edit_item.html', {'form': form})

@login_required
def edit_item(request, item_id):
    item = get_object_or_404(Item, id=item_id)
    user = request.user

    if Item.objects.filter(creator=user, id=item_id).count() == 0:
        return redirect('item', item_id)

    if request.method == 'POST':
        form = ItemForm(request.POST, request.FILES, instance=item)
        if form.is_valid():
            if request.POST.get('is_official') == 'on':
                is_official = True
            else:
                is_official = False
            item.is_official = is_official
            item.save()

            item.tags.clear()
            raw_tags = form.cleaned_data['tags'].split(';')
            for raw_tag in raw_tags:
                if raw_tag != "" and raw_tag != " ":
                    tag = get_or_create_tag(raw_tag.lower().strip())
                    item.tags.add(tag)

            message = "edited an item: "
            user_item = user.get_or_create_user_item(item)

            user.hide_all_updates_by_user_item(user_item)

            Update.objects.create(user=user,
                                  message=message,
                                  is_visible=True,
                                  interaction=user_item)

            return redirect('item', item_id)
        else:
            return render(request, 'edit_item.html', {'item': item,
                                                      'form': form})
    else:
        return render(request, 'edit_item.html', {'item': item})

@login_required
def delete_item(request, item_id):
    user = request.user
    item = Item.objects.get(id=item_id, creator=user)

    for tag in item.tags.all():

        if tag.item_count() == 1:
            tag.delete()

    item.delete()

    return redirect('home')

@login_required
def compare_items(request, username):
    your_user = request.user
    their_user = User.objects.get(username=username)

    comparison_list = get_comparison_list(your_user, their_user)

    return render(request, 'compare_items.html', {'their_user': their_user,
                                                  'comparison_list': comparison_list})

def search(request):
    query = request.GET.get('q')

    item_results = Item.objects.filter(
        Q(name__icontains=query) | Q(description__icontains=query))

    user_results = User.objects.filter(Q(username__icontains=query) | Q(profile__fullname__icontains=query))

    tag_results = Tag.objects.filter(name__icontains=query)
    if query.startswith('#'):
        tag_name = query.split('#')[1].strip()
        tag_results = Tag.objects.filter(name__icontains=tag_name)

    return render(request, 'search.html', {'item_results': item_results,
                                           'user_results': user_results,
                                           'tag_results': tag_results})

@login_required
def settings(request):
    user = request.user
    latest_items = get_latest_items(5)

    if request.method == 'POST':
        try:
            profile = Profile.objects.get(user=user)
            form = ProfileForm(request.POST, request.FILES, instance=profile)
            if form.is_valid():
                profile.save()
                return redirect('user', user.username)
        except:
            form = ProfileForm(request.POST, request.FILES)
            if form.is_valid():
                profile = form.save(commit=False)
                profile.user = user
                profile.save()
                return redirect('user', user.username)
    else:
        form = ProfileForm()
    return render(request, 'settings.html', {'form': form,
                                             'latest_items': latest_items})

@login_required
def follow(request):
    if request.POST:
        user_tag_id = request.POST.get('user_tag_id')
        user_tag = get_object_or_404(User_Tag, id=user_tag_id)

        if Following.objects.filter(follower=request.user, user_tag=user_tag).count() > 0:
            Following.objects.filter(follower=request.user, user_tag=user_tag).delete()
            data = {
                'message': 'Follow'
            }
        else:
            Following.objects.create(follower=request.user, user_tag=user_tag)

            message = "@" + request.user.username + " is following your '" + user_tag.tag.name + "' user tag!"
            Notification.objects.create(user=user_tag.user, message=message, is_new=True, was_new=False)
            data = {
                'message': 'Unfollow'
            }

        return JsonResponse(data)

def following_list(request, username):
    user = get_object_or_404(User, username=username)

    following_queryset = Following.objects.filter(follower=user)
    followings = []

    for fq in following_queryset:
        already_following = False

        for f in followings:
            if f.user_tag.user.id == fq.user_tag.user.id:
                already_following = True
        if already_following == False:
            followings.append(fq)

    return render(request, 'following.html', {'followings': followings,
                                              'user': user})

def follower_list(request, username):
    user = get_object_or_404(User, username=username)

    follower_list = user.get_followers()

    return render(request, 'followers.html', {'user': user,
                                              'follower_list': follower_list})

@login_required
def update_score(request):
    user_id = request.POST.get('user_id')
    item_id = request.POST.get('item_id')

    user = User.objects.get(id=user_id)
    item = Item.objects.get(id=item_id)

    score = int(request.POST.get('score'))

    form = UpdateScoreForm(request.POST)
    if form.is_valid():
        user_item = form.save(commit=False)
        user_item.user = user
        user_item.item = item

        if User_Item.objects.filter(user=user, item=item).count() > 0:
            user_item = User_Item.objects.get(user=user, item=item)

            if user_item.rating != None and int(user_item.rating) == int(score):
                user_item.rating = None
                user_item.save()

                if user_item.rating == None and user_item.interest == None:
                    user_item.delete()

                data = {
                    'message': 'Score deleted.'
                }
                return JsonResponse(data)

        user_item.rating = score
        user_item.save()
        user_item.item.calc_average()

        user.hide_all_updates_by_user_item(user_item)

        message = "scored "

        for x in range(1, 6):
            if int(user_item.rating) >= x:
                message = message + "★"
            else:
                message = message + "✰"

        message = message + ":"

        Update.objects.create(user=user,
                              message=message,
                              interaction=user_item,
                              is_visible=True)

        for tag in item.tags.all():
            update_user_tag(user, tag)

        data = {
            'message': 'You ' + message + ' (' + item.name + ')'
        }
        return JsonResponse(data)

@login_required
def update_interest(request):
    if request.method == 'POST':
        user_id = request.POST.get('user_id')
        item_id = request.POST.get('item_id')

        user = User.objects.get(id=user_id)
        item = Item.objects.get(id=item_id)

        interest = request.POST.get('interest')

        form = UpdateInterestForm(request.POST)
        if form.is_valid():
            user_item = form.save(commit=False)
            user_item.user = user
            user_item.item = item

            if User_Item.objects.filter(user=user, item=item).count() > 0:
                user_item = User_Item.objects.get(user=user, item=item)

                if int(user_item.interest) == int(interest):
                    user_item.interest = None
                    user_item.save()
                    if user_item.rating == None and user_item.interest == None:
                        user_item.delete()
                    data = {
                        'message': 'You removed an interest.'
                    }
                    return JsonResponse(data)

                user_item.interest = interest

            user_item.save()
            user_item.item.calc_average()

            user.hide_all_updates_by_user_item(user_item)

            message = Update.generate_message_by_interest(int(interest))

            Update.objects.create(user=user,
                                  message=message,
                                  interaction=user_item,
                                  is_visible=True)

            for tag in item.tags.all():
                update_user_tag(user, tag)

            data = {
                'message': "You're " + message + " " + item.name
            }
            return JsonResponse(data)

@login_required
def recommend_item(request):
    if request.method == 'POST':
        item_id = request.POST.get('item_id')
        user_id = request.POST.get('user_id')

        item = Item.objects.get(id=item_id)
        user = User.objects.get(id=user_id)

        message = '@' + request.user.username + " recommended you: '" + item.name + "'"
        Notification.objects.create(user=user, message=message)

        data = {
            'message': 'You recommended @' + user.username + ' an item!'
        }

        return JsonResponse(data)

@login_required
def private_user_tag(request, user_tag_id):
    user = request.user
    user_tag = User_Tag.objects.get(id=user_tag_id)
    if user_tag.user == user:
        if user_tag.is_private:
            user_tag.is_private = False
        else:
            user_tag.is_private = True
        user_tag.save()
    return redirect(reverse('rating_list', kwargs={'username': user.username}) + '?tag=' + user_tag.tag.name)

@login_required
def favorite_user_tag(request):
    if request.POST:
        user_tag_id = request.POST.get('user_tag_id')
        user_tag = get_object_or_404(User_Tag, id=user_tag_id)

        if Favorite_User_Tag.objects.filter(user=request.user, user_tag=user_tag).count() > 0:
            Favorite_User_Tag.objects.filter(user=request.user, user_tag=user_tag).delete()
            data = {
                'message': 'Favorite'
            }
        else:
            Favorite_User_Tag.objects.create(user=request.user, user_tag=user_tag)

            data = {
                'message': 'Unfavorite'
            }

        return JsonResponse(data)

@login_required
def hide_update(request, update_id):
    update = get_object_or_404(Update, id=update_id)

    if update.user == request.user:
        update.is_visible = False
        update.save()

    return redirect('user', update.user.username)

def get_search_results(request):
    if request.is_ajax():
        q = request.GET.get('term', '')
        results = []

        items = Item.objects.filter(name__icontains=q)
        users = User.objects.filter(username__icontains=q)
        tags = Tag.objects.filter(name__icontains=q)

        for item in items:
            item_json = {}
            item_json['id'] = item.id
            item_json['label'] = item.name
            item_json['value'] = "item/" + str(item.id)
            results.append(item_json)

        for user in users:
            user_json = {}
            user_json['id'] = user.id
            user_json['label'] = "@" + user.username
            user_json['value'] = "user/" + user.username
            results.append(user_json)

        for tag in tags:
            tag_json = {}
            tag_json['id'] = tag.id
            tag_json['label'] = "#" + tag.name
            tag_json['value'] = "items?tag=" + tag.name
            results.append(tag_json)

        data = json.dumps(results)
    else:
        data = 'fail'
    mimetype = 'application/json'
    return HttpResponse(data, mimetype)

@login_required
def notifications(request):
    notifications = Notification.objects.filter(user=request.user).order_by('-timestamp')
    for notification in notifications:
        if notification.was_new:
            notification.was_new = False
            notification.save()
        if notification.is_new:
            notification.is_new = False
            notification.was_new = True
            notification.save()

    return render(request, 'notifications.html', {'notifications': notifications, })

@login_required
def update_following(request):
    list = request.POST.getlist('list[]', [])

    for x in list:
        y = json.loads(x)
        user_tag_id = y['user_tag_id']

        user_tag = User_Tag.objects.get(id=user_tag_id)

        if y['is_following'] == True:
            if request.user.is_following(user_tag) == False:
                Following.objects.create(follower=request.user, user_tag=user_tag)
                message = "@" + request.user.username + " is following your '" + user_tag.tag.name + "' user tag!"
                Notification.objects.create(user=user_tag.user, message=message, is_new=True, was_new=False)

        else:
            if request.user.is_following(user_tag) == True:
                Following.objects.get(follower=request.user, user_tag=user_tag).delete()

    data = {
        'message': 'Success!'
    }
    return JsonResponse(data)


import jsonschema_default
import json

default_obj = jsonschema_default.create_from("../docs/schema.json")
json_str = json.dumps(default_obj, indent=2)
go_default = '''package config

const DefaultConfigJSON = `
'''+json_str+'''
`
'''

filename = '../config/default.go'
text_file = open(filename, "w")
n = text_file.write(go_default)
text_file.close()

filename = '../docs/example.json'
text_file = open(filename, "w")
n = text_file.write(json_str)
text_file.close()


from flask import Flask, request, render_template
import tensorflow as tf
import numpy as np
from PIL import Image
import io

app = Flask(__name)

# 모델 로드 (VGG16)
model = tf.keras.applications.VGG16(weights='imagenet')

# 모델 예측 함수
def predict_image(image_data):
    img = Image.open(io.BytesIO(image_data))
    img = img.resize((224, 224))  # 모델의 입력 크기에 맞게 이미지 크기 조정
    img = np.array(img)
    img = tf.keras.applications.vgg16.preprocess_input(img)  # 이미지 전처리
    img = np.expand_dims(img, axis=0)  # 배치 차원 추가
    prediction = model.predict(img)
    return tf.keras.applications.vgg16.decode_predictions(prediction)

@app.route('/', methods=['GET', 'POST'])
def upload_image():
    if request.method == 'POST':
        if 'file' not in request.files:
            return render_template('index.html', prediction="이미지를 업로드하세요")

        file = request.files['file']
        if file.filename == '':
            return render_template('index.html', prediction="파일을 선택하세요")

        if file:
            image_data = file.read()
            prediction = predict_image(image_data)
            # 상위 3개 예측 결과 표시
            top3_predictions = prediction[0][:3]
            return render_template('index.html', prediction=top3_predictions)

    return render_template('index.html', prediction=None)

if __name__ == '__main__':
    app.run(debug=True)


import csv
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')

NO = "0"
YES = "1"

''' if you change the read file to the set that's already transformed,
switch global variable TRANSFORM to True
this saves the data quality pie chart saved in Output '''

# READ_FILE = "Output/conditional_transformed.csv"
READ_FILE = "Data/cs-training.csv"
TRANSFORM = False

WRITE_TEXT = "Output/Summary-Statistics.txt"
MISSING = 'NA'
NUMERICAL = [1,2,3,4,5,6,7,8,9,10,11]
CLASS_ID = 1
HISTOGRAM = [1]

def graph_data_quality(counts, conditional, maps):
	'''
	Takes two counting dictionaries and the maps dictionary,
	Graphs the target classifications against missing values
	To get some perspective on how much we're relying on the data being
	Not a mistake
	'''
	fig = plt.figure()
	d = counts[maps[CLASS_ID]]
	fracs = [d[YES], d[NO]]
	labels = [YES, NO]

	for each in list(counts.keys()):
		if MISSING in counts[each]:
			fracs.append(counts[each][MISSING])
			labels.append(each)

	plt.pie(fracs, labels=labels)
	plt.title("Comparision of target counts to missing values \n Or Data Quality")
	if not TRANSFORM:
		fig.savefig("Output/Images/DataQuality.png")

def get_initial_dicts(row):
	'''
	Takes a csv row, returns a two-way dictionary of headers
	And initialized counts dict with each header : {}
	'''
	maps = {}
	counts = {}
	for idx, field in enumerate(row):
		if idx > 0:
			counts[field] = {}
			maps[idx] = field
			maps[field] = idx
	return maps, counts

def read():
	'''
	Reads in the global csv file
	Returns four dictionaries:
		counts: {column : {instance: count, instance: count},
				column : {instance: cout, instance: count}}
		maps: {column: index, index: column}
		numerical: {Age: list, GPA: list, Days Missed: list}
		conditional: {Age: {"Yes": list, "No": list}, GPA: {"Yes": list, "No":list}
	'''
	numerical = {}
	conditional = {}

	with open(READ_FILE, 'rU') as f:
		fields = csv.reader(f)
		maps, counts = get_initial_dicts(next(fields))

		# count all the values, add to the dictionaries
		for row in fields:
			for idx, val in enumerate(row):

				# skip the index row
				if idx == 0:
					pass
				else:
					header = maps[idx]
					if val not in counts[header]:
						counts[header][val] = 1
					else:
						counts[header][val] += 1

					if idx in NUMERICAL:
						try:
							i = float(val)
							if header not in numerical:
								numerical[header] = []
							numerical[header].append(i)

							if header not in conditional:
								conditional[header] = {}
							if row[CLASS_ID] not in conditional[header]:
								conditional[header][row[CLASS_ID]] = [i]
							else:
								conditional[header][row[CLASS_ID]].append(i)

						except:
							pass

	return counts, maps, numerical, conditional

def missing(each, counts, f):
	'''
	Takes a key, a dictionary, and a csv pointer object
	Writes the number of missing values to the csv
	'''
	if MISSING in counts[each]:
		f.write("\nMissing Values: {}".format(counts[each][MISSING]))
	else:
		f.write("\nMissing Values: 0")

def graph_distribution():
	return

def graph_hist(header, inner_dict):
	'''
	Takes column header and inner dict of types and counts,
	Creates a histogram
	'''
	labels = []
	values = []
	for x, y in inner_dict.items():
		if x == MISSING:
			labels.append("Missing")
		else:
			labels.append(x)
		values.append(y)
	xs = np.arange(len(labels))
	x_ticks = xs + 0.4
	fig = plt.figure()
	plt.title("Histogram for {}".format(header))
	plt.xlabel("Possible Values")
	plt.ylabel("Number of each value")
	plt.xticks(x_ticks, labels)
	plt.bar(xs, values)
	fig.savefig("Output/Images/Histogram-{}.png".format(header))

def summary(counts, maps, numerical, conditional):
	with open(WRITE_TEXT, "w") as f:
		f.write("Summary Statistics")
		for each in list(counts.keys()):
			mode = 0
			mode_str = ""
			f.write("\n")
			f.write("\n{}".format(each))
			missing(each, counts, f)

			for val in counts[each]:
				if val == MISSING:
					pass
				else:
					c = counts[each][val]
					if c > mode:
						mode = c
						mode_str = val

			f.write("\nMode: {}, Count: {}".format(mode_str, mode))

			if maps[each] in NUMERICAL:
				write_logic(f, numerical, conditional, each)
			graph_distribution()

			if maps[each] in HISTOGRAM:
				graph_hist(each, counts[each])

		find_correlations(numerical, f)
		graph_data_quality(counts, conditional, maps)

# also want to add conditional missing values
def write_logic(f, numerical, conditional, each):
	'''
	Takes file pointer object, two dictionaries, and a key,
	Writes to file f based on the key
	'''
	f.write("\nMin: {}".format(min(numerical[each])))
	f.write("\nMax: {}".format(max(numerical[each])))
	f.write("\nMean: {}".format(np.mean(numerical[each])))
	f.write("\nMedian: {}".format(np.median(numerical[each])))
	f.write("\nStd Dev: {}".format(np.std(numerical[each])))
	f.write("\nConditional No Mean: {}".format(np.mean(conditional[each][NO])))
	f.write("\nCond. Yes Mean: {}".format(np.mean(conditional[each][YES])))
	f.write("\nCond. No Min: {}".format(min(conditional[each][NO])))
	f.write("\nCond. Yes Min: {}".format(min(conditional[each][YES])))
	f.write("\nCond. No Max :{}".format(max(conditional[each][NO])))
	f.write("\nCond. Yes Max :{}".format(max(conditional[each][YES])))

# graph correlation matrix here
# want to also get t-values here for each bivariate correlation
# then do multivariate correlations of each against all the others, plus t_values
def find_correlations(numerical, f):
	'''
	Takes dict of numerical values and pointer to write file
	Writes correlations of each variable against the other
	'''
	f.write("\n")
	f.write("\nCross-Variable Correlations")
	for each in list(numerical.keys()):
		f.write("\n")
		f.write("\n   {}".format(each))
		for k in list(numerical.keys()):
			if (k != each):
				try:
					beta = np.corrcoef(numerical[each], numerical[k])[1][0]
					f.write("\n     - {}: {}".format(k, beta))
				except:
					pass

if __name__ == "__main__":
	counts, maps, numerical, conditional = read()
	summary(counts, maps, numerical, conditional)


from django.conf.urls import patterns, include, url
from rest_framework.urlpatterns import format_suffix_patterns
from tracker.views import UserList, UserDetail, GroupList, GroupDetail, ArtistList, ArtistDetail, AlbumList, AlbumDetail, AlbumHoldingList, AlbumHoldingDetail, artist_album_list, user_holder_list, ArtistAutocompleteList, artist_album_autocomplete_list

urlpatterns = patterns('',
    url(r'^user/$', UserList.as_view(), name='user-list'),
    url(r'^user/(?P<pk>\d+)/$', UserDetail.as_view(), name='user-detail'),
    url(r'^group/$', GroupList.as_view(), name='group-list'),
    url(r'^group/(?P<pk>\d+)/$', GroupDetail.as_view(), name='group-detail'),
    url(r'^artist/$', ArtistList.as_view(), name='artist-list'),
    url(r'^artist/(?P<pk>\d+)/$', ArtistDetail.as_view(), name='artist-detail'),
    url(r'^album/$', AlbumList.as_view(), name='album-list'),
    url(r'^album/(?P<pk>\d+)/$', AlbumDetail.as_view(), name='album-detail'),
    url(r'^albumholding/$', AlbumHoldingList.as_view(), name='albumholding-list'),
    url(r'^albumholding/(?P<pk>\d+)/$', AlbumHoldingDetail.as_view(), name='albumholding-detail'),

    # Autocomplete views
    url(r'^autocomplete/artist/$', ArtistAutocompleteList.as_view(), name='artist-autocomplete'),
    url(r'^autocomplete/artist/(?P<pk>\d+)/albums/$', artist_album_autocomplete_list, name='artist-album-autocomplete'),

    # Filtered views
    url(r'^artist/(?P<pk>\d+)/albums/$', artist_album_list),
    url(r'^user/(?P<pk>\d+)/holders/$', user_holder_list),
)

urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'api'])

# Default login/logout views
urlpatterns += patterns('',
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
)


from mock import patch

import jenkins
from tests.base import JenkinsTestBase
from tests.helper import build_response_mock

class JenkinsGetJobFolderTest(JenkinsTestBase):

    @patch.object(jenkins.Jenkins, 'jenkins_open')
    def test_simple(self, jenkins_mock):
        folder, name = self.j._get_job_folder('my job')
        self.assertEqual(folder, '')
        self.assertEqual(name, 'my job')

    @patch.object(jenkins.Jenkins, 'jenkins_open')
    def test_single_level(self, jenkins_mock):
        folder, name = self.j._get_job_folder('my folder/my job')
        self.assertEqual(folder, 'job/my folder/')
        self.assertEqual(name, 'my job')

    @patch.object(jenkins.Jenkins, 'jenkins_open')
    def test_multi_level(self, jenkins_mock):
        folder, name = self.j._get_job_folder('folder1/folder2/my job')
        self.assertEqual(folder, 'job/folder1/job/folder2/')
        self.assertEqual(name, 'my job')

class JenkinsCreateFolderTest(JenkinsTestBase):

    @patch('jenkins.requests.Session.send', autospec=True)
    def test_create_new_folder(self, session_send_mock):
        session_send_mock.side_effect = iter([
            build_response_mock(200, self.crumb_data),  # crumb
            build_response_mock(200, None),  # request
        ])
        self.j.create_folder(u'New-Test-Folder')

    @patch('jenkins.requests.Session.send', autospec=True)
    def test_create_new_folder_that_already_exists(self, session_send_mock):
        session_send_mock.side_effect = iter([
            build_response_mock(200, self.crumb_data),  # crumb
            build_response_mock(400),  # request
        ])

        folder_name = u'Folder-that-already-exists'

        with self.assertRaises(jenkins.JenkinsException) as context_manager:
            self.j.create_folder(folder_name)
        self.assertEqual(
            str(context_manager.exception),
            u'Error creating folder [' + folder_name + ']. Probably it already exists.')

    @patch('jenkins.requests.Session.send', autospec=True)
    def test_create_new_folder_that_already_exists_ignoring_errors(self, session_send_mock):
        session_send_mock.side_effect = iter([
            build_response_mock(200, self.crumb_data),  # crumb
            build_response_mock(400),  # request
        ])

        self.j.create_folder(u'Folder-that-already-exists', ignore_failures=True)


import datetime
import pendulum
from airflow.decorators import dag, task
import pandas
from sqlalchemy import create_engine, text

CONN_STRING = "sqlite:///melb_housing_data.sqlite3"
MELB_URI = "https://cs.famaf.unc.edu.ar/~mteruel/datasets/diplodatos/melb_data.csv"
AIRBNB_URI = "https://cs.famaf.unc.edu.ar/~mteruel/datasets/diplodatos/cleansed_listings_dec18.csv"

@dag(
    'etl_dag',
    schedule_interval="0 0 * * *",
    start_date=pendulum.datetime(2022, 5, 1, tz="UTC"),
    catchup=False,
    dagrun_timeout=datetime.timedelta(minutes=60),
)
def Etl():
    @task
    def extract():
        # NOTA: Se consideró "extracción" a la obtención de los datos desde el repositorio remoto, para
        # cargarlos en la base de datos SQLite local. Se usa esta base de datos para compartir la información
        # entre las Task en lugar de usar XComs.
        #
        # Por lo tanto, en cada task los datos se van a obtener desde la BD
        engine = create_engine(CONN_STRING, echo=True)

        melb_df = pandas.read_csv(MELB_URI)
        melb_df.to_sql("mlb_data", con=engine, if_exists="replace")

        airbnb_df_all = pandas.read_csv(AIRBNB_URI)
        airbnb_df_all.to_sql("airbnb_data", con=engine, if_exists="replace")

    @task
    def transform():
        engine = create_engine(CONN_STRING, echo=True)
        melb_df = pandas.read_sql_table('mlb_data', engine)
        airbnb_df_all = pandas.read_sql_table('airbnb_data', engine)

        colums_predict_price = [
            "Address",
            "Rooms",
            "Price",
            "Bedroom2",
            "Bathroom",
            "Landsize",
            "Car",
        ]
        melb_df.loc[:, colums_predict_price]

        min_price = melb_df.Price.quantile(0.05)
        max_price = melb_df.Price.quantile(0.95)

        melb_df_filtered = melb_df[
            (melb_df["Price"] > min_price) & (melb_df["Price"] < max_price)
        ]

        airbnb_df_all["zipcode"] = pandas.to_numeric(airbnb_df_all.zipcode, errors="coerce")

        airbnb_df_all["airbnb_zipcode_counter"] = airbnb_df_all.zipcode.value_counts()

        airbnb_df_all.loc[:3, ["accommodates", "bathrooms", "bedrooms", "beds"]]

        relevant_cols = [
            "price",
            "weekly_price",
            "monthly_price",
            "security_deposit",
            "review_scores_rating",
            "review_scores_location",
            "zipcode",
        ]

        airbnb_price_by_zipcode = (
            airbnb_df_all[relevant_cols]
            .groupby("zipcode")
            .agg(
                airbnb_price_mean=("price", "mean"),
                airbnb_price_median=("price", "median"),
                airbnb_price_min=("price", "min"),
                airbnb_price_max=("price", "max"),
                airbnb_weekly_price_mean=("weekly_price", "mean"),
                airbnb_monthly_price_mean=("monthly_price", "mean"),
                airbnb_security_deposit_mean=("security_deposit", "mean"),
                airbnb_review_scores_rating_mean=("review_scores_rating", "mean"),
                airbnb_review_scores_location_mean=("review_scores_location", "mean"),
            )
            .reset_index()
        )

        reg_mayor_50 = airbnb_df_all[airbnb_df_all["airbnb_zipcode_counter"] >= 25]
        zipcode_reg = reg_mayor_50["zipcode"]
        airbnb_price_by_zipcode_filtered = airbnb_price_by_zipcode[
            airbnb_price_by_zipcode["zipcode"].isin(zipcode_reg)
        ]

        merged_sales_df = melb_df_filtered.merge(
            airbnb_price_by_zipcode_filtered, how="left", left_on="Postcode", right_on="zipcode"
        )
        merged_sales_df.to_sql("merged_sales_data", con=engine, if_exists="replace")

    @task
    def load():
        # En este punto, el "load" en lugar de cargar el resultado en el data warehouse, se
        # desacrgó en un archivo csv.
        engine = create_engine(CONN_STRING, echo=True)
        merged_sales_df = pandas.read_sql_table('merged_sales_data', engine)
        merged_sales_df.to_csv("melb_data_extended_{}.csv".format(datetime.date.today()), index=None)

    extract() >> transform() >> load()

dag = Etl()


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

from functools import partial
import numpy as np
import wandb
import sys

# ---------- debug functions --------------------------------
def debug(*mes): print("D> ",mes)
# ---------- Basic Functions ------------------------

def split_to_chunks(arr:torch.Tensor, chunk_size=8):
    "Split the array into chunks"
    chunked =  [arr[i:i + chunk_size] for i in range(0, len(arr), chunk_size)]
    # 0-padding last chunk
    if len(chunked[-1]) < chunk_size:
        padding = torch.tensor([0] * (chunk_size - len(chunked[-1])), dtype=arr.dtype).to( chunked[-1].device)
        chunked[-1] = torch.cat((chunked[-1], padding))
    return chunked
assert tuple(torch.stack(split_to_chunks(torch.randn(15)>0)).shape)==(2,8)

def unpackbits(x, bitorder='big'):
    assert x.dtype == torch.uint8
    if bitorder == 'big':
        bit_indices = torch.arange(7, -1, -1, dtype=torch.int64, device=x.device)
    elif bitorder == 'little':
        bit_indices = torch.arange(0, 8, dtype=torch.int64, device=x.device)
    else:
        raise ValueError("bitorder must be either 'big' or 'little'")
    new_shape = list(x.shape)
    new_shape[-1] *= 8
    return ((x[..., None] >> bit_indices) & 1).type(torch.int8).reshape(new_shape)

assert unpackbits(torch.tensor([7], dtype=torch.uint8)).tolist() == [0, 0, 0, 0, 0, 1, 1, 1]
assert list(unpackbits(torch.randn((32,8)).type(torch.uint8)).shape) == [32, 64]
assert list(unpackbits(torch.randn((32,8,3)).type(torch.uint8)).shape) == [32, 8, 24]

def packbits(x, bitorder='big')-> torch.Tensor:
    "Bit-packing 1D tensors of arbitrary length (with 0-padding)"
    if bitorder == 'big':
        bit_indices = torch.arange(7, -1, -1, dtype=torch.int64, device=x.device)
    elif bitorder == 'little':
        bit_indices = torch.arange(0, 8, dtype=torch.int64, device=x.device)
    else:
        raise ValueError("bitorder must be either 'big' or 'little'")
    new_shape = list(x.shape)
    new_shape[-1] = int(np.ceil(new_shape[-1]/8))
    x_padded = F.pad(x, (0, (8-x.shape[-1]%8)%8), value=0)
    return torch.stack([(xx * (1 << bit_indices)).sum(dim=-1, dtype=torch.uint8)
        for xx in split_to_chunks(x_padded.flatten())]).reshape(new_shape)
assert packbits(torch.tensor([0, 0, 0, 0, 0, 1, 1, 1], dtype=torch.uint8)).tolist() == [7]
assert tuple(packbits(torch.randn((32,64))>0).shape)==(32,8)
assert tuple(packbits(torch.randn((32,64,3))>0).shape)==(32,64,1)

def a2bstr(arr):  # arr:list,torch.Tensor
    if type(arr) == torch.Tensor: arr = arr.tolist()
    arr = np.array(arr)
    return np.vectorize(lambda x:f"{int(x):08b}")(arr).tolist()

def xnor(v1:torch.Tensor,v2:torch.Tensor):
    "２つのベクトルのxnor演算（ビットが同じなら１）を行う。"
    return torch.bitwise_not(torch.bitwise_xor(v1, v2))
v1 = torch.tensor([0b0001, 0b0001], dtype=torch.uint8)
v2 = torch.tensor([0b0011, 0b0001], dtype=torch.uint8)
assert a2bstr(xnor(v1,v2))==['11111101', '11111111']
assert tuple(xnor(True, torch.randn((2,3,4)).type(torch.uint8)).shape)==(2,3,4), "ブロードキャストしてビット反転"

def popcount(arr:torch.Tensor, dim=-1):
    """popcount（1のビッチの数）を数える。arrはuint8のみ。
    Parameters
    -----------
    arr
    dim:int - 集計対象の軸。None:全軸で集計して0次テンソルを返す
    """
    assert type(arr) == torch.Tensor, "input must be cupy.ndarray"
    assert arr.dtype == torch.uint8, "dtype must be cupy.uint8"
    return torch.count_nonzero(unpackbits(arr), dim=dim)
assert popcount(torch.tensor([0b11111101, 0b11111111], dtype=torch.uint8))==15
assert list(popcount(torch.randn((32,8,3)).type(torch.uint8)).shape) == [32,8]
assert popcount(torch.tensor([[7,7],[7,7]], dtype=torch.uint8), dim=None).tolist() == 12

def sum_votes(flip_in, sum_axis):
    "flipを指定軸で、反転すべきか票数を合計する。"
    vote = unpackbits(flip_in)*2-1   # 1 -> 1, 0 -> -1
    return vote.sum(axis=sum_axis, dtype=torch.int32)

def standardize_bitbalance(tensor:torch.Tensor, n:int, pts_sigma=15)->torch.Tensor:
    """二項分布（binomial distribution）として正規化されたint8テンソルを返す
    Parameters
    ----------
    n:int      dimension of binomial distribution. (usualy, total number of bits)
    pts_sigma:int  pts/sigma after standardization
    """
    return (tensor/(0.5*np.sqrt(n))*pts_sigma).clamp(-127,127).type(torch.int8)
assert tuple(standardize_bitbalance(torch.randn((4,8)).type(torch.int32), 64).shape) == (4,8)

def standardize_popcount(tensor:torch.Tensor, n:int, pts_sigma=15)->torch.Tensor:
    """二項分布（binomial distribution）として正規化されたuint8テンソルを返す
    平均 127, 標準偏差 pts_sigma(15)
    Parameters
    ----------
    n:int      dimension of binomial distribution. (usualy, total number of bits)
    pts_sigma:int  pts/sigma after standardization
    """
    std = tensor.type(torch.float32).std()
    return ((tensor-0.5*n)/std*pts_sigma+127).clamp(0,255).type(torch.uint8)
assert tuple(standardize_popcount(torch.randn((4,8)).type(torch.int32), 64).shape) == (4,8)

def calc_1Ratio(x:torch.Tensor)->float:
    """1のビットの割合を求める
    用途：flipに対して - 反転する割合（Trueの数 / 全要素数）を返す
    ビットバランス - 0.5なら均衡、1なら全部True
    """
    sum_bits = x.numel()*8
    return float(popcount(x, dim=None)/sum_bits)
assert calc_1Ratio(torch.tensor([[7,7],[7,7]], dtype=torch.uint8)) == 0.375

# ------------------- BNN Modules ----------------
class WandbLogger():
    _dict = {}
    def log(self, dict):
        """
        Usage
        ---------
        log float  : logger.log({"BinaryTensor flip_ratio:": floatValue})
        log tensor : logger.log({"x_grad": wandb.Histogram(x_grad.cpu().numpy())})
        """
        self._dict.update(dict)
    def clear(self): self._dict.clear()
    def getLog(self): return self._dict

logger = WandbLogger()

class BinaryTensor(nn.Module):
    "Binary Tensor Module class"
    def __init__(self, shape, weights=None):
        """
        Parameters
        ------------------
        weights : bitpacked uint8 Tensor (or None)
        """
        super().__init__()
        self.shape = shape
        self.weights = nn.Parameter(torch.randint(low=0, high=256, size=shape, dtype=torch.uint8), requires_grad=False) \
            if weights is None else nn.Parameter(torch.tensor(weights, dtype=torch.uint8), requires_grad=False)
        self.vote_accumulator = None
        self.flip_sigma = 0
        self.half_mask = None  # update... None:100%, 1:50%, 2:25%, ...

    def __repr__(self):
        return f"BinaryTensor(weights={self.shape}, dtype=uint8)"
    def has_accumulator(self):
        return hasattr(self, 'vote_accumulator') and self.vote_accumulator is not None
    def update(self):
        if not self.has_accumulator(): return
        flip = self.vote_accumulator > 127+15*self.flip_sigma
        mask = packbits(flip)
        if self.half_mask is not None:
            times_rand = self.half_mask
            rand_mask = torch.ones(mask.shape, dtype=torch.uint8)*255
            for j in range(times_rand): rand_mask &= torch.randint(0,256, mask.shape, dtype=torch.uint8)
            rand_mask = 255 ^ rand_mask  # when 2: 25% -> 75%
            mask &= rand_mask.to(mask.device)
        self.weights = self.weights.to(mask.device)
        self.weights ^= mask  # update weights (using xor)
        logger.log({"BinaryTensor update_ratio:": calc_1Ratio(mask)})
        del self.vote_accumulator  # Clear memory
        self.vote_accumulator = None

    def forward(self):
        return self.weights

    def backward(self, flip):
        """
        Parameters
        ------------------
        vote:Tensor(uint8)  flip vote (i.e. should flip or not)
        """
        if not self.has_accumulator():
            self.vote_accumulator = unpackbits(self.weights).type(torch.int32).to(flip.device)
            self.vote_accumulator[:] = 0

        logger.log({"w_flip_ratio:": calc_1Ratio(flip)})
        vote = unpackbits(flip).sum([0,1], dtype=torch.int32) # b,d,o,i -> o,i
        vote = standardize_popcount(vote, n=torch.tensor(flip.shape[0:2]).prod())
        logger.log({"w_vote": wandb.Histogram(vote.cpu().numpy())})

        self.vote_accumulator += vote  # o,i
        return None

    def __call__(self): return self.forward()

TH_DEPTH15 = [-1.53, -1.15, -0.89, -0.67, -0.49, -0.32, -0.16, 0.00, 0.16, 0.32, 0.49, 0.67, 0.89, 1.15, 1.53]
TH_DEPTH7 = [-1.15, -0.67, -0.32, 0.00, 0.32, 0.67, 1.15]
TH_DEPTH3 = [-0.67, 0.00, 0.67]
TH_DEPTH1 = [0.00]

class Binarize(nn.Module):
    """
    FP32入力をBinarizeするレイヤ

    期待する入力： FP32 tensor (normalized: mean 0, std 1)
    出力： uint8 tensor. axis:b,d,i(batch, depth, input)
    """
    def __init__(self, depth_ths:list=TH_DEPTH3):
        """
        depth_ths  list of threshold values. ex) [0] -> 1bit, [-1,0,1] -> 2bit
        """
        super().__init__()
        self.depth = torch.tensor(depth_ths).unsqueeze(0).unsqueeze(-1)  # b,d,i

    def forward(self, x):
        h = packbits(x.unsqueeze(1)>self.depth.to(x.device)).clone().detach()  # b,d,i
        return h
    def backward(self, flip, x):
        logger.log({"x_flip_ratio:": calc_1Ratio(flip)})
        sign = (unpackbits(self.forward(x).unsqueeze(2)))*2-1  # b,d,i -> b,d,o,i
        x_grad = (sign * unpackbits(flip)).sum([1,2], dtype=torch.float32) # b,d,o,i -> b,i
        x_grad /= x_grad.std()  # 標準偏差が１になるように標準化。期待平均値は0のまま変わらず。
        # x_grad /= 0.5*torch.tensor(flip.shape[1:3]).prod().sqrt()
        logger.log({"x_grad": wandb.Histogram(x_grad.cpu().numpy())})
        return x_grad
    def __call__(self, x): return self.forward(x)

class BMA(nn.Module):
    """Binary Multiply-Accumulate Layer Module class

    期待する入力： packed uint8 tensor
    出力：FP32 (normalized mean0, std1)
    """
    def __init__(self, in_features:int=1, out_features:int=1, depth_ths:list=TH_DEPTH3, weights=None, is_standardize=True, pts_sigma=15):
        """
        Parameters
        ------------------
        in_features:int  - numbers of input bits
        out_features:int - numbers of output bits
        depth_features:int - numbers of bit depth
        weights : uint8 Tensor (or None)
        """
        super().__init__()
        in_features = int(np.ceil(in_features/8))
        self.weights = BinaryTensor(shape=(out_features, in_features), weights=weights)
        self.depth_ths = depth_ths
        self.is_standardize = is_standardize
        self.pts_sigma = pts_sigma
        self.weights_next = None

    def forward(self, x):
        x = x.unsqueeze(-2) # b,d,i -> b,d,o,i
        w = self.weights().unsqueeze(0).unsqueeze(0).to(x.device) # o,i -> b,d,o,i
        h0 = xnor(w, x)
        n1 = popcount(h0).sum(axis=1, dtype=torch.int32)  # b,d,o,i -> b,o
        n_all = (h0.shape[-1]*8)*h0.shape[2]
        assert n1.max() < n_all
        o0 = (2*n1 - n_all).type(torch.float32).requires_grad_().to(x.device)
        logger.log({"BMA_o0": wandb.Histogram(o0.cpu().numpy())})
        o1 = o0/(0.5*torch.tensor(n_all).sqrt())
        logger.log({"BMA_o1": wandb.Histogram(o1.cpu().numpy())})
        return o1

    def backward(self, grad, x):
        grad = grad.unsqueeze(-1).unsqueeze(1)  # b,o -> b,d,o,i
        x = x.unsqueeze(-2)  # b,d,i -> b,d,o,i

        # Update weights
        ww = self.weights().unsqueeze(0).to(x.device)  # d,o,i -> b,d,o,i
        hh =  xnor(x, ww)  # b,d,o,i
        w_flip = torch.bitwise_xor(hh, (grad<0))  # grad:b,d,o -> b,d,o,i
        self.weights.backward(w_flip)
        self.weights.update()
        ww_new = self.weights().unsqueeze(0).to(x.device)  # d,o,i -> b,d,o,i

        # Calculate x flip
        h_new =  xnor(x, ww_new)  # b,d,o,i
        x_flip = torch.bitwise_xor(h_new, (grad<0)) # b,d,o,i
        return x_flip

    def __call__(self, x): return self.forward(x)


from nltk_setup import setup
from tkinter import *
from tkinter import font
from nltk.corpus import wordnet

def getSynonyms():  #Funktion för att hämta synonymer till inmatat ord
    ord = fönster.intext.get()
    synonymer = set()
    for syn in wordnet.synsets(ord):
        for lemma in syn.lemmas():
            synonymer.add(lemma.name())
    synonymer = [synonym.replace('_', ' ') for synonym in synonymer]
    fönster.resultat.config(text="Synonyms: " + ", ".join(synonymer))
    showResFrame()

def getDefinition():  #Funktion för att hämta definitionen för inmatat ord
    ord = fönster.intext.get()
    definition = wordnet.synsets(ord)
    if definition:
        fönster.resultat.config(text="Definition: " + definition[0].definition())
    else:
        fönster.resultat.config(text="Can not find a definition for the given word.")
    showResFrame()

def getAutonyms():  #Funktion för att hämta autonymer till inmatat ord
    ord = fönster.intext.get()
    autonymer = set()
    synonymer = wordnet.synsets(ord)
    for syn in synonymer:
        for lemma in syn.lemmas():
            if lemma.antonyms():
                autonymer.add(lemma.antonyms()[0].name())
    autonymer = [autonym.replace('_', ' ') for autonym in autonymer]
    fönster.resultat.config(text="Autonyms: " + ", ".join(autonymer))
    showResFrame()

def getWordClass():  #Funktion för att bestämma ordklarr för inmatat ord
    ord = fönster.intext.get()
    synset = wordnet.synsets(ord)
    klass = NONE
    for syn in synset:
        klass = syn.pos()
    if klass == "n":
        klass = "Substantive"
    elif klass == "v":
        klass = "Verb"
    elif klass == "a":
        klass = "Adjective"
    elif klass == "r":
        klass = "Adverb"
    else:
        for syn in synset:
            for lemma in syn.lemmas():
                synset_sat = wordnet.synsets(lemma.name())
                for syn_sat in synset_sat:
                    pos_sat = syn_sat.pos()
                    if pos_sat != "s":
                        klass = pos_sat
                        break
                if klass:
                    break
            if klass:
                break
        if klass:
            if klass == "n":
                klass = "Substantive"
            elif klass == "v":
                klass = "Verb"
            elif klass == "a":
                klass = "Adjective"
            elif klass == "r":
                klass = "Adverb"
        else:
            klass = "Unknown"
    fönster.resultat.config(text="Word class: " + klass)
    showResFrame()

def showResFrame(): #Öppnar gömt resultatfönster med nedre ram när användaren skickar begäran
    bd_frame.grid()
    bot_frame.grid()

def splash(): #Startskärm som laddar en fiktionell företagslogotyp
    global splash_screen
    splash_screen = Tk()
    splash_screen.title("LeAl Solutions")
    splash_screen.geometry("957x623")
    splash_img = PhotoImage(file="LeAl_logotyp.png", master=splash_screen)
    splash_label = Label(splash_screen, image=splash_img)
    splash_label.image = splash_img
    splash_label.pack()
    splash_screen.after(4000, splash_2)    #Timer för splashscreen
    splash_screen.mainloop()

def splash_2(): #Startskärm nr.2 som stänger föregående bild och laddar programmets egen logotyp
    global titelbild
    titelbild = Tk()
    titelbild.title("Mattias fantasticly spectacular english encyclopedia")
    titelbild.geometry("957x623")
    titel_img = PhotoImage(file="Programnamn.png", master=titelbild)
    titel_label = Label(titelbild, image=titel_img)
    titel_label.image = titel_img
    titel_label.pack()
    splash_screen.destroy()
    titelbild.after(4000, splash_screen_stäng)    #Timer för splashscreen

def splash_screen_stäng(): #Stänger andra bilden och startar själva huvudprogrammet i eget fönster
    titelbild.destroy()
    startWin()

def startWin(): #Huvudfönstret som lirar mot samtliga funktioner
    global fönster
    global bd_frame
    global bot_frame
    fönster = Tk()
    fönster.title("Mattias fantasticly spectacular english encyclopedia")
    fönster.maxsize(800,500)
    fönster.config(bg="skyblue")
    l_frame = Frame(fönster, width=200, height=480, bg="white", bd = 13)
    l_frame.grid(row=1,column=0, padx=20,pady=20)
    etikett = Label(l_frame, text="Enter a word:")
    fet_font = font.Font(weight="bold")
    etikett.config(fg="#426cb6", bg="white",font=fet_font)
    etikett.grid(row=1,column=1,padx=10,pady=5)
    fönster.intext = Entry(l_frame)
    fönster.intext.config(font=fet_font)
    fönster.intext.grid(row=1,column=3,padx=10,pady=5)
    knapp = Button(l_frame, text="Get synonyms", command= getSynonyms)
    knapp_font = font.Font(weight="bold", size=11)
    knapp.config(bg="skyblue", fg="#426cb6", font=knapp_font, activebackground="white")
    knapp.grid(row=3,column=1,padx=10,pady=10)
    knapp2 = Button(l_frame, text="Get definition", command= getDefinition)
    knapp2.config(bg="skyblue", fg="#426cb6", font=knapp_font, activebackground="white")
    knapp2.grid(row=3,column=3,padx=10,pady=10)
    knapp3 = Button(l_frame, text="Get autonyms", command= getAutonyms)
    knapp3.config(bg="skyblue", fg="#426cb6", font=knapp_font, activebackground="white")
    knapp3.grid(row=4,column=1,padx=10,pady=10)
    knapp4 = Button(l_frame, text="Get class", command= getWordClass)
    knapp4.config(bg="skyblue", fg="#426cb6", font=knapp_font, activebackground="white")
    knapp4.grid(row=4,column=3,padx=10,pady=10)
    bd_frame = Frame(fönster,width=180,height=250, bg="white",bd=5)
    bd_frame.grid(row=5,column=0,padx=10,pady=5)
    bot_frame = Frame(fönster, width=0, height=20, bg="skyblue")
    bot_frame.grid(row=7,column=0)
    fönster.resultat = Label(bd_frame, text=" ", wraplength=300, justify="left")
    fönster.resultat.config(bg="white", fg="#426cb6", font=knapp_font)
    fönster.resultat.grid(row=1,column=0,padx=10,pady=10)
    bd_frame.grid_forget()
    bot_frame.grid_forget()

setup()
splash()


from flask import Flask, render_template, request
import datetime
from flask import make_response
import json
import urllib

import feedparser

app =Flask(__name__)

DEFAULTS = {
    "publication" : "bbc",
    "city" : "London, UK",
    "currency_from" : "GBP",
    "currency_to" : "USD"
}

CURRENCY_URL = "https://openexchangerates.org//api/latest.json?app_id=2504504501d94da3aa68e00eec522526"
WEATHER_URL = "http://api.openweathermap.org/data/2.5/weather?q={}&units=metric&appid=49edf448fe3e52ba925bd5549dc6ffc6"

RSS_FEEDS = {
    "bbc": "http://feeds.bbci.co.uk/news/rss.xml",
    'cnn': 'http://rss.cnn.com/rss/edition.rss',
    "time": "http://time.com/feed/",
    "ndtv": "http://feeds.feedburner.com/ndtvnews-latest",
    "fortune" : "http://fortune.com/feed/"

}

@app.route("/")
def home():
    publication = request.args.get("publication")
    if not publication:
        publication = DEFAULTS["publication"]
    articles = get_news(publication)
    city = request.args.get("city")
    if not city:
        city = DEFAULTS["city"]
    weather = get_weather(city)
    currency_from = request.args.get("currency_from")
    if not currency_from:
        currency_from = DEFAULTS["currency_from"]
    currency_to = request.args.get("currency_to")
    if not currency_to:
        currency_to = DEFAULTS["currency_to"]
    rate, currencies = get_rate(currency_from, currency_to)
    return render_template("home.html", articles=articles, weather=weather,currency_from=currency_from, currency_to=currency_to,rate=rate, currencies=sorted(currencies), publication=RSS_FEEDS)

def get_news(query):
    if not query or query.lower() not in RSS_FEEDS:
        publication = DEFAULTS["publication"]
    else:
        publication = query.lower()
    feed = feedparser.parse(RSS_FEEDS[publication])
    return feed["entries"]

def get_weather(query):
    query = urllib.parse.quote(query)
    url = WEATHER_URL.format(query)
    data = urllib.request.urlopen(url).read().decode("utf-8")
    parsed = json.loads(data)
    weather = None
    if parsed.get("weather"):
        weather = {"description":parsed["weather"][0]["description"],"temperature":parsed["main"]["temp"],"city":parsed["name"]
                  }
    return weather

def get_rate(frm, to):
    all_currency = urllib.request.urlopen(CURRENCY_URL).read().decode("utf-8")
    parsed = json.loads(all_currency).get("rates")
    frm_rate = parsed.get(frm.upper())
    to_rate = parsed.get(to.upper())
    return (to_rate / frm_rate, parsed.keys())

if __name__ == "__main__":
    app.run(port=5000, debug=True)