import requests
import json

class ConfdSession(object):

    OK_STATUSES = (requests.codes.ok,
                   requests.codes.created,
                   requests.codes.no_content)

    READ_HEADERS = {'Accept': 'application/json'}

    WRITE_HEADERS = {'Accept': 'application/json',
                     'Content-Type': 'application/json'}

    def __init__(self, session, base_url):
        self.session = session
        self.base_url = base_url

    def check_response(self, response, check=True):
        if not check:
            return

        if response.status_code not in self.OK_STATUSES:
            try:
                messages = response.json()
                response.reason = ". ".join(messages)
            finally:
                response.raise_for_status()

    def clean_url(self, part):
        return "{}/{}".format(self.base_url.rstrip('/'), part.lstrip('/'))

    def get(self, url, **kwargs):
        kwargs.setdefault('headers', self.READ_HEADERS)
        check_response = kwargs.pop('check_response', True)

        url = self.clean_url(url)
        response = self.session.get(url, **kwargs)

        self.check_response(response, check_response)
        return response

    def post(self, url, body=None, **kwargs):
        kwargs.setdefault('headers', self.WRITE_HEADERS)
        check_response = kwargs.pop('check_response', True)

        url = self.clean_url(url)
        encoded_body = self.encode_body(body, kwargs)
        response = self.session.post(url, data=encoded_body, **kwargs)

        self.check_response(response, check_response)
        return response

    def put(self, url, body=None, **kwargs):
        kwargs.setdefault('headers', self.WRITE_HEADERS)
        check_response = kwargs.pop('check_response', True)

        url = self.clean_url(url)
        encoded_body = self.encode_body(body, kwargs)
        response = self.session.put(url, data=encoded_body, **kwargs)

        self.check_response(response, check_response)
        return response

    def encode_body(self, body, kwargs):
        raw = kwargs.pop('raw', None)
        if raw:
            return raw
        if body is not None:
            return json.dumps(body)
        return None

    def delete(self, url, **kwargs):
        kwargs.setdefault('headers', self.READ_HEADERS)
        check_response = kwargs.pop('check_response', True)

        url = self.clean_url(url)
        response = self.session.delete(url, **kwargs)

        self.check_response(response, check_response)
        return response


from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication,QPushButton,QWidget,QLineEdit,QHBoxLayout,QLabel

import sys
from PyQt5 import QtGui
from PyQt5.QtCore import QRect
from PyQt5 import QtCore
from PyQt5.QtGui import  QPixmap

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Line Edit"
        self.left = 500
        self.top = 200
        self.width = 300
        self.height = 250
        self.iconName = "icon.png"
        self.InitWindow()

    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon(self.iconName))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        hqbox = QHBoxLayout()
        self.lineedit = QLineEdit(self)
        self.lineedit.setFont(QtGui.QFont("Sanserif",15))
        self.lineedit.returnPressed.connect(self.onPressed)
        hqbox.addWidget(self.lineedit)
        self.label = QLabel(self)
        self.label.setFont((QtGui.QFont("Sanserif",15)))

        hqbox.addWidget(self.label)
        self.setLayout(hqbox)
        self.show()

    def onPressed(self):
        self.label.setText(self.lineedit.text())
if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    sys.exit(App.exec())


import sys
_module = sys.modules[__name__]
del sys
data_loader = _module
gat = _module
gat_layers = _module
gcn = _module
gcn_layers = _module
pscn = _module
train = _module
utils = _module

from _paritybench_helpers import _mock_config, patch_functional
from unittest.mock import mock_open, MagicMock
from torch.autograd import Function
from torch.nn import Module
import abc, collections, copy, enum, functools, inspect, itertools, logging, math, matplotlib, numbers, numpy, pandas, queue, random, re, scipy, sklearn, string, tensorflow, time, torch, torchaudio, torchtext, torchvision, types, typing, uuid, warnings
import numpy as np
from torch import Tensor
patch_functional()
open = mock_open()
yaml = logging = sys = argparse = MagicMock()
ArgumentParser = argparse.ArgumentParser
_global_config = args = argv = cfg = config = params = _mock_config()
argparse.ArgumentParser.return_value.parse_args.return_value = _global_config
yaml.load.return_value = _global_config
sys.argv = _global_config
__version__ = '1.0.0'
xrange = range
wraps = functools.wraps

import numpy as np

import torch

from torch.utils.data import Dataset

from torch.utils.data.sampler import Sampler

import sklearn

import itertools

import logging

from sklearn import preprocessing

import torch.nn as nn

import torch.nn.functional as F

import torch.nn.init as init

from torch.nn.parameter import Parameter

from torch.nn.modules.module import Module

from torch.autograd import Variable

import time

import torch.optim as optim

from torch.utils.data import DataLoader

from sklearn.metrics import precision_recall_fscore_support

from sklearn.metrics import roc_auc_score

from sklearn.metrics import precision_recall_curve

class BatchMultiHeadGraphAttention(nn.Module):

    def __init__(self, n_head, f_in, f_out, attn_dropout, bias=True):
        super(BatchMultiHeadGraphAttention, self).__init__()
        self.n_head = n_head
        self.w = Parameter(torch.Tensor(n_head, f_in, f_out))
        self.a_src = Parameter(torch.Tensor(n_head, f_out, 1))
        self.a_dst = Parameter(torch.Tensor(n_head, f_out, 1))
        self.leaky_relu = nn.LeakyReLU(negative_slope=0.2)
        self.softmax = nn.Softmax(dim=-1)
        self.dropout = nn.Dropout(attn_dropout)
        if bias:
            self.bias = Parameter(torch.Tensor(f_out))
            init.constant_(self.bias, 0)
        else:
            self.register_parameter('bias', None)
        init.xavier_uniform_(self.w)
        init.xavier_uniform_(self.a_src)
        init.xavier_uniform_(self.a_dst)

    def forward(self, h, adj):
        bs, n = h.size()[:2]
        h_prime = torch.matmul(h.unsqueeze(1), self.w)
        attn_src = torch.matmul(F.tanh(h_prime), self.a_src)
        attn_dst = torch.matmul(F.tanh(h_prime), self.a_dst)
        attn = attn_src.expand(-1, -1, -1, n) + attn_dst.expand(-1, -1, -1, n).permute(0, 1, 3, 2)
        attn = self.leaky_relu(attn)
        mask = 1 - adj.unsqueeze(1)
        attn.data.masked_fill_(mask, float('-inf'))
        attn = self.softmax(attn)
        attn = self.dropout(attn)
        output = torch.matmul(attn, h_prime)
        if self.bias is not None:
            return output + self.bias
        else:
            return output

class BatchGAT(nn.Module):

    def __init__(self, pretrained_emb, vertex_feature, use_vertex_feature, n_units=[1433, 8, 7], n_heads=[8, 1], dropout=0.1, attn_dropout=0.0, fine_tune=False, instance_normalization=False):
        super(BatchGAT, self).__init__()
        self.n_layer = len(n_units) - 1
        self.dropout = dropout
        self.inst_norm = instance_normalization
        if self.inst_norm:
            self.norm = nn.InstanceNorm1d(pretrained_emb.size(1), momentum=0.0, affine=True)
        self.embedding = nn.Embedding(pretrained_emb.size(0), pretrained_emb.size(1))
        self.embedding.weight = nn.Parameter(pretrained_emb)
        self.embedding.weight.requires_grad = fine_tune
        n_units[0] += pretrained_emb.size(1)
        self.use_vertex_feature = use_vertex_feature
        if self.use_vertex_feature:
            self.vertex_feature = nn.Embedding(vertex_feature.size(0), vertex_feature.size(1))
            self.vertex_feature.weight = nn.Parameter(vertex_feature)
            self.vertex_feature.weight.requires_grad = False
            n_units[0] += vertex_feature.size(1)
        self.layer_stack = nn.ModuleList()
        for i in range(self.n_layer):
            f_in = n_units[i] * n_heads[i - 1] if i else n_units[i]
            self.layer_stack.append(BatchMultiHeadGraphAttention(n_heads[i], f_in=f_in, f_out=n_units[i + 1], attn_dropout=attn_dropout))

    def forward(self, x, vertices, adj):
        emb = self.embedding(vertices)
        if self.inst_norm:
            emb = self.norm(emb.transpose(1, 2)).transpose(1, 2)
        x = torch.cat((x, emb), dim=2)
        if self.use_vertex_feature:
            vfeature = self.vertex_feature(vertices)
            x = torch.cat((x, vfeature), dim=2)
        bs, n = adj.size()[:2]
        for i, gat_layer in enumerate(self.layer_stack):
            x = gat_layer(x, adj)
            if i + 1 == self.n_layer:
                x = x.mean(dim=1)
            else:
                x = F.elu(x.transpose(1, 2).contiguous().view(bs, n, -1))
                x = F.dropout(x, self.dropout, training=self.training)
        return F.log_softmax(x, dim=-1)

class MultiHeadGraphAttention(nn.Module):

    def __init__(self, n_head, f_in, f_out, attn_dropout, bias=True):
        super(MultiHeadGraphAttention, self).__init__()
        self.n_head = n_head
        self.w = Parameter(torch.Tensor(n_head, f_in, f_out))
        self.a_src = Parameter(torch.Tensor(n_head, f_out, 1))
        self.a_dst = Parameter(torch.Tensor(n_head, f_out, 1))
        self.leaky_relu = nn.LeakyReLU(negative_slope=0.2)
        self.softmax = nn.Softmax(dim=-1)
        self.dropout = nn.Dropout(attn_dropout)
        if bias:
            self.bias = Parameter(torch.Tensor(f_out))
            init.constant_(self.bias, 0)
        else:
            self.register_parameter('bias', None)
        init.xavier_uniform_(self.w)
        init.xavier_uniform_(self.a_src)
        init.xavier_uniform_(self.a_dst)

    def forward(self, h, adj):
        n = h.size(0)
        h_prime = torch.matmul(h.unsqueeze(0), self.w)
        attn_src = torch.bmm(h_prime, self.a_src)
        attn_dst = torch.bmm(h_prime, self.a_dst)
        attn = attn_src.expand(-1, -1, n) + attn_dst.expand(-1, -1, n).permute(0, 2, 1)
        attn = self.leaky_relu(attn)
        attn.data.masked_fill_(1 - adj, float('-inf'))
        attn = self.softmax(attn)
        attn = self.dropout(attn)
        output = torch.bmm(attn, h_prime)
        if self.bias is not None:
            return output + self.bias
        else:
            return output

class BatchGraphConvolution(Module):

    def __init__(self, in_features, out_features, bias=True):
        super(BatchGraphConvolution, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.Tensor(in_features, out_features))
        if bias:
            self.bias = Parameter(torch.Tensor(out_features))
            init.constant_(self.bias, 0)
        else:
            self.register_parameter('bias', None)
        init.xavier_uniform_(self.weight)

    def forward(self, x, lap):
        expand_weight = self.weight.expand(x.shape[0], -1, -1)
        support = torch.bmm(x, expand_weight)
        output = torch.bmm(lap, support)
        if self.bias is not None:
            return output + self.bias
        else:
            return output

class BatchGCN(nn.Module):

    def __init__(self, n_units, dropout, pretrained_emb, vertex_feature, use_vertex_feature, fine_tune=False, instance_normalization=False):
        super(BatchGCN, self).__init__()
        self.num_layer = len(n_units) - 1
        self.dropout = dropout
        self.inst_norm = instance_normalization
        if self.inst_norm:
            self.norm = nn.InstanceNorm1d(pretrained_emb.size(1), momentum=0.0, affine=True)
        self.embedding = nn.Embedding(pretrained_emb.size(0), pretrained_emb.size(1))
        self.embedding.weight = nn.Parameter(pretrained_emb)
        self.embedding.weight.requires_grad = fine_tune
        n_units[0] += pretrained_emb.size(1)
        self.use_vertex_feature = use_vertex_feature
        if self.use_vertex_feature:
            self.vertex_feature = nn.Embedding(vertex_feature.size(0), vertex_feature.size(1))
            self.vertex_feature.weight = nn.Parameter(vertex_feature)
            self.vertex_feature.weight.requires_grad = False
            n_units[0] += vertex_feature.size(1)
        self.layer_stack = nn.ModuleList()
        for i in range(self.num_layer):
            self.layer_stack.append(BatchGraphConvolution(n_units[i], n_units[i + 1]))

    def forward(self, x, vertices, lap):
        emb = self.embedding(vertices)
        if self.inst_norm:
            emb = self.norm(emb.transpose(1, 2)).transpose(1, 2)
        x = torch.cat((x, emb), dim=2)
        if self.use_vertex_feature:
            vfeature = self.vertex_feature(vertices)
            x = torch.cat((x, vfeature), dim=2)
        for i, gcn_layer in enumerate(self.layer_stack):
            x = gcn_layer(x, lap)
            if i + 1 < self.num_layer:
                x = F.elu(x)
                x = F.dropout(x, self.dropout, training=self.training)
        return F.log_softmax(x, dim=-1)

class BatchPSCN(nn.Module):

    def __init__(self, n_units, dropout, pretrained_emb, vertex_feature, use_vertex_feature, instance_normalization, neighbor_size, sequence_size, fine_tune=False):
        super(BatchPSCN, self).__init__()
        assert len(n_units) == 4
        self.dropout = dropout
        self.inst_norm = instance_normalization
        if self.inst_norm:
            self.norm = nn.InstanceNorm1d(pretrained_emb.size(1), momentum=0.0, affine=True)
        self.embedding = nn.Embedding(pretrained_emb.size(0), pretrained_emb.size(1))
        self.embedding.weight = nn.Parameter(pretrained_emb)
        self.embedding.weight.requires_grad = fine_tune
        n_units[0] += pretrained_emb.size(1)
        self.use_vertex_feature = use_vertex_feature
        if self.use_vertex_feature:
            self.vertex_feature = nn.Embedding(vertex_feature.size(0), vertex_feature.size(1))
            self.vertex_feature.weight = nn.Parameter(vertex_feature)
            self.vertex_feature.weight.requires_grad = False
            n_units[0] += vertex_feature.size(1)
        self.conv1 = nn.Conv1d(in_channels=n_units[0], out_channels=n_units[1], kernel_size=neighbor_size, stride=neighbor_size)
        k = 1
        self.conv2 = nn.Conv1d(in_channels=n_units[1], out_channels=n_units[2], kernel_size=k, stride=1)
        self.fc = nn.Linear(in_features=n_units[2] * (sequence_size - k + 1), out_features=n_units[3])

    def forward(self, x, vertices, recep):
        emb = self.embedding(vertices)
        if self.inst_norm:
            emb = self.norm(emb.transpose(1, 2)).transpose(1, 2)
        x = torch.cat((x, emb), dim=2)
        if self.use_vertex_feature:
            vfeature = self.vertex_feature(vertices)
            x = torch.cat((x, vfeature), dim=2)
        bs, l = recep.size()
        n = x.size()[1]
        offset = torch.ger(torch.arange(0, bs).long(), torch.ones(l).long() * n)
        offset = Variable(offset, requires_grad=False)
        offset = offset
        recep = (recep.long() + offset).view(-1)
        x = x.view(bs * n, -1)
        x = x.index_select(dim=0, index=recep)
        x = x.view(bs, l, -1)
        x = x.transpose(1, 2)
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.dropout(x, self.dropout, training=self.training)
        x = x.view(bs, -1)
        x = self.fc(x)
        return F.log_softmax(x, dim=-1)

import torch
from torch.nn import MSELoss, ReLU
from _paritybench_helpers import _mock_config, _mock_layer, _paritybench_base, _fails_compile

TESTCASES = [
    # (nn.Module, init_args, forward_args, jit_compiles)
    (BatchGraphConvolution,
     lambda: ([], {'in_features': 4, 'out_features': 4}),
     lambda: ([torch.rand([4, 4, 4]), torch.rand([4, 4, 4])], {}),
     True),
]

class Test_xptree_DeepInf(_paritybench_base):
    def test_000(self):
        self._check(*TESTCASES[0])


import tensorflow as tf
from tensorflow.keras import backend as K
from tensorflow.keras import layers as KL
from tensorflow.keras import *

channel_axis = 1 if K.image_data_format() == "channels_first" else 3

# CAM 特征通道注意力
def channel_attention(input_xs, reduction_ratio=0.125):
    # get channel
    channel = int(input_xs.shape[channel_axis])
    maxpool_channel = KL.GlobalMaxPooling2D()(input_xs)
    maxpool_channel = KL.Reshape((1, 1, channel))(maxpool_channel)
    avgpool_channel = KL.GlobalAvgPool2D()(input_xs)
    avgpool_channel = KL.Reshape((1, 1, channel))(avgpool_channel)
    Dense_One = KL.Dense(units=int(channel * reduction_ratio), activation='relu', kernel_initializer='he_normal',
                         use_bias=True, bias_initializer='zeros')
    Dense_Two = KL.Dense(units=int(channel), activation='relu', kernel_initializer='he_normal', use_bias=True,
                         bias_initializer='zeros')
    # max path
    mlp_1_max = Dense_One(maxpool_channel)
    mlp_2_max = Dense_Two(mlp_1_max)
    mlp_2_max = KL.Reshape(target_shape=(1, 1, int(channel)))(mlp_2_max)
    # avg path
    mlp_1_avg = Dense_One(avgpool_channel)
    mlp_2_avg = Dense_Two(mlp_1_avg)
    mlp_2_avg = KL.Reshape(target_shape=(1, 1, int(channel)))(mlp_2_avg)
    channel_attention_feature = KL.Add()([mlp_2_max, mlp_2_avg])
    channel_attention_feature = KL.Activation('sigmoid')(channel_attention_feature)
    return KL.Multiply()([channel_attention_feature, input_xs])

# SAM 空间注意力
def spatial_attention(channel_refined_feature):
    maxpool_spatial = KL.Lambda(lambda x: K.max(x, axis=3, keepdims=True))(channel_refined_feature)
    avgpool_spatial = KL.Lambda(lambda x: K.mean(x, axis=3, keepdims=True))(channel_refined_feature)
    max_avg_pool_spatial = KL.Concatenate(axis=3)([maxpool_spatial, avgpool_spatial])
    return KL.Conv2D(filters=1, kernel_size=(2, 2), padding="same", activation='sigmoid',
                     kernel_initializer='he_normal', use_bias=False)(max_avg_pool_spatial)

'''
# 标准的CBAM，没有卷积！！！加入ResBlock'''
def cbam_module(input_xs, reduction_ratio=0.5):
    channel_refined_feature = channel_attention(input_xs, reduction_ratio=reduction_ratio)
    spatial_attention_feature = spatial_attention(channel_refined_feature)
    refined_feature = KL.Multiply()([channel_refined_feature, spatial_attention_feature])
    return KL.Add()([refined_feature, input_xs])

# 3条支路，每个支路做串行的卷积注意力,在网络层加入标准化，注意顺序,早期网络加入1×1卷积
def EnergyCNN():
    input1 = KL.Input(shape=(60, 80))
    input11 = tf.expand_dims(input=input1, axis=3)
    input11 = KL.BatchNormalization()(input11)

    #早期融合网络，加入1×1卷积
    x1 = KL.Conv2D(filters=16, kernel_size=(3, 3), strides=(1, 1), activation='relu', padding='same',kernel_regularizer=regularizers.l1(0.01))(input11)
    x1 = KL.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same')(x1)
    x1 = KL.Conv2D(filters=32, kernel_size=(3, 3), strides=(1, 1), activation='relu', padding='same',kernel_regularizer=regularizers.l1(0.01))(x1)
    x1 = KL.MaxPool2D(pool_size=(2, 2), strides=(1,1), padding='same')(x1)
    x1 = KL.Conv2D(filters=64, kernel_size=(3, 3), strides=(1, 1), activation='relu', padding='same',kernel_regularizer=regularizers.l1(0.01))(x1)
    x1 = KL.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same')(x1)
    # x1 = cbam_module(x1)
    # x1 = KL.Conv1D(filters=64, kernel_size=20, strides=20, activation='relu', padding='valid')(input1)
    # x1 = KL.MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding='same')(x1)
    # x1 = KL.Conv2D(filters=128, kernel_size=(5, 5), strides=(1, 1), activation='relu', padding='valid')(x1)
    # x1 = KL.MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding='same')(x1)

    c = KL.Flatten()(x1)
    X = KL.Dense(128, activation='relu')(c)
    X = KL.Dropout(0.5)(X)
    s = KL.Dense(17, activation='softmax')(X)
    model = tf.keras.Model(inputs=input1, outputs=s)
    model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.001),
                  loss='categorical_crossentropy', metrics=['accuracy'])
    return model


#imports
import glob
import logging
import os
import shutil
import ssl
import time
import tkinter as tk
import urllib.request
from datetime import datetime
from tkinter.filedialog import askdirectory

import matplotlib.pyplot as plt
import numpy as np
import pydicom
import requests
from dateutil import tz
from dipy.align.imwarp import SymmetricDiffeomorphicRegistration
from dipy.align.metrics import SSDMetric, CCMetric
from PIL import Image, ImageDraw, ImageFont
from scipy import ndimage
from skimage.measure import label, find_contours
from tifffile import imread, imwrite
from tqdm import tqdm

try:
    import config as config
except:
    import config_base as config

###############################################################################
# Functions for startup, dataloading and assertion_check
###############################################################################

def ask_continue(mode):
    '''
    Asks the user if he wishes to continue the execution in case of
    problems with the data.

    Requires user input:

    y for yes
    n for no

    Returns
    -------
    True if the user wants to continue
    '''

    if mode == "Multi":
        return True

    print(f'{"Do You want to continue [y] or close the application [n]?":^120}')
    user_input = input()
    if user_input in ['y','yes', 'ja' ]:
        print(f'{"Continuing...":^120}\n')
        return True
    elif user_input in ['n', 'no', 'nein']:
        print(f'{"Shuting down!":^120}\n')
        exit()
    else:
        print(f'{"Please enter only [y] or [n]!":^120}')
        ask_continue(mode)

def update_neural_nets():
    '''
    Updates the neuralnetworks by checking if there are any updates on the server.
    '''

    sources = ['https://biomedisa.org/media/img_hernie.h5']

    destinations = [f'{config.path_names["neuralnet"]}/img_hernie.h5']

    for src, dst in zip(sources,destinations):
        update = False
        if os.path.exists(dst):
            # source
            response = requests.head(src)
            timestamp1 = response.headers.get('Last-Modified')
            timestamp1 = timestamp1.split(' ')
            del timestamp1[0]
            timestamp1 = ' '.join(timestamp1)
            for i, month in enumerate(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov']):
                if month in timestamp1:
                    timestamp1 = timestamp1.replace(month, '0%s'%(i+1))
            t1 = datetime.strptime(timestamp1, "%d %m %Y %H:%M:%S %Z")
            t1 = t1.replace(tzinfo=tz.tzutc())  # tell datetime it's in UTC time zone
            t1 = t1.astimezone(tz.tzlocal())    # convert to local time
            print(f'{f"Web:{t1}": ^120}')

            # destination
            timestamp2 = os.path.getmtime(dst)
            timestamp2 = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp2))
            t2 = datetime.strptime(timestamp2, '%Y-%m-%d %H:%M:%S')
            t2 = t2.astimezone(tz.tzlocal())
            print(f'{f"Local:{timestamp2}":_^120}\n\n')
            update = (t1>t2)
        #difference = t1 - t2
        #print(t1>t2, difference, difference.days)

        if update or not os.path.exists(dst):
            with urllib.request.urlopen(src,context=ssl._create_unverified_context()) as response,open(dst,'wb') as out_file:
                shutil.copyfileobj(response,out_file)

def load_directorys(main_folder):
    '''
    Asks the user to select a Dicom Dataset and builds the required
    directory structur by extracting the information from the Dataset.

    Parameter
    ---------
    main_folder: string
        Pathstring to the standard main directory

    Returns
    -------
    string
        Pathstring to the patients main directory
    '''

    # ask the user for the Path to the Data via Tkinterface
    tk.Tk().withdraw()
    path_to_dir = askdirectory(title='Select Dataset')

    # get the raw dcm files
    files = glob.glob(path_to_dir + '/**/*', recursive=True)

    # set patients directory
    for file in files:
        if os.path.isfile(file):
            ds = pydicom.filereader.dcmread(file)
            PatientBirthDate = str(ds.PatientBirthDate)
            PatientName = str(ds.PatientName)
            PatientName = PatientName.replace('Ü','Ue')
            PatientName = PatientName.replace('Ä','Ae')
            PatientName = PatientName.replace('Ö','Oe')
            PatientName = PatientName.replace('ü','ue')
            PatientName = PatientName.replace('ä','ae')
            PatientName = PatientName.replace('ö','oe')
            PatientName = PatientName.replace('ß','ss')

            StudyDate = str(ds.StudyDate)
            StudyDescription = str(ds.StudyDescription)

            # define Directories to store results and Dicom Data
            first_level = f'{main_folder}/{PatientName}'
            second_level = f'{first_level}/Dicom_Data_{StudyDate}'
            # check existence and create non existing directorys
            if not os.path.exists(first_level):
                os.mkdir(first_level)
            if not os.path.exists(second_level):
                os.mkdir(second_level)
            break

    # loop over all files create subdirectories and copy the files
    for file in files:
        if os.path.isfile(file):
            ds = pydicom.filereader.dcmread(file)
            try:
                SeriesNumber = str(ds.SeriesNumber)
                SeriesDescription = str(ds.SeriesDescription)
            except:
                SeriesNumber      = "404"
                SeriesDescription = "No series description"
            # windows Name exceptions: /, \, :, *, ?, ", <, > and |
            SeriesDescription = SeriesDescription.replace('/',' ')
            SeriesDescription = SeriesDescription.replace('\\',' ')
            SeriesDescription = SeriesDescription.replace(':',' ')
            SeriesDescription = SeriesDescription.replace('*',' ')
            SeriesDescription = SeriesDescription.replace('?',' ')
            SeriesDescription = SeriesDescription.replace('"',' ')
            SeriesDescription = SeriesDescription.replace('<',' ')
            SeriesDescription = SeriesDescription.replace('>',' ')
            SeriesDescription = SeriesDescription.replace('|',' ')

            third_level = f'{second_level}/{SeriesNumber} {SeriesDescription}'

            if not os.path.exists(third_level):
                os.mkdir(third_level)

            # set the path to the current .dcm files and copy it
            path_to_dest = f'{third_level}/{str(ds.InstanceNumber).zfill(6)}.dcm'
            if not os.path.exists(path_to_dest):
                shutil.copy(file, path_to_dest)

    logging.debug('Data Loaded successfully.')

    return first_level

def create_patient_directory_auto(dcm_dir,main_folder):
    '''
    Creates the main directory of a single Patient when programm is run in
    multi/auto mode.

    Parameters
    ----------
    dcm_dir: string
        Path to the directory of the .dcm datset
    main_folder: string
        Pathstring to the standard main directory

    Returns
    ----------
    string
        Pathstring to the patients directory
    '''

    # get the raw dcm files
    files = os.listdir(dcm_dir)
    # set patients directory
    ds = pydicom.filereader.dcmread(f'{dcm_dir}/{files[1]}')
    PatientBirthDate = str(ds.PatientBirthDate)
    PatientName = str(ds.PatientName)
    PatientName = PatientName.replace('Ü','Ue')
    PatientName = PatientName.replace('Ä','Ae')
    PatientName = PatientName.replace('Ö','Oe')
    PatientName = PatientName.replace('ü','ue')
    PatientName = PatientName.replace('ä','ae')
    PatientName = PatientName.replace('ö','oe')
    PatientName = PatientName.replace('ß','ss')

    # define Directories to store results and Dicom Data
    first_level = f'{main_folder}/{PatientName}'

    if not os.path.exists(first_level):
        os.mkdir(first_level)

    return first_level

def get_data_shape(dcm_dir):
    '''
    Returns the shape of the given tif data

    Parameter
    ---------
    dcm_dir: string
        Path to the directory of the .dcm datset

    Returns
    -------
    z_sh: string
        The shape in z-direction
    y_sh: string
        The shape in y-direction
    x_sh: string
        The shape in x-direction
    '''

    files = os.listdir(dcm_dir)
    # load a .dcm file of the datset and extract voxel side lengths
    ds = pydicom.filereader.dcmread(f'{dcm_dir}/{files[1]}')
    z_sh = len(files)
    y_sh = ds.Columns
    x_sh = ds.Rows

    return str(z_sh), str(y_sh), str(x_sh)

def get_pixel_spacing(dcm_dir):
    '''
    Returns the three voxel sidelengths of the given dataset.

    Parameter
    ---------
    dcm_dir: string
        Path to the directory of the .dcm dataset

    Returns
    -------
    z_res: string
        Voxel length in z-direction
    y_res: string
        Voxel length in y-direction
    x_res: string
        Voxel length in x-direction
    '''

    files = os.listdir(dcm_dir)
    # load a .dcm file of the datset and extract voxel side lengths
    ds = pydicom.filereader.dcmread(f'{dcm_dir}/{files[1]}')
    z_res = ds.SliceThickness
    y_res, x_res = ds.PixelSpacing

    return str(z_res), str(y_res), str(x_res)

def get_centroid(path_to_mask):
    '''
    Returns the coordinates of the centroid from a 3-dimensional binary array

    Parameters
    ---------
    path_to_mask: string
        Path to the binary mask in tifffile format

    Returns
    -------
    z: string
        z-coordinate of the centroid
    y: string
        y-coordinate of the centroid
    x: string
        x-coordinate of the centroid
    '''

    mask = imread(path_to_mask)
    z,y,x = ndimage.center_of_mass(mask)

    return str(z), str(y), str(x)

def compare_slice_amount(rest_dcm_dir,valsalva_dcm_dir,mode):
    '''
    Compare the amount of slices between the rest and valsalva Dataset.
    Prompts the user in case of a missmatch, when mode is Single.

    Parameters
    ----------
    rest_dcm_dir: string
        Path to the directory containing the .dcm data at rest
    valsalva_dcm_dir: string
        Path to the directory containing the .dcm data during valsalva
    mode: string
        The operation mode of HEDI one of 'Single' or 'Multi'

    Raises
    ------
    ask_continue()
        When the data has a missmatch
    '''

    rest_slice_amount = len(os.listdir(rest_dcm_dir))
    valsalva_slice_amount = len(os.listdir(valsalva_dcm_dir))
    if rest_slice_amount > valsalva_slice_amount:
        print(f'There are {rest_slice_amount - valsalva_slice_amount} more rest than valsalva({valsalva_slice_amount}) scans! \n This will impact the result. \n\n')
        logging.warning(f'There are {rest_slice_amount - valsalva_slice_amount} more rest than valsalva({valsalva_slice_amount}) scans! \n This will impact the result. \n\n')
        return ask_continue(mode)
    elif rest_slice_amount < valsalva_slice_amount:
        print(f'There are {valsalva_slice_amount - rest_slice_amount} more valsalva than rest({rest_slice_amount}) scans! \n This will impact the result. \n\n')
        logging.warning(f'There are {valsalva_slice_amount - rest_slice_amount} more valsalva than rest({rest_slice_amount}) scans! \n\n')
        return ask_continue(mode)

###############################################################################
# Creation of the mask for displacement detection
###############################################################################

def load_mask_data(dcm_dir):
    '''
    Retruns the pixel information stored in the .dcm files

    Parameters
    ----------
    dcm_dir: string
        Path to the directory of the .dcm dataset

    Returns
        volume: nd.array
            Pixel information of the .dcm dataset
        header: list
            Header information of the .dcm dataset
    '''

    if os.path.isdir(dcm_dir):
        files = glob.glob(dcm_dir+'/**/*.dcm', recursive=True)
        for name in files:
            try:
                ds = pydicom.filereader.dcmread(name)
            except:
                files.remove(name)
        slice = ds.pixel_array
        volume = np.zeros((len(files), ds.Rows, ds.Columns), dtype=slice.dtype)
        header = [0] * len(files)
        MinInstanceNumber = np.inf
        for file_name in files:
            ds = pydicom.filereader.dcmread(file_name)
            if ds.InstanceNumber < MinInstanceNumber:
                MinInstanceNumber = ds.InstanceNumber
        for file_name in files:
            ds = pydicom.filereader.dcmread(file_name)
            slice = ds.pixel_array
            if len(slice.shape) == 3 and slice.shape[2] == 1:
                slice = slice[:,:,0]
            volume[ds.InstanceNumber-MinInstanceNumber] = slice
            header[ds.InstanceNumber-MinInstanceNumber] = ds
    else:
        volume, header = None, None
    return volume, header

def reduce_blocksize(data):
    zsh, ysh, xsh = data.shape
    argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = zsh, 0, ysh, 0, xsh, 0
    for k in range(zsh):
        y, x = np.nonzero(data[k])
        if x.any():
            argmin_x = min(argmin_x, np.amin(x))
            argmax_x = max(argmax_x, np.amax(x))
            argmin_y = min(argmin_y, np.amin(y))
            argmax_y = max(argmax_y, np.amax(y))
            argmin_z = min(argmin_z, k)
            argmax_z = max(argmax_z, k)
    argmin_x = max(argmin_x - 1, 0)
    argmax_x = min(argmax_x + 1, xsh-1) + 1
    argmin_y = max(argmin_y - 1, 0)
    argmax_y = min(argmax_y + 1, ysh-1) + 1
    argmin_z = max(argmin_z - 1, 0)
    argmax_z = min(argmax_z + 1, zsh-1) + 1
    data = np.copy(data[argmin_z:argmax_z, argmin_y:argmax_y, argmin_x:argmax_x])
    return data, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x

def clean(image, threshold=0.9):
    image_i = np.copy(image)
    allLabels = np.unique(image_i)
    mask = np.empty_like(image_i)
    s = [[[0,0,0], [0,1,0], [0,0,0]], [[0,1,0], [1,1,1], [0,1,0]], [[0,0,0], [0,1,0], [0,0,0]]]
    for k in allLabels[1:]:

        # get mask
        label = image_i==k
        mask.fill(0)
        mask[label] = 1

        # reduce size
        reduced, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = reduce_blocksize(mask)

        # get clusters
        labeled_array, _ = ndimage.label(reduced, structure=s)
        size = np.bincount(labeled_array.ravel())

        # get reference size
        biggest_label = np.argmax(size[1:]) + 1
        label_size = size[biggest_label]

        # preserve large segments
        reduced.fill(0)
        for l, m in enumerate(size[1:]):
            if m > threshold * label_size:
                reduced[labeled_array==l+1] = 1

        # get original size
        mask.fill(0)
        mask[argmin_z:argmax_z, argmin_y:argmax_y, argmin_x:argmax_x] = reduced

        # write cleaned label to array
        image_i[label] = 0
        image_i[mask==1] = k

    return image_i

def fill(image, threshold=0.9):
    image_i = np.copy(image)
    allLabels = np.unique(image_i)
    mask = np.empty_like(image_i)
    s = [[[0,0,0], [0,1,0], [0,0,0]], [[0,1,0], [1,1,1], [0,1,0]], [[0,0,0], [0,1,0], [0,0,0]]]
    for k in allLabels[1:]:

        # get mask
        label = image_i==k
        mask.fill(0)
        mask[label] = 1

        # reduce size
        reduced, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = reduce_blocksize(mask)

        # reference size
        label_size = np.sum(reduced)

        # invert
        reduced = 1 - reduced # background and holes of object

        # get clusters
        labeled_array, _ = ndimage.label(reduced, structure=s)
        size = np.bincount(labeled_array.ravel())
        biggest_label = np.argmax(size)

        # get label with all holes filled
        reduced.fill(1)
        reduced[labeled_array==biggest_label] = 0

        # preserve large holes
        for l, m in enumerate(size[1:]):
            if m > threshold * label_size and l+1 != biggest_label:
                reduced[labeled_array==l+1] = 0

        # get original size
        mask.fill(0)
        mask[argmin_z:argmax_z, argmin_y:argmax_y, argmin_x:argmax_x] = reduced

        # write filled label to array
        image_i[label] = 0
        image_i[mask==1] = k

    return image_i

def threshold(img):
    # Image -> Adjust -> Threshold (-224 HU, -100==fat, 0==water, -1024==air)
    a = np.zeros_like(img)
    a[img>-224] = 1
    a[0] = 1
    a[-1] = 1
    a = fill(a, 0.9)
    a[0] = 0
    a[-1] = 0
    a = clean(a, 0.9)
    return a

def create_mask(observation_dict):
    '''
    Creates a mask of the abdominal region given by the dicom data.
    Pixels inside are labeled 1. Pixels outside 0.

    Parameters
    ----------
    observation_dict: dict of string
        A patients path dictionary for either the Rest or Valsalva data

    '''

    #load the dicom data
    data, header = load_mask_data(observation_dict['dcm_dir'])
    #adjust the data to the hounsfield range
    rescale_intercept = header[0].RescaleIntercept
    rescale_slope = header[0].RescaleSlope
    data = rescale_slope*data + rescale_intercept
    #pad data with a blackpixels
    data = np.pad(data,((1,1),(256,256),(256,256)),constant_values=-1024)
    body_outline = threshold(data)
    #remove the padded area
    mask = body_outline[1:-1,256:-256,256:-256]
    #save the mask
    imwrite(observation_dict['mask'],mask,compression='zlib')

###############################################################################
# Creation of the displacement and strain arrays
###############################################################################

def get_strain_tensor(Ux,Uy):
    # initialize output matrix
    E = np.zeros([Ux.shape[0],Ux.shape[1],2,2])
    # displacement images gradients
    Uxy, Uxx = np.gradient(Ux)
    Uyy, Uyx = np.gradient(Uy)
    # the 2-D Eulerian strain tensor
    E[:,:,0,0] = 2*Uxx - (Uxx**2 + Uyx**2)
    E[:,:,0,1] = Uxy + Uyx - Uxx*Uxy - Uyx*Uyy
    E[:,:,1,0] = Uxy + Uyx - Uxy*Uxx - Uyy*Uyx
    E[:,:,1,1] = 2*Uyy - (Uxy**2 + Uyy**2)

    E = 0.5*E
    return E

def create_strain_layer(Ux, Uy):
    '''
    Create a layer of strain values corresponding to the displacment in direction
    X and Y.

    Parameters
    ----------
        Ux: nd.array
            Array of the displacement in direction X
        Uy: nd.array
            Array of the displacement in direction Y

    Returns
    ----------
        strain_magnitude: nd.array
            Array of the strain values, same shape as Ux

    '''
    # calculate strain tensors
    E = get_strain_tensor(Ux,Uy)

    # compute a scalar comparison Value from the strain tensor
    # E_vgl = sqrt(E_xx^2 + E_yy^2 - E_xx*E_yy + 3*(E_xy^2))
    strain_magnitude = np.sqrt(
        E[:,:,0,0]**2 +
        E[:,:,1,1]**2 -
        E[:,:,0,0] * E[:,:,1,1] +
        3 * E[:,:,0,1]**2)

    return strain_magnitude

def symmetric_registration(static,moving):
    '''
    Create the displacement from moving to static and back,
    using symmetric diffemomorphic registration.
    The result is split into Y and X direction.

    Parameters
    ----------
    static: nd.array
        Array of the valsalva mask.
    moving: nd.array
        Array of the rest Mask

    Returns
    --------
    outward_field: nd.array of shape [Y|X]
    inward_field: nd.array of shape [Y|X]
    '''

    # initialize the symmetric diffeomoprphic registration
    metric = SSDMetric(dim = 2, smooth = 6, inner_iter = 10 )
    #metric = CCMetric(dim = 2,  sigma_diff = 2, radius= 4 )

    sdr = SymmetricDiffeomorphicRegistration(metric, level_iters=[64, 32, 16])

    mapping = sdr.optimize(static,moving)
    if mapping.is_inverse:
        inward_field = mapping.get_forward_field()
        outward_field = mapping.get_backward_field()
    else:
        inward_field = mapping.get_backward_field()
        outward_field = mapping.get_forward_field()

    return outward_field, inward_field

def create_displacement_layer(displacement_field, mode, y_shape=512, x_shape=512, centroid=[512,255.5]):
    '''
    Create the outward displacement for the outward and inward arrays.

    Parameters
    ----------
    displacement_field: nd.array
        Array of shape [Y|X] containing the components of displacement
    mode: string
        One of 'outward' or 'inward'
    y_shape: int
        Shape of the data in Y direction
    x_shape: int
        Shape of the data in X direction
    centroid: nd.array(2)
        2-D coordinates of the centroid from the displaced mask

    Returns
    --------
    absolute_displacement: nd.array
        Array of the displacement values of shape (y_shape,x_shape)
    '''

    absolute_displacement = np.sqrt(displacement_field[:,:,1]**2 + displacement_field[:,:,0]**2)

    x = np.arange(x_shape) - centroid[1]
    y = np.arange(y_shape) - centroid[0]
    X, Y = np.meshgrid(x,y)
    Normalization = np.sqrt(X**2 + Y**2)
    Normalization[Normalization==0] = 1
    X = X/Normalization
    Y = Y/Normalization

    # compute the Component of the vector field pointing away from the centroid
    if mode == 'outward':
        absolute_displacement[(displacement_field[:,:,1]*X + displacement_field[:,:,0]*Y) < 0] = 0
    elif mode == 'inward':
        absolute_displacement[(displacement_field[:,:,1]*X + displacement_field[:,:,0]*Y) > 0] = 0

    return absolute_displacement

def create_displacement_array(path_dict):
    '''
    Create the 3-D displacement and strain arrays.
    Saves them to the given paths.

    Parameters
    ----------
    path_dict: dict of dict of string
        A patients path dictionary
    '''
    # load both masks
    rest = imread(path_dict['Rest']['mask'])
    valsalva = imread(path_dict['Valsalva']['mask'])
    # get shapes of the masks
    num_slices, y_shape, x_shape = rest.shape
    num_slices = min(num_slices, valsalva.shape[0])
    # initilazie the arrays
    outward_inward = np.zeros((num_slices,y_shape,x_shape,2),dtype=float)
    outward_inward_strain = np.zeros((num_slices,y_shape,x_shape,2),dtype=float)
    # initialize the step size as 1cm per evaluation
    step_size = int(10 // float(path_dict['Rest']['z_spacing']))

    # loop over one layer every cm
    for layer in tqdm(range(0,num_slices,step_size)):
        if np.any(valsalva[layer]) and np.any(rest[layer]):
            outward, inward = symmetric_registration(valsalva[layer],rest[layer])

            # Get the centroid of the mask
            rest_centroid = ndimage.center_of_mass(rest[layer])
            valsalva_centroid = ndimage.center_of_mass(valsalva[layer])

            # create the displacement and strain values for this layer
            outward_inward[layer,:,:,0] = create_displacement_layer(outward,'outward',y_shape,x_shape,rest_centroid)
            outward_inward[layer,:,:,1] = create_displacement_layer(inward,'inward',y_shape,x_shape,valsalva_centroid)
            outward_inward_strain[layer,:,:,0] = create_strain_layer(outward[:,:,1],outward[:,:,0])
            outward_inward_strain[layer,:,:,1] = create_strain_layer(inward[:,:,1],inward[:,:,0])

        if layer>=step_size:
            for step in range(1, step_size, 1):
                outward_inward[layer-step_size + step,...] = (1 - step/step_size)* outward_inward[layer-step_size,...] + (step/step_size)* outward_inward[layer,...]
                outward_inward_strain[layer-step_size + step,...] = (1 - step/step_size)* outward_inward_strain[layer-step_size,...] + (step/step_size)* outward_inward_strain[layer,...]

    if layer < num_slices-1 and np.any(valsalva[num_slices-1]) and np.any(rest[num_slices-1]):
        outward_last, inward_last = symmetric_registration(valsalva[num_slices-1],rest[num_slices-1])
        # get the centroid of the mask
        rest_centroid = ndimage.center_of_mass(rest[num_slices-1])
        valsalva_centroid = ndimage.center_of_mass(valsalva[num_slices-1])
        outward_inward[-1,:,:,0] = create_displacement_layer(outward_last,'outward',y_shape,x_shape,rest_centroid,)
        outward_inward[-1,:,:,1] = create_displacement_layer(inward_last,'inward',y_shape,x_shape,valsalva_centroid)
        outward_inward_strain[-1,:,:,0] = create_strain_layer(outward_last[:,:,1],outward_last[:,:,0])
        outward_inward_strain[-1,:,:,1] = create_strain_layer(inward_last[:,:,1],inward_last[:,:,0])

    for step in range(1,num_slices-layer,1):
        outward_inward[layer+step,...] = (1 - step/(num_slices-layer))*outward_inward[layer,...] + (step/(num_slices-layer))*outward_inward[-1,...]
        outward_inward_strain[layer+step,...] = (1 - step/(num_slices-layer))*outward_inward_strain[layer,...] + (step/(num_slices-layer))*outward_inward_strain[-1,...]

    # set negativ/imward displacement to 0
    outward_inward[outward_inward < 0] = 0
    # remove strain outliers
    strain_threshold = np.quantile(outward_inward_strain, 0.95)
    outward_inward_strain[outward_inward_strain > strain_threshold] = strain_threshold

    #outward_inward_strain[...,0] = ndimage.gaussian_filter(outward_inward_strain[...,0],6)
    #outward_inward_strain[...,1] = ndimage.gaussian_filter(outward_inward_strain[...,1],6)

    # save to a given location
    imwrite(path_dict['Rest']['displacement_array'],outward_inward[...,0],compression='zlib')
    imwrite(path_dict['Valsalva']['displacement_array'],outward_inward[...,1],compression='zlib')
    imwrite(path_dict['Rest']['strain_array'],outward_inward_strain[...,0],compression='zlib')
    imwrite(path_dict['Valsalva']['strain_array'],outward_inward_strain[...,1],compression='zlib')

def get_displacement_dims(path_dict,threshold):
    '''
    Computes the length and width of the largest connected region
    where the displacment is larger than threshold.

    Parameters
    ----------
    path_dict: dict of dict of string
        A patients path dictionary
    threshold: float
        Threshold value defining a displacment as to large

    Returns
    ----------
    length: float
        length of the largest displacment area
    width: float
        width of the largest displacemnt area
    '''

    # get the largest CC
    mask = imread(path_dict['mask'])
    displacement = imread(path_dict['displacement_array'])
    mask = np.copy(mask[:displacement.shape[0]])
    displacement = displacement >= threshold
    displacement[mask==0] = 0
    components = label(displacement)
    # assume at least 1 CC
    if (components.max() != 0):
        largestCC = components == np.argmax(np.bincount(components.flat)[1:])+1
        projection = largestCC.sum(axis=1)
        length = round(np.amax(np.count_nonzero(projection, axis=0))*float(path_dict['z_spacing'])*0.1)
        width  = round(np.amax(np.count_nonzero(projection, axis=1))*float(path_dict['x_spacing'])*0.1)
    else:
        length = 0
        width  = 0

    return length, width

def annotate_displacement_image(observation,observation_dict,area,individual_threshold):
    '''
    Annotate the displacement image.
    Adding name and dimensions of relevant area.

    Parameter
    ---------
    observation: string
        One of 'Rest' or 'Valsalva'
    observation_dict: dict of string
        A patients path dictionary for either the Rest or Valsalva data
    area: float
        Area of the displacment larger than threshold
    individual_threshold: int
        The patient specific threshold for the displacement.
    '''
    img = Image.open(observation_dict['displacement_png'])
    length, width = get_displacement_dims(observation_dict,individual_threshold)
    # annotate the image
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype("arial.ttf",size=20)
    draw.text(xy=(img.width/2,0),
            text= (f'{observation}\n'
                   f'Unstable Abdominal Wall (Displacement > {individual_threshold}mm)\n'
                   f'Width: {width}cm  |  Length: {length}cm  |  Area: {round(area)}cm²'
                   ),
            fill=(0,0,0),
            anchor='ma',
            align = 'center',
            font=font,
            )
    # save the image
    img.save(observation_dict['displacement_png'], format='png', dpi=(300,300))

def annotate_strain_image(observation,observation_dict):
    '''
    Annotate the strain image.
    Adding name and dimensions of relevant area.

    Parameter
    ---------
    observation: string
        One of 'Rest' or 'Valsalva'
    obseravtion_dict: dict of string
        A patients dictionary containing the paths to either the rest or valsalva data
    '''
    # loaf the strain image
    img = Image.open(observation_dict['strain_png'])
    # annotate the imge
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype("arial.ttf",size=20)
    draw.text(xy=(img.width/2,0),
            text= (f'{observation}\n'
                   f'Strain\n'),
            fill=(0,0,0),
            anchor='ma',
            align = 'center',
            font=font,
            )
    # save the image
    img.save(observation_dict['strain_png'],format='png', dpi=(300,300))

def plot_displacement(path_dict,path_to_save):
    font = {'family': 'serif', 'color':  'red'}
    # plot areas of displacement with respect to their magnitute
    plt.plot(np.arange(10,len(path_dict['Rest']['displacement_areas'])),
            path_dict['Rest']['displacement_areas'][10:],
            color='blue', label='Rest')
    plt.plot([15],path_dict['Rest']['displacement_areas'][15],
            marker='v', color='g',
            label=f'{np.round(path_dict["Rest"]["displacement_areas"][15])}cm²')
    plt.plot(np.arange(10,len(path_dict['Valsalva']['displacement_areas'])),
            path_dict['Valsalva']['displacement_areas'][10:],
            color='red',label='Valsalva')
    plt.plot([15],path_dict['Valsalva']['displacement_areas'][15],
            marker='^', color='orange',
            label=f'{np.round(path_dict["Valsalva"]["displacement_areas"][15])}cm²')
    plt.title('Work in Progress. NOT for Clinical Use!',fontdict=font)
    plt.ylabel('Defectarea in cm²')
    plt.xlabel('Magnitude of displacement in mm')
    plt.legend(title = 'Modus')
    plt.savefig(f'{path_to_save}/DefectareaDiagramm.png', dpi=300)
    plt.close()

def plot_displacement_lower(path_dict,path_to_save):
    font = {'family': 'serif', 'color':  'red'}

    total_area_rest = path_dict['Rest']['displacement_areas'][0]
    total_area_val = path_dict['Valsalva']['displacement_areas'][0]

    # plot areas of displacement with respect to their magnitute
    plt.plot(np.arange(0, len(path_dict['Rest']['displacement_areas'])),
            total_area_rest - path_dict['Rest']['displacement_areas'],
            color='blue', label='Rest')
    plt.plot([15],(total_area_rest - path_dict['Rest']['displacement_areas'][15]),
            marker='v', color='g',
            label=f'{np.round(total_area_rest - path_dict["Rest"]["displacement_areas"][15])}cm²')
    plt.plot(np.arange(0,len(path_dict['Valsalva']['displacement_areas'])),
            total_area_val - path_dict['Valsalva']['displacement_areas'],
            color='red',label='Valsalva')
    plt.plot([15],(total_area_val - path_dict['Valsalva']['displacement_areas'][15]),
            marker='^', color='orange',
            label=f'{np.round(total_area_val - path_dict["Valsalva"]["displacement_areas"][15])}cm²')
    plt.title('Work in Progress. NOT for Clinical Use!',fontdict=font)
    plt.ylabel('Stable Area in cm²')
    plt.xlabel('Magnitude of displacement in mm')
    plt.legend(title = 'Modus')
    plt.savefig(f'{path_to_save}/Area_lower_than_treshhold.png', dpi=300)
    plt.close()

def plot_displacement_difference(path_dict,path_to_save):
    font = {'family': 'serif', 'color':  'red'}

    total_area_rest = path_dict['Rest']['displacement_areas'][0]
    total_area_valsalva = path_dict['Valsalva']['displacement_areas'][0]
    area_difference = total_area_valsalva - total_area_rest

    # plot areas of displacement with respect to their magnitute
    plt.plot(np.arange(0, len(path_dict['Rest']['displacement_areas'])),
            area_difference + path_dict['Rest']['displacement_areas'] - path_dict['Valsalva']['displacement_areas'],
            color='blue')

    plt.title('Work in Progress. NOT for Clinical Use!',fontdict=font)
    plt.ylabel('Area difference lower than treshhold in cm²')
    plt.xlabel('Magnitude of displacement in mm')
    # plt.legend(title = 'Modus')
    plt.savefig(f'{path_to_save}/DefectAreaDifference.png', dpi=300)
    plt.close()

def plot_individual_threshold(path_dict,path_to_save,threshold):

    area_rest_unstable = np.copy(path_dict['Rest']['displacement_areas'])
    area_valsalva_unstable = np.copy(path_dict['Valsalva']['displacement_areas'])
    area_rest_stable = np.copy(area_rest_unstable[0] - area_rest_unstable)
    area_valsalva_stable = np.copy(area_valsalva_unstable[0] - area_valsalva_unstable)

    min_area = 20

    relative_threshold = np.ones_like(area_rest_unstable)
    for k in range(len(relative_threshold)):
        if area_rest_stable[k]>0 and area_valsalva_unstable[k]>0 and area_rest_unstable[k]>min_area:
            relative_threshold[k] = (1 - (area_valsalva_stable[k]) / (area_rest_stable[k]))**2 + ((area_rest_unstable[k]) / (area_valsalva_unstable[k]))**2

    # x-achse
    plt.plot(np.arange(0, len(relative_threshold)),
            relative_threshold,
            color='blue')

    # threshold
    if not threshold:
        threshold = max(np.argmin(relative_threshold), 10)

    # marker
    plt.plot([threshold],[relative_threshold[threshold]],
                marker='^', color='orange',
                label=f'{threshold}mm')

    plt.ylabel('Instability ratio')
    plt.xlabel('Magnitude of translation in mm')
    plt.legend()
    plt.savefig(f'{path_to_save}/RelativeThreshold.png', dpi=300)
    plt.close()

    return threshold

###############################################################################
# Helper functions for the CT-Crosssection
###############################################################################

def create_crosssection(observation_dict):
    '''
    Create a png of the CT slice with the largest displacement.

    Parameters
    ----------
    obseravtion_dict: dict of string
        A patients dictionary containing the paths to either the rest or valsalva data

    Returns
    --------
    layer: int
        Slice id of the slice with max. displacement

    '''
    # load the labels
    label_array = imread(observation_dict['labels'])
    # load the displacement array
    displacement_array = imread(observation_dict['displacement_array'])
    # get the layer of the largest hernia sac or displacment
    if np.any(label_array==7):
        layer = np.argmax(np.sum(label_array==7,axis=(1,2)))
    else:
        layer = np.argmax(np.amax(displacement_array,axis=(1,2)))
    # get the dcm file containg that layer (they start at 1)
    layer_path = f'{observation_dict["dcm_dir"]}/{str(layer+1).zfill(6)}.dcm'
    # convert the dcm file into an 8bit rgb array and adjust the size to fit with other data
    ds = pydicom.filereader.dcmread(layer_path)
    CT_img = ds.pixel_array
    CT_img = np.pad(CT_img, ((44,44),(5,5)), mode='constant',constant_values=0)
    # get corresponding slice of label data
    label_array = label_array[layer]
    label_array = np.pad(label_array, ((44,44),(5,5)), mode='constant',constant_values=0)
    resolution = 600
    if resolution != 600:
        import cv2
        CT_img = cv2.resize(CT_img, (resolution, resolution), interpolation=cv2.INTER_CUBIC)
        label_a = np.zeros_like(CT_img)
        for k in np.unique(label_array):
            tmp = np.zeros_like(label_array)
            tmp[label_array==k]=1
            tmp = cv2.resize(tmp, (resolution, resolution), interpolation=cv2.INTER_CUBIC)
            label_a[tmp==1]=k
        label_array = np.copy(label_a)
    label_color = ['black', 'darkblue', 'royalblue', 'lightsteelblue', 'beige', 'sandybrown', None, 'red']
    # add the contours of every label over the img
    fig = plt.figure(frameon=False, figsize=(.87, 1))
    ax = plt.Axes(fig, [0.,0.,1.,1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(CT_img, aspect='auto', cmap = plt.cm.gray)
    for label_value in np.unique(label_array):
        label = label_array == label_value
        contours = find_contours(label)
        for contour in contours:
            ax.plot(contour[:, 1], contour[:, 0], color=label_color[label_value], linewidth = .2,)

    plt.savefig(observation_dict['crosssection'], dpi=resolution)
    plt.close()
    return layer

def annotate_crosssection(observation,observation_dict,layer):
    '''
    Annotate the ct_crosssection image.
    Adding name and dimensions of relevant area.

    Parameter
    ---------
    observation: string
        One of 'Rest' or 'Valsalva'
    obseravtion_dict: dict of string
        A patients dictionary containing the paths to either the rest or valsalva data
    layer: int
        Slice id of the slice with max. displacement
    '''
    to_annotate = Image.open(observation_dict['crosssection'])
    # annotate the image
    draw = ImageDraw.Draw(to_annotate)
    font = ImageFont.truetype("arial.ttf",size=18)
    draw.text(xy=(to_annotate.width/2,0),
            text=(f'{observation}\n'
                  f'Hernia sac largest at Y = {(int(observation_dict["z_sh"]) - layer)*float(observation_dict["z_spacing"])}mm \n'
                  f'(slice {int(observation_dict["z_sh"]) - layer})'
                ),
            fill=(255,255,255),
            anchor='ma',
            align='center',
            font=font,
            )
    # save the crosssection
    to_annotate.save(observation_dict['crosssection'], format='png', dpi=(300,300))

###############################################################################
# Anotation of the label image
###############################################################################

def get_label_sizes(path_to_tif,x_res,y_res,z_res):
    '''
    Computes the length, width, area and volume of the labeled hernia.
    as well as the volume of the abdominal cavity.

    Parameters
    ----------
        path_to_tif: string
            Pathstring to the .tiff file containing the labels.
        x_res: float
            The real length represented by one pixel in X direction.
        y_res: float
            The real length represented by one pixel in Y direction.
        z_res: float
            The real length represented by one pixel in Z direction.

    Returns
    ----------
        hernia_width: float
            Width of the Hernia Sac
        hernia_length: float
            Length of the Hernia Sac
        hernia_area: float
            Area of the Hernia Sac
        hernia_volume: float
            Volume of the Hernia Sac
        abdomen_volume: float
            Volume of the abdominal cavity
    '''
    # load segmentation
    label_array = imread(path_to_tif)
    zsh, _, _ = label_array.shape

    hernia_only = label_array==7

    # compute Volume of herniasack
    hernia_volume = round(np.sum(hernia_only)*x_res*y_res*z_res*0.001)

    # compute Volume of Abdomen
    abdomen_volume= round(np.sum(label_array==5)*x_res*y_res*z_res*0.001)

    # compute largest CC
    components = label(hernia_only)
    if (components.max() != 0): # assume at least 1 CC
        largestCC = components == np.argmax(np.bincount(components.flat)[1:])+1

        # compute area of herniasack
        hernia_area   = round(np.sum(np.amax(largestCC,axis=1))*x_res*z_res*0.01)

        # compute width and length of herniasack
        hernia_width  = round(np.sum(np.amax(largestCC,axis=(0,1)))*x_res*0.1)

        # compute width and length of herniasack
        hernia_length = round(np.sum(np.amax(largestCC,axis=(1,2)))*z_res*0.1)

    else:
        hernia_area   = 0
        hernia_width  = 0
        hernia_length = 0

    return hernia_width, hernia_length, hernia_area, hernia_volume, abdomen_volume

def annotate_label_image(observation,observation_dict):
    '''
    Annotate the image of the labels.
    Adding names and dimensions of relevant areas and volumes.

    Parameters
    ----------
    observation: string
        One of 'Rest' or 'Valsalva'
    obseravtion_dict: dict of string
        A patients dictionary containing the paths to either the rest or valsalva data
    '''
    hernia_width, hernia_length, hernia_area, hernia_volume, abdominal_volume = get_label_sizes(
            observation_dict['labels'],float(observation_dict['x_spacing']),
            float(observation_dict['y_spacing']),float(observation_dict['z_spacing']))

    # write hernia dimensions on the image
    to_annotate = Image.open(observation_dict['labels_png'])
    draw = ImageDraw.Draw(to_annotate)
    font = ImageFont.truetype("arial.ttf", size=18)
    draw.text(xy=(to_annotate.width/2,0),
            text= (f'{observation}\n'
                   f'Hernia Width: {hernia_width}cm  |  Hernia Length: {hernia_length}cm  |  Hernia Area: {hernia_area}cm²\n'
                   f'Hernia Volume: {hernia_volume}cm³  |  Abd. Cavity Volume: {abdominal_volume}cm³\n'
                   f'Loss of Domain: {round(hernia_volume/(hernia_volume+abdominal_volume),2)} (Sabbagh)  |  {round(hernia_volume/abdominal_volume,2)} (Tanaka)'
                   ),
            fill=(0,0,0),
            anchor='ma',
            align = 'center',
            font = font,
            )
    to_annotate.save(observation_dict['labels_png'], format='png', dpi=(300,300))


from datetime import datetime, timedelta
from decimal import Decimal
from typing import List, Union

from django.conf import settings

from dateutil.parser import parse as date_parse

from aqua_voting_tracker.voting.exceptions import VoteParsingError
from aqua_voting_tracker.voting.models import Vote

def is_locked_predicate(predicate: dict) -> bool:
    return predicate == {
        'not': {
            'unconditional': True,
        },
    }

def parse_claim_after_predicate(predicate: dict) -> Union[datetime, timedelta, None]:
    abs_after = predicate.get('not', {}).get('abs_before', None)
    if abs_after:
        try:
            return date_parse(abs_after)
        except ValueError:
            raise VoteParsingError('Invalid date format.')

    rel_after = predicate.get('not', {}).get('rel_before', None)
    if rel_after:
        return timedelta(seconds=int(rel_after))

    return None

def parse_asset(claimable_balance: dict) -> str:
    asset = claimable_balance['asset']
    if asset not in settings.VOTING_ASSETS:
        raise VoteParsingError('Invalid asset.')

    return asset

def parse_claimants(claimable_balance: dict) -> (str, str, Union[datetime, timedelta]):
    sponsor = claimable_balance['sponsor']
    claimants = claimable_balance['claimants']
    if len(claimants) != 2:
        raise VoteParsingError('Invalid claimants.')

    claim_back_claimant, market_claimant = sorted(claimants, key=lambda cl: is_locked_predicate(cl['predicate']))
    if not is_locked_predicate(market_claimant['predicate']):
        raise VoteParsingError('Market predicate not locked.')

    if claim_back_claimant['destination'] != sponsor and sponsor != settings.VOTING_BALANCES_DISTRIBUTOR:
        raise VoteParsingError('Invalid claimant destination.')

    balance_locked_until = parse_claim_after_predicate(claim_back_claimant['predicate'])
    if not balance_locked_until:
        raise VoteParsingError('Invalid predicate.')

    return market_claimant['destination'], claim_back_claimant['destination'], balance_locked_until

def parse_claimable_balance(claimable_balance: dict) -> Vote:
    asset = parse_asset(claimable_balance)

    balance_id = claimable_balance['id']
    amount = claimable_balance['amount']
    balance_created_at = claimable_balance['last_modified_time']
    market_key, voting_key, locked_until = parse_claimants(claimable_balance)

    try:
        balance_created_at = date_parse(balance_created_at)
    except ValueError:
        raise VoteParsingError('Invalid date format.')

    if isinstance(locked_until, timedelta):
        locked_until = balance_created_at + locked_until

    return Vote(
        balance_id=balance_id,
        voting_account=voting_key,
        market_key=market_key,
        amount=amount,
        asset=asset,
        locked_at=balance_created_at,
        locked_until=locked_until,
    )

def parse_claimable_balance_created_effect(effect: dict) -> (str, str, str, Decimal, datetime):
    balance_id = effect['balance_id']
    account = effect['account']
    asset = effect['asset']
    amount = Decimal(effect['amount'])
    created_at = date_parse(effect['created_at'])

    if asset not in settings.VOTING_ASSETS:
        raise VoteParsingError('Invalid asset.')

    return balance_id, account, asset, amount, created_at

def parse_claim_back_claimant_created_effect(effect: dict) -> (str, datetime):
    account = effect['account']
    balance_locked_until = parse_claim_after_predicate(effect['predicate'])
    if not balance_locked_until:
        raise VoteParsingError('Invalid predicate.')

    if isinstance(balance_locked_until, timedelta):
        created_at = date_parse(effect['created_at'])
        balance_locked_until = created_at + balance_locked_until

    return account, balance_locked_until

def parse_market_claimant_created_effect(effect: dict) -> str:
    account = effect['account']
    if not is_locked_predicate(effect['predicate']):
        raise VoteParsingError('Invalid predicate.')

    return account

def parse_claimable_balance_from_effects(effects: List[dict]) -> Vote:
    claimable_balance_created_effect = next(
        effect for effect in effects if effect['type'] == 'claimable_balance_created'
    )

    balance_id, sponsor, asset, amount, created_at = parse_claimable_balance_created_effect(
        claimable_balance_created_effect,
    )

    if len([effect for effect in effects if effect['type'] == 'claimable_balance_claimant_created']) != 2:
        raise VoteParsingError('Invalid claimants.')
    claim_back_claimant_created_effect, market_claimant_created_effect = sorted([
        effect for effect in effects if effect['type'] == 'claimable_balance_claimant_created'
    ], key=lambda effect: is_locked_predicate(effect['predicate']))

    voting_key, locked_until = parse_claim_back_claimant_created_effect(claim_back_claimant_created_effect)
    market_key = parse_market_claimant_created_effect(market_claimant_created_effect)

    if sponsor != voting_key and sponsor != settings.VOTING_BALANCES_DISTRIBUTOR:
        raise VoteParsingError('Invalid sponsor.')

    return Vote(
        balance_id=balance_id,
        voting_account=voting_key,
        market_key=market_key,
        amount=amount,
        asset=asset,
        locked_at=created_at,
        locked_until=locked_until,
    )


"""
def palindrome(n):
    millier = int (n/1000)
    centaine = int(n/100) - millier * 10
    dizaine = int(n/10) - (centaine * 10  + millier *100)
    unite =  n - (millier * 1000 + centaine * 100 + dizaine * 10)
    nouveauNombre = unite * 1000 + dizaine * 100 + centaine * 10 + millier
    print(unite)
    if (nouveauNombre == n) :
        return True
    else:
        return False
"""

import math

"""
def isPalindrome(n):
    if n>=10:
        digits = int(math.log10(n))+1
        if digits >= 2 :
            fact = 10**(digits-1)
            l = []
            l.append(int(n/fact))
            for i in range(1 , digits):
                fact = int(10**(digits - i ))
                l.append( int((n%fact)/ (10**(digits - i - 1 ))) )
            l1 = l.copy()
            l.reverse()
            if l1 == l :
                return True
            else :
                return False
        else :
            return False
    else :
        return False
"""
def isPalindrome(nb):
    if str(nb) == str(nb)[::-1]:
        return True
    return False

resultat = 0
n = 1
for i in range(999, -1, -1):
    for j in range(999, -1, -1):
        n = i * j
        if isPalindrome(n):
            if n > resultat :
                resultat = n

print(resultat)


"""2.Створіть об’єкт happy, що містить дату вашого народження. Використайте функції для роботи з датою і часом, \
щоб дізнатися відповіді на такі питання (виведення організуйте в окремий файл у вигляді, на зразок \
«запитання: відповідь», у окремих рядках):
a. "Яка дата вашого народження?"
b. "У який день тижня ви народилися?"
c. "Коли вам буде (або вже було) 13 330 днів від народження?"
"""

import datetime, time

happy = datetime.date(1985, 12, 11)

print('Дата народження: ', happy)
#
daysOftheWeek = ("ISO Week days start from 1", "Monday", "Tuesday", "Wednesday", "Thursday", \
    "Friday", "Saturday", "Sunday")
isoWeekDay = happy.isoweekday()
print('У який день тижня ви народилися? ', daysOftheWeek[isoWeekDay])
#
one_day = datetime.timedelta(days=1)
h13330 = happy + one_day * 13300
print('13330 днів від народження було: ', h13330)


from urllib.request import urlopen
from bs4 import BeautifulSoup
import sys
from astropy.time import Time
import astropy.units as u
import numpy as np

def readGCN(RA, DEC, ERR, RA1, RA2, start, stop, tel):

    htmlAddress = "https://gcn.gsfc.nasa.gov/%s_grbs.html"%tel.lower()

    if tel == 'Swift':
        trigInd  = 0
        dateInd  = 1
        timeInd  = 2
        raInd    = 3
        decInd   = 4
        errorInd = 5
    elif tel == "MAXI" or tel=="Fermi" or tel=='Integral':
        trigInd  = 0
        dateInd  = 1
        timeInd  = 2
        raInd    = 4
        decInd   = 5
        errorInd = 6
    else:
        print("%s is not a valid choice. Valid choices are MAXI, Fermi, Integral, Swift. Exit")
        sys.exit()

    sock = urlopen(htmlAddress)
    htmlSource = sock.read()
    sock.close()
    parsed_html = BeautifulSoup(htmlSource, "lxml")

    table = parsed_html.body.find("table")
    rows = table.findAll('tr')

    triggerN = []
    burstDate = []
    burstTime = []
    RAs = []
    Decs = []
    Error = []

    for row in rows:
        cols = row.findAll("td")
        cols = [ele.text.strip() for ele in cols]
        if len(cols) > 1:
            triggerN.append(cols[trigInd])
            burstDate.append(cols[dateInd])
            burstTime.append(cols[timeInd])
            RAs.append(cols[raInd])
            Decs.append(cols[decInd])
            Error.append(cols[errorInd])

    for i in range(len(triggerN)):
        if((triggerN[i] == '') or (RAs[i] == '') or (Decs[i] == '') or (burstDate[i] == '') or \
           (burstTime[i] == '') or (Error[i] == '')):
            RAs[i] = 500
            Decs[i] = 500
            burstDate[i] = '00/6/6'
            burstTime[i] = '00:00:00'
        triggerN[i] = tel + ' ' + triggerN[i]
        burstTime[i] = '20' + burstDate[i] + ' ' + burstTime[i]
        burstTime[i] = burstTime[i].replace('/', '-')

    burstTime = Time(burstTime, format = 'iso', scale = 'utc')
    t1 = start - 300 * u.s
    t2 = stop + 300 * u.s

    triggerN = np.asarray(triggerN)
    burstTime = np.asarray(burstTime)
    RAs = np.asarray(RAs)
    Decs = np.asarray(Decs)
    Error = np.asarray(Error)

    RAs = RAs.astype('float')
    Decs = Decs.astype('float')

    RAs[RAs < RA1] += 360
    RAs[RAs > RA2] -= 360

    mask = (((RAs - RA) ** 2 + (Decs - DEC) ** 2) <= (ERR ** 2)) & (t1 < burstTime) & (burstTime < t2)

    triggerN = triggerN[mask]
    burstTime = burstTime[mask]
    RAs = RAs[mask]
    Decs = Decs[mask]
    Error = Error[mask]

    return triggerN, burstTime, RAs, Decs, Error


import sys
import math

from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from PyQt5.QtWidgets import QLabel, QStatusBar, QToolBar
from PyQt5.QtWidgets import QPushButton, QProgressBar
from PyQt5.QtWidgets import QVBoxLayout, QGridLayout
from PyQt5.QtWidgets import QWidget
from PyQt5 import QtCore
from PyQt5.QtCore import Qt

from PyQt5.QtGui import QIcon, QPixmap

import sip

import socket
import os, os.path
import random
from collections import deque
import requests
import ctypes
import platform
import subprocess

sub_reddits = [
                'wallpaper', 'wallpapers', 'wallpaperdump', 'wallpaperengine',
                'ImaginaryLandscapes', 'EarthPorn', 'food', 'foodphotography',
                'LandscapePhotography', 'Minecraft', 'blender', 'skyporn'
           ]
SCRIPT = """/usr/bin/osascript<<END
tell application "Finder"
set desktop picture to POSIX file "%s"
end tell
END"""

def check_ext(img_url):
    for ext in img_exts:
        if img_url.endswith(ext):
            return True
    return False

def download_file(file_url, path):
    r = requests.get(file_url, allow_redirects=True)
    open(path, 'wb').write(r.content)

def get_ext(url):
    return url.split('.')[-1]

def num_files():
    DIR = 'pics/'
    return len([name for name in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, name))])

def update_num_files():
        num = num_files()+1
        return num

def pic_files():
    DIR = 'pics/'
    return [name for name in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, name))]

def is_connected():
    try:
        socket.create_connection(("www.google.com", 80))
        return True
    except OSError:
        pass
    return False

def set_bg(pic_path):
    if os.uname()[1] == 'raspberrypi':
        os.system('pcmanfm --set-wallpaper {}'.format(pic_path))
    elif platform.system() == 'Windows':
        ctypes.windll.user32.SystemParametersInfoW(20, 0, pic_path , 0)
    elif platform.system() == "Darwin":
        subprocess.Popen(SCRIPT%pic_path, shell=True)

class Window(QMainWindow):
    """Main Window."""
    def __init__(self, parent=None):
        """Initializer."""
        super().__init__(parent)
        self.setWindowTitle('WallPaperChanger')
        self._createMenu()
        self._createToolBar()
        self._createStatusBar()

        try:
            os.mkdir('pics')
        except FileExistsError:
            pass

        self.layout = QGridLayout()
        self.window = QWidget()

        self.btn = QPushButton('Change Via Online Pics')
        self.btn.clicked.connect(self.fetch_online_wallpaper)
        self.progress_bar = QProgressBar()
        self.progress_bar.setMaximum(100)
        self.c_status = QLabel('')
        self.connect_label = QLabel()
        has_internet = is_connected()
        if has_internet:
            self.pixmap = QPixmap('ctrl_icons/checked.png')
        else:
            self.pixmap = QPixmap('ctrl_icons/cancel.png')
        self.smaller_pixmap = self.pixmap.scaled(32, 32, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation)
        self.connect_label.setPixmap(self.smaller_pixmap)
        self.stats = QLabel('Internet On: {}
Offline Images: {}'.format(is_connected(), num_files()))

        self.prev_but = QPushButton()
        self.prev_but.clicked.connect(self.prev_pic)
        prev_but_icon = QPixmap("ctrl_icons/previous.png");
        self.prev_but.setIcon(QIcon(prev_but_icon))

        self.next_but = QPushButton()
        self.next_but.clicked.connect(self.next_pic)
        next_but_icon = QPixmap("ctrl_icons/next-1.png");
        self.next_but.setIcon(QIcon(next_but_icon))

        self.rand_but = QPushButton()
        self.rand_but.clicked.connect(self.choose_randpic)
        rand_but_icon = QPixmap("ctrl_icons/refresh-1.png");
        self.rand_but.setIcon(QIcon(rand_but_icon))

        self.delete_img_but = QPushButton()
        self.delete_img_but.clicked.connect(self.confirm_img_deletion)
        delete_img_but_icon = QPixmap("ctrl_icons/cancel.png");
        self.delete_img_but.setIcon(QIcon(delete_img_but_icon))

        self.showofflinepic_label = QLabel()
        self.showofflinepic_label.setAlignment(Qt.AlignCenter)
        self.showofflinepic_label.setFixedSize(300, 300)
        self.choose_randpic()

        self.chooseoffline_but = QPushButton('Change via Offline Pic')
        self.chooseoffline_but.clicked.connect(self.change_offline_pic)

        self.layout.addWidget(self.connect_label, 0, 2)
        self.layout.addWidget(self.stats, 0, 1)
        self.layout.addWidget(self.btn, 1, 1, 1, 2)
        self.layout.addWidget(self.progress_bar, 2, 1, 1, 2)
        self.layout.addWidget(self.c_status, 3, 1, 1, 2)
        self.layout.addWidget(self.prev_but, 4, 0)
        self.layout.addWidget(self.showofflinepic_label, 4, 1, 1, 2)
        self.layout.addWidget(self.next_but, 4, 3)
        self.layout.addWidget(self.rand_but, 5, 1, 1, 2)
        self.layout.addWidget(self.delete_img_but, 6, 1, 1, 2)
        self.layout.addWidget(self.chooseoffline_but, 7, 1, 1, 2)

        self.window.setLayout(self.layout)
        self.setWindowIcon(QIcon('icon.jpg'))
        self.setCentralWidget(self.window)

    def _createMenu(self):
        self.menu = self.menuBar().addMenu("&Menu")
        self.menu.addAction('&Exit', self.close)

    def _createToolBar(self):
        tools = QToolBar()
        self.addToolBar(tools)
        tools.addAction('Exit', self.close)

    def _createStatusBar(self):
        status = QStatusBar()
        status.showMessage("")
        self.setStatusBar(status)

    def fetch_online_wallpaper(self):
        self.c_status.setText('starting ...')
        self.progress_bar.setValue(0)
        self.wallpaper_change()
        self.progress_bar.setValue(100)
        self.c_status.setText('completed!')

    def display_pic(self, pic_name):
        self.showofflinepic_pixmap = QPixmap('pics/{}'.format(pic_name))
        self.smaller_showofflinepic_pixmap = self.showofflinepic_pixmap.scaled(300, 300, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation)
        self.showofflinepic_label.setPixmap(self.smaller_showofflinepic_pixmap)

    def choose_randpic(self):
        self.offline_pics = deque(pic_files())
        if self.offline_pics:
            self.chosen_offlinepic = random.choice(self.offline_pics)
            self.display_pic(self.chosen_offlinepic)

    def prev_pic(self):
        self.offline_pics.rotate(1)
        self.chosen_offlinepic = self.offline_pics[0]
        self.display_pic(self.chosen_offlinepic)

    def next_pic(self):
        self.offline_pics.rotate(-1)
        self.chosen_offlinepic = self.offline_pics[0]
        self.display_pic(self.chosen_offlinepic)

    def change_offline_pic(self):
        self.wallpaper_change(offline_pic=self.chosen_offlinepic)

    def set_step_status(self, text):
        self.c_status.setText(text)
        QApplication.processEvents()

    def confirm_img_deletion(self):
        buttonReply = QMessageBox.question(self, 'Delete confirmation', "Do you really want to delete this picture?",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            print('Yes clicked.')
            # remove image
            os.remove('pics/{}'.format(self.chosen_offlinepic))
            # update display
            self.choose_randpic()
        else:
            print('No clicked.')

    def wallpaper_change(self, offline_pic=None):
        try:
            if offline_pic:
                Fpath = os.path.realpath(os.getcwd())
                pic_path = os.path.join(os.sep, Fpath, 'pics', '{}'.format(offline_pic))
                ctypes.windll.user32.SystemParametersInfoW(20, 0, pic_path, 0)
            else:
                img_urls = []

                headers = {
                    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                    'Accept-Language': 'en-US,en;q=0.5',
                    'Accept-Encoding': 'deflate',
                    'Connection': 'keep-alive',
                    'Upgrade-Insecure-Requests': '1',
                    'Cache-Control': 'max-age=0',
                    'TE': 'Trailers'
                }
                random.shuffle(sub_reddits)

                self.set_step_status('gathering images ...')
                for i,sub_reddit in enumerate(sub_reddits):
                    perc = ((i+1)/len(sub_reddits)) * 50
                    self.set_progress(math.ceil(perc))
                    self.set_step_status('passing over source: {}'.format(i+1))
                    req = requests.get('https://www.reddit.com/r/{}/.json'.format(sub_reddit), headers=headers)
                    jsondata = req.json()
                    # print(jsondata)
                    posts = jsondata['data']['children']
                    current_urls = [post['data']['url'] for post in posts if (post['data']['url']).split('.')[-1] in
                    ['jpg', 'jpeg', 'png'] and 'imgur' not in post['data']['url']]
                    img_urls += current_urls
                print('found', img_urls)
                chosen_img = random.choice(img_urls)
                self.progress_bar.setValue(50)
                self.set_step_status('choosing image ...')
                print('choose', chosen_img)
                chosen_img_name = '{}.{}'.format(chosen_img[::-8], get_ext(chosen_img))
                chosen_img_path = 'pics/{}'.format(chosen_img_name)

                self.set_progress(75)
                self.set_step_status('fetching image ...')
                download_file(chosen_img, chosen_img_path)

                Fpath = os.path.realpath(os.getcwd())
                pic_path = os.path.join(os.sep, Fpath, 'pics', chosen_img_name)
                self.chosen_offlinepic = chosen_img_name
                self.offline_pics.append(chosen_img_name)
                self.display_pic(self.chosen_offlinepic)
                self.set_progress(90)
                self.set_step_status('setting image ...')
                set_bg(pic_path)
                self.layout.removeWidget(self.stats)
                sip.delete(self.stats)
                self.stats = None
                self.stats = QLabel('Internet On: {}
Offline Images: {}'.format(is_connected(), update_num_files()))
                self.layout.addWidget(self.stats, 0, 1)
        except Exception as e:
            print('error', e)

    def set_progress(self, value):
        self.progress_bar.setValue(value)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())


import inflect

p = inflect.engine()
answer = "Adieu, adieu, to "
names = []
mylist = []

while True:
    try:
        prompt = input()
        names.append(prompt)
        print(names)
    except EOFError:
        mylist = p.join((names))
        print(answer + mylist)
        break


import pandas as pd
import warnings
import Model.TimeTable as mTimeTable
import ApiFunctions.TimeTable as apiTimeTable
import Model.Venue as mVenue
import ApiFunctions.Venue as apiVenue
from sql import MySQL
import ApiFunctions.Offered_Courses as apiOfferedCourses
import Model.Offered_Courses as mOfferedCourses
import ApiFunctions.Section_Offer as apiSectionOffer
import Model.Section_Offer as mSectionOffer

class Parse_Excel:

    def parse_excel(path):
        df = pd.read_excel(path)

        df = df.iloc[1:]

        warnings.filterwarnings('ignore')
        for i in df.columns:
            if str(i).split('-')[0] in ('MCS','MIT') or str(i).split(':')[0]=='Unnamed':
                #print(f'{i} Column Deleted')
                df.drop(columns=i,axis=1,inplace=True)
            else:
                pass

        newdata = df.columns
        df.loc[-1] = newdata
        df.index = df.index + 1
        df.sort_index(inplace=True)

        data = df.values.tolist()

        tempday=''
        temptime=''
        daysOfWeek = ('Monday','Tuesday','Wednesday','Thursday','Friday')
        section = ('BCS','BIT','BAI','BSE','BSSE')
        time = ('8:30 - 10:00','10:00 - 11:30','11:30 - 1:00','1:30 - 3:00','3:00 - 4:30','4:30 - 6:00')
        lstdays = []
        lsttime=[]
        lstcourse=[]
        for i in range(len(data)):
            for j in range(len(data[i])):
                if data[i][j] in daysOfWeek:
                    tempday=data[i][j]
                if data[i][j] in time:
                    temptime=data[i][j]
                if data[i][j]==" ":
                    pass
                elif str(data[i][j]).split('-')[0] not in section and  data[i][j] not in daysOfWeek and data[i][j] not in time:
                    if str(data[i][j])!='nan':
                        lstdays.append(tempday)
                        lstcourse.append(data[i][j])
                        lsttime.append(temptime)

        lstcoursecode=[]
        lstcoursename=[]
        lstdisp=[]
        lstteacher=[]
        lstvenue=[]

        for i,j,k in zip(lstdays,lsttime,lstcourse):
            lstcoursecode.append(k.split(' ')[0].split('_')[0])
            lstcoursename.append(k.split(' ')[0].split('_')[1])
            lstdisp.append(k.split(' ')[2].split(')')[0].split('(')[1])
            lstteacher.append(k.split('(')[2].split(')')[0])
            lstvenue.append(k.split('(')[2].split('_')[1])

        data = {
            'course_code':lstcoursecode,
            'course_name':lstcoursename,
            'discipline':lstdisp,
            'teacher_name':lstteacher,
            'venue':lstvenue,
            'day':lstdays,
            'time':lsttime
        }

        for cc,cn,disp,tn,v,d,t in zip(lstcoursecode,lstcoursename,lstdisp,lstteacher,lstvenue,lstdays,lsttime):
            timetable_object = apiTimeTable.TimeTableApi(timetable=mTimeTable)
            temp = mTimeTable.TimeTable
            temp.id=0
            temp.discipline=str(disp)
            temp.starttime=str(t).split('-')[0]
            temp.endtime=str(t).split('-')[1]
            temp.day=str(d)
            temp.courseCode=str(cc)
            temp.courseName=str(cn)
            temp.teacherName=str(tn)
            temp.venue=str(v)
            timetable_object.add_timetable(timetable= temp)

        venue_object = apiVenue.VenueApi(venue=mVenue)
        sql = MySQL()
        sql.__enter__()
        cursor = sql.conn.cursor()
        cursor.execute(f'''
               SELECT DISTINCT TIMETABLE.Venue FROM TIMETABLE
                ''')
        for venueData in cursor.fetchall():
            tempVenue = mVenue.Venue
            tempVenue.name = venueData.Venue
            tempVenue.id=0
            venue_object.add_venue(venue=tempVenue)

        offeredCourseObject = apiOfferedCourses.OfferedCoursesApi(offeredCourses=mOfferedCourses)
        cursor.execute(f'''
               SELECT DISTINCT TIMETABLE.CourseName,TIMETABLE.CourseCode,TIMETABLE.SessionId FROM TIMETABLE WHERE
                TIMETABLE.SessionId=  (SELECT TOP 1 SESSION.ID
                FROM SESSION ORDER BY ID DESC)
                ''')
        for courseData in cursor.fetchall():
            tempCourse = mOfferedCourses.OfferedCourse
            tempCourse.courseName = courseData.CourseName
            tempCourse.courseCode=courseData.CourseCode
            tempCourse.id=0
            tempCourse.sessionId = courseData.SessionId
            offeredCourseObject.add_offeredCourses(offeredCourses=tempCourse)

        sectionOfferObject = apiSectionOffer.SectionOfferApi(sectionOffer=mSectionOffer)
        cursor.execute(
            f'''
            SELECT DISTINCT oc.ID, t.Discipline FROM OFFERED_COURSES oc INNER JOIN TIMETABLE t ON
            t.SessionId=oc.SessionId AND oc.CourseCode = t.CourseCode AND oc.CourseName=t.CourseName AND t.SessionId=
            (SELECT TOP 1 SESSION.ID FROM SESSION ORDER BY ID DESC)

            '''
            )
        for sectionOfferData in cursor.fetchall():
            tempSectionOffer = mSectionOffer.SectionOffer
            tempSectionOffer.courseOfferId=sectionOfferData.ID
            tempSectionOffer.discipline=sectionOfferData.Discipline
            sectionOfferObject.add_SectionOffer(sectionOffer=tempSectionOffer)


import pygame
from copy import deepcopy
from random import choice, randrange
import pygame.mixer

# количество клеток по горизонтали
W = 11
# количество клеток по вертикали
H = 22
# размер клетки игрового поля
TILE = 30
# размер игрового поля
GAME_RES = W * TILE, H * TILE
# размер всего экрана
RES = 800, 700
# частота смены кадров
FPS = 60
# ожидание нажатия  запуска игры
start_game = False
vol = 1
grid = []
pygame.init()

sc = pygame.display.set_mode(RES)
game_sc = pygame.Surface(GAME_RES)
clock = pygame.time.Clock()

# прорисовка игрового поля
for x in range(W):
    for y in range(H):
        grid.append(pygame.Rect(x * TILE, y * TILE, TILE, TILE))

# фигуры
figures_pos = [[(-1, 0), (-2, 0), (0, 0), (1, 0)],
               [(0, -1), (-1, -1), (-1, 0), (0, 0)],
               [(-1, 0), (-1, 1), (0, 0), (0, -1)],
               [(0, 0), (-1, 0), (0, 1), (-1, -1)],
               [(0, 0), (0, -1), (0, 1), (-1, -1)],
               [(0, 0), (0, -1), (0, 1), (1, -1)],
               [(0, 0), (0, -1), (0, 1), (-1, 0)]]

# появление и прорисовка фиуры
figures = [[pygame.Rect(x + W // 2, y + 1, 1, 1) for x, y in fig_pos] for fig_pos in figures_pos]
figure_rect = pygame.Rect(0, 0, TILE - 2, TILE - 2)
#заполняем поле нулевыми значенями
field = [[0 for i in range(W)] for j in range(H)]

anim_count = 0
anim_speed = 60 #скорость анимации
anim_limit = 2000 # максимальная скорость анимации

newrecord = False # новый рекорд в игре

start_bg = pygame.image.load('tet.jpg').convert()
bg = pygame.image.load('bf.jpg').convert()
game_bg = pygame.image.load('game.jpg').convert()

main_font = pygame.font.SysFont('comicsansms', 65)
font = pygame.font.SysFont('comicsansms', 45)

title_tetris = main_font.render('OLD TETRIS', True, pygame.Color('white'))
title_score = font.render('очки:', True, pygame.Color('green'))
title_record = font.render('record:', True, pygame.Color('gold'))

get_color = lambda: (randrange(30, 240), randrange(30, 240), randrange(30, 240))
figure = deepcopy(choice(figures))
next_figure = deepcopy(choice(figures))
color = get_color()
next_color = get_color()

score = 0
lines = 0
scores = {0: 0, 1: 100, 2: 300, 3: 700, 4: 1500}

def check_borders():
    if figure[i].x < 0 or figure[i].x > W - 1:
        return False
    elif figure[i].y > H - 1 or field[figure[i].y][figure[i].x]:
        return False
    return True

# игровая пауза
def paused(pauza):
    pygame.mixer.music.pause()
    font = pygame.font.SysFont('comicsansms', 42)
    follow = font.render('Paused', 1, GREEN, RED)
    screen.blit(follow, (300, 300))
    pygame.display.update()

    while pauza:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    pauza = False
                    pygame.mixer.music.play(-1)

        clock.tick(15)

def get_record():
    try:
        with open('record') as f:
            return f.readline()
    except FileNotFoundError:
        with open('record', 'w') as f:
            f.write('0')

def set_record(record, score):
    rec = max(int(record), score)
    with open('record', 'w') as f:
        f.write(str(rec))

while True:
    while not start_game:
        pygame.mixer.music.stop()
        screen = pygame.display.set_mode(RES)
        screen.blit(start_bg, (0, 0))
        pygame.display.set_caption("Старый тетрис...")
        img = pygame.image.load('Tetris.jpg')
        pygame.display.set_icon(img)
        font = pygame.font.SysFont('comicsansms', 45)
        RED = (255, 0, 0)
        GREEN = (0, 255, 0)
        follow = font.render('ИГРА ТЕТРИС', 1, RED, GREEN)
        screen.blit(follow, (280, 50))
        font = pygame.font.SysFont('comicsansms', 42)
        follow = font.render('START', 1, GREEN, RED)
        screen.blit(follow, (360, 550))

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                exit()

            key = pygame.key.get_pressed()
            if key[pygame.K_KP_ENTER] and start_game == False:
                s = pygame.mixer.Sound('1.mp3')
                s.play(0)
                follow = font.render('START', 1, RED, GREEN)
                font = pygame.font.SysFont('comicsansms', 45)
                screen.blit(follow, (360, 550))
                start_game = True

                for i in range(10):
                    for j in range(9):
                        pygame.draw.rect(screen, get_color(), (i * 80, j * 80, 76, 76))
                        pygame.display.flip()
                        clock.tick(150)

            elif event.type == pygame.MOUSEBUTTONDOWN and start_game == False:
                if event.pos[0] > 370 and event.pos[0] < 520 and event.pos[1] < 610 and event.pos[1] > 550:
                    s = pygame.mixer.Sound('1.mp3')
                    s.play(0)
                    follow = font.render('START', 1, RED, GREEN)
                    font = pygame.font.SysFont('comicsansms', 45)
                    screen.blit(follow, (360, 550))
                    start_game = True
                else:
                    start_game = False
        pygame.display.flip()
        clock.tick(FPS)

    # заставка переход к игре
    for i in range(10):
        for j in range(9):
            pygame.draw.rect(screen, get_color(), (i * 80, j * 80, 76, 76))
            pygame.display.flip()
            clock.tick(150)
    pygame.display.flip()
    clock.tick(2)

    # проигрывание фоновой мелодии
    pygame.mixer.music.load('3.mp3')
    pygame.mixer.music.play(-1)
    pygame.mixer.music.set_volume(vol)

    # начало игры
    while start_game:
        record = get_record()
        dx, rotate = 0, False
        sc.blit(bg, (0, 0))
        sc.blit(game_sc, (20, 20))
        game_sc.blit(game_bg, (0, 0))

        # delay for full lines
        for i in range(lines):
            pygame.time.wait(200)

        # control
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    dx = -1
                elif event.key == pygame.K_RIGHT:
                    dx = 1
                elif event.key == pygame.K_DOWN:
                    anim_limit = 100
                    s = pygame.mixer.Sound('4.mp3')
                    s.play(0)
                elif event.key == pygame.K_UP:
                    rotate = True
                elif event.key == pygame.K_s:
                    vol = abs(vol - 1)
                    pygame.mixer.music.set_volume(vol)
                elif event.key == pygame.K_SPACE:
                    pause = True
                    paused(pause)

        # смещение фигуры по Х
        figure_old = deepcopy(figure)
        for i in range(4):
            figure[i].x += dx
            if not check_borders():
                figure = deepcopy(figure_old)
                break

        # смещение фигуры по у
        # увеличение скорости
        anim_count += anim_speed
        if anim_count > anim_limit:
            anim_count = 0
            figure_old = deepcopy(figure)
            for i in range(4):
                figure[i].y += 1
                if not check_borders():
                    for i in range(4):
                        field[figure_old[i].y][figure_old[i].x] = color
                    figure = next_figure
                    color = next_color
                    next_figure = deepcopy(choice(figures))
                    next_color = get_color()
                    anim_limit = 2000
                    break

        # вращение фигуры
        center = figure[0]
        figure_old = deepcopy(figure)
        if rotate:
            for i in range(4):
                x = figure[i].y - center.y
                y = figure[i].x - center.x
                figure[i].x = center.x - x
                figure[i].y = center.y + y
                if not check_borders():
                    figure = deepcopy(figure_old)
                    break

        # проверка линий
        line = H - 1
        lines = 0
        for row in range(H - 1, -1, -1):
            count = 0
            for i in range(W):
                if field[row][i]:
                    count += 1
                field[line][i] = field[row][i]
            if count < W:
                line -= 1
            else:
                anim_speed += 3
                lines += 1

        # подсчет очков
        score += scores[lines]

        if score > int(record) and not newrecord:
            s = pygame.mixer.Sound('6.mp3')
            s.play(0)
            newrecord = True

        # прорисовка решетки игры
        for i_rect in grid:
            pygame.draw.rect(game_sc, (40, 40, 40), i_rect, 1)

        # прорисовка элементов (кубиков) фигуры
        for i in range(4):
            figure_rect.x = figure[i].x * TILE
            figure_rect.y = figure[i].y * TILE
            pygame.draw.rect(game_sc, color, figure_rect)

        #
        for y, raw in enumerate(field):
            for x, col in enumerate(raw):
                if col:
                    figure_rect.x, figure_rect.y = x * TILE, y * TILE
                    pygame.draw.rect(game_sc, col, figure_rect)

        # прорисовка следующей фигуры
        for i in range(4):
            figure_rect.x = next_figure[i].x * TILE + 340
            figure_rect.y = next_figure[i].y * TILE + 515
            pygame.draw.rect(sc, next_color, figure_rect)

        # прорисовка надписей
        sc.blit(title_tetris, (370, 50))
        sc.blit(title_score, (440, 175))
        sc.blit(font.render(str(score), True, pygame.Color('green')), (560, 180))
        sc.blit(title_record, (430, 230))
        sc.blit(font.render(record, True, pygame.Color('gold')), (590, 232))

        # завершение игры, обнуление значений игрового поля, заполнение поля цветными квадратиками
        for i in range(W):
            if field[0][i]:
                set_record(record, score)
                field = [[0 for i in range(W)] for i in range(H)]
                anim_count = 0
                anim_speed = 60
                anim_limit = 2000
                score = 0
                start_game = False

                # заставка заверщения игры
                for i_rect in grid:
                    pygame.draw.rect(game_sc, get_color(), i_rect)
                    sc.blit(game_sc, (20, 20))
                    pygame.display.flip()
                    clock.tick(200)

        pygame.display.flip()
        clock.tick(FPS)


import csv
import requests
import json

#ISBN scrapper function
def main():
    isbn_list = []
    with open('isbns/unique_notNYT_isbn.csv', mode='r') as fiction_input:
        with open('unique_notNYT_isbn_json.json', mode='w') as output:
            reader = csv.reader(fiction_input, delimiter=' ')
            line_count = 0
            for row in reader:
                row_array = row[0].split('\t')
                if line_count == 0:
                    line_count += 1
                else:
                    r = requests.get('https://openlibrary.org/api/books?bibkeys=ISBN:'+ row_array[1] +'&format=json&jscmd=data')
                    json.dump(r.json(), output)
                    output.write("\n")
                    line_count += 1

if __name__ == '__main__':
    main()