import sys
import os

import pymel.core as pm
import maya.OpenMayaUI as omui
import maya.OpenMaya as OpenMaya
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

from PySide2 import QtCore
from PySide2 import QtGui
from PySide2 import QtWidgets
from PySide2 import QtUiTools
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile
from shiboken2 import wrapInstance

def shorten_string(string, max_length_including_dots=20):
    newString = string
    if (len(string) > max_length_including_dots):
        newString = newString[:max_length_including_dots-3]
        newString+= "..."
    return newString

def get_selection_as_string(selection):
    string = ", ".join([str(s) for s in selection])
    return string

class SelectionItem(QtWidgets.QListWidgetItem):
    def __init(self, *args, **kwargs):
        super(SelectionItem, self).__init__(*args, **kwargs)
        self.key = ""
        self.selection = []

    def setKey(self, string):
        self.key = string

    def setSel(self, sel):
        self.selection = sel

    def getSelection(self):
        return self.selection

def maya_main_window():
    main_window_ptr = omui.MQtUtil.mainWindow()
    return wrapInstance(long(main_window_ptr), QtWidgets.QWidget)

class CreateSelectionHistoryUI(MayaQWidgetDockableMixin, QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(CreateSelectionHistoryUI, self).__init__(*args, **kwargs)
        self.setWindowTitle("Selection History v1.0")
        self.initUI()
        self.callback_id = OpenMaya.MEventMessage.addEventCallback("SelectionChanged", self.cb_selection_changed)
        self.setParent(maya_main_window())
        self.setWindowFlags(QtCore.Qt.Window)

        self.selected = ""
        self.selected_saved = ""
        self.save_names = []
        self.last_selection = []
        self.ignore_next_selection = False

    def cb_selection_changed(self, *args, **kwargs):
        if not self.ignore_next_selection:
            current_selection = pm.ls(sl=True, flatten=True)
            min_elements_required = self.ui.include_single_checkbox.isChecked() and 1 or 2

            if (len(current_selection) >= min_elements_required):
                current_selection = pm.ls(sl=True, flatten=False)
                if not (set(self.last_selection) == set(current_selection)):
                    self.last_selection = current_selection
                    selection_string = get_selection_as_string(current_selection)
                    count = self.ui.selection_history_list.count()

                    item = SelectionItem(selection_string)
                    if (count >= self.ui.max_history.value()):
                        item = self.ui.selection_history_list.takeItem(count-1)
                        item.setText(selection_string)

                    item.setKey(selection_string)
                    item.setSel(current_selection)

                    self.ui.selection_history_list.insertItem(0, item)
                    self.layout().update()

        else: self.ignore_next_selection = False

    def initUI(self):
        loader = QUiLoader()

        splitPath = os.path.realpath(__file__).split("\\")
        uiPath = "/".join(splitPath[:-1]) + "/selection_history.ui"

        file = QFile(uiPath)
        file.open(QFile.ReadOnly)
        self.ui = loader.load(file)
        file.close()

        #Init ui
        self.ui.closeEvent = self.closeEvent
        self.ui.setParent(maya_main_window())
        self.ui.setWindowFlags(QtCore.Qt.Window)
        self.ui.show()
        self.setCentralWidget(self.ui)

        #Init signals
        self.ui.selection_history_list.currentItemChanged.connect(self.on_item_changed)
        self.ui.selection_history_list.itemDoubleClicked.connect(self.on_item_double_clicked)
        self.ui.saved_selections_list.currentItemChanged.connect(self.on_item_changed_saved)
        self.ui.saved_selections_list.itemDoubleClicked.connect(self.on_item_double_clicked_saved)
        self.ui.export_button.clicked.connect(self.on_export)
        self.ui.import_button.clicked.connect(self.on_import)
        self.ui.save_button.clicked.connect(self.on_save)
        self.ui.clear_history_button.clicked.connect(self.on_clear)
        self.ui.clear_saved_button.clicked.connect(self.on_clear_saved)

    def dockCloseEventTriggered(self):
        OpenMaya.MMessage.removeCallback(self.callback_id)
        print("Closed")

    def closeEvent(self, event):
        #remove callback and close window
        print("Closed.")
        OpenMaya.MMessage.removeCallback(self.callback_id)
        event.accept()

    def on_export(self):
        count = self.ui.saved_selections_list.count()
        if count == 0:
            print("There is nothing to export.")
        else:
            fileName = QtWidgets.QFileDialog.getSaveFileName(self, ("Export Selection"), self.file_dir, ("Text Files (*.txt)"))
            if (fileName[0]):
                export_file = open(fileName[0], "w+")
                reversed_names = self.save_names[::-1]

                for i in range(count):
                    name = reversed_names[i]
                    item = self.ui.saved_selections_list.item(i)
                    selection_string = "|".join(str(s) for s in item.getSelection())
                    export_file.write(name + "|")
                    export_file.write(selection_string + "\n")
                export_file.close()

    def on_import(self):
        fileName = QtWidgets.QFileDialog.getOpenFileName(self, ("Import Selection"), self.file_dir, ("Text Files (*.txt)"))

        if (fileName[0]):
            import_file = open(fileName[0], "r")

            selections = import_file.readlines()
            for selection in selections[::-1]:
                split_selection = selection.split('|')
                name = split_selection[0]
                selection_string = get_selection_as_string(split_selection[1:])

                item = SelectionItem(name)
                item.setKey(selection_string)
                item.setSel(split_selection[1:])
                self.ui.saved_selections_list.insertItem(0, item)

            import_file.close()

    def on_save(self):
        name = self.ui.lineEdit.text()
        selection = pm.ls(sl=True, flatten=False)
        print(selection)
        if selection:
            selection_string = get_selection_as_string(selection)

            if not name:
                name = get_selection_as_string(selection)

            proper_name = self.get_proper_name(name)

            item = SelectionItem(proper_name)
            item.setKey(selection_string)
            item.setSel(selection)
            self.ui.saved_selections_list.insertItem(0, item)
            self.save_names.append(proper_name)

    def on_load(self):
        #self.ui.selection_history_list.takeItem(self.ui.selection_history_list.currentRow())
        pm.select(self.selected.getSelection())

    def on_load_saved(self):
        #self.ui.selection_history_list.takeItem(self.ui.selection_history_list.currentRow())
        pm.select(self.selected_saved.getSelection())

    def on_clear(self):
        self.ui.selection_history_list.clear()
        self.last_selection = []

    def on_clear_saved(self):
        self.ui.saved_selections_list.clear()

    def on_item_changed(self):
        self.selected = self.ui.selection_history_list.currentItem()
        if (not self.ui.double_click_checkbox.isChecked()):
            self.on_load()

    def on_item_changed_saved(self):
        self.selected_saved = self.ui.saved_selections_list.currentItem()
        if (not self.ui.double_click_checkbox.isChecked()):
            self.on_load_saved()

    def on_item_double_clicked(self):
        if (self.ui.double_click_checkbox.isChecked()):
            self.ignore_next_selection = True
            self.on_load()

    def on_item_double_clicked_saved(self):
        if (self.ui.double_click_checkbox.isChecked()):
            self.on_load_saved()

    def get_proper_name(self, name):
        """ Returns a new name if the history already contains
        the name. A number in parentheses is added and incremented
        until we have a unique name.
        """
        if not name:
            return ""

        new_name = name
        while (new_name in self.save_names):
            if new_name[len(new_name)-1] == ')':
                open_brace = new_name.find('(')
                if open_brace != -1:
                    brace_contents = str(new_name[open_brace+1:len(new_name)-1])
                    if brace_contents.isdigit():
                        number = int(brace_contents)
                        new_name = new_name[:open_brace]+'('+str(number+1)+')'
            else:
                new_name += ("(1)")
        return new_name

import sys
import os

def run():
    app = QApplication.instance()
    win = CreateSelectionHistoryUI()
    win.show(dockable=True)
    app.exec_()


# quickScaleFix.py - Python Script

# DESCRIPTION: Tool for quick scale adjustment
# REQUIRE: Python3
# AUTHOR: BulinThira - GitHub

import maya.cmds as mc
import math

def scaleLayout():
    if mc.window('scaleLayout_window', q=True, ex=True):
        mc.deleteUI('scaleLayout_window', window=True)
    mc.window('scaleLayout_window', title='Quick scale adjustment')
    mc.columnLayout(adj=True)

    mc.frameLayout(label='Scale by Unit', bgc=(0.1, 0.5, 0.5))
    mc.rowLayout(numberOfColumns=4)
    mc.text(label='source')
    mc.textField('source_TF', w=150)
    mc.button(label='Get', w=50, c=getSource)
    mc.button(label='Create Ruler', w=100, c=createSourceRuler)
    mc.setParent('..')

    mc.rowLayout(numberOfColumns=4)
    mc.text(label='target ')
    mc.textField('target_TF', w=150)
    mc.button(label='Get', w=50, c=getTarget)
    mc.button(label='Create Ruler', w=100, c=createTargetRuler)
    mc.setParent('..')

    mc.button('ss_but', label='Set scale', c=scaleSetting)

    mc.showWindow('scaleLayout_window')
    mc.window('scaleLayout_window', e=True, wh=(350,150))

def msRuler(*args):
    #ruler = mc.polyCube()
    mc.polyCube(n='msRuler', d=0.5)
    mc.move(0,0.5,0, 'msRuler')
    mc.move(0,0,0, "msRuler.scalePivot","msRuler.rotatePivot", absolute=True)

def rulerShader(*args):
    shd = mc.shadingNode("lambert", asShader=True)

def getSource(*args):
    sels = mc.ls(sl=True)

    if sels:
        mc.textField('source_TF', e=True, tx=sels[0])
    else:
        mc.textField('source_TF', e=True, tx='')

def getTarget(*args):
    sels = mc.ls(sl=True)

    if sels:
        mc.textField('target_TF', e=True, tx=sels[0])
    else:
        mc.textField('target_TF', e=True, tx='')

def createSourceRuler(*args):
    sourceAttr = mc.textField('source_TF', q=True, tx=True)

    mc.circle(nr=(0,1,0), c=(0,0,0), n='source_ruler_base', r=10, ch=0)
    mc.circle(nr=(0,1,0), c=(0,0,0), n='source_ruler_measure', r=6, ch=0)
    mc.circle(nr=(0,1,0), c=(0,0,0), n='source_ruler_head', r=8, ch=0)

    mc.parentConstraint('source_ruler_base','source_ruler_head', mo=True, st='y', weight=1)
    mc.parentConstraint('source_ruler_base','source_ruler_measure', mo=True, st='y', weight=1)

    mc.setAttr('source_ruler_base.tx', lock=True)
    mc.setAttr('source_ruler_base.ty', lock=True)
    mc.setAttr('source_ruler_base.tz', lock=True)

    mc.setAttr('source_ruler_head.tx', lock=True)
    mc.setAttr('source_ruler_head.tz', lock=True)

    mc.setAttr('source_ruler_measure.tx', lock=True)
    mc.setAttr('source_ruler_measure.tz', lock=True)

    mc.scaleConstraint('source_ruler_base','source_ruler_head', mo=True, weight=1)
    mc.scaleConstraint('source_ruler_base','source_ruler_measure', mo=True, weight=1)

    sourceGroup = mc.group('source_ruler_base','source_ruler_head','source_ruler_measure', n='sourceRuler_grp')

    tX_source = mc.getAttr(f'{sourceAttr}.translateX')
    tY_source = mc.getAttr(f'{sourceAttr}.translateY')
    tZ_source = mc.getAttr(f'{sourceAttr}.translateZ')

    mc.setAttr(f'{sourceGroup}.translateX', tX_source)
    #mc.setAttr(f'{sourceGroup}.translateY', tY_source
    # in case obj isn't on the ground, can be unbypass if wanted.
    mc.setAttr(f'{sourceGroup}.translateZ', tZ_source)
    mc.delete('source_ruler_measure')

def createTargetRuler(*args):
    targetAttr = mc.textField('target_TF', q=True, tx=True)

    mc.circle(nr=(0,1,0), c=(0,0,0), n=f'{targetAttr}_ruler_base', r=10, ch=0)
    mc.circle(nr=(0,1,0), c=(0,0,0), n=f'{targetAttr}_ruler_head', r=8, ch=0)

    mc.parentConstraint(f'{targetAttr}_ruler_base',f'{targetAttr}_ruler_head', mo=True, st='y', weight=1)
    mc.setAttr(f'{targetAttr}_ruler_base.tx', lock=True)
    mc.setAttr(f'{targetAttr}_ruler_base.ty', lock=True)
    mc.setAttr(f'{targetAttr}_ruler_base.tz', lock=True)

    mc.setAttr(f'{targetAttr}_ruler_head.tx', lock=True)
    mc.setAttr(f'{targetAttr}_ruler_head.tz', lock=True)

    mc.scaleConstraint(f'{targetAttr}_ruler_base',f'{targetAttr}_ruler_head', mo=True, weight=1)
    targetGroup = mc.group(f'{targetAttr}_ruler_base',f'{targetAttr}_ruler_head', n=f'{targetAttr}Ruler_grp')

    tX_target = mc.getAttr(f'{targetAttr}.translateX')
    tY_target = mc.getAttr(f'{targetAttr}.translateY')
    tZ_target = mc.getAttr(f'{targetAttr}.translateZ')

    mc.setAttr(f'{targetGroup}.translateX', tX_target)
    #mc.setAttr(f'{targetGroup}.translateY', tY_target
    # in case obj isn't on the ground, can be unbypass if wanted.
    mc.setAttr(f'{targetGroup}.translateZ', tZ_target)

def scaleSetting(*args):
    source = mc.textField('source_TF', q=True, tx=True)
    target = mc.textField('target_TF', q=True, tx=True)

    sourceLen = mc.getAttr('source_ruler_head.translateY')
    targetLen = mc.getAttr(f'{target}_ruler_head.translateY')
    targetScale = (1/sourceLen)*targetLen
    targetRealScale = 1/targetScale

    print(targetRealScale)

    mc.setAttr(f'{target}.scaleX', targetRealScale)
    mc.setAttr(f'{target}.scaleY', targetRealScale)
    mc.setAttr(f'{target}.scaleZ', targetRealScale)

    #mc.group(f'{target}', n=f'{target}_grp')
    # to set zero attributes

scaleLayout()


from artmaps.maps.models import Location
from artmaps.maps.models import Gallery
from artmaps.maps.models import Show
from artmaps.maps.models import Event
from django.contrib import admin

class LocationAdmin(admin.ModelAdmin):
	list_display = ('nickname', 'address1', 'city', 'state', 'zipcode', 'url')

class ShowAdmin(admin.ModelAdmin):
	list_display = ('name', 'gallery', 'opening_date')
	list_filter = ['opening_date', 'gallery']
	search_fields = ['name', 'artist', 'description']
	fields = ['gallery','name', 'image', 'thumbnail', 'description',
		  'start_date', 'end_date', 'opening_date', 'time_info',
		  'pick', 'artist']

class EventAdmin(admin.ModelAdmin):
	list_display = ('title', 'gallery', 'event_type', 'date')
	list_filter = ['date', 'gallery', 'event_type']
	search_fields = ['title', 'description']

admin.site.register(Location, LocationAdmin)
admin.site.register(Gallery)
admin.site.register(Show, ShowAdmin)
admin.site.register(Event, EventAdmin)


import copy
import logging
import shutil
from math import exp
import numpy as np
from sklearn.preprocessing import StandardScaler, normalize
from sklearn.utils import shuffle
from tqdm import trange
from lib.bqueue import Bqueue
from lib.dnn import Dnn
from lib.helper import Helper
from lib.som import SOM

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("Model")
terminal_columns = shutil.get_terminal_size().columns // 2

class Model:
    def __init__(
            self, input_dim, batch_size, som_x, som_y,
            label_class_num, xt, tt, limit, stm
    ):
        self.input_dim = input_dim
        self.som_x = som_x
        self.som_y = som_y
        self.class_num = label_class_num
        self.batch_size = batch_size
        self.som = SOM(self.som_x, self.som_y, self.input_dim)
        self.dnn = Dnn(self.som_x * self.som_y, self.class_num)
        self.x_test = xt
        self.t_test = tt
        self.stm = Bqueue(max_size=stm)
        self.limit = limit
        self.scaler = StandardScaler()
        self.max = 1.0

    def transfer(self, dist, test=False):
        if self.max < np.max(dist) and not test:
            self.max = np.max(dist)
        dist /= self.max
        return self.scaler.fit_transform(dist)

    @staticmethod
    def flatten(samples):
        return np.reshape(samples, newshape=[-1, samples.shape[1] * samples.shape[2]])

    def reply(self):
        samples = None
        labels = None
        stm_samples = np.array([s[0] for s in self.stm.get_list()]).astype("float32")
        stm_labels = np.array([s[1] for s in self.stm.get_list()]).astype("float32")
        if stm_samples.shape[0] > 0:
            for i in trange(self.class_num, desc="Replaying Data"):
                class_stm_idx = np.argwhere(np.argmax(stm_labels, axis=1) == i).ravel()
                if class_stm_idx.shape[0] == 0:
                    break
                class_prototypes = stm_samples[class_stm_idx]
                ll = stm_labels[class_stm_idx]
                g_samples = np.repeat(
                    class_prototypes, self.limit // class_prototypes.shape[0], axis=0
                )
                g_labels = np.repeat(ll, self.limit // class_prototypes.shape[0], axis=0)
                if i == 0:
                    samples = g_samples
                    labels = g_labels
                else:
                    samples = np.concatenate((samples, g_samples))
                    labels = np.concatenate((labels, g_labels))
        return samples, labels

    def fill_stm(self, samples, z_som, labels):
        logger.info("\rFilling STM")
        _, acc = self.dnn.evaluate(z_som, labels, batch_size=1, verbose=0)
        acc = np.array(acc).astype("float32")
        stm_idx = np.argwhere(acc > 0.5).ravel()
        for s in range(self.class_num):
            class_idx = np.argwhere(np.argmax(labels[stm_idx], axis=1) == s).ravel()
            np.random.shuffle(class_idx)
            class_samples = samples[class_idx]
            class_labels = labels[class_idx]
            class_samples, class_labels = shuffle(class_samples, class_labels)
            loop_iter = min(self.stm.max_size // self.class_num, class_idx.shape[0])
            for i in range(loop_iter):
                self.stm.push(
                    (class_samples[i], class_labels[i])
                )

    def train(
            self, samples, labels, dnn_iter, som_lr, som_rad, ce, sub_task, epoch
    ):
        samples, labels = shuffle(samples, labels)
        logger.info("\r".center(terminal_columns, "="))
        logger.info(f"\r Sub-Task D{sub_task}")
        logger.info("\r".center(terminal_columns, "="))
        confusion_matrices = []
        sigma = []
        r_samples = None
        r_labels = None
        if sub_task > 1 and self.stm.max_size > 0:
            m_samples, m_labels = self.reply()
            if m_samples is not None:
                r_samples = np.concatenate((samples, m_samples))
                r_labels = np.concatenate((labels, m_labels))
                r_samples, r_labels = shuffle(r_samples, r_labels)
        else:
            r_samples = samples
            r_labels = labels

        for ep, e in enumerate(range(epoch)):
            new_labels = np.unique(np.argmax(labels, axis=1))
            x, t = Helper.generate_batches(r_samples, r_labels, self.batch_size)
            sigma = []
            d_acc = 0.0
            cm_list = range(len(x))
            pbar = trange(len(x))
            d_counter = 0
            for i in pbar:
                z_som = self.transfer(self.som.get_distances(x[i]), test=True)
                loss, acc = self.dnn.evaluate(z_som, t[i], verbose=0)
                loss = np.array(loss)
                wrong_idx = np.argwhere(np.greater(np.array(loss), ce)).ravel()
                if wrong_idx.shape[0] > 0:
                    decay = exp(-1 * ((10 / sub_task) * d_counter / len(x)))
                    sigma.append(som_rad * decay)
                    d_counter += 1
                    mask = np.isin(np.argmax(t[i][wrong_idx], axis=1), new_labels)
                    new_wrong_samples = x[i][wrong_idx][mask]
                    self.som.train(
                        new_wrong_samples, learning_rate=som_lr * decay,
                        radius=som_rad * decay, global_order=self.batch_size
                    )
                    z_som = self.transfer(
                        self.som.get_distances(x[i], batch_size=self.batch_size)
                    )
                    z_som_test = self.transfer(
                        self.som.get_distances(self.x_test, batch_size=self.batch_size), test=True
                    )
                    cm = i in cm_list
                    d_loss, d_acc, confusion_matrix = self.dnn.train(
                        z_som, t[i], z_som_test, self.t_test,
                        cm=cm, epoch=dnn_iter, batch_size=self.batch_size
                    )
                    if len(confusion_matrix) > 0:
                        for m in confusion_matrix:
                            confusion_matrices.append(m)
                    d_acc = np.mean(np.array(d_acc).astype("float32"))
                else:
                    confusion_matrices.append(copy.copy(confusion_matrices[-1]))
                pbar.set_description(
                    f"Epoch{ep + 1}/{epoch}"
                    f"|Batch:{i + 1}/{len(x)}"
                    f"|CE:{wrong_idx.shape[0]}/{x[i].shape[0]}"
                    f"|Train Acc.:{d_acc:.4f}"
                )
                pbar.refresh()
        logger.info("\rEvaluation...")
        z_som_test = self.transfer(self.som.get_distances(self.x_test, batch_size=self.batch_size), test=True)
        z_som_stm = self.transfer(self.som.get_distances(r_samples, batch_size=self.batch_size), test=True)
        loss, accuracy = self.dnn.evaluate(z_som_test, self.t_test, verbose=1)
        if self.stm.max_size > 0:
            self.fill_stm(r_samples, z_som_stm, r_labels)
        return accuracy, np.array(sigma), confusion_matrices


import csv
from flask import Flask,render_template,request,redirect

app = Flask(__name__)

students=[]

@app.route("/")
def home_page():
    return render_template("home.html")

@app.route("/register",methods=['POST'])
def register():

    if not request.form.get("name") or not request.form.get('dorm') :
       return render_template("failure.html")

    file =open("registered.csv","a")
    writer=csv.writer(file)
    writer.writerow((request.form.get("name"),request.form.get('dorm')))
    file.close()

    return render_template("success.html")

@app.route("/registered")
def registered():

    with open("registered.csv","r") as file:
         reader = csv.reader(file)
         students = list(reader)
    return render_template("registered.html",students=students)


import json
import os
import re
import sqlite3

import numpy as np
import pandas as pd

import global_imports
from src import write_to_excel, report

def import_database_from_sql(sql_query, db_path):
    """Imports database from db or sqlite file as a pandas dataframe. Provide the SQL query and database path
as arguments"""

    connector = sqlite3.connect(db_path)
    db = pd.read_sql(sql_query, connector)
    return db

def split_metadata_into_columns(column_list, db, gpt):
    """Splits metadata of the main_db into different rows. Arguments are a list of column_names
    and the dataframe"""

    # Creates columns for each item in column_list
    for val in column_list:
        type_id = int(gpt.loc[gpt['type'] == val, 'id'])
        db[val] = db.loc[db['gamePieceTypeId'] == type_id, 'value']

    # Drop rows that are null in all of the generated columns. This will remove every row that
    # is not in column_list
    db = db.dropna(subset=column_list, how='all')

    # Drop every other row we do not need anymore
    db = db.drop(['value', 'gamePieceTypeId'], axis=1)

    # Group by releaseKey since the data is in its own rows. Fill row with NAN if every row is NAN
    db = db.groupby('releaseKey', as_index=True).agg(lambda x: np.nan if x.isnull().all() else x.dropna()).reset_index()
    db = db.set_index(np.arange(1, len(db) + 1))
    return db

def extract_date(db):
    """Extract Release Date from metadata and convert it into YYYY MM format"""

    date_pattern = 'releaseDate\":(\d{9,10})'

    def format_date(x):
        """Takes epoch time as argument and returns date in YYYY MM format"""
        date = re.search(date_pattern, x)
        if date:
            val = pd.to_datetime(date.group(1), unit='s')
            val = val.strftime('%Y %b')
            return val
        else:
            return 'No Date'

    db['date'] = db['meta'].apply(format_date)
    db = db.drop('meta', axis=1)
    return db

def create_platform(db):
    """Extraction of platform by reading json from data/platform.json"""

    # Read the json file
    with open(global_imports.platforms_json) as platform_file:
        platform = json.load(platform_file)

    # Create a regex pattern of all platforms to match and then return the actual platform name
    platform_keys = list(platform.keys())
    platform_pattern = re.compile(r"(\b{}\b)".format("|".join(platform_keys)))

    def platform_extract(x):
        """Returns matching platform by using above generated regex as a pattern"""
        m = platform_pattern.match(x)
        if m:
            return platform[m.group(1)]

    db['platform'] = db['releaseKey'].apply(platform_extract)
    return db

def remove_hidden_games_by_gog(db):
    """Imports ReleaseProperties and deletes any games marked as hidden or DLC"""

    hidden_db_query = 'SELECT releaseKey, isDlc, isVisibleInLibrary FROM ReleaseProperties'
    hidden_db = import_database_from_sql(hidden_db_query, global_imports.main_db_path)

    # Delete everything marked as DLC or not visible in library
    db = db[~db['releaseKey'].isin(hidden_db.loc[hidden_db['isVisibleInLibrary'] == 0, 'releaseKey'])]
    db = db[~db['releaseKey'].isin(hidden_db.loc[hidden_db['isDlc'] == 1, 'releaseKey'])]
    db = db.reset_index(drop=True)
    return db

def remove_manual_hidden_games_by_user(db):
    """Imports UserReleaseProperties and deletes any games marked as hidden or not in table"""

    user_hidden_db_query = 'SELECT releaseKey, isHidden FROM UserReleaseProperties'
    user_hidden_db = import_database_from_sql(user_hidden_db_query, global_imports.main_db_path)

    # Remove games in main_db but not in user_hidden_db as these were never owned
    db = db.drop(db[~db['releaseKey'].isin(user_hidden_db['releaseKey'])].index)

    # Remove all games marked hidden
    db = db[~db['releaseKey'].isin(user_hidden_db.loc[user_hidden_db['isHidden'] == 1, 'releaseKey'])]
    db = db.reset_index(drop=True)
    return db

def format_titles(db, column):
    """Format Title by removing unneccessary items"""

    # Remove special apostrophe
    db[column] = db[column].str.replace('’', '')

    # Remove the at the beginning of the word
    db[column] = db[column].str.replace('^The\s', '')

    # Remove Trademark and Copyright Symbols
    db[column] = db[column].str.replace('\u2122|\u00AE', '')

    return db

def remove_duplicates(db):
    """Remove duplicates by various categories"""
    # Delete rows with same title and platform
    db = db.drop_duplicates(subset=['title', 'platform'], keep='last').reset_index(drop=True)

    # Delete rows with same title in 'Other' Platform
    dup = db[db.duplicated(subset=['title'], keep=False)].sort_values('title')
    db = db.drop(dup[dup['platform'].str.match('Other')].index).reset_index(drop=True)
    db = db[~db['title'].str.contains('trial$', flags=re.IGNORECASE)]

    return db

def create_tag(db):
    """Extracts tags from UserRelease and formats them into Length and Status"""

    # Import tags database
    tag_db_query = 'SELECT releaseKey, tag FROM UserReleaseTags'
    tag_db = import_database_from_sql(tag_db_query, global_imports.main_db_path)
    temp = db.merge(tag_db, how='left', left_on=['releaseKey'], right_on=['releaseKey'])

    def create_status(x):
        pattern = re.compile(r"S - (\w+)")
        if pattern.match(x):
            return pattern.match(x).group(1)
        return np.nan

    def create_length(x):
        pattern = re.compile(r"L - (\w+)")
        if pattern.match(x):
            return pattern.match(x).group(1)
        return np.nan

    def create_platform_other(x):
        pattern = re.compile(r"P - (\w+)")
        if pattern.match(x):
            return pattern.match(x).group(1)
        return np.nan

    # Create Status and Length as two separate Columns
    temp['tag'] = temp['tag'].fillna('No tag')
    temp['Status'] = temp['tag'].apply(create_status)
    temp['Length'] = temp['tag'].apply(create_length)
    temp['other_platform'] = temp['tag'].apply(create_platform_other)
    temp.drop(['tag'], axis=1, inplace=True)
    temp = temp.groupby('releaseKey')[['Status', 'Length', 'other_platform']].first().reset_index()
    db = db.merge(temp, how='left', on='releaseKey')
    return db

def platform_tag_correction(db):
    """Adds miscellaneous platforms like disc, pirated etc to original platform"""

    db.loc[db['platform'] == 'Other', 'platform'] = np.NaN
    db.loc[db['platform'] == 'Xbox Gamepass', 'platform'] = np.NaN
    db['platform'] = db['platform'].combine_first(db['other_platform'])
    db.drop(['other_platform'], axis=1, inplace=True)
    return db

def group_platform(db):
    """Group games owned in multiple platforms to one row"""

    temp = db.groupby('Title').agg({'Platform': '; '.join})
    db = db.merge(temp, how='inner', left_on=['Title'], right_on=['Title'])
    db = db.drop('Platform_x', axis=1)
    db = db.drop_duplicates(subset=['Title', 'Platform_y'], keep='last')
    db = db.reset_index(drop=True)
    db = db.rename(columns={"Platform_y": "Platform"})
    return db

def add_gameplay_time(db):
    """Extract Gameplay time from Gameplay Time Tracker and add it as a row to the main database"""

    time_query = 'SELECT ProductName, StatTotalFullRuntime FROM Applications'
    time_db = import_database_from_sql(time_query, global_imports.gtt_db_path)
    db = db.merge(time_db, how='left', left_on='Title', right_on='ProductName')
    db = db.drop('ProductName', axis=1)
    db['StatTotalFullRuntime'] = db['StatTotalFullRuntime'].fillna(0)
    db = db.rename(columns={"StatTotalFullRuntime": "GameplayTime"})

    def convert_to_hours(x):
        if x:

            # The value of gameplay has additional zeroes. Remove zeroes and convert to minutes
            x = x / (10000000 * 60)
            hours = int(x / 60)
            minutes = int(x % 60)

            # Return output according to whether minutes or hours are zero
            if hours and minutes:
                return str(hours) + ' Hours ' + str(minutes) + ' Minutes'
            elif hours:
                return str(hours) + ' Hours'
            elif minutes:
                return str(minutes) + ' Minutes'
        else:
            return 0

    db['GameplayTime'] = db['GameplayTime'].apply(convert_to_hours)
    return db

def save_dataframe(db):
    """Removes old database, marks the new database as old and generates a new database as a new database"""

    old = 'data/main_db_old'
    new = 'data/main_db'
    os.remove(old)
    os.rename(new, old)
    db.to_pickle(new)

def main():
    """Import mainDB based on GamePieceTypes and GamePieces"""

    # Import GamePieceTypes table for mapping the types of data to extract from GamePieces
    game_piece_type_query = 'SELECT * FROM GamePieceTypes'
    game_piece_type = import_database_from_sql(game_piece_type_query, global_imports.main_db_path)

    # Import the GamePieces table which contains all the values about library games. Perform join on gamepieces to
    # get the metadata mapping
    main_db_query = """SELECT GamePieces.releaseKey, GamePieces.gamePieceTypeId, GamePieces.value
        FROM GameLinks
    	JOIN GamePieces ON GameLinks.releaseKey = GamePieces.releaseKey
    	JOIN LibraryReleases ON GameLinks.releaseKey = LibraryReleases.releaseKey"""
    main_db = import_database_from_sql(main_db_query, global_imports.main_db_path)

    """Split the metadata into columns and delete all other metadata"""

    # Add any metadata needed to this list. Valid values are type names from GamePieceTypes
    column_names = ['title', 'meta']
    main_db = split_metadata_into_columns(column_names, main_db, game_piece_type)

    """Extract Release Date of games from metadata"""
    main_db = extract_date(main_db)

    """Extract platform from releaseKey and add it as a row"""
    main_db = create_platform(main_db)

    """Some games are hidden by GOG in ReleaseProperties. Import the table and delete these games"""
    main_db = remove_hidden_games_by_gog(main_db)

    """Delete games hidden by the user from UserReleaseProperties"""
    main_db = remove_manual_hidden_games_by_user(main_db)

    """Remove brackets and quotes around title"""
    main_db['title'] = main_db['title'].apply(lambda x: x[10:-2])

    """Format the Title and remove special Characters, certain keywords"""
    main_db = format_titles(main_db, 'title')

    """Remove duplicates by various categories"""
    main_db = remove_duplicates(main_db)

    """Extracts tags from UserRelease and formats them into Length and Status"""
    main_db = create_tag(main_db)

    """Combines miscellanous platforms like pirated, disc into main platform"""
    main_db = platform_tag_correction(main_db)

    main_db = main_db.rename(columns={"title": "Title", "date": "Release", "platform": "Platform"})
    main_db.reset_index(drop=True, inplace=True)

    """Group games owned on multiple platforms"""
    main_db = group_platform(main_db)

    """Sort values by Title ignoring case"""
    main_db['Upper'] = main_db['Title'].str.upper()
    main_db.sort_values(by='Upper', inplace=True)
    del main_db['Upper']

    """Extract Gameplay from Gameplay Time Tracker and add it as a row"""
    main_db = add_gameplay_time(main_db)

    """Replaces old dataframe and new dataframe with newer updates"""
    save_dataframe(main_db)

    """Write database to excel"""
    write_to_excel.main()

    """Call report function to generate log"""
    report.main()

if __name__ == '__main__':
    main()


import cv2
import numpy as np

fld = cv2.ximgproc.createFastLineDetector()
"""
The array is colored
"""
def get_otsu_threshold(img:np.ndarray):
    gray_img= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    return cv2.threshold(gray_img,100,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

def get_kmeans_on_image(img:np.ndarray):
    datapoints= img.reshape((-1,3)).astype(np.float32)
    # print(datapoints.shape)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    ret,labels,centers= cv2.kmeans(datapoints,3, None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
    labels=labels.reshape((img.shape[0],img.shape[1]))
    # print(labels.shape)
    images= []
    for i in range(3):
        output= img.copy()
        # print(output.shape)
        output[labels!=i]=np.uint8([0,0,0])
        images.append(output)
    return images

def detect_lines(img:np.ndarray):
    gray_img= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    gray_img= cv2.threshold(gray_img,30,255,cv2.THRESH_BINARY)[1]
    blurred= cv2.GaussianBlur(gray_img,(5,3),33)
    lines= fld.detect(blurred, 150)
    output_img= img.copy()
    if lines is None:return
    for line in lines:
        line= line[0].astype('int')
        cv2.line(output_img,(line[0],line[1]),(line[2],line[3]),(255,0,0),5)
    cv2.imshow('Line Detector', output_img)

def draw_contours(img:np.ndarray):
    processed= cv2.medianBlur(img,21)
    processed= get_otsu_threshold(img)
    processed= cv2.Canny(img,30,130)
    cv2.imshow('Canny',processed)
    contours, hierarchy = cv2.findContours(processed,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    output_img= np.zeros_like(img,dtype=np.uint8)
    cv2.drawContours(output_img, contours, -1, (0, 255, 0), 3)

    cv2.imshow('Contours', output_img)

cap= cv2.VideoCapture(0)

while True:
    frame= cap.read()[1]

    cv2.imshow('Original',frame)
    detect_lines(frame)
    draw_contours(frame)
    if cv2.waitKey(1) == 27:
        break
cv2.destroyAllWindows()
cap.release()


import os
import pygraphviz
import inherit_relation as ir
from clang.cindex import Index

kInputsDir = os.path.join(os.path.dirname(__file__), 'cpp')

def draw(G, output_filename):
    G.layout(prog='dot')
    G.draw(output_filename)

def build():
    cpp_file_path = os.path.join(kInputsDir, 'main.cpp')
    index = Index.create()
    tu = index.parse(cpp_file_path)
    G = pygraphviz.AGraph(directed=True)
    for this, parent in ir.each_inheritance_relation(tu.cursor):
        edge = (this, parent)
        G.add_edge(edge)
    return G

if __name__ == '__main__':
    graph = build()
    draw(graph, 'inherit.png')


import time
import sys as eeeeee
from webbrowser import open_new_tab as eeeeeeeeeeeeeeeeee
from plyer import notification as eeee
from typing import List as eee
from base64 import urlsafe_b64decode as e
from traceback import format_exception as eeeeeeeeeeee

def not_my_toes(*args, **kwargs):
    print(*args, **kwargs)

def main(args: eee[str] = None):
    if args is not None or len(args) > 0:
        not_my_toes(e(b'T2ggeW91IHBhc3NlZCBzb21lIGFyZ3VtZW50cywgYnV0IHNhZGx5IGlkZ2FmLiBSaXAgYm96bw==').decode("utf-8"))

    eeeeeeeeeeeeeeeeee(e(b'aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g_dj1kUXc0dzlXZ1hjUQ==').decode("utf-8"))
    while True:
        with open("NewFile1.txt", "r") as file:
            try:
                for eeeee in file:
                    eeee.notify(
                        title="Garbage",
                        message=eeeee,
                        timeout=3,
                    )
                    time.sleep(5)
            except BaseException as eeeeee:
                eeee.notify(
                    title="Garbage",
                    message=f"Uh oh, cringe: {''.join(eeeeeeeeeeee(eeeeee))}",
                    timeout=3
                )
        not_my_toes("A")

if __name__ == "__main__":
    main(eeeeee.argv)


import random
import math

import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from tqdm import tqdm

from torch.utils.data import DataLoader
from torchvision.transforms import Compose, ToTensor, Lambda
from torchvision.datasets.mnist import MNIST

seed = 42069
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

def show_images(images, title=""):
    images = images.detach().cpu().numpy()
    fig = plt.figure(figsize=(4, 4))
    cols = math.ceil(len(images) ** (1 / 2))
    rows = math.ceil(len(images) / cols)
    for r in range(rows):
        for c in range(cols):
            idx = cols * r + c
            ax = fig.add_subplot(rows, cols, idx + 1)
            ax.axis("off")
            if idx < len(images):
                ax.imshow(images[idx][0], cmap="gray")
    fig.suptitle(title, fontsize=18)
    plt.show()

class ConditionalDDPM:
    """
    Conditional Denoising Diffusion Probabilistic Model, based on Classifier-Free Diffusion Guidance.
    Class contains methods for sampling from a specific class and training on a dataset.
    """

    def __init__(
        self,
        model,
        image_size,
        steps,
        beta_min,
        beta_max,
        w,
        p_unconditional,
    ):
        self.model = model
        self.image_size = image_size
        self.steps = steps
        self.beta = torch.linspace(beta_min, beta_max, steps, device=device)
        self.alpha = 1.0 - self.beta
        self.alpha_bar = torch.cumprod(self.alpha, dim=0)
        self.sigma = self.beta**0.5
        self.w = w
        self.p_unconditional = p_unconditional

    def p_sample(self, x_t, t, z, epsilon):
        """
        Sample from the reverse process. Removes noise from the processed image.
        """
        return (
            1.0
            / ((self.alpha[t]) ** 0.5)
            * (x_t - (1 - self.alpha[t]) / ((1 - self.alpha_bar[t]) ** 0.5) * epsilon)
            + self.sigma[t] * z
        )

    def sample(self, batch_size, c):
        """
        Sample a batch of images from the given class using the diffusion model.
        The class to sample is put into a tensor before being inputed into the network.
        It could be possible to sample different classes at the same time, but that is not implemented here.
        As the network requires two forward passes, the input variables x, t and c are concatenated with themselves
        so that the two forward passes are done as one batched forward pass.
        The conditionals are a tensor consisting of ones for the guided pass and zeros for the unguided pass.
        """
        c = torch.ones((batch_size,), dtype=torch.int64, device=device) * c
        c_input = torch.cat((c, c), dim=0)
        conditionals = torch.ones((2 * batch_size, 1), device=device)
        conditionals[batch_size:, 0] = 0.0
        x_t = torch.randn(
            (batch_size, 1, self.image_size, self.image_size), device=device
        )
        for t in tqdm(range(self.steps - 1, -1, -1)):
            if t > 0:
                z = torch.randn(x_t.shape, device=device)
            else:  # Don't add any noise on the last iteration
                z = torch.zeros(x_t.shape, device=device)
            t_input = torch.ones((2 * batch_size, 1), device=device) * t
            x_t_input = torch.cat((x_t, x_t), dim=0)
            with torch.no_grad():
                epsilon_pred = self.model(x_t_input, t_input, c_input, conditionals)
            epsilon_pred_guided = epsilon_pred[:batch_size, :, :, :]
            epsilon_pred_unguided = epsilon_pred[batch_size:, :, :, :]
            epsilon_tilde_t = (
                1.0 + self.w
            ) * epsilon_pred_guided - self.w * epsilon_pred_unguided
            x_t = torch.vmap(self.p_sample, in_dims=(0, None, 0, 0))(
                x_t, t, z, epsilon_tilde_t
            )
        return x_t

    def q_sample(self, x_0, t, epsilon):
        """
        Sample from the forward process. Adds gaussian noise to an initial data point.
        """
        return (self.alpha_bar[t] ** 0.5) * x_0 + (
            (1 - self.alpha_bar[t]) ** 0.5
        ) * epsilon

    def train(self, x_0, c):
        """
        Performs one training iteration for the diffusion model.
        Takes in a batch of images along with class labels to train on.
        """
        batch_size = x_0.shape[0]
        t = torch.randint(1, self.steps, size=(batch_size, 1), device=device)
        epsilon = torch.randn(
            (batch_size, 1, self.image_size, self.image_size), device=device
        )
        x_input = torch.vmap(self.q_sample)(x_0, t, epsilon)
        conditionals = torch.rand((batch_size, 1), device=device) > self.p_unconditional
        epsilon_pred = self.model(x_input, t, c, conditionals)
        loss = torch.sum((epsilon - epsilon_pred) ** 2, dim=1)
        loss = torch.mean(loss)
        return loss

class PositionalEmbedding(nn.Module):
    """
    Sinusoidal positional embedding based on the definition from the original attention paper.
    Used for embedding the time input to the network.
    """

    def __init__(self, dimension):
        super().__init__()
        self.dimension = dimension

    def forward(self, t):
        indexes = torch.arange(0, self.dimension, 2, device=device)
        sin_embedding = torch.sin(t / (10000.0 ** (indexes / self.dimension)))
        cos_embedding = torch.cos(t / (10000.0 ** (indexes / self.dimension)))
        embedding = torch.stack((sin_embedding, cos_embedding), dim=1)
        embedding = embedding.transpose(1, 2).reshape(t.shape[0], self.dimension)
        return embedding

class SelfAttentionBlock(nn.Module):
    """
    Multi-Headed Self Attention modified with 2D convolutions instead of linear layers.
    """

    def __init__(self, dimension, heads, hidden_dimension, groups):
        super().__init__()
        self.dimension = dimension
        self.heads = heads
        self.hidden_dimension = hidden_dimension

        self.layer_q = nn.Conv2d(
            dimension, heads * hidden_dimension, kernel_size=1, stride=1, padding=0
        )
        self.layer_k = nn.Conv2d(
            dimension, heads * hidden_dimension, kernel_size=1, stride=1, padding=0
        )
        self.layer_v = nn.Conv2d(
            dimension, heads * hidden_dimension, kernel_size=1, stride=1, padding=0
        )
        self.layer_out = nn.Conv2d(
            heads * hidden_dimension, dimension, kernel_size=1, stride=1, padding=0
        )
        self.softmax = nn.Softmax(dim=-1)
        self.normalization = nn.GroupNorm(num_groups=groups, num_channels=dimension)

    def forward(self, x):
        batch_size = x.shape[0]
        height = x.shape[2]
        width = x.shape[3]

        x = self.normalization(x)

        q = self.layer_q(x)
        k = self.layer_k(x)
        v = self.layer_v(x)

        # Permute and reshape he tensors so that the matrix multiplications work out correctly.
        # For q and v, the channel dimension is moved last.
        # All three tensors are also flattened along the height and width dimensions, thus making each of them a batch of matrices.
        q = q.permute(0, 2, 3, 1).reshape(
            batch_size, height * width, self.heads * self.hidden_dimension
        )
        k = k.reshape(batch_size, self.heads * self.hidden_dimension, height * width)
        v = v.permute(0, 2, 3, 1).reshape(
            batch_size, height * width, self.heads * self.hidden_dimension
        )

        # Batched matrix multiplication. The channel dimensions for both q and k are multiplied together.
        # This means that the attention scores are comparing the similarity of pixel positions, instead of channel positions.
        qk = torch.bmm(q, k)
        qk = self.softmax(qk / (self.hidden_dimension**0.5))
        attention = torch.bmm(qk, v)

        # Move the channel dimension back to the second dimension as is the convention.
        attention = attention.reshape(
            batch_size, height, width, self.heads * self.hidden_dimension
        ).permute(0, 3, 1, 2)

        out = self.layer_out(attention)  # Do one final layer after the self attention.
        return x + out

class ResidualBlock(nn.Module):
    """
    Fully convolutional residual block using the SiLU activation function and group normalization layers.
    The time embedding t and class label c is sent through linear layers to make the shape fit with the input image x.
    """

    def __init__(
        self,
        channels,
        groups,
        time_embedding_dimension,
        class_embedding_dimension,
        image_size,
    ):
        super().__init__()
        self.layer1 = nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1)
        self.layer2 = nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1)
        self.layer_t1 = nn.Linear(time_embedding_dimension, time_embedding_dimension)
        self.layer_t2 = nn.Linear(
            time_embedding_dimension, channels * image_size * image_size
        )
        self.layer_c1 = nn.Linear(time_embedding_dimension, time_embedding_dimension)
        self.layer_c2 = nn.Linear(
            class_embedding_dimension, channels * image_size * image_size
        )
        self.activation = nn.SiLU()
        self.normalization1 = nn.GroupNorm(num_groups=groups, num_channels=channels)
        self.normalization2 = nn.GroupNorm(num_groups=groups, num_channels=channels)

    def forward(self, x, t, c):
        x_init = x

        x = self.normalization1(x)
        x = self.layer1(x)
        x = self.activation(x)

        t = self.layer_t1(t)
        t = self.activation(t)
        t = self.layer_t2(t)
        t = t.reshape(x.shape)
        x += t

        c = self.layer_c1(c)
        c = self.activation(c)
        c = self.layer_c2(c)
        c = c.reshape(x.shape)
        x += c

        x = self.normalization2(x)
        x = self.layer2(x)
        x = self.activation(x)

        return x + x_init

class UNet(nn.Module):
    """
    Network used for learning the reverse diffusion process.
    Consists of a U-Net style architecture based on fully convolutional residual blocks together with a series of downsampling and upsampling operations.
    The same number of downsamples and upsamples are used, and the state at each step during the downsampling is concatenated to the corresponding step in the upsampling.
    The time t is send through a sinusoidal positional embedding followed by some linear layers, and added as an input to each residual block.
    The class labels c are transformed to a one-hot encoding, and then multiplied with the conditionals. The conditionals are either a 1 or a 0, which will disable the classes when the process is unguided.
    Multi-Headed Self Attention blocks working on the image state is added at the middle positions of each stage.
    """

    def __init__(
        self,
        hidden_channels,
        groups,
        attention_heads,
        time_embedding_dimension,
        class_embedding_dimension,
        num_classes,
    ):
        super().__init__()
        self.positional_embedding = PositionalEmbedding(time_embedding_dimension)
        self.layer_t1 = nn.Linear(time_embedding_dimension, time_embedding_dimension)
        self.layer_t2 = nn.Linear(time_embedding_dimension, time_embedding_dimension)
        self.layer_c1 = nn.Linear(num_classes, class_embedding_dimension)
        self.layer_c2 = nn.Linear(class_embedding_dimension, class_embedding_dimension)
        self.num_classes = num_classes

        self.layer_down1 = nn.Conv2d(
            1, hidden_channels, kernel_size=3, stride=1, padding=1
        )
        self.layer_down2 = ResidualBlock(
            hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size,
        )
        self.layer_down3 = ResidualBlock(
            hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size // 2,
        )
        self.layer_down4 = SelfAttentionBlock(
            hidden_channels, attention_heads, hidden_channels, groups
        )
        self.layer_down5 = ResidualBlock(
            hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size // 4,
        )

        self.layer_middle1 = ResidualBlock(
            hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size // 4,
        )
        self.layer_middle2 = SelfAttentionBlock(
            hidden_channels, attention_heads, hidden_channels, groups
        )
        self.layer_middle3 = ResidualBlock(
            hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size // 4,
        )

        self.layer_up1 = ResidualBlock(
            2 * hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size // 4,
        )
        self.layer_up2 = ResidualBlock(
            3 * hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size // 2,
        )
        self.layer_up3 = SelfAttentionBlock(
            3 * hidden_channels, attention_heads, 3 * hidden_channels, groups
        )
        self.layer_up4 = ResidualBlock(
            4 * hidden_channels,
            groups,
            time_embedding_dimension,
            class_embedding_dimension,
            image_size,
        )
        self.layer_up5 = nn.Conv2d(
            4 * hidden_channels, 1, kernel_size=3, stride=1, padding=1
        )

        self.downsample = nn.MaxPool2d(2)
        self.upsample = nn.Upsample(scale_factor=2)
        self.activation = nn.SiLU()

    def forward(self, x, t, c, conditionals=1.0):
        t_embed = self.positional_embedding(t)
        t_embed = self.layer_t1(t_embed)
        t_embed = self.activation(t_embed)
        t_embed = self.layer_t2(t_embed)

        c = nn.functional.one_hot(c, num_classes=self.num_classes).type(torch.float32)
        c *= conditionals
        c_embed = self.layer_c1(c)
        c_embed = self.activation(c_embed)
        c_embed = self.layer_c2(c_embed)

        x1 = self.layer_down1(x)
        x1 = self.activation(x1)
        x1 = self.layer_down2(x1, t_embed, c_embed)
        x1 = self.activation(x1)

        x2 = self.downsample(x1)
        x2 = self.layer_down3(x2, t_embed, c_embed)
        x2 = self.activation(x2)
        x2 = self.layer_down4(x2)
        x2 = self.activation(x2)

        x3 = self.downsample(x2)
        x3 = self.layer_down5(x3, t_embed, c_embed)
        x3 = self.activation(x3)

        xx = self.layer_middle1(x3, t_embed, c_embed)
        xx = self.activation(xx)
        xx = self.layer_middle2(xx)
        xx = self.activation(xx)
        xx = self.layer_middle3(xx, t_embed, c_embed)
        xx = self.activation(xx)

        xx = torch.cat((xx, x3), dim=1)
        xx = self.layer_up1(xx, t_embed, c_embed)
        xx = self.activation(xx)

        xx = self.upsample(xx)
        xx = torch.cat((xx, x2), dim=1)
        xx = self.layer_up2(xx, t_embed, c_embed)
        xx = self.activation(xx)
        xx = self.layer_up3(xx)
        xx = self.activation(xx)

        xx = self.upsample(xx)
        xx = torch.cat((xx, x1), dim=1)
        xx = self.layer_up4(xx, t_embed, c_embed)
        xx = self.activation(xx)
        xx = self.layer_up5(xx)

        return xx

if __name__ == "__main__":
    batch_size = 64
    image_size = 28
    steps = 1000
    beta_min = 1e-4
    beta_max = 0.02

    epochs = 10
    lr = 1e-4

    hidden_channels = 200
    groups = 10
    attention_heads = 5
    time_embedding_dimension = 100
    class_embedding_dimension = 100
    num_classes = 10

    w = 0.3
    p_unconditional = 0.1

    transform = Compose([ToTensor(), Lambda(lambda x: (x - 0.5) * 2)])

    dataset = MNIST("./datasets", download=True, train=True, transform=transform)
    loader = DataLoader(dataset, batch_size, shuffle=True)

    model = UNet(
        hidden_channels,
        groups,
        attention_heads,
        time_embedding_dimension,
        class_embedding_dimension,
        num_classes,
    )
    model.to(device)

    ddpm = ConditionalDDPM(
        model, image_size, steps, beta_min, beta_max, w, p_unconditional
    )

    # samples = ddpm.sample(16, 0)
    # show_images(samples)

    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.9)

    losses = []

    pbar = tqdm(range(1, epochs + 1))
    for epoch in pbar:
        for iteration, (x_batch, y_batch) in enumerate(loader):
            optimizer.zero_grad()
            x_batch = x_batch.to(device)
            y_batch = y_batch.to(device)
            loss = ddpm.train(x_batch, y_batch)
            loss.backward()
            optimizer.step()
            pbar.set_postfix(
                {
                    "Loss": loss.item(),
                    "Iteration": f"{iteration} / {60000 // batch_size}",
                }
            )
            losses.append(loss.item())
        scheduler.step()

    plt.plot(losses)
    plt.xlabel("Iterations")
    plt.ylabel("Loss")
    plt.yscale("log")
    plt.show()

    for i in range(num_classes):
        samples = ddpm.sample(16, i)
        show_images(samples)


import falcon
import json
import pandas as pd
import pickle
import io,sys
import pandas.core.indexes
import numpy as np
sys.modules['pandas.indexes'] = pandas.core.indexes
from sklearn.preprocessing import LabelEncoder,OneHotEncoder
import lime
import lime.lime_tabular

class Data():
    def __init__(self):
        self.x_train = ""
        self.x_test = ""
        self.y_test = ""
        self.inversed_x_test = ""
        self.categorical_features = ""
        self.categorical_names = ""
        self.feature_names = ""
        self.label_names = ""
        self.trained_model = ""
        self.accessor = ""
        self.isOneHot = False
        self.prediction_result = ""
        self.oneHotEncoder = ""

    def input_data(self,target_name,data):
        if target_name == "X_train":
            self.x_train = data
        elif target_name == "X_test":
            self.x_test = data
        elif target_name == "y_test":
            self.y_test = data
        elif target_name == "Categorical Features":
            self.categorical_features = data
        elif target_name == "Categorical Names":
            self.categorical_names = data
        elif target_name == "Feature Names":
            self.feature_names = data
        elif target_name == "Label Names":
            self.label_names = data
        elif target_name == "Trained Model":
            self.trained_model = data

    def inverse_and_add_prediction(self):
        test = self.x_test.copy()
        ## categorical features inverse
        categorical_names = self.categorical_names
        categorical_features = self.categorical_features
        for i in categorical_features:
            le = LabelEncoder()
            le.fit(categorical_names[i])
            test.iloc[:,i] = le.inverse_transform(test.iloc[:,i])
        ## add prediction_result
        test[len(test.columns)] = self.prediction_result
        ## add index
        test[len(test.columns)] = list(np.arange(len(test)))
        test.columns = self.accessor
        self.inversed_x_test = test

    def create_accessor(self):
        self.feature_names = np.append(self.feature_names,"Prediction")
        self.feature_names = np.append(self.feature_names,"Index")
        feature_names = self.feature_names
        res = []
        for i in feature_names:
            res.append(i.replace(" ","").lower())
        self.accessor = res

    def addPrediction(self,isOneHot):
        self.isOneHot = isOneHot
        model = self.trained_model
        if not isinstance(self.x_train,np.ndarray):
            train = self.x_train.as_matrix()
        else:
            train = self.x_train
        if not isinstance(self.x_test,np.ndarray):
            test = self.x_test.as_matrix()
        else:
            test = self.x_test
        if isOneHot:
            encoder = OneHotEncoder(categorical_features=self.categorical_features)
            alldata = np.concatenate([train,test])
            encoder.fit(alldata)
            self.oneHotEncoder = encoder
            encoded_test = encoder.transform(test)
            prediction_result = model.predict_proba(encoded_test)
        else:
            ## for example, LightGBM
            prediction_result = model.predict_proba(test)
        t_res = []
        for i in prediction_result:
            t_res.append(i[0])
        self.prediction_result = t_res

class TabularLIME():
    def __init__(self):
        self.explainer = ""
        self.predict_fn = ""
        self.exp_instance = ""
        self.expScore = ""
        self.expList = ""
        self.expProba = ""

    def trainLIME(self,data):
        if not isinstance(data.x_train,np.ndarray):
            train = data.x_train.as_matrix()
        else:
            train = data.x_train
        self.explainer = lime.lime_tabular.LimeTabularExplainer(train,
        feature_names=data.feature_names, class_names=data.label_names,
        categorical_features=list(data.categorical_features),
        categorical_names=data.categorical_names, kernel_width=3)
        ## custom predict function
        self.predict_fn = lambda x: data.trained_model.predict_proba(data.oneHotEncoder.transform(x)).astype(float)
        print("train tabular LIME complete")

    def doExplanation(self,index,data):
        test = data.x_test.as_matrix()
        exp = self.explainer.explain_instance(test[int(index)], self.predict_fn, num_features=len(data.feature_names))
        self.exp_instance = exp
        self.expScore = exp.score
        self.expProba = exp.predict_proba
        self.expList = exp.as_list()

def check_train_test_dtype(df):
    message = "Data check passed"
    result = True
    if len(df.columns) < 1:
        message = "This dataframe is empty"
        result = False
        return message,result
    for column in df:
        if df[column].dtype not in ["int32","int64","float32","float64","int","float"]:
            message = "This data contains something other than int or float column"
            result = False
    return message,result

def check_target_test_dtype(target_test):
    message = "Data check passed"
    result = True
    if len(target_test.columns) > 1:
        message = "This target data is over 1 column"
        result = False
    if target_test.iloc[:,0].dtype not in ["int32","int64","int"]:
        message = "This data contains something other than int column"
        result = False

    return message,result

def check_pickle_list_dtype(pickle_list,target_name):
    message = "Data check passed"
    result = True
    ## nested array or not
    if isinstance(pickle_list[0],list):
        message = "Nested array can not be used"
        result = False
    if target_name == "Feature Names":
        for i in pickle_list:
            if not isinstance(i,str):
                message = "This list contains not string data"
                result = False
    elif target_name == "Categorical Features":
        for i in pickle_list:
            if not isinstance(i,int) and not isinstance(i,np.int32) and not isinstance(i,np.int64):
                message = "This list contains not integer data"
                result = False

    return message,result

def check_pickle_dict_dtype(pickle_dict):
    message = "Data check passed"
    result = True
    ## dict or not
    if not isinstance(pickle_dict,dict):
        message = "Not dict object can not be used"
        result = False
    return message,result

def methods_for_pickle(message,result,df,file,target_name):
    pickle_file = pickle.loads(file)
    ## if dataframe
    if isinstance(pickle_file,pd.core.frame.DataFrame):
        df = pickle_file
        if target_name == "X_train" or target_name == "X_test":
            message,result = check_train_test_dtype(df)
        elif target_name == "y_test":
            message,result = check_target_test_dtype(df)
    ## if numpy.ndarray, convert dataframe
    elif isinstance(pickle_file,np.ndarray):
        if target_name == "X_train" or target_name == "X_test":
            df = pd.DataFrame(pickle_file)
            message,result = check_train_test_dtype(df)
        elif target_name == "y_test":
            df = pd.DataFrame(pickle_file)
            message,result = check_target_test_dtype(df)
        elif target_name == "Categorical Features" or target_name == "Feature Names" or target_name == "Label Names":
            message,result = check_pickle_list_dtype(pickle_file,target_name)
    elif isinstance(pickle_file,list):
        if target_name == "Categorical Features" or target_name == "Feature Names" or target_name == "Label Names":
            np_pickle_file = np.array(pickle_file)
            df = np_pickle_file
            message,result = check_pickle_list_dtype(np_pickle_file,target_name)
        else:
            message = "This data is not pickled list although this data should be list"
            result = False
    elif isinstance(pickle_file,dict):
        df = pickle_file
        if target_name == "Categorical Names":
            message,result = check_pickle_dict_dtype(pickle_file)
        else:
            message = "This data is not pickled dict although this data should be dict"
            result = False
    else:
        message = "This data is not dataframe,numpy.ndarray,list or dict"
        result = False
    if target_name == "Trained Model":
        message = "Data Check Passed"
        df = pickle_file
        result = True
    return message,result,df

def check_data_content(file,target_name,file_type):
    message = "Data check passed"
    result = True
    df = ""
    if target_name == "X_train" or target_name == "X_test":
        if file_type == ".csv":
            df = pd.read_csv(io.BytesIO(file))
            ## df can contain number only
            message,result = check_train_test_dtype(df)
        elif file_type == ".pickle":
            message,result,df = methods_for_pickle(message,result,df,file,target_name)
        else:
            message = "This data is not dataframe or numpy.ndarray"
            result = False

    elif target_name == "y_test":
        if file_type == ".csv":
            df = pd.read_csv(io.BytesIO(file))
            message,result = check_target_test_dtype(df)
        elif file_type == ".pickle":
            message,result,df = methods_for_pickle(message,result,df,file,target_name)

    elif target_name == "Categorical Features" or target_name == "Feature Names" or target_name == "Categorical Names" or target_name == "Label Names":
        ## list only(pickle)
        if file_type == ".pickle":
            message,result,df = methods_for_pickle(message,result,df,file,target_name)
        else:
            message = "This data not pickle"
            result = False
            df = ""

    elif target_name == "Trained Model":
        ## pickle,h5 model file
        if file_type == ".pickle":
            message,result,df = methods_for_pickle(message,result,df,file,target_name)
        # elif file_type == ".h5":
        #     message,result,df = methods_for_h5(message,result,df,file,target_name)
        # elif file_type == ".model":
        #     message,result,df = methods_for_model(message,result,df,file,target_name)
        else:
            message = "This data not pickle"
            result = False
            df = ""

    return message,result,df