from PIL import Image

def ReductionImage(image: Image.Image, column: int) -> Image.Image:
    # split the image into N column, and revert them
    # 0         N
    # 1       ...
    # 2     ->  3
    # 3         2
    # ...       1
    # N         0
    '''
    A simple example, an image with height 12[0, 12), and split it into 4 column
    12 / 4 = 3 ... 0, it is good

    column0 [0,  3)     height 3        newColumn0(column3) [0,  3), raw[9,  12),   height 3
    column1 [3,  6)     height 3  ----\ newColumn1(column2) [3,  6), raw[6,   9),   height 3
    column2 [6,  9)     height 3  ----/ newColumn2(column1) [6,  9), raw[3,   6),   height 3
    column3 [9, 12)     height 3        newColumn3(column0) [9, 12), raw[0,   3),   height 3

    But what if height % column not equal 0 ?

    Such as an image with height 13, and split it into 4 column
    so columnHeight is 13 // 4 = 3, and we also have a mod 1
    and columnN contains lines between [0 + N * columnHeight, 0 + (N + 1) * columnHeight)
    column0 [0,   3)     height 3
    column1 [3,   6)     height 3
    column2 [6,   9)     height 3
    column3 [9,  12)     height 3
            line 12      we lost it

    In order to not lost the line 12, we have to modify column3 to [9,  12)
    then we can put column3 to the first column in new image
    newColumnN contains lines between [0 + N * columnHeight, 0 + (N + 1) * columnHeight)

    column0 [0,  3)     height 3        newColumn0(column3) [0,   4), raw[9,  13),   height 4
    column1 [3,  6)     height 3  ----\ newColumn1(column2) [4,   7), raw[6,   9),   height 3
    column2 [6,  9)     height 3  ----/ newColumn2(column1) [7,  10), raw[3,   6),   height 3
    column3 [9, 13)     height 4        newColumn3(column0) [10, 13), raw[0,   3),   height 3

    now we are clear, if we have a mod from height // column, we have to do :
    1. columnN    = [N * columnHeight, (N + 1) * columnHeight + mod]       if N == column - 1
                  = [N * columnHeight, (N + 1) * columnHeight]             if N != column - 1
    2. newColumnN = [0, columnHeight + mod]                                if N == 0
                  = [N * columnHeight + mod, (N + 1) * columnHeight + mod] if N != 0
    '''

    # create a same (mode, size) image
    newImage = Image.new(image.mode, image.size)
    width, height = image.size
    columnHeight = height // column
    mod = height % column
    # print(height, columnHeight, mod)

    for i in range(column):
        N = column - i - 1   # [column-1, ..., 2, 1, 0]
        rawYstart = N * columnHeight
        rawYend = rawYstart + columnHeight
        if N == column - 1:  # last column in raw (first iteration)
            rawYend += mod

        N = i
        newYstart = N * columnHeight + mod
        newYend = newYstart + columnHeight
        if N == 0:  # first column in new (first iteration)
            newYstart, newYend = 0, columnHeight + mod

        try:
            # note, crop use [ ), left open right closed interval
            newImage.paste(image.crop((0, rawYstart, width, rawYend)),
                           (0, newYstart, width, newYend))
        finally:
            # print(f"{rawYstart}, {rawYend}.......{newYstart}, {newYend}")
            pass
    return newImage


import comm
import config
import sys
import xbmcgui
import xbmcplugin

from aussieaddonscommon import utils

def make_list():
    try:
        for category in config.CATEGORIES:
            url = "%s?category=%s" % (sys.argv[0], category)
            listitem = xbmcgui.ListItem(category)

            # add the item to the media list
            ok = xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
                                             url=url,
                                             listitem=listitem,
                                             isFolder=True,
                                             totalItems=len(config.CATEGORIES))

        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=ok)
    except Exception:
        utils.handle_error('Unable build video category list')

def make_seasons_list():
    try:
        seasons = comm.get_seasons()
        sorted_seasons = sorted(
            seasons, key=lambda x: x.get('name'), reverse=True)
        for season in sorted_seasons:
            id = season.get('id')
            current_round = season.get('currentRoundId')
            name = season.get('name')
            url = "{0}?season={1}&current_round={2}&name={3}".format(
                sys.argv[0], id, current_round, name)
            listitem = xbmcgui.ListItem(name)

            # add the item to the media list
            ok = xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
                                             url=url,
                                             listitem=listitem,
                                             isFolder=True,
                                             totalItems=len(seasons))

        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=ok)
    except Exception:
        utils.handle_error('Unable build seasons list')


from direct.gui.DirectGui import *
import direct.gui.DirectGuiGlobals as DGG
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
from math import pi as PI

class MyGame(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        base.setFrameRateMeter(True)
        self.set_up_camera()
        self.set_up_collision_handling()
        self.create_menu()
        self.randomizer = Randomizer(0)
        self.totalNeedles = 0
        self.touchingNeedles = 0
        self.amountOfNeedles = 1000
        self.needleHolder = render.attachNewNode('needleHolder')

    def create_menu(self):
        self.menuHolder = DirectFrame(
                parent = aspect2d,
                frameSize = (-0.91, 0.91, -0.81, 0.81),
                relief = DGG.RAISED,
                borderWidth = (0.02, 0.02))
        self.explainationLabel = DirectLabel(
                parent = self.menuHolder,
                text = 'Guesstimate pi by dropping needles onto evenly spaced lines.',
                pos = (0, 0, 0.74),
                scale = 0.063)
        self.buffonsLabel = DirectLabel(
                parent = self.menuHolder,
                text = "A side effect of Buffon's needle problem.",
                pos = (0, 0, 0.66),
                scale = 0.063)
        self.thicknessLabel = DirectLabel(
                parent = self.menuHolder,
                text = "Distance between the lines: 3.00",
                pos = (0, 0, 0.45),
                scale = 0.063)
        self.thicknessSlider = DirectSlider(
                parent = self.menuHolder,
                range = (1.00, 5.00),
                value = 3.00,
                pageSize = 0.2,
                pos = (0, 0, 0.40),
                scale = 0.5,
                command = self.cmd_thicknessSlider)
        self.thicknessMinLabel = DirectLabel(
                parent = self.menuHolder,
                text = "1.00",
                pos = (-0.575, 0, 0.3878),
                scale = 0.063)
        self.thicknessMaxLabel = DirectLabel(
                parent = self.menuHolder,
                text = "5.00",
                pos = (0.575, 0, 0.3878),
                scale = 0.063)
        self.shorterLabel = DirectLabel(
                parent = self.menuHolder,
                text = "The needle must be equal to or shorter than the line distance.",
                pos = (0, 0, 0.25),
                scale = 0.063)
        self.needleLabel = DirectLabel(
                parent = self.menuHolder,
                text = "Needle length: 3.00 (100%)",
                pos = (0, 0, 0.17),
                scale = 0.063)
        self.needleLengthSlider = DirectSlider(
                parent = self.menuHolder,
                range = (0.1, 1.0),
                value = 1,
                pageSize = 0.1,
                pos = (0, 0, 0.12),
                scale = 0.5,
                command = self.cmd_needleLengthSlider)
        self.needleMinLabel = DirectLabel(
                parent = self.menuHolder,
                text = "10%",
                pos = (-0.57, 0, 0.11),
                scale = 0.063)
        self.needleMaxLabel = DirectLabel(
                parent = self.menuHolder,
                text = "100%",
                pos = (0.575, 0, 0.11),
                scale = 0.063)
        self.startButton = DirectButton(
                parent = self.menuHolder,
                text = 'start',
                pos = (0, 0, -0.5),
                scale = 0.1,
                frameColor = (0.8, 0.95, 0.8, 1),
                command = self.cmd_start)

    def create_interface(self):
        font = loader.loadFont('Assets/FontsFree-Net-SLC_.ttf')
        self.textCount = OnscreenText(
                text = 'needles thrown: 0',
                pos = (0.99, 0.95),
                scale = 0.05,
                align = TextNode.ARight,
                font = font,
                mayChange = True)
        self.textPiGuess = OnscreenText(
                text = 'guesstimate: /',
                pos = (0.99, 0.9),
                scale = 0.05,
                align = TextNode.ARight,
                font = font,
                mayChange = True)
        self.textPi = OnscreenText(
                text = f'pi: {round(PI,5)}',
                pos = (0.99, 0.85),
                align = TextNode.ARight,
                font = font,
                scale = 0.05)

    def cmd_needleLengthSlider(self):
        thickValue = self.thickness
        needleValue = self.needleLengthSlider['value']
        percent = round(needleValue * 100)
        length = round(percent/100 * thickValue, 2)
        needleString = f"Needle length: {length} ({percent}%)"
        self.needleLabel['text'] = needleString
        self.length = length

    def cmd_start(self):
        windowWidth = self.win.get_properties().size.x
        distance = 20 * self.thickness
        self.amountOfLines = int(windowWidth/distance) + 1
        self.place_lines(self.thickness, self.amountOfLines)
        self.menuHolder.hide()
        self.create_interface()
        self.start_task_drop_needles()

    def cmd_thicknessSlider(self):
        thickValue = round(self.thicknessSlider['value'], 1)
        thickString = f"Distance between the lines: {thickValue}"
        self.thicknessLabel['text'] = thickString
        self.thickness = thickValue
        self.cmd_needleLengthSlider()

    def set_up_collision_handling(self):
        self.colTraverser = CollisionTraverser('traverser')
        self.colHandler = CollisionHandlerQueue()

    def set_up_camera(self):
        base.disableMouse() # disables default mouse control camera
        base.camera.setPos(0, 0, 20)
        base.camera.lookAt(0, 0, 0)
        lens = OrthographicLens()
        lens.setFilmSize(20, 15)
        base.cam.node().setLens(lens)

    def start_task_drop_needles(self):
        taskMgr.add(self.task_drop_needles, 'drop needles', extraArgs = [self.amountOfNeedles], appendTask = True)

    def place_lines(self, distance, amount):
        for a in range(amount):
            line = render.attachNewNode(CollisionNode('line'))
            pointA = Point3(0, -10, 2)
            pointB = Point3(0, -10, -2)
            pointC = Point3(0, 10,  -2)
            pointD = Point3(0, 10, 2)
            collisionSolid = CollisionPolygon(pointA, pointB, pointC, pointD)
            line.node().addSolid(collisionSolid)
            line.setPos((amount/2 - a) * distance, 0, 0)
            line.setCollideMask(BitMask32(1))
            line.node().setFromCollideMask(BitMask32().all_off())
            line.show()

    def place_needle(self, length):
        self.totalNeedles += 1
        newNeedle = self.needleHolder.attachNewNode(CollisionNode('needle'))
        collisionSegment = CollisionSegment(-length/2, 0, 0, length/2, 0, 0)
        newNeedle.node().addSolid(collisionSegment)
        newNeedle.show()
        t = self.thickness
        x = self.randomizer.random_real(t*5) - t*2.5
        y = self.randomizer.random_real(t*3) - t*1.5
        z = 0
        h = self.randomizer.random_real(360)
        newNeedle.setPos(x, y, z)
        newNeedle.setH(h)
        newNeedle.setCollideMask(BitMask32().all_off())
        newNeedle.node().setFromCollideMask(BitMask32(1))
        self.colTraverser.add_collider(newNeedle, self.colHandler)

    def task_drop_needles(self, amountOfNeedles, task):
        for n in range(amountOfNeedles):
            self.place_needle(self.length)
        taskMgr.add(self.task_count_needles, 'count needles')
        return task.done

    def task_count_needles(self, task):
        self.colTraverser.traverse(render)
        self.touchingNeedles += len(self.colHandler.entries)
        self.guesstimate_pi()
        taskMgr.doMethodLater(0.2, self.task_remove_dropped_needles, 'remove needles')
        return task.done

    def task_remove_dropped_needles(self, task):
        for needle in self.needleHolder.getChildren():
            needle.removeNode()
        self.colTraverser.clearColliders()
        self.start_task_drop_needles()
        return task.done

    def guesstimate_pi(self):
        numerator = 2 * self.length * self.totalNeedles
        denominator = self.thickness * self.touchingNeedles
        piApprox = numerator/denominator
        self.randomizer = Randomizer(0)
        countString = f'needles thrown: {self.totalNeedles}'
        self.textCount.setText(countString)
        guesstimateString = f'guesstimate: {round(piApprox,5)}'
        self.textPiGuess.setText(guesstimateString)

app = MyGame()
app.run()


from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from dialog import Dialog

class RecipeTab(QWidget):
    RECIPE, = range(1)
    RAW_MATERIAL, AMOUNT, AMOUNT_LEFT = range(3)

    def __init__(self, db, parent = None):
        super(RecipeTab, self).__init__(parent)
        self.layout = QVBoxLayout()
        self.db = db

        dataGroupBox = QGroupBox()
        self.recipe_data_view = QTreeView()
        self.recipe_data_view.setRootIsDecorated(False)
        self.recipe_data_view.setAlternatingRowColors(True)
        self.recipe_data_view.setSortingEnabled(True)
        self.recipe_data_view.clicked.connect(self.recipe_view_onclick)

        self.raw_mat_data_view = QTreeView()
        self.raw_mat_data_view.setRootIsDecorated(False)
        self.raw_mat_data_view.setAlternatingRowColors(True)
        self.raw_mat_data_view.setSortingEnabled(True)
        self.raw_mat_data_view.clicked.connect(self.raw_mat_view_onclick)

        rm_button_labels=['Add', 'Update', 'Remove']
        rm_buttons_onclick = [self.add_onclick, self.update_onclick, self.remove_onclick]
        self.rm_buttons = [i for i in range(len(rm_button_labels))]
        hbox = QHBoxLayout()
        for i in range(len(self.rm_buttons)):
            self.rm_buttons[i] = QPushButton(rm_button_labels[i], self)
            self.rm_buttons[i].clicked.connect(rm_buttons_onclick[i])
            self.rm_buttons[i].setEnabled(rm_button_labels[i] == 'Add')
            hbox.addWidget(self.rm_buttons[i])

        self.search_box = QLineEdit(self)
        self.search_box.resize(280, 40)
        search_button = QPushButton('Search', self)
        search_button.clicked.connect(self.search_raw_mat)
        self.search_box.returnPressed.connect(search_button.click)
        reset_button = QPushButton('Reset', self)
        reset_button.clicked.connect(self.reset_search)
        dataLayout = QHBoxLayout()

        v_raw_mat_box = QVBoxLayout()

        h_searchbox = QHBoxLayout()
        v_raw_mat_box.addWidget(self.raw_mat_data_view)

        h_searchbox.addWidget(self.search_box)
        h_searchbox.addWidget(search_button)
        h_searchbox.addWidget(reset_button)
        v_raw_mat_box.addLayout(hbox)
        v_raw_mat_box.addLayout(h_searchbox)
        dataLayout.addWidget(self.recipe_data_view)
        dataLayout.addLayout(v_raw_mat_box)
        dataGroupBox.setLayout(dataLayout)

        self.recipe_model = self.createRecipeModel(self)
        self.raw_mat_model = self.createRawMaterialModel(self)

        self.recipe_data_view.setModel(self.recipe_model)
        self.raw_mat_data_view.setModel(self.raw_mat_model)
        self.update_recipe_view()
        self.update_raw_mat_view()

        self.layout.addWidget(dataGroupBox)

    @pyqtSlot()
    def add_onclick(self):
        raw_material, amount, ok = Dialog.getInfo(['Raw material', 'Amount'], 'Add')
        if (ok):
            self.db.insert_raw_material(raw_material, amount)
            self.update_raw_mat_view()

    @pyqtSlot()
    def remove_onclick(self):
        index = self.raw_mat_data_view.selectedIndexes()[0]
        item = index.model().itemFromIndex(index)
        self.db.delete_raw_material(item.text())
        self.update_raw_mat_view()

    @pyqtSlot()
    def update_onclick(self):
        rm_index, _, amount_index = self.raw_mat_data_view.selectedIndexes()
        raw_m = rm_index.model().itemFromIndex(rm_index)
        amount = amount_index.model().itemFromIndex(amount_index)
        amount, ok = Dialog.getInfo(['Amount'], 'Update', [amount.text()])
        if (ok):
            item = rm_index.model().itemFromIndex(rm_index)
            self.db.update_raw_material(item.text(), amount)
            self.update_raw_mat_view()

    @pyqtSlot()
    def recipe_view_onclick(self):
        index = self.recipe_data_view.selectedIndexes()[0]
        recipe = index.model().itemFromIndex(index).text()
        self.update_raw_mat_view(recipe)
        self.search_box.setText("")

    @pyqtSlot()
    def raw_mat_view_onclick(self):
        for b in self.rm_buttons:
            b.setEnabled(True)

    @pyqtSlot()
    def search_raw_mat(self):
        self.recipe_data_view.clearSelection()
        self.disable_buttons()
        raw_mats = self.db.get_raw_materials(self.search_box.text())
        self.raw_mat_model.setRowCount(0)
        if (len(raw_mats) > 0):
            for r in raw_mats:
                self.addRawMaterial(self.raw_mat_model, r.raw_material, '-', r.amount)
        else:
            self.search_box.setText("")
            self.addRawMaterial(self.raw_mat_model, 'No results found', '', '')
        self.raw_mat_data_view.resizeColumnToContents(0)

    @pyqtSlot()
    def reset_search(self):
        self.raw_mat_data_view.clearSelection()
        self.recipe_data_view.clearSelection()
        self.update_raw_mat_view()
        self.search_box.setText("")
        self.search_box.setFocus()

    def disable_buttons(self):
        for i in [1, 2]:
            self.rm_buttons[i].setEnabled(False)

    @pyqtSlot()
    def test(self):
        index = self.recipe_data_view.selectedIndexes()
        print(index)

    def update_recipe_view(self):
        self.recipe_model.setRowCount(0)
        recipes = self.db.get_all_recipes()
        recipes.sort(reverse = True)
        for recipe in recipes:
            self.addRecipe(self.recipe_model, recipe)
        self.recipe_data_view.resizeColumnToContents(0)

    def update_raw_mat_view(self, recipe = None):
        self.raw_mat_model.setRowCount(0)
        self.disable_buttons()
        if recipe is None:
            raw_materials =  self.db.get_all_raw_materials()
        else:
            raw_materials = self.db.get_recipe_items(recipe)
        raw_materials.sort(key = lambda raw_mat: raw_mat.raw_material, reverse = True)
        for rm in raw_materials:
            self.addRawMaterial(self.raw_mat_model, rm.raw_material, rm.amount, rm.amount_left)
        self.raw_mat_data_view.resizeColumnToContents(0)
        for i in [1, 2]:
            self.raw_mat_data_view.header().setSectionResizeMode(i, QHeaderView.Stretch);

    def createRecipeModel(self, parent):
        model = QStandardItemModel(0, 1, parent)
        model.setHeaderData(self.RECIPE, Qt.Horizontal, "Recipe")
        return model

    def createRawMaterialModel(self, parent):
        model = QStandardItemModel(0, 3, parent)
        model.setHeaderData(self.RAW_MATERIAL, Qt.Horizontal, "Raw material")
        model.setHeaderData(self.AMOUNT, Qt.Horizontal, "Amount")
        model.setHeaderData(self.AMOUNT_LEFT, Qt.Horizontal, "Amount left")
        return model

    def addRecipe(self, model, recipe):
        model.insertRow(0)
        model.setData(model.index(0, self.RECIPE), recipe)

    def addRawMaterial(self, model, raw_material, amount, amount_left):
        model.insertRow(0)
        model.setData(model.index(0, self.RAW_MATERIAL), raw_material)
        model.setData(model.index(0, self.AMOUNT), amount)
        model.setData(model.index(0, self.AMOUNT_LEFT), amount_left)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue May 30 22:34:17 2023

@author: hbonen
"""

import numpy as np
import os
import networkx as nx
from sklearn.preprocessing import LabelEncoder
from sklearn.utils import shuffle
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
from torch_geometric.data import Data, DataLoader
import torch
import torch.nn.functional as F
from torch.nn import Linear
from torch_geometric.nn import GCNConv
from torch_geometric.datasets import Planetoid
from sklearn.manifold import TSNE
from torch_geometric.nn import DataParallel
from torch_geometric.data import Batch
from torch_geometric.utils import to_networkx
from torch_geometric.nn import MessagePassing
from torch_geometric.utils import add_remaining_self_loops, to_undirected
from torch_scatter import scatter_add
import argparse
import matplotlib.pyplot as plt

# Set random seed for reproducibility
torch.manual_seed(42)

# Load the Cora dataset
dataset = Planetoid(root='./cora', name='Cora')

data = dataset[0]
x, y = data.x, data.y

# Convert labels to one-hot encoding
label_encoder = LabelEncoder()
y_encoded = label_encoder.fit_transform(y)
y_encoded = torch.tensor(y_encoded)

# Split the data into train, validation, and test sets
train_idx, test_idx, train_y, test_y = train_test_split(range(len(y_encoded)), y_encoded, stratify=y_encoded, test_size=0.2, random_state=42)

# Create the adjacency matrix (A)
edge_index = data.edge_index
adj = torch.zeros((x.shape[0], x.shape[0]))
adj[edge_index[0], edge_index[1]] = 1

# Convert data to PyTorch tensors
x = torch.tensor(x, dtype=torch.float32)
adj = adj.to(torch.float32)

parser = argparse.ArgumentParser()
parser.add_argument('--K', type=int, default=10)
args = parser.parse_args()

def gcn_norm(edge_index, edge_weight=None, num_nodes=None, improved=False,
             add_self_loops=True, dtype=None):

    fill_value = 2. if improved else 1.
    num_nodes = int(edge_index.max()) + 1 if num_nodes is None else num_nodes
    if edge_weight is None:
        edge_weight = torch.ones((edge_index.size(1), ), dtype=dtype,
                                 device=edge_index.device)

    if add_self_loops:
        edge_index, tmp_edge_weight = add_remaining_self_loops(
            edge_index, edge_weight, fill_value, num_nodes)
        assert tmp_edge_weight is not None
        edge_weight = tmp_edge_weight

    row, col = edge_index[0], edge_index[1]
    deg = scatter_add(edge_weight, col, dim=0, dim_size=num_nodes)
    deg_inv_sqrt = deg.pow_(-0.5)
    deg_inv_sqrt.masked_fill_(deg_inv_sqrt == float('inf'), 0)
    return edge_index, deg_inv_sqrt[row] * edge_weight * deg_inv_sqrt[col]

class Prop(MessagePassing):
    def __init__(self, num_classes, K, bias=True, **kwargs):
        super(Prop, self).__init__(aggr='add', **kwargs)
        self.K = K
        self.proj = Linear(num_classes, 1)

    def forward(self, x, edge_index, edge_weight=None):
        # edge_index, norm = GCNConv.norm(edge_index, x.size(0), edge_weight, dtype=x.dtype)
        edge_index, norm = gcn_norm(edge_index, edge_weight, x.size(0), dtype=x.dtype)

        preds = []
        preds.append(x)
        for k in range(self.K):
            x = self.propagate(edge_index, x=x, norm=norm)
            preds.append(x)

        pps = torch.stack(preds, dim=1)
        retain_score = self.proj(pps)
        retain_score = retain_score.squeeze()
        retain_score = torch.sigmoid(retain_score)
        retain_score = retain_score.unsqueeze(1)
        out = torch.matmul(retain_score, pps).squeeze()
        return out

    def message(self, x_j, norm):
        return norm.view(-1, 1) * x_j

    def __repr__(self):
        return '{}(K={})'.format(self.__class__.__name__, self.K)

    def reset_parameters(self):
        self.proj.reset_parameters()

class GCN(torch.nn.Module):
    def __init__(self, num_features, hidden_layers, num_classes, dropout_rate):
        super(GCN, self).__init__()
        self.linears = torch.nn.ModuleList()
        self.linears.append(torch.nn.Linear(num_features, hidden_layers[0]))
        self.dropout_rate = dropout_rate
        self.bn = []
        self.bn.append(torch.nn.BatchNorm1d(hidden_layers[0]))
        for i in range(1, len(hidden_layers)):
            self.linears.append(torch.nn.Linear(hidden_layers[i-1], hidden_layers[i]))
            self.bn.append(torch.nn.BatchNorm1d(hidden_layers[i]))
        self.linears.append(torch.nn.Linear(hidden_layers[-1], num_classes))
        # self.bn = torch.nn.BatchNorm1d(hidden_layers[-1])
        self.prop = Prop(num_classes, args.K)
        # self.dropout = dropout

    def reset_parameters(self):
        for l in self.linears:
            self.linears[l].reset_parameters()
            self.bn[l].reset_parameters()
        self.prop.reset_parameters()

    def forward(self, x, edge_index):
        for l in range(len(self.linears[:-1])):
            x = self.linears[l](x)
            x = F.dropout(x, p=self.dropout_rate, training=self.training)
            x = F.relu(self.bn[l](x))
            x = F.dropout(x, p=self.dropout_rate, training=self.training)
        x = self.linears[-1](x)
        x = self.prop(x, edge_index)
        return F.log_softmax(x, dim=1)

hidden_node = 16
hidden_layer = 5
accs = []

for i in range(1, hidden_layer +1 ):
    hidden_layers = []
    for j in range(1,i+1):
        hidden_layers.append(hidden_layer)

    dropout_rate = 0.
    model = GCN(num_features=x.shape[1], hidden_layers=hidden_layers, num_classes=dataset.num_classes, dropout_rate=dropout_rate)

    # Set up the optimizer
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

    # Training loop
    def train():
        model.train()
        optimizer.zero_grad()
        out = model(x, edge_index)  # Pass the entire feature tensor
        loss = F.nll_loss(out[train_idx], train_y)  # Only compute loss for the training nodes
        loss.backward()
        optimizer.step()

    # Evaluation loop
    def evaluate():
        model.eval()
        out = model(x, edge_index)
        pred = out.argmax(dim=1)
        test_correct = pred[test_idx] == test_y
        test_acc = int(test_correct.sum()) / len(test_idx)
        return test_acc

    # Training and evaluation
    best_test_acc = 0
    for epoch in range(1, 201):
        train()
        test_acc = evaluate()
        if test_acc > best_test_acc:
            best_test_acc = test_acc
            torch.save(model.state_dict(), 'best_model.pt')
        if epoch % 10 == 0:
            print(f'Epoch: {epoch:03d}, Test Acc: {test_acc:.4f}')

    # Load the best model and evaluate on the test set
    model.load_state_dict(torch.load('best_model.pt'))
    test_acc = evaluate()
    accs.append(test_acc)
    print(f'Test Accuracy: {test_acc:.4f}')

    # Classification report
    model.eval()
    out = model(x, edge_index)
    pred = out.argmax(dim=1)
    pred_labels = label_encoder.inverse_transform(pred.detach().numpy())
    true_labels = label_encoder.inverse_transform(y_encoded.numpy())
    print(classification_report(true_labels, pred_labels))

    # Convert the output features to numpy array
    out_features = out.detach().numpy()

    # Apply t-SNE for dimensionality reduction
    tsne = TSNE(n_components=2)
    out_tsne = tsne.fit_transform(out_features)

    # Visualize the input and output features using scatter plots
    plt.figure(figsize=(12, 6))
    plt.subplot(121)
    plt.scatter(out_tsne[:, 0], out_tsne[:, 1], c=y, cmap='tab10')
    plt.title('Output Features')

    # Convert the input features to numpy array
    x_features = x.detach().numpy()

    # Apply t-SNE for dimensionality reduction
    tsne = TSNE(n_components=2)
    x_tsne = tsne.fit_transform(x_features)

    plt.subplot(122)
    plt.scatter(x_tsne[:, 0], x_tsne[:, 1], c=y, cmap='tab10')
    plt.title('Input Features')

    plt.tight_layout()
    plt.show()

layers = range(1, hidden_layer + 1)

plt.figure()
plt.plot(layers, accs, marker='o')
plt.xlabel('Layer')
plt.ylabel('Test Accuracy')
plt.title('Test Accuracy vs. Layer')
plt.show()


from typing import Optional, Callable, Dict, Tuple, List

import numpy as np
import tensornetwork as tn

from time_evolving_mpo.backends.tensor_network.util import create_delta
from time_evolving_mpo.backends.tensor_network.util import add_singleton
from time_evolving_mpo.dynamics import Dynamics

class TensorNetworkProcessTensorBackend:
    """See BaseProcessTensorBackend for docstring. """
    def __init__(
            self,
            tensors: List[np.ndarray],
            initial_tensor: np.ndarray,
            trace: np.ndarray,
            config: Dict):
        """Create a TensorNetworkProcessTensorBackend object. """
        if "backend" in config:
            self._backend = config["backend"]
        else:
            self._backend = None

        if len(tensors) > 0 and len(tensors[0].shape) == 3:
            _tensors = [ create_delta(t, [0, 1, 2, 2]) for t in tensors ]
        elif len(tensors) > 0 and len(tensors[0].shape) == 4:
            _tensors = tensors
        else:
            _tensors = []

        self._tensors = [ tn.Node(t, backend=self._backend) for t in _tensors ]

        if initial_tensor is not None:
            self._initial_tensor = tn.Node(initial_tensor,
                                           backend=self._backend)
        else:
            self._initial_tensor = None

        self._trace = tn.Node(trace, backend=self._backend)

        self._caps = None

    def _compute_caps(self) -> None:
        """Compute trace caps of process tensor. """
        self._caps  = [tn.Node(np.array([1.0]), backend=self._backend)]

        for tensor in reversed(self._tensors):
            old_cap = self._caps[-1].copy()
            trace_out = self._trace.copy()
            trace_in = self._trace.copy()
            ten = tensor.copy()

            ten[1] ^ old_cap[0]
            ten[2] ^ trace_in[0]
            ten[3] ^ trace_out[0]

            new_cap = ten @ trace_in @ trace_out @ old_cap
            self._caps.append(new_cap)

    def get_bond_dimensions(self) -> np.ndarray:
        """See BaseProcessTensorBackend.get_bond_dimensions() for docstring. """
        if len(self._tensors) == 0:
            return None
        dims = [self._tensors[0].shape[0]]
        for ten in self._tensors:
            dims.append(ten.shape[1])
        return np.array(dims, dtype=int)

    def export_tensors(self) -> Tuple[np.ndarray, np.ndarray]:
        """See BaseProcessTensorBackend.export_tensors() for docstring. """
        tensors = [ t.get_tensor() for t in self._tensors ]
        if self._initial_tensor is not None:
            initial_tensor = self._initial_tensor.get_tensor()
        else:
            initial_tensor = None

        return tensors, initial_tensor

    def compute_dynamics(
            self,
            controls: Callable[[int], Tuple[np.ndarray, np.ndarray]],
            initial_state: Optional[np.ndarray] = None) -> Dynamics:
        """See BaseProcessTensorBackend.compute_dynamics() for docstring. """
        assert (initial_state is None) ^ (self._initial_tensor is None), \
            "Initial state must be either (exclusively) encoded in the " \
            + "process tensor or given as an argument."

        if self._caps is None:
            self._compute_caps()

        if self._initial_tensor is None:
            initial_tensor = add_singleton(initial_state, 0)
            current = tn.Node(initial_tensor, backend=self._backend)
        else:
            current = self._initial_tensor.copy()

        current_bond_leg = current[0]
        current_state_leg = current[1]

        states = []

        for i, tensor in enumerate(self._tensors):
            pre, post = controls(i)
            cap = self._caps[-1-i].copy()
            pre_node = tn.Node(pre, backend=self._backend)
            post_node = tn.Node(post, backend=self._backend)

            node_dict, edge_dict = tn.copy([current])
            edge_dict[current_bond_leg] ^ cap[0]
            state_node = node_dict[current] @ cap
            states.append(state_node.get_tensor())

            current_bond_leg ^ tensor[0]
            current_state_leg ^ pre_node[0]
            pre_node[1] ^ tensor[2]
            tensor[3] ^ post_node[0]

            current_bond_leg = tensor[1]
            current_state_leg = post_node[1]
            current = current @ pre_node @ tensor @ post_node

        cap = self._caps[0]
        current_bond_leg ^ cap[0]
        final_state_node = current @ cap
        states.append(final_state_node.get_tensor())

        return states

    def compute_final_state(
            self,
            controls: Callable[[int], Tuple[np.ndarray, np.ndarray]],
            initial_state: Optional[np.ndarray] = None) -> Dynamics:
        """See BaseProcessTensorBackend.compute_dynamics() for docstring. """
        assert (initial_state is None) ^ (self._initial_tensor is None), \
            "Initial state must be either (exclusively) encoded in the " \
            + "process tensor or given as an argument."

        if self._initial_tensor is None:
            initial_tensor = add_singleton(initial_state, 0)
            current = tn.Node(initial_tensor, backend=self._backend)
        else:
            current = self._initial_tensor.copy()

        current_bond_leg = current[0]
        current_state_leg = current[1]

        for i, tensor in enumerate(self._tensors):
            pre, post = controls(i)
            pre_node = tn.Node(pre, backend=self._backend)
            post_node = tn.Node(post, backend=self._backend)

            current_bond_leg ^ tensor[0]
            current_state_leg ^ pre_node[0]
            pre_node[1] ^ tensor[2]
            tensor[3] ^ post_node[0]

            current_bond_leg = tensor[1]
            current_state_leg = post_node[1]

            current = current @ pre_node @ tensor @ post_node

        one = tn.Node(np.array([1.0]), backend=self._backend)
        current_bond_leg ^ one[0]

        final_tensor = current @ one
        return final_tensor.get_tensor()


"""
    File Name:          RSNA_ICH/pixel_array_regularizing.py
    Author:             Xiaotian Duan (xduan7)
    Email:              xduan7@uchicago.edu
    Date:               10/29/19
    Python Version:     3.5.4
    File Description:

"""
import cv2
from .constants import *

from typing import Optional

RESIZE_INTERPOLATION = cv2.INTER_CUBIC

def regularize_pixel_arrays(
        pixel_arrays: np.ndarray,
        dimension: Optional[int],
) -> np.ndarray:

    # Note that openCV requires input array to be float32 for resizing, etc.
    pixel_arrays = pixel_arrays.astype(dtype=np.float32)

    num_channel, height, width = pixel_arrays.shape
    assert num_channel == 3

    # Pad the pixel array so that its square (3, N, N)
    if height != width:
        _square_size = height if height > width else width

        _height_padding = _square_size - height
        _height_upper_padding = _height_padding // 2
        _height_lower_padding = _height_padding - _height_upper_padding

        _width_padding = _square_size - width
        _width_upper_padding = _width_padding // 2
        _width_lower_padding = _width_padding - _width_upper_padding

        _padded_pixel_array = \
            np.pad(pixel_arrays,
                   ((0, 0),
                    (_height_upper_padding, _height_lower_padding),
                    (_width_upper_padding, _width_lower_padding)),
                   mode='minimum')
    else:
        _padded_pixel_array = pixel_arrays

    # Resize into the desired dimension
    if dimension:
        regularized_pixel_arrays = []
        for _channel_pixel_array in _padded_pixel_array:

            _resized_pixel_array = cv2.resize(
                _channel_pixel_array,
                dsize=(dimension, dimension),
                interpolation=cv2.INTER_CUBIC)
            regularized_pixel_arrays.append(_resized_pixel_array)

        return np.array(regularized_pixel_arrays, dtype=PIXEL_PROCESSING_DTYPE)
    else:
        return np.array(_padded_pixel_array, dtype=PIXEL_PROCESSING_DTYPE)


from lxml import html,etree
import requests
from fake_useragent import UserAgent
import openpyxl
ua = UserAgent()
headers = {
     'User-Agent':ua.random
}
workbook = openpyxl.load_workbook('part2.xlsx')
sheet = workbook.worksheets[1]
row_count = sheet.max_row
def geturl(imglst):
    try:
        return imglst[0]
    except IndexError:
        return ''
for i in range(2,row_count+1):
    resp = requests.get(url=sheet.cell(row=i,column=3).value,headers=headers)
    print("Row Number : ",i,"--->",resp.status_code)
    tree = html.fromstring(html=resp.content)
    description = tree.xpath("//div[@class='midd_sec']/p")[-3:-1]
    imgUrl = geturl(tree.xpath("//div[@class='fea_img']/img/@src"))
    sheet.cell(row=i,column=4).value = ''.join(list((html.tostring(descp, pretty_print=True).decode() for descp in description)))
    sheet.cell(row=i,column=5).value = imgUrl
workbook.save('part2.xlsx')


# coding=utf-8

import tensorflow as tf
import tensorflow.contrib as tc

import gpu_env
from .common import dropout, dense, get_var

def single_rnn_cell(cell_name, num_units, is_train=None, keep_prob=0.75):
    """
    Get a single rnn cell
    """
    cell_name = cell_name.upper()
    if cell_name == "GRU":
        cell = tf.contrib.rnn.GRUCell(num_units)
    elif cell_name == "LSTM":
        cell = tf.contrib.rnn.LSTMCell(num_units)
    else:
        cell = tf.contrib.rnn.BasicRNNCell(num_units)

    # dropout wrapper
    if is_train and keep_prob < 1.0:
        cell = tf.contrib.rnn.DropoutWrapper(
            cell=cell,
            input_keep_prob=keep_prob,
            output_keep_prob=keep_prob)

    return cell

def multi_rnn_cell(cell_name, num_units, is_train=None, keep_prob=1.0, num_layers=3):
    cell_name = cell_name.upper()
    if cell_name == "GRU":
        cells = [tf.contrib.rnn.GRUCell(num_units) for _ in range(num_layers)]
    elif cell_name == "LSTM":
        cells = [tf.contrib.rnn.LayerNormBasicLSTMCell(num_units) for _ in range(num_layers)]
    else:
        cells = [tf.contrib.rnn.BasicRNNCell(num_units) for _ in range(num_layers)]

    cell = tf.contrib.rnn.MultiRNNCell(cells)
    # dropout wrapper
    if is_train and keep_prob < 1.0:
        cell = tf.contrib.rnn.DropoutWrapper(
            cell=cell,
            input_keep_prob=keep_prob,
            output_keep_prob=keep_prob)
    return cell

def get_lstm_init_state(batch_size, num_layers, num_units, direction, scope=None, reuse=None, **kwargs):
    with tf.variable_scope(scope or 'lstm_init_state', reuse=reuse):
        num_dir = 2 if direction.startswith('bi') else 1
        c = get_var('lstm_init_c', shape=[num_layers * num_dir, num_units])
        c = tf.tile(tf.expand_dims(c, axis=1), [1, batch_size, 1])
        h = get_var('lstm_init_h', shape=[num_layers * num_dir, num_units])
        h = tf.tile(tf.expand_dims(h, axis=1), [1, batch_size, 1])
        return c, h

def LSTM_encode(seqs, scope=None, reuse=None, **kwargs):
    with tf.variable_scope(scope or 'lstm_encode_block', reuse=reuse):
        batch_size = tf.shape(seqs)[0]
        # to T, B, D
        _seqs = tf.transpose(seqs, [1, 0, 2])
        lstm = tf.contrib.cudnn_rnn.CudnnLSTM(**kwargs)
        init_state = get_lstm_init_state(batch_size, **kwargs)
        output, state = lstm(_seqs, init_state)
        return tf.transpose(output, [1, 0, 2]), state

def custom_dynamic_rnn(cell, inputs, inputs_len, initial_state=None):
    """
    Implements a dynamic rnn that can store scores in the pointer network,
    the reason why we implements this is that the raw_rnn or dynamic_rnn function in Tensorflow
    seem to require the hidden unit and memory unit has the same dimension, and we cannot
    store the scores directly in the hidden unit.
    Args:
        cell: RNN cell
        inputs: the input sequence to rnn
        inputs_len: valid length
        initial_state: initial_state of the cell
    Returns:
        outputs and state
    """
    batch_size = tf.shape(inputs)[0]
    max_time = tf.shape(inputs)[1]

    inputs_ta = tf.TensorArray(dtype=gpu_env.DTYPE_F, size=max_time)
    inputs_ta = inputs_ta.unstack(tf.transpose(inputs, [1, 0, 2]))

    emit_ta = tf.TensorArray(dtype=gpu_env.DTYPE_F, dynamic_size=True, size=0)
    t0 = tf.constant(0, dtype=tf.int32)
    if initial_state is not None:
        s0 = initial_state
    else:
        s0 = cell.zero_state(batch_size, dtype=gpu_env.DTYPE_F)
    f0 = tf.zeros([batch_size], dtype=tf.bool)

    def loop_fn(time, prev_s, emit_ta, finished):
        """
        the loop function of rnn
        """
        cur_x = inputs_ta.read(time)
        scores, cur_state = cell(cur_x, prev_s)

        # copy through
        scores = tf.where(finished, tf.zeros_like(scores), scores)

        if isinstance(cell, tc.rnn.LSTMCell):
            cur_c, cur_h = cur_state
            prev_c, prev_h = prev_s
            cur_state = tc.rnn.LSTMStateTuple(tf.where(finished, prev_c, cur_c),
                                              tf.where(finished, prev_h, cur_h))
        else:
            cur_state = tf.where(finished, prev_s, cur_state)

        emit_ta = emit_ta.write(time, scores)
        finished = tf.greater_equal(time + 1, inputs_len)
        return [time + 1, cur_state, emit_ta, finished]

    _, state, emit_ta, _ = tf.while_loop(
        cond=lambda _1, _2, _3, finished: tf.logical_not(
            tf.reduce_all(finished)),
        body=loop_fn,
        loop_vars=(t0, s0, emit_ta, f0),
        parallel_iterations=32,
        swap_memory=False)

    outputs = tf.transpose(emit_ta.stack(), [1, 0, 2])
    return outputs, state

def reduce_state(fw_state, bw_state, hidden_size, act_fn=tf.nn.relu, scope=None):
    # concatennation of fw and bw cell
    with tf.variable_scope(scope or "reduce_final_state"):
        _c = tf.concat([fw_state.c, bw_state.c], axis=1)
        _h = tf.concat([fw_state.h, bw_state.h], axis=1)

        c = act_fn(dense(_c, hidden_size, use_bias=True, scope="reduce_c"))
        h = act_fn(dense(_h, hidden_size, use_bias=True, scope="reduce_h"))
        return tc.rnn.LSTMStateTuple(c, h)

class CudaRNN:
    def __init__(self, num_layers, num_units, cell_type):
        self.num_layers = num_layers
        self.num_units = num_units
        if cell_type.endswith('gru'):
            self.grus = [(tf.contrib.cudnn_rnn.CudnnGRU(1, num_units),
                          tf.contrib.cudnn_rnn.CudnnGRU(1, num_units)) for _ in range(num_layers)]
        elif cell_type.endswith('lstm'):
            self.grus = [(tf.contrib.cudnn_rnn.CudnnLSTM(1, num_units),
                          tf.contrib.cudnn_rnn.CudnnLSTM(1, num_units)) for _ in range(num_layers)]
        else:
            raise NotImplementedError

    def __call__(self, inputs, seq_len, keep_prob=1.0,
                 is_train=None, init_states=None, concat_layers=True):
        outputs = [tf.transpose(inputs, [1, 0, 2])]
        batch_size = tf.shape(inputs)[0]
        if not init_states:
            init_states = []
            for layer in range(self.num_layers):
                init_fw = tf.tile(tf.Variable(
                    tf.zeros([1, 1, self.num_units])), [1, batch_size, 1])
                init_bw = tf.tile(tf.Variable(
                    tf.zeros([1, 1, self.num_units])), [1, batch_size, 1])
                init_states.append((init_fw, init_bw))

        dropout_mask = []
        for layer in range(self.num_layers):
            input_size_ = inputs.get_shape().as_list(
            )[-1] if layer == 0 else 2 * self.num_units
            mask_fw = dropout(tf.ones([1, batch_size, input_size_], dtype=tf.float32),
                              keep_prob=keep_prob, is_train=is_train, mode=None)
            mask_bw = dropout(tf.ones([1, batch_size, input_size_], dtype=tf.float32),
                              keep_prob=keep_prob, is_train=is_train, mode=None)

            dropout_mask.append((mask_fw, mask_bw))

        for layer in range(self.num_layers):
            gru_fw, gru_bw = self.grus[layer]
            init_fw, init_bw = init_states[layer]
            mask_fw, mask_bw = dropout_mask[layer]
            with tf.variable_scope("fw_{}".format(layer)):
                out_fw, _ = gru_fw(
                    outputs[-1] * mask_fw, initial_state=(init_fw,))
            with tf.variable_scope("bw_{}".format(layer)):
                inputs_bw = tf.reverse_sequence(
                    outputs[-1] * mask_bw, seq_lengths=seq_len, seq_dim=0, batch_dim=1)
                out_bw, _ = gru_bw(inputs_bw, initial_state=(init_bw,))
                out_bw = tf.reverse_sequence(
                    out_bw, seq_lengths=seq_len, seq_dim=0, batch_dim=1)
            outputs.append(tf.concat([out_fw, out_bw], axis=2))
        if concat_layers:
            res = tf.concat(outputs[1:], axis=2)
        else:
            res = outputs[-1]
        res = tf.transpose(res, [1, 0, 2])
        return res

class native_gru:

    def __init__(self, num_layers, num_units, batch_size, input_size, keep_prob=1.0, is_train=None, scope="native_gru"):
        self.num_layers = num_layers
        self.grus = []
        self.inits = []
        self.dropout_mask = []
        self.scope = scope
        for layer in range(num_layers):
            input_size_ = input_size if layer == 0 else 2 * num_units
            gru_fw = tf.contrib.rnn.GRUCell(num_units)
            gru_bw = tf.contrib.rnn.GRUCell(num_units)
            init_fw = tf.tile(tf.Variable(
                tf.zeros([1, num_units])), [batch_size, 1])
            init_bw = tf.tile(tf.Variable(
                tf.zeros([1, num_units])), [batch_size, 1])
            mask_fw = dropout(tf.ones([batch_size, 1, input_size_], dtype=tf.float32),
                              keep_prob=keep_prob, is_train=is_train, mode=None)
            mask_bw = dropout(tf.ones([batch_size, 1, input_size_], dtype=tf.float32),
                              keep_prob=keep_prob, is_train=is_train, mode=None)
            self.grus.append((gru_fw, gru_bw,))
            self.inits.append((init_fw, init_bw,))
            self.dropout_mask.append((mask_fw, mask_bw,))

    def __call__(self, inputs, seq_len, keep_prob=1.0, is_train=None, concat_layers=True):
        outputs = [inputs]
        with tf.variable_scope(self.scope):
            for layer in range(self.num_layers):
                gru_fw, gru_bw = self.grus[layer]
                init_fw, init_bw = self.inits[layer]
                mask_fw, mask_bw = self.dropout_mask[layer]
                with tf.variable_scope("fw_{}".format(layer)):
                    out_fw, _ = tf.nn.dynamic_rnn(
                        gru_fw, outputs[-1] * mask_fw, seq_len, initial_state=init_fw, dtype=tf.float32)
                with tf.variable_scope("bw_{}".format(layer)):
                    inputs_bw = tf.reverse_sequence(
                        outputs[-1] * mask_bw, seq_lengths=seq_len, seq_dim=1, batch_dim=0)
                    out_bw, _ = tf.nn.dynamic_rnn(
                        gru_bw, inputs_bw, seq_len, initial_state=init_bw, dtype=tf.float32)
                    out_bw = tf.reverse_sequence(
                        out_bw, seq_lengths=seq_len, seq_dim=1, batch_dim=0)
                outputs.append(tf.concat([out_fw, out_bw], axis=2))
        if concat_layers:
            res = tf.concat(outputs[1:], axis=2)
        else:
            res = outputs[-1]
        return res


import itertools

N, M = map(int, input().split())
G = [set() for _ in range(N)]
for _ in range(M):
    a, b = map(lambda x: int(x)-1, input().split())
    G[a].add(b)
    G[b].add(a)

ans = 0
for x in itertools.permutations(list(range(1, N))):
    s = 0
    res = True
    for xi in x:
        if not xi in G[s]:
            res = False
            break

        s = xi

    if res:
        ans += 1

print(ans)


#!/usr/bin/env python
# coding: utf-8

# In[3]:

import flask
from flask import Flask
from flask import render_template
from flask import request
import pandas as pd
import numpy as np
import pickle

# In[ ]:

with open(f'notebooks/model.pkl','rb') as f:
    model = pickle.load(f)

app = Flask(__name__,template_folder= 'templates')

@app.route('/',methods = ['GET','POST'])
def main():
    if request.method == 'GET':
        return(render_template('main.html'))
    if request.method == 'POST':
        Pclass = request.form['Pclass']
        Sex = request.form['Sex']
        Age = request.form['Age']
        SibSp = request.form['SibSp']
        Parch = request.form['Parch']
        Fare = request.form['Fare']
        Embarked = request.form['Embarked']
        family_size = SibSp + Parch
        title = request.form['title']
        input_values = pd.DataFrame([[Pclass,Sex,Age,SibSp,Parch,Fare,Embarked,family_size,title]],columns = ['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked','family_size','title'])

        sex_dict = {'male':0,'female':1}
        input_values['Sex'] = input_values['Sex'].map(sex_dict)
        embark_dict = {'C':0,'Q':1,'S':2}
        input_values['Embarked'] = input_values['Embarked'].map(embark_dict)
        title_dict = {"Mr":0,"Mrs":1,"Miss":2,"Master":3}
        input_values['title'] = input_values['title'].map(title_dict)

        p = model.predict(input_values.values)

        if p[0] == 0:
            prediction = "No"
        else:
            prediction = 'Yes'

        return render_template('main.html',original_input = {'Pclass':Pclass,'Sex':Sex,'Age':Age,'SibSp':SibSp,'Parch':Parch,'Fare':Fare,'Embarked':Embarked,'title':title},result = prediction)

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

# In[ ]:


import os
import json

image_path = "images/"  # Replace with the actual path to your image directory

jpeg_files = [f for f in os.listdir(image_path) if f.endswith(".jpg") or f.endswith(".jpeg")]
sorted_files = sorted(jpeg_files)

with open(f"{image_path}/filelist.json", "w") as file:
    json.dump(sorted_files, file)


import matplotlib.pyplot as plt
import numpy as np

dataCount = 100

file = np.genfromtxt( './data', delimiter=',' )
#time1 = file[:,0]
#time2 = file[:,2]
val1 = file[:,0]
val2 = file[:,1]
y = np.arange( 0, 300 )
fig1 = plt.figure()
ax = fig1.add_subplot(111)
ax.plot( y, val1[:300] )
ax.plot( y, val2[:300] )
ax.set_xlabel( "Time" )
ax.set_ylabel( "Voltage (Volts)" )
ax.grid()

#bnext = Button(axnext, 'Next')
#bnext.on_clicked(callback.next)
#bprev = Button(axprev, 'Previous')
#bprev.on_clicked(callback.prev)

plt.show()


import glob
import logging
import os
import time

import numpy as np
from astropy.io import fits
from astropy.stats import biweight_location
from multiprocess import Process, Queue
# Then use system installed version instead
from ppxf.ppxf import ppxf
from printStatus import printStatus

from gistPipeline.auxiliary import _auxiliary
from gistPipeline.prepareTemplates import _prepareTemplates

# Physical constants
C = 299792.458  # speed of light in km/s

"""
PURPOSE:
  This module performs the extraction of non-parametric star-formation histories
  by full-spectral fitting.  Basically, it acts as an interface between pipeline
  and the pPXF routine from Cappellari & Emsellem 2004
  (ui.adsabs.harvard.edu/?#abs/2004PASP..116..138C;
  ui.adsabs.harvard.edu/?#abs/2017MNRAS.466..798C).
"""

def robust_sigma(y, zero=False):
     """
     Biweight estimate of the scale (standard deviation).
     Implements the approach described in
     "Understanding Robust and Exploratory Data Analysis"
     Hoaglin, Mosteller, Tukey ed., 1983, Chapter 12B, pg. 417
     Added for sigma-clipping method
     """
     y = np.ravel(y)
     d = y if zero else y - np.median(y)

     mad = np.median(np.abs(d))
     u2 = (d/(9.0*mad))**2  # c = 9
     good = u2 < 1.0
     u1 = 1.0 - u2[good]
     num = y.size * ((d[good]*u1**2)**2).sum()
     den = (u1*(1.0 - 5.0*u2[good])).sum()
     sigma = np.sqrt(num/(den*(den - 1.0)))  # see note in above reference

     return sigma

def workerPPXF(inQueue, outQueue):
    """
    Defines the worker process of the parallelisation with multiprocessing.Queue
    and multiprocessing.Process.
    """

    for (
        templates,
        galaxy,
        noise,
        velscale,
        start,
        goodPixels_sfh,
        mom,
        offset,
        degree,
        mdeg,
        regul_err,
        doclean,
        fixed,
        velscale_ratio,
        npix,
        ncomb,
        nbins,
        i,
        optimal_template_in,
    ) in iter(inQueue.get,'STOP'):
        (
            sol,
            w_row,
            bestfit,
            optimal_template,
            mc_results,
            formal_error,
            spectral_mask
        ) = run_ppxf(templates,
            galaxy,
            noise,
            velscale,
            start,
            goodPixels_sfh,
            mom,
            offset,
            degree,
            mdeg,
            regul_err,
            doclean,
            fixed,
            velscale_ratio,
            npix,
            ncomb,
            nbins,
            i,
            optimal_template_in,
        )

        outQueue.put(
            (
                i,
                sol,
                w_row,
                bestfit,
                optimal_template,
                mc_results,
                formal_error,
                spectral_mask,
            )
        )

def run_ppxf_firsttime(
    templates,
    log_bin_data,
    log_bin_error,
    velscale,
    start,
    goodPixels,
    nmoments,
    offset,
    degree,
    mdeg,
    regul_err,
    doclean,
    fixed,
    velscale_ratio,
    npix,
    ncomb,
    nbins,
    i,
    optimal_template_in,
):
    """
    Calls the penalised Pixel-Fitting routine from Cappellari & Emsellem 2004
    (ui.adsabs.harvard.edu/?#abs/2004PASP..116..138C;
    ui.adsabs.harvard.edu/?#abs/2017MNRAS.466..798C), in order to determine the
    non-parametric star-formation histories.
    """
        # Call PPXF for first time to get optimal template
    print("Running pPXF for the first time")
    logging.info("Using the new 3-step pPXF implementation")
    pp = ppxf(
        templates,
        log_bin_data,
        log_bin_error,
        velscale,
        start,
        goodpixels=goodPixels,
        plot=False,
        quiet=True,
        moments=nmoments,
        degree=-1,
        vsyst=offset,
        mdegree=mdeg,
        regul = 1./regul_err,
        fixed=fixed,
        velscale_ratio=velscale_ratio,
    )

    normalized_weights = pp.weights / np.sum( pp.weights )
    optimal_template   = np.zeros( templates.shape[0] )
    for j in range(0, templates.shape[1]):
        optimal_template = optimal_template + templates[:,j]*normalized_weights[j]

    return optimal_template

def run_ppxf(
    templates,
    log_bin_data,
    log_bin_error,
    velscale,
    start,
    goodPixels,
    nmoments,
    offset,
    degree,
    mdeg,
    regul_err,
    doclean,
    fixed,
    velscale_ratio,
    npix,
    ncomb,
    nbins,
    i,
    optimal_template_in,
):

    """
    Calls the penalised Pixel-Fitting routine from Cappellari & Emsellem 2004
    (ui.adsabs.harvard.edu/?#abs/2004PASP..116..138C;
    ui.adsabs.harvard.edu/?#abs/2017MNRAS.466..798C), in order to determine the
    non-parametric star-formation histories.
    """
    printStatus.progressBar(i, nbins, barLength=50)

    try:

        if len(optimal_template_in) > 1:
            # First Call PPXF - do fit and estimate noise
            # use fake noise for first iteration
            fake_noise=np.full_like(log_bin_data, 1.0)

            pp_step1 = ppxf(
                optimal_template_in,
                log_bin_data,
                fake_noise,
                velscale,
                start,
                goodpixels=goodPixels,
                plot=False,
                quiet=True,
                moments=nmoments,
                degree=-1,
                vsyst=offset,
                mdegree=mdeg,
                fixed=fixed,
                velscale_ratio=velscale_ratio,
            )
            # Find a proper estimate of the noise
            #noise_orig = biweight_location(log_bin_error[goodPixels])
            #goodpixels is one shorter than log_bin_error
            noise_orig = np.mean(log_bin_error[goodPixels])
            noise_est = robust_sigma(pp_step1.galaxy[goodPixels]-pp_step1.bestfit[goodPixels])

            # Calculate the new noise, and the sigma of the distribution.
            noise_new = log_bin_error*(noise_est/noise_orig)
            noise_new_std = robust_sigma(noise_new)

            # A temporary fix for the noise issue where a single high S/N spaxel causes clipping of the entire spectrum
            noise_new[np.where(noise_new <= noise_est-noise_new_std)] = noise_est

            ################ 2 ##################
            # Second Call PPXF - use best-fitting template, determine outliers
            if doclean == True:
                pp_step2 = ppxf(
                    optimal_template_in,
                    log_bin_data,
                    noise_new,
                    velscale,
                    start,
                    goodpixels=goodPixels,
                    plot=False,
                    quiet=True,
                    moments=nmoments,
                    degree=-1,
                    vsyst=offset,
                    mdegree=mdeg,
                    fixed=fixed,
                    velscale_ratio=velscale_ratio,
                    clean=True,
                )

                # update goodpixels
                goodPixels = pp_step2.goodpixels

                # repeat noise scaling # Find a proper estimate of the noise
                noise_orig = biweight_location(log_bin_error[goodPixels])
                noise_est = robust_sigma(pp_step1.galaxy[goodPixels]-pp_step2.bestfit[goodPixels])

                # Calculate the new noise, and the sigma of the distribution.
                noise_new = log_bin_error*(noise_est/noise_orig)
                noise_new_std = robust_sigma(noise_new)

                # A temporary fix for the noise issue where a single high S/N spaxel causes clipping of the entire spectrum
                noise_new[np.where(noise_new <= noise_est-noise_new_std)] = noise_est

            ################ 3 ##################
            # Third Call PPXF - use all templates, get best-fit
            pp = ppxf(
                templates,
                log_bin_data,
                noise_new,
                velscale,
                start,
                goodpixels=goodPixels,
                plot=False,
                quiet=True,
                moments=nmoments,
                degree=-1,
                vsyst=offset,
                mdegree=mdeg,
                regul = 1./regul_err,
                fixed=fixed,
                velscale_ratio=velscale_ratio,
            )

        #update goodpixels again
        goodPixels = pp.goodpixels

        #make spectral mask
        spectral_mask = np.full_like(log_bin_data, 0.0)
        spectral_mask[goodPixels] = 1.0

        # Make the unconvolved optimal stellar template
        normalized_weights = pp.weights / np.sum( pp.weights )
        optimal_template   = np.zeros( templates.shape[0] )
        for j in range(0, templates.shape[1]):
            optimal_template = optimal_template + templates[:,j]*normalized_weights[j]

        # Correct the formal errors assuming that the fit is good
        formal_error = pp.error * np.sqrt(pp.chi2)

        weights = pp.weights.reshape(templates.shape[1:])/pp.weights.sum()
        w_row   = np.reshape(weights, ncomb)

        # # Do MC-Simulations - Amelia - this is not currently implemented. Add back in later.
        # sol_MC     = np.zeros((nsims,nmoments))
        mc_results = np.zeros(nmoments)
        #
        # for o in range(0, nsims):
        #     # Add noise to bestfit:
        #     #   - Draw random numbers from normal distribution with mean of 0 and sigma of 1 (np.random.normal(0,1,npix)
        #     #   - standard deviation( (galaxy spectrum - bestfit)[goodpix] )
        #     noisy_bestfit = pp.bestfit  +  np.random.normal(0, 1, len(log_bin_data)) * np.std( log_bin_data[goodPixels] - pp.bestfit[goodPixels] )
        #
        #     mc = ppxf(templates, noisy_bestfit, log_bin_error, velscale, start, goodpixels=goodPixels, plot=False, \
        #             quiet=True, moments=nmoments, degree=-1, mdegree=mdeg, velscale_ratio=velscale_ratio, vsyst=offset, bias=0.0)
        #     sol_MC[o,:] = mc.sol[:]
        #
        # if nsims != 0:
        #     mc_results = np.nanstd( sol_MC, axis=0 )
        # print(pp.sol[:])

        return(pp.sol[:], w_row, pp.bestfit, optimal_template, mc_results, formal_error, spectral_mask) # AMELIA: do I neeed the pp.reddening?

    except:
        return( np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan )

# ## *****************************************************************************
# #        noise_i = noise_i * np.sqrt(  / len(goodPixels) )
# #        regul_err =
#
#         pp = ppxf(templates, galaxy_i, noise_i, velscale, start, goodpixels=goodPixels, plot=False, quiet=True,\
#               moments=nmom, degree=-1, vsyst=dv, mdegree=mdeg, regul=1./regul_err, fixed=fixed, velscale_ratio=velscale_ratio)
#
# #        if i == 0:
# #            print()
# #            print( i, pp.chi2 )
# #            print( len( goodPixels ) )
# #            print( np.sqrt(2 * len(goodPixels)) )
# #            print()
#
#         weights = pp.weights.reshape(templates.shape[1:])/pp.weights.sum()
#         w_row   = np.reshape(weights, ncomb)
#
#         # Correct the formal errors assuming that the fit is good
#         formal_error = pp.error * np.sqrt(pp.chi2)
#
#         return(pp.sol, w_row, pp.bestfit, formal_error)
#
#     except:
#         return(np.nan, np.nan, np.nan, np.nan)

def mean_agemetalalpha(w_row, ageGrid, metalGrid, alphaGrid, nbins):
    """
    Calculate the mean age, metallicity and alpha enhancement in each bin.
    """
    mean = np.zeros( (nbins,3) ); mean[:,:] = np.nan

    for i in range( nbins ):
        mean[i,0] = np.sum(w_row[i] * ageGrid.ravel())   / np.sum(w_row[i])
        mean[i,1] = np.sum(w_row[i] * metalGrid.ravel()) / np.sum(w_row[i])
        mean[i,2] = np.sum(w_row[i] * alphaGrid.ravel()) / np.sum(w_row[i])

    return(mean)

def save_sfh(
    mean_result,
    ppxf_result,
    w_row,
    mc_results,
    formal_error,
    logAge_grid,
    metal_grid,
    alpha_grid,
    ppxf_bestfit,
    logLam,
    goodPixels,
    velscale,
    logLam1,
    ncomb,
    nAges,
    nMetal,
    nAlpha,
    npix,
    config,
    spectral_mask,
    optimal_template_comb,
):
    """ Save all results to disk. """

    # ========================
    # SAVE KINEMATICS
    outfits_sfh = (
        os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
        + "_sfh.fits"
    )
    printStatus.running("Writing: " + config["GENERAL"]["RUN_ID"] + "_sfh.fits")

    # Primary HDU
    priHDU = fits.PrimaryHDU()

    # Table HDU with stellar kinematics
    cols = []
    cols.append(fits.Column(name="AGE", format="D", array=mean_result[:, 0]))
    cols.append(fits.Column(name="METAL", format="D", array=mean_result[:, 1]))
    cols.append(fits.Column(name="ALPHA", format="D", array=mean_result[:, 2]))

    if config["SFH"]["FIXED"] == False:
        cols.append(fits.Column(name="V", format="D", array=kin[:, 0]))
        cols.append(fits.Column(name="SIGMA", format="D", array=kin[:, 1]))
        if np.any(kin[:, 2]) != 0:
            cols.append(fits.Column(name="H3", format="D", array=kin[:, 2]))
        if np.any(kin[:, 3]) != 0:
            cols.append(fits.Column(name="H4", format="D", array=kin[:, 3]))
        if np.any(kin[:, 4]) != 0:
            cols.append(fits.Column(name="H5", format="D", array=kin[:, 4]))
        if np.any(kin[:, 5]) != 0:
            cols.append(fits.Column(name="H6", format="D", array=kin[:, 5]))

        cols.append(
            fits.Column(name="FORM_ERR_V", format="D", array=formal_error[:, 0])
        )
        cols.append(
            fits.Column(name="FORM_ERR_SIGMA", format="D", array=formal_error[:, 1])
        )
        if np.any(formal_error[:, 2]) != 0:
            cols.append(
                fits.Column(name="FORM_ERR_H3", format="D", array=formal_error[:, 2])
            )
        if np.any(formal_error[:, 3]) != 0:
            cols.append(
                fits.Column(name="FORM_ERR_H4", format="D", array=formal_error[:, 3])
            )
        if np.any(formal_error[:, 4]) != 0:
            cols.append(
                fits.Column(name="FORM_ERR_H5", format="D", array=formal_error[:, 4])
            )
        if np.any(formal_error[:, 5]) != 0:
            cols.append(
                fits.Column(name="FORM_ERR_H6", format="D", array=formal_error[:, 5])
            )

    dataHDU = fits.BinTableHDU.from_columns(fits.ColDefs(cols))
    dataHDU.name = "SFH"

    # Create HDU list and write to file
    priHDU = _auxiliary.saveConfigToHeader(priHDU, config["SFH"])
    dataHDU = _auxiliary.saveConfigToHeader(dataHDU, config["SFH"])
    HDUList = fits.HDUList([priHDU, dataHDU])
    HDUList.writeto(outfits_sfh, overwrite=True)

    printStatus.updateDone("Writing: " + config["GENERAL"]["RUN_ID"] + "_sfh.fits")
    logging.info("Wrote: " + outfits_sfh)

    # ========================
    # SAVE WEIGHTS AND GRID
    outfits_sfh = (
        os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
        + "_sfh-weights.fits"
    )
    printStatus.running("Writing: " + config["GENERAL"]["RUN_ID"] + "_sfh-weights.fits")

    # Primary HDU
    priHDU = fits.PrimaryHDU()

    # Table HDU with weights
    cols = []
    cols.append(
        fits.Column(name="WEIGHTS", format=str(w_row.shape[1]) + "D", array=w_row)
    )
    dataHDU = fits.BinTableHDU.from_columns(fits.ColDefs(cols))
    dataHDU.name = "WEIGHTS"

    logAge_row = np.reshape(logAge_grid, ncomb)
    metal_row = np.reshape(metal_grid, ncomb)
    alpha_row = np.reshape(alpha_grid, ncomb)

    # Table HDU with grids
    cols = []
    cols.append(fits.Column(name="LOGAGE", format="D", array=logAge_row))
    cols.append(fits.Column(name="METAL", format="D", array=metal_row))
    cols.append(fits.Column(name="ALPHA", format="D", array=alpha_row))
    gridHDU = fits.BinTableHDU.from_columns(fits.ColDefs(cols))
    gridHDU.name = "GRID"

    # Create HDU list and write to file
    priHDU = _auxiliary.saveConfigToHeader(priHDU, config["SFH"])
    dataHDU = _auxiliary.saveConfigToHeader(dataHDU, config["SFH"])
    gridHDU = _auxiliary.saveConfigToHeader(gridHDU, config["SFH"])
    HDUList = fits.HDUList([priHDU, dataHDU, gridHDU])
    HDUList.writeto(outfits_sfh, overwrite=True)

    fits.setval(outfits_sfh, "NAGES", value=nAges)
    fits.setval(outfits_sfh, "NMETAL", value=nMetal)
    fits.setval(outfits_sfh, "NALPHA", value=nAlpha)

    printStatus.updateDone(
        "Writing: " + config["GENERAL"]["RUN_ID"] + "_sfh-weights.fits"
    )
    logging.info("Wrote: " + outfits_sfh)

    # ========================
    # SAVE BESTFIT
    outfits_sfh = (
        os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
        + "_sfh-bestfit.fits"
    )
    printStatus.running("Writing: " + config["GENERAL"]["RUN_ID"] + "_sfh-bestfit.fits")

    # Primary HDU
    priHDU = fits.PrimaryHDU()

    # Table HDU with SFH bestfit
    cols = []
    cols.append( fits.Column(name='BESTFIT', format=str(npix)+'D', array=ppxf_bestfit ))

    dataHDU = fits.BinTableHDU.from_columns(fits.ColDefs(cols))
    dataHDU.name = "BESTFIT"

    # Table HDU with SFH logLam
    cols = []

    cols.append( fits.Column(name='LOGLAM', format='D', array=logLam ))

    logLamHDU = fits.BinTableHDU.from_columns(fits.ColDefs(cols))
    logLamHDU.name = "LOGLAM"

    # Table HDU with SFH goodpixels
    cols = []
    cols.append(fits.Column(name="GOODPIX", format="J", array=goodPixels))
    goodpixHDU = fits.BinTableHDU.from_columns(fits.ColDefs(cols))
    goodpixHDU.name = "GOODPIX"

    # Create HDU list and write to file
    priHDU = _auxiliary.saveConfigToHeader(priHDU, config["SFH"])
    dataHDU = _auxiliary.saveConfigToHeader(dataHDU, config["SFH"])
    logLamHDU = _auxiliary.saveConfigToHeader(logLamHDU, config["SFH"])
    goodpixHDU = _auxiliary.saveConfigToHeader(goodpixHDU, config["SFH"])
    HDUList = fits.HDUList([priHDU, dataHDU, logLamHDU, goodpixHDU])
    HDUList.writeto(outfits_sfh, overwrite=True)

    fits.setval(outfits_sfh, "VELSCALE", value=velscale)
    fits.setval(outfits_sfh, "CRPIX1", value=1.0)
    fits.setval(outfits_sfh, "CRVAL1", value=logLam1[0])
    fits.setval(outfits_sfh, "CDELT1", value=logLam1[1] - logLam1[0])

    printStatus.updateDone(
        "Writing: " + config["GENERAL"]["RUN_ID"] + "_sfh-bestfit.fits"
    )
    logging.info("Wrote: " + outfits_sfh)

def extractStarFormationHistories(config):
    """
    Starts the computation of non-parametric star-formation histories with
    pPXF.  A spectral template library sorted in a three-dimensional grid of
    age, metallicity, and alpha-enhancement is loaded.  Emission-subtracted
    spectra are used for the fit. An according emission-line mask is
    constructed. The stellar kinematics can or cannot be fixed to those obtained
    with a run of unregularized pPXF and the analysis started.  Results are
    saved to disk and the plotting routines called.
    """

    # Read LSF information
    LSF_Data, LSF_Templates = _auxiliary.getLSF(config, "SFH")

    # Prepare template library
    velscale = fits.open(
        os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
        + "_BinSpectra.fits"
    )[0].header["VELSCALE"]
    velscale_ratio = 2

    (
        templates,
        lamRange_temp,
        logLam_template,
        ntemplates,
        logAge_grid,
        metal_grid,
        alpha_grid,
        ncomb,
        nAges,
        nMetal,
        nAlpha,
    ) = _prepareTemplates.prepareTemplates_Module(
        config,
        config['SFH']['LMIN'],
        config['SFH']['LMAX'],
        velscale/velscale_ratio,
        LSF_Data,
        LSF_Templates,
        'SFH',
        sortInGrid=True,
    )
    templates = templates.reshape( (templates.shape[0], ntemplates) )

    # Read spectra
    if (
        os.path.isfile(
            os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
            + "_ppxf-bestfit-emlines_BIN.fits"
        )
        == True
    ):
        logging.info(
            "Using emission-subtracted spectra at "
            + os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
            + "__ppxf-bestfit-emlines_BIN.fits"
        )
        printStatus.done("Using emission-subtracted spectra")

        hdu = fits.open(
            os.path.join(config['GENERAL']['OUTPUT'],
            config['GENERAL']['RUN_ID'])+'_gas-cleaned_'+config['GAS']['LEVEL']+'.fits'
        )
        # Adding a bit in to also load the BinSpectra.fits to grab the error spectrum, even if using the cleaned gas specrum
        # But sometimes this isn't always the right shape. So really, you want the error saved to the _gas_cleaned_BIN.fits hdu
        #hdu2 = fits.open(os.path.join(config['GENERAL']['OUTPUT'],config['GENERAL']['RUN_ID'])+'_BinSpectra.fits')

    else:
        logging.info(
            "Using regular spectra without any emission-correction at "
            + os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
            + "_BinSpectra.fits"
        )
        printStatus.done("Using regular spectra without any emission-correction")
        hdu = fits.open(
            os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
            + "_BinSpectra.fits"
        )

    galaxy = np.array( hdu[1].data.SPEC )
    logLam = hdu[2].data.LOGLAM
    idx_lam = np.where( np.logical_and( np.exp(logLam) > config['SFH']['LMIN'], np.exp(logLam) < config['SFH']['LMAX'] ) )[0]
    galaxy = galaxy[:,idx_lam]
    #galaxy = galaxy/np.median(galaxy) # Amelia added to normalise normalize flux. Do we use this again?
    logLam = logLam[idx_lam]
    nbins = galaxy.shape[0]
    npix = galaxy.shape[1]
    ubins = np.arange(0, nbins)
    noise = np.full(npix, config['SFH']['NOISE'])
    dv = (np.log(lamRange_temp[0]) - logLam[0])*C
    #bin_err = np.array( hdu2[1].data.ESPEC.T ) #This will almost certainly not work, as galaxy array isn't transposed
    bin_err = np.array( hdu[1].data.ESPEC.T ) #This will almost certainly not work, as galaxy array isn't transposed. Does this still need to be transposed?
    bin_data = np.array( hdu[1].data.SPEC.T ) # Amelia this doens't bode well
    bin_data = bin_data[idx_lam,:]
    bin_err = bin_err[idx_lam,:]
    # Last preparatory steps
    offset = (logLam_template[0] - logLam[0])*C
    #noise = np.ones((npix,nbins))
    noise = bin_err # is actual noise, not variance
    nsims = config['SFH']['MC_PPXF']

    # Implementation of switch FIXED
    # Do fix kinematics to those obtained previously
    if config["SFH"]["FIXED"] == True:
        logging.info("Stellar kinematics are FIXED to the results obtained before.")
        # Set fixed option to True
        fixed = [True] * config["KIN"]["MOM"]

        # Read PPXF results
        ppxf_data = fits.open(
            os.path.join(config["GENERAL"]["OUTPUT"], config["GENERAL"]["RUN_ID"])
            + "_kin.fits"
        )[1].data
        start = np.zeros((nbins, config["KIN"]["MOM"]))
        for i in range(nbins):
            start[i, :] = np.array(ppxf_data[i][: config["KIN"]["MOM"]])

    # Do *NOT* fix kinematics to those obtained previously
    elif config["SFH"]["FIXED"] == False:
        logging.info(
            "Stellar kinematics are NOT FIXED to the results obtained before but extracted simultaneously with the stellar population properties."
        )
        # Set fixed option to False and use initial guess from Config-file
        fixed = None
        start = np.zeros((nbins, 2))
        for i in range(nbins):
            start[i, :] = np.array([0.0, config["KIN"]["SIGMA"]])

    # Define goodpixels
    goodPixels_sfh = _auxiliary.spectralMasking(config, config['SFH']['SPEC_MASK'], logLam)

    # Define output arrays
    ppxf_result = np.zeros((nbins,6    ))
    w_row = np.zeros((nbins,ncomb))
    ppxf_bestfit = np.zeros((nbins,npix))
    optimal_template = np.zeros((nbins,templates.shape[0]))
    mc_results = np.zeros((nbins,6))
    formal_error = np.zeros((nbins,6))
    spectral_mask = np.zeros((nbins,bin_data.shape[0]))

    # ====================
    # Run PPXF once on combined mean spectrum to get a single optimal template
    comb_spec = np.nanmean(bin_data[:,:],axis=1)
    comb_espec = np.nanmean(bin_err[:,:],axis=1)
    #comb_spec = comb_spec/np.nanmedian(comb_spec) # Amelia added to mormalise normalize spectrum
    #comb_espec = comb_espec/np.nanmedian(comb_espec) # and the error spectrum
    optimal_template_init = [0]

    optimal_template_out = run_ppxf_firsttime(
        templates,
        comb_spec ,
        comb_espec,
        velscale,
        start[0,:],
        goodPixels_sfh,
        config['SFH']['MOM'],
        offset,-1,
        config['SFH']['MDEG'],
        config['SFH']['REGUL_ERR'],
        config["SFH"]["DOCLEAN"],
        fixed,
        velscale_ratio,
        ncomb,
        nsims,
        nbins,
        0,
        optimal_template_init,
    )

    # now define the optimal template that we'll use throughout
    optimal_template_comb = optimal_template_out

    # ====================

    # ====================
    # Run PPXF
    start_time = time.time()
    if config["GENERAL"]["PARALLEL"] == True:
        printStatus.running("Running PPXF in parallel mode")
        logging.info("Running PPXF in parallel mode")

        # Create Queues
        inQueue = Queue()
        outQueue = Queue()

        # Create worker processes
        ps = [
            Process(target=workerPPXF, args=(inQueue, outQueue))
            for _ in range(config["GENERAL"]["NCPU"])
        ]

        # Start worker processes
        for p in ps:
            p.start()

        # Fill the queue
        for i in range(nbins):
            inQueue.put(
                (
                    templates,
                    bin_data[:,i],
                    noise[:,i],
                    velscale,
                    start[i,:],
                    goodPixels_sfh,
                    config['SFH']['MOM'],
                    offset,
                    -1,
                    config['SFH']['MDEG'],
                    config['SFH']['REGUL_ERR'],
                    config["SFH"]["DOCLEAN"],
                    fixed,
                    velscale_ratio,
                    npix,
                    ncomb,
                    nbins,
                    i,
                    optimal_template_comb,
                )
            )

        # now get the results with indices
        ppxf_tmp = [outQueue.get() for _ in range(nbins)]

        # send stop signal to stop iteration
        for _ in range(config["GENERAL"]["NCPU"]):
            inQueue.put("STOP")

        # stop processes
        for p in ps:
            p.join()

        # Get output
        index = np.zeros(nbins)
        for i in range(0, nbins):

            index[i] = ppxf_tmp[i][0]
            ppxf_result[i,:config['SFH']['MOM']] = ppxf_tmp[i][1]
            w_row[i,:] = ppxf_tmp[i][2]
            ppxf_bestfit[i,:] = ppxf_tmp[i][3]
            optimal_template[i,:] = ppxf_tmp[i][4]
            mc_results[i,:config['SFH']['MOM']] = ppxf_tmp[i][5]
            formal_error[i,:config['SFH']['MOM']] = ppxf_tmp[i][6]
            spectral_mask[i,:] = ppxf_tmp[i][7]

        # Sort output
        argidx = np.argsort( index )
        ppxf_result = ppxf_result[argidx,:]
        w_row = w_row[argidx,:]
        ppxf_bestfit = ppxf_bestfit[argidx,:]
        optimal_template = optimal_template[argidx,:]
        mc_results = mc_results[argidx,:]
        formal_error = formal_error[argidx,:]
        spectral_mask = spectral_mask[argidx,:]

        printStatus.updateDone("Running PPXF in parallel mode", progressbar=True)

    if config['GENERAL']['PARALLEL'] == False: # Amelia you haven't tested this yet. Come back to.
        printStatus.running("Running PPXF in serial mode")
        logging.info("Running PPXF in serial mode")
        for i in range(nbins):
            (
                kin[i,:config['SFH']['MOM']],
                w_row[i,:],
                bestfit[i,:],
                formal_error[i,:config['SFH']['MOM']],
            ) = run_ppxf(
                templates,
                galaxy[i,:],
                noise,
                velscale,
                start[i,:],
                goodPixels_sfh,
                config['SFH']['MOM'],
                dv,
                -1,
                config['SFH']['MDEG'],
                config['SFH']['REGUL_ERR'],
                config["KIN"]["DOCLEAN"],
                fixed,
                velscale_ratio,
                npix,
                ncomb,
                nbins,
                i,
                optimal_template_in,
            )
        printStatus.updateDone("Running PPXF in serial mode", progressbar=True)

    print(
        "             Running PPXF on %s spectra took %.2fs using %i cores"
        % (nbins, time.time() - start_time, config["GENERAL"]["NCPU"])
    )
    logging.info(
        "Running PPXF on %s spectra took %.2fs using %i cores"
        % (nbins, time.time() - start_time, config["GENERAL"]["NCPU"])
    )

    # Check for exceptions which occurred during the analysis
    idx_error = np.where( np.isnan( ppxf_result[:,0] ) == True )[0]

    if len(idx_error) != 0:
        printStatus.warning(
            "There was a problem in the analysis of the spectra with the following BINID's: "
        )
        print("             " + str(idx_error))
        logging.warning(
            "There was a problem in the analysis of the spectra with the following BINID's: "
            + str(idx_error)
        )
    else:
        print("             " + "There were no problems in the analysis.")
        logging.info("There were no problems in the analysis.")
    print("")

    # Calculate mean age, metallicity and alpha
    mean_results = mean_agemetalalpha(
        w_row, 10**logAge_grid, metal_grid, alpha_grid, nbins
    )

    # Save to file

    save_sfh(
        mean_results,
        ppxf_result,
        w_row,
        mc_results,
        formal_error,
        logAge_grid,
        metal_grid,
        alpha_grid,
        ppxf_bestfit,
        logLam,
        goodPixels_sfh,
        velscale,
        logLam,
        ncomb,
        nAges,
        nMetal,
        nAlpha,
        npix,
        config,
        spectral_mask,
        optimal_template_comb,
    )

    # Return
    return None


from torch import nn
import torch

class TomatoNN(nn.Module):
    def __init__(self):
        super(TomatoNN, self).__init__()
        self.conv1 = self.conv_block(n=1, nIn=3, nOut=128)
        self.pool1 = nn.MaxPool2d(kernel_size=(4, 4))
        self.conv2 = self.conv_block(n=1, nIn=128, nOut=128)
        self.pool3 = nn.MaxPool2d(kernel_size=(3,3))

        self.conv4 = self.conv_block(n=1, nIn=128, nOut=512)
        self.conv5 = self.conv_block(n=1, nIn=512, nOut=512)
        self.pool6 = nn.MaxPool2d(kernel_size=(3, 3))

        self.conv7 = self.conv_block(n=1, nIn=512, nOut=1024)
        self.conv8 = self.conv_block(n=1, nIn=1024, nOut=1024)
        self.pool9 = nn.MaxPool2d(kernel_size=(2, 2))

        # self.conv10 = self.conv_block(n=1, nIn=1024, nOut=1024)
        # self.conv11 = self.conv_block(n=1, nIn=1024, nOut=1024)

        self.flatten = nn.Flatten(1)

        self.dense1 = self.dense_block(2, nIn=1024, nOut=256, drop_out=True)
        self.dense2 = self.dense_block(2, nIn=256, nOut=128, drop_out= True)
        self.dense4 = self.dense_block(2, nIn=128, nOut=1, drop_out=False)

    def conv_block(self, n, nIn, nOut, batch_norm=True, pad=0):
        block = nn.Sequential()
        block.add_module(f"down_conv_{n}",nn.Conv2d(nIn,nOut,kernel_size=(5,5), stride=(1,1), padding=pad))
        if(batch_norm):
            block.add_module(f"down_bn_{n}", nn.BatchNorm2d(nOut))
        block.add_module(f"down_act_{n}", nn.ReLU())
        return block

    def dense_block(self, n, nIn, nOut, batch_norm=True,drop_out=True):
        block = nn.Sequential()
        block.add_module(f"dense_{n}", nn.Linear(nIn, nOut))
        if(batch_norm):
            block.add_module(f"down_bn_{n}", nn.BatchNorm1d(nOut))
        block.add_module(f"down_act_{n}", nn.ReLU())
        if(drop_out):
            block.add_module(f"drop_out_{n}", nn.Dropout(0.2))
        return block

    def forward(self, X):
        X = self.conv1(X)
        X = self.pool1(X)
        X = self.conv2(X)
        X = self.pool3(X)

        X = self.conv4(X)
        # X = self.pool5(X)
        X = self.conv5(X)
        X = self.pool6(X)

        X = self.conv7(X)
        X = self.conv8(X)
        X = self.pool9(X)

        X = self.flatten(X)

        X = self.dense1(X)
        X = self.dense2(X)
        X = self.dense4(X)

        return X


import gzip
from math import log, isnan
from collections import OrderedDict, defaultdict
import random

class ProtagonistModel(object):
    def __init__(self, marg_counts, joint_counts,
                 marg_total, joint_total, directional=False):
        self.marg_counts = marg_counts
        self.joint_counts = joint_counts
        self.marg_total = marg_total
        self.joint_total = joint_total
        self.directional = directional

    def pmi(self, event1, event2):

        e1_count = self.marg_counts.get(event1, 0)
        e2_count = self.marg_counts.get(event2, 0)
        if e1_count == 0 or e2_count == 0:
            return float('nan')

        if self.directional is False:
            joint_key = self._joint_key(event1, event2)
        else:
            joint_key = '{}\t{}'.format(event1, event2)

        joint_count = self.joint_counts.get(self._joint_key(event1, event2), 0)
        if joint_count == 0:
            return float('nan')

        return log(joint_count * self.marg_total * self.marg_total) - \
            log(e1_count * e2_count * self.joint_total)

    def _joint_key(self, event1, event2):
        if event1 < event2:
            return '{}\t{}'.format(event1, event2)
        else:
            return '{}\t{}'.format(event2, event1)

    def avg_pmi(self, events, skip_nan=True):
        tot_pmi = 0
        tot_pairs = 0
        for idx1, event1 in enumerate(events):
            for event2 in events[idx1+1:]:
                pmi = self.pmi(event1, event2)
                if pmi is not float('nan') or skip_nan is True:
                    tot_pmi += pmi
                    tot_pairs += 1

        return tot_pmi / float(tot_pairs)

    def rank(self, event1):
        ranked_items = []
        for event2 in self.marg_counts.iterkeys():
            pmi = self.pmi(event1, event2)
            if isnan(pmi) is not True:
                ranked_items.append((pmi, intern(event2)))
        ranked_items.sort(key=lambda x: x[0])
        return ranked_items

def read_protagonist_model_counts(path):
    marg_counts = OrderedDict()
    joint_counts = {}
    marg_total = 0
    joint_total = 0

    with gzip.open(path, u"r") as f:
        for line in f:
            items = line.strip().split('\t')
            if len(items) == 2:
                event = items[0]
                count = int(items[1])
                marg_counts[intern(event)] = count
                marg_total += 1
            if len(items) == 3:
                event1 = items[0]
                event2 = items[1]
                count = int(items[2])
                joint_counts['{}\t{}'.format(event1, event2)] = count
                joint_total += 1

    return ProtagonistModel(marg_counts, joint_counts, marg_total, joint_total)

class ProtagonistModelSampler:
    def __init__(self, idx2event_dict,
        event_samples, event_event_samples, idx2cooc, is_direct):

        self.idx2event_dict_ = idx2event_dict

        self.event_samples_ = event_samples
        random.shuffle(self.event_samples_)
        self.neg_sample_idx_ = 0

        self.event_event_samples_ = event_event_samples
        random.shuffle(self.event_event_samples_)
        self.pos_sample_idx_ = 0

        self.idx2cooc_ = idx2cooc
        self.num_events = len(idx2event_dict)
        self.is_direct = is_direct

    def positive_sample(self):
        if self.pos_sample_idx_ >= len(self.event_event_samples_):
            self.pos_sample_idx_ = 0
            random.shuffle(self.event_event_samples_)
        sample = self.event_event_samples_[self.pos_sample_idx_]
        self.pos_sample_idx_ += 1
        return sample

    def negative_sample(self, idx1):

        while 1:
            if self.neg_sample_idx_ >= len(self.event_samples_):
                self.neg_sample_idx_ = 0
                random.shuffle(self.event_samples_)

            idx2 = self.event_samples_[self.neg_sample_idx_]
            self.neg_sample_idx_ += 1

            if idx2 not in self.idx2cooc_[idx1]:
                break
        return idx2

    def pos_sample_seq_iter(self):
        self.pos_sample_idx_ = 0
        while self.pos_sample_idx_ < len(self.event_event_samples_):
            yield self.positive_sample()

    def get_word(self, idx):
        return self.idx2event_dict_.get(idx, None)

def read_protagonist_model_counts_to_sampler(path, freq_cutoff,
                                             is_directional):

    event_samples = []
    event_event_samples = []

    event2idx = OrderedDict()
    idx2event = OrderedDict()
    e2e = defaultdict(set)

    idx2cooc = []

    idx = 0
    with gzip.open(path, u'r') as f:
        for line in f:
            line = line.strip()
            items = line.split("\t")
            if len(items) == 2:
                event = intern(items[0])
                count = int(items[1])
                if count > freq_cutoff:
                    if idx2event.get(idx, None) is None:
                        idx2event[idx] = event
                        event2idx[event] = idx
                        for i in range(count):
                            event_samples.append(idx)
                        idx2cooc.append(set())
                        idx += 1

            elif len(items) == 3:
                event1 = intern(items[0])
                event2 = intern(items[1])
                if event1 == event2:
                    continue
                if event1 not in event2idx:
                    continue
                if event2 not in event2idx:
                    continue

                count = int(items[2])
                idx1 = event2idx[event1]
                idx2 = event2idx[event2]
                idx2cooc[idx1].add(idx2)
                if is_directional is False:
                    idx2cooc[idx2].add(idx1)
                for i in range(count):
                    event_event_samples.append((idx1, idx2))

    event2idx = None
    return ProtagonistModelSampler(
        idx2event, event_samples, event_event_samples,
        idx2cooc, is_directional)

               # print event, count
                #prot_event_pairs.append((intern(prot), intern(event)))
            #prot2events[intern(prot)].add(intern(event))
            #events.add(intern(event))
#    with gzip.open(dict_file, u'r') as f:
#        for line in f:
#            items = line.strip().split("\t")
#            assert len(items) == 3
#            typ, token, idx = items
#            idx = int(idx)
#            if typ == "p":
#                p2idx[intern(token)] = idx
#            else:
#                e2idx[intern(token)] = idx
#            if idx > max_idx:
#                max_idx = idx
#            vocab_size += 1
#    assert vocab_size == max_idx + 1
#    assert vocab_size == len(p2idx) + len(e2idx)
#
#    return ProtEventSample(
#        prot_event_pairs, events, prot2events, p2idx, e2idx)


import matplotlib.pyplot as plt
import math
import random

#initialize theta and R
currentTheta = 0
currentR = 0
#set up polar plt
ax = plt.subplot(111, projection="polar")
ax.set_ylim([0,30])
#enable interactive mode so the plot doesn't close and open and is non-blocking
plt.ion()
plt.show()
#just for demonstration
countingUp = True
while(1):
    #***demo stuff***
    #currentTheta = random.randrange(0,359)
    currentTheta += 5
    #currentR = random.randrange(0,10)
    if(currentR == 30):
        countingUp = False
    elif(currentR == 0):
        countingUp = True
    if(countingUp):
        currentR += 0.75
    else:
        currentR -= 0.75
    #***demo stuff end***
    #set theta and r
    theta = [0,math.radians(currentTheta)]
    r = [0,currentR]
    #clear plot and prep for redraw
    ax.cla()
    #set limit on polar plot range
    ax.set_ylim([0,30])
    #tell the user what the range is at the moment in the bottom left corner
    ax.annotate('Range: {:0.2f}'.format(currentR) + " Feet",
            xy=(currentTheta, currentR),  # theta, radius
            xytext=(0.05, 0.05),    # fraction, fraction
            textcoords='figure fraction',
    #        arrowprops=dict(facecolor='black', shrink=0.05),
            horizontalalignment='left',
            verticalalignment='bottom',
            )
    #plot first a red diamond and then a red line, both for position
    ax.plot(theta, r,'^r')
    ax.plot(theta, r,'-r')
    #give the plot a moment to breathe and do its processing
    plt.pause(0.05)


import os
from typing import Dict

class Screen:
    def __init__(self, actions: Dict[str, any]=None):
        if actions is None:
            actions = {}

        # Columns, Rows
        self.width, self.length = os.get_terminal_size() # Get terminal size using os module

        # Build screen matrix w/ default value
        self.default_value = ' '
        self.display_grid = self.blank_screen_data
        self.occupied_data = self.reset_occupied_data

        # TODO: Implement Caches
        self.data_cache = None
        self.string_cache = None
        self.occupied_data_cache = None

        #  "Trigger": action
        self.input_choices = actions

    @property
    def blank_screen_data(self):
        # self.display_grid = [[self.default_value for y in range(self.width)] for x in range(self.length-2)]
        return  [[self.default_value for y in range(self.width)] for x in range(self.length-2)]

    @property
    def reset_occupied_data(self):
        # i -> ROWS
        # j -> COLUMNS
        reset_data = {}
        for i in range(self.length):
            for j in range(self.width):
                curr_key = f"{i}{j}"
                reset_data[curr_key] = False

        return reset_data

    def __str__(self):
        grid_text = ""
        for line_data in self.display_grid:
            new_line = ""
            for line_item in line_data:
                new_line += f"{line_item}"
            grid_text += f"{new_line}\n"
        return grid_text

    def print_screen(self):
        grid_text = ""
        for line_data in self.display_grid:
            new_line = ""
            for line_item in line_data:
                new_line += f"{line_item}"
            grid_text += f"{new_line}\n"
        os.system('cls')
        print(grid_text)
        input(">>> ")

    def reset_screen(self):
        self.display_grid = self.blank_screen_data
        self.occupied_data = self.reset_occupied_data

    def fill_screen(self, fill_value):
        self.display_grid = [[fill_value for y in range(self.width)] for x in range(self.length-2)]

    def set_grid_point(self, new_char: str, row: int, col: int):
        built_key = f"{row}{col}"
        # Boundary Check
        if row < 0 and row > self.length:
            return False
        if col < 0 and col > self.width:
            return False

        # Check if already occupied
        if self.occupied_data[built_key]:
            return False

        self.display_grid[row][col] = new_char
        self.occupied_data[built_key] = True
        return True

    def update_grid_point(self, new_char: str, row: int, col: int):
         # Boundary Check
        if row < 0 and row > self.length:
            return False
        if col < 0 and col > self.width:
            return False

        self.display_grid[row][col] = new_char
        return True

    def draw_text_box(self, text_box_data, start_row: int=0, start_col: int=0):
        for i, row in enumerate(text_box_data):
            r = start_row + i
            if r >= self.length-2:
                continue

            for j, char in enumerate(row):
                c = start_col + j
                # print(r , c)
                if c >= self.width-1:
                    continue

                self.update_grid_point(char, r, c)

    def perform_action(self, trigger: str):
        # if trigger in self.input_choices:
        # empty_function => lambda x: None
        empty_fun = lambda x: None
        self.input_choices.get(trigger, empty_fun)(self)

    def run(self):
        while True:
            os.system('cls')
            print(self)
            user_input = input(">>> ")

            if user_input.lower() == 'q':
                break
            self.perform_action(user_input)

def main():

    def action1():
        print(4)
        input()

    def action2():
        print(8)
        input()

    sample_actions = {
        "trig1": action1,
        "trig2": action2
    }
    new_screen = Screen(sample_actions)

    # MAX ROW = 21
    # MAX COLUMN = 72
    new_screen.set_grid_point("X", 21, 71)

    new_screen.run()

if __name__ == '__main__':
    main()


import os
import logging
import zipfile
import shutil

logging.basicConfig(level=logging.DEBUG)

class bandcampRawZip:
    def __init__(self, path: str, library: str):
        self.zip_path = path
        self.lib_path = library
        self.new_folder: str

    def extract_zip(self):
        """
        Extract files from a .zip archive.

        Parameters:
        - zip_path: Path to the .zip file
        - output_folder: Folder where extracted files will be saved
        """

        logging.debug("Extracting %s to temporary file" % self.zip_path)

        with zipfile.ZipFile(self.zip_path, 'r') as zip_ref:
            zip_ref.extractall("temp")

    def create_album_folder(self):

        filename = os.path.basename(os.path.splitext(self.zip_path)[-2])
        artist = filename.split(" - ")[0]
        album = filename.split(" - ")[1]

        # library = os.listdir(self.lib_path)
        self.new_folder = os.path.join(self.lib_path, artist, album)

        logging.debug("Creating new folder %s" % self.new_folder)

        try:
            os.makedirs(self.new_folder)
        except FileExistsError:
            pass

    def copy_and_rename(self):

        logging.debug("Copying files...")

        for item in os.listdir("temp"):

            if os.path.splitext(item)[-1] in [".jpg", ".png"]:
                shutil.copy2(os.path.join("temp", item), os.path.join(self.new_folder, item))

                logging.debug("Copying %s to %s" % (item, os.path.join(self.new_folder, item)))

            elif os.path.splitext(item)[-1] == ".mp3":
                """
                Making a somewhat dangerous assumption here that I can use " - " as my delimiter. It may not work in all
                cases. So, should this be put into a config?
                """

                first_delim = item.find(" - ")
                second_delim = item.find(" - ", first_delim + 1)

                name = item[second_delim+6:]
                #os.rename()

                shutil.copy2(os.path.join("temp", item), os.path.join(self.new_folder, name))

                logging.debug("Copying %s to %s" % (item, os.path.join(self.new_folder, name)))

    def cleanup(self):

        shutil.rmtree("temp")
        logging.debug("Removing temporary folder")

    def processNewMusic(self):
        self.extract_zip()
        self.create_album_folder()
        self.copy_and_rename()
        self.cleanup()

def collect_zips(path):

    files = []

    for file in os.listdir(path):
        if os.path.splitext(file)[-1] == "zip":
            files.append(file)

if __name__ == "__main__":

    bcfolder = bandcampRawZip("E:\Music\Raw Compressed\Spoon - Memory Dust EP.zip",
                              "E:\\Music\\Albums\\")
    bcfolder.processNewMusic()