"""Finetune 3D CNN."""
import os
import argparse
import itertools
import time
import math
import random
import builtins
import warnings
import string
import numpy as np
import pandas as pd
from PIL import ImageFilter
import torch
import torch.nn as nn
import torch.nn.parallel
import torch.backends.cudnn as cudnn
import torch.distributed as dist
import torch.utils.data
import torch.multiprocessing as mp
import torch.utils.data.distributed
from torch.utils.data import DataLoader, random_split
from torchvision import transforms
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter

from datasets.ucf101 import UCF101Dataset_Classify
# from datasets.hmdb51 import HMDB51Dataset
from models.c3d import C3D
from models.r3d import R3DNet
from models.r21d import R2Plus1DNet

class GaussianBlur(object):

    def __init__(self, sigma=[.1, 2.]):
        self.sigma = sigma

    def __call__(self, x):
        sigma = random.uniform(self.sigma[0], self.sigma[1])
        x = x.filter(ImageFilter.GaussianBlur(radius=sigma))
        return x

def adjust_learning_rate(optimizer, epoch, args):
    """Decay the learning rate based on schedule"""
    lr = args.lr
    ft_lr =args.ft_lr
    if args.cos:  # cosine lr schedule
        lr *= 0.5 * (1. + math.cos(math.pi * epoch / args.epochs))
        ft_lr *= 0.5 * (1. + math.cos(math.pi * epoch / args.epochs))
    else:  # stepwise lr schedule
        for milestone in args.schedule:
            lr *= 0.1 if epoch >= milestone else 1.
            ft_lr *= 0.1 if epoch >= milestone else 1.
    for param_group in optimizer.param_groups:
        optimizer.param_groups[0]['lr'] = lr
        # optimizer.param_groups[1]['lr'] = ft_lr

def load_pretrained_weights(ckpt_path):
    """load pretrained weights and adjust params name."""
    adjusted_weights = {}
    pretrained_weights = torch.load(ckpt_path, map_location=torch.device('cpu'))
    for name, params in pretrained_weights.items():
        if 'module' in name and 'base_network' in name and 'encoder_q' in name:
            print(name)
            name = name.split('base_network.')[1]
            adjusted_weights[name] = params
            print('Pretrained weight name: [{}]'.format(name))
#         if 'module' and 'base_network' in name:
#             name = name.split('base_network.')[1]
# #             name = name.replace('base_network.','')
#             adjusted_weights[name] = params
#             print('Pretrained weight name: [{}]'.format(name))
        # if 'base_network' in name:
        #     name = name[name.find('.')+1:]
        #     adjusted_weights[name] = params
        #     print('Pretrained weight name: [{}]'.format(name))
    return adjusted_weights

def train(args, model, criterion, optimizer, device, train_dataloader, writer, epoch,lr,ft_lr):
    torch.set_grad_enabled(True)
    model.train()

    running_loss = 0.0
    correct = 0
    for i, data in enumerate(train_dataloader, 1):
        # get inputs
        clips, idxs = data
        inputs = clips.to(device)
        targets = idxs.to(device)-1
        # forward and backward
        outputs = model(inputs)  # return logits here
        loss = criterion(outputs, targets)
        # zero the parameter gradients
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        # compute loss and acc
        running_loss += loss
        pts = torch.argmax(outputs, dim=1)
        correct += torch.sum(targets == pts)
        correct = correct.type(torch.float)

        torch.distributed.barrier()
        # print statistics and write summary every N batch
        if i % args.pf == 0:
            avg_loss = running_loss / args.pf
            avg_acc = correct / (args.pf * args.bs)
            reduced_avg_loss = reduce_mean(avg_loss, args.ngpus_per_node)
            reduced_avg_acc = reduce_mean(avg_acc, args.ngpus_per_node)
            print('[TRAIN] epoch-{}, batch-{}, loss: {:.3f}, acc: {:.3f},lr:{:.6f},ft_lr:{:.6f}'.format(epoch, i, reduced_avg_loss, reduced_avg_acc,lr,ft_lr))
            step = (epoch-1)*len(train_dataloader) + i
            if args.gpu == 0:
                writer.add_scalar('train/CrossEntropyLoss', reduced_avg_loss.item(), step)
                writer.add_scalar('train/Accuracy', reduced_avg_acc.item(), step)
            running_loss = 0.0
            correct = 0
    # summary params and grads per eopch
    for name, param in model.named_parameters():
        if args.gpu == 0:
            writer.add_histogram('params/{}'.format(name), param, epoch)
            writer.add_histogram('grads/{}'.format(name), param.grad, epoch)

def validate(args, model, criterion, device, val_dataloader, writer, epoch):
    torch.set_grad_enabled(False)
    model.eval()

    total_loss = 0.0
    correct = 0
    for i, data in enumerate(val_dataloader):
        # get inputs
        clips, idxs = data
        inputs = clips.to(device)
        targets = idxs.to(device)-1
        # forward
        # forward
        outputs = model(inputs)  # return logits here
        loss = criterion(outputs, targets)
        # compute loss and acc
        total_loss += loss
        pts = torch.argmax(outputs, dim=1)
        correct += torch.sum(targets == pts)
        correct = correct.type(torch.float)
        # print('correct: {}, {}, {}'.format(correct, targets, pts))
    avg_loss = total_loss / (i + 1)
    avg_acc = correct / (i + 1) / args.bs
    torch.distributed.barrier()
    reduced_avg_loss = reduce_mean(avg_loss, args.ngpus_per_node)
    reduced_avg_acc = reduce_mean(avg_acc, args.ngpus_per_node)
    if args.gpu == 0:
        writer.add_scalar('val/CrossEntropyLoss', reduced_avg_loss.item(), epoch)
        writer.add_scalar('val/Accuracy', reduced_avg_acc.item(), epoch)
    print('[VAL] loss: {:.3f}, acc: {:.3f}'.format(reduced_avg_loss, reduced_avg_acc))
    return reduced_avg_loss

def test(args, model, criterion, device, test_dataloader):
    torch.set_grad_enabled(False)
    model.eval()

    total_loss = 0.0
    correct = 0
    for i, data in enumerate(test_dataloader, 1):
        sampled_clips, idxs = data
        targets = idxs.to(device)-1
        # forward
        # forward
        #outputs = model(inputs)
        outputs = []
        for clips in sampled_clips:
            inputs = clips.to(device)
            # forward
            o = model(inputs)
            # print(o.shape)
            o = torch.mean(o, dim=0)
            # print(o.shape)
            # exit()
            outputs.append(o)
        outputs = torch.stack(outputs)
        loss = criterion(outputs, targets)
        # compute loss and acc
        total_loss += loss
        pts = torch.argmax(outputs, dim=1)
        # print(pts, targets)
        correct += torch.sum(targets == pts)
        #correct = correct.type(torch.float)
        # print('correct: {}, {}, {}'.format(correct, targets, pts))

    torch.distributed.barrier()
    reduced_sum_loss = reduce_sum(total_loss)
    reduced_sum_acc = reduce_sum(correct)

    print(len(test_dataloader), len(test_dataloader.dataset))
    reduced_avg_loss = reduced_sum_loss / len(test_dataloader)
    reduced_sum_acc = reduced_sum_acc.type(torch.float)
    reduced_avg_acc = reduced_sum_acc / len(test_dataloader.dataset)
    print('[TEST] loss: {:.3f}, acc: {:.3f}'.format(reduced_avg_loss, reduced_avg_acc))
    return reduced_avg_loss, reduced_avg_acc

def reduce_mean(tensor, nprocs):
    rt = tensor.clone()
    dist.all_reduce(rt, op=dist.ReduceOp.SUM)
    rt /= nprocs
    return rt

def reduce_sum(tensor):
    rt = tensor.clone()
    dist.all_reduce(rt, op=dist.ReduceOp.SUM)
    return rt

def parse_args():
    parser = argparse.ArgumentParser(description='Video Clip Order Prediction')
    parser.add_argument('--mode', type=str, default='train', help='train/test')
    parser.add_argument('--model', type=str, default='c3d', help='c3d/r3d/r21d')
    parser.add_argument('--dataset', type=str, default='ucf101', help='ucf101/hmdb51')
    parser.add_argument('--cl', type=int, default=16, help='clip length')
    parser.add_argument('--split', type=str, default='1', help='dataset split')
    parser.add_argument('--gpu', type=int, default=None, help='GPU id')
    parser.add_argument('--lr', type=float, default=1e-3, help='learning rate')
    parser.add_argument('--ft_lr', type=float, default=1e-3, help='finetune learning rate')
    parser.add_argument('--momentum', type=float, default=9e-1, help='momentum')
    parser.add_argument('--wd', type=float, default=5e-4, help='weight decay')
    parser.add_argument('--log', type=str, help='log directory')
    parser.add_argument('--ckpt', type=str, help='checkpoint path')
    parser.add_argument('--desp', type=str, help='additional description')
    parser.add_argument('--schedule', default=[120,150], nargs='*', type=int,
                        help='learning rate schedule (when to drop lr by 10x)')
    parser.add_argument('--epochs', type=int, default=180, help='number of total epochs to run')
    parser.add_argument('--start-epoch', type=int, default=1, help='manual epoch number (useful on restarts)')
    parser.add_argument('--bs', type=int, default=8, help='mini-batch size')
    parser.add_argument('--workers', type=int, default=4, help='number of data loading workers')
    parser.add_argument('--pf', type=int, default=100, help='print frequency every batch')
    parser.add_argument('--seed', type=int, default=None, help='seed for initializing training.')
    parser.add_argument('--world-size', default=-1, type=int, help='number of nodes for distributed training')
    parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str,
                        help='url used to set up distributed training')
    parser.add_argument('--multiprocessing-distributed', action='store_true',
                        help='Use multi-processing distributed training to launch '
                        'N processes per node, which has N GPUs. This is the '
                        'fastest way to use PyTorch for either single node or '
                        'multi node data parallel training')
    parser.add_argument('--rank', default=-1, type=int,
                        help='node rank for distributed training')
    parser.add_argument('--dist-backend', default='nccl', type=str,
                        help='distributed backend')
    parser.add_argument('--cos', action='store_true',
                        help='use cosine lr schedule')
    args = parser.parse_args()
    return args

def main():
    args = parse_args()

    if args.seed is not None:
        random.seed(args.seed)
        np.random.seed(args.seed)
        torch.manual_seed(args.seed)
        torch.cuda.manual_seed_all(args.seed)
        cudnn.deterministic = True
        warnings.warn('You have chosen to seed training. '
                      'This will turn on the CUDNN deterministic setting, '
                      'which can slow down your training considerably! '
                      'You may see unexpected behavior when restarting '
                      'from checkpoints.')

    if args.gpu is not None:
        warnings.warn('You have chosen a specific GPU. This will completely '
                      'disable data parallelism.')

    if args.dist_url == "env://" and args.world_size == -1:
        args.world_size = int(os.environ["WORLD_SIZE"])

    args.distributed = args.world_size > 1 or args.multiprocessing_distributed

    ngpus_per_node = torch.cuda.device_count()
    args.ngpus_per_node = ngpus_per_node
    if args.multiprocessing_distributed:
        # Since we have ngpus_per_node processes per node, the total world_size
        # needs to be adjusted accordingly
        args.world_size = ngpus_per_node * args.world_size
        # Use torch.multiprocessing.spawn to launch distributed processes: the
        # main_worker process function
        mp.spawn(main_worker, nprocs=ngpus_per_node, args=(ngpus_per_node, args))
    else:
        # Simply call main_worker function
        main_worker(args.gpu, ngpus_per_node, args)

def main_worker(gpu, ngpus_per_node, args):
    args.gpu = gpu
    cudnn.benchmark = True

    if args.gpu is not None:
        print("Use GPU: {} for training".format(args.gpu))

    # suppress printing if not master
    if args.multiprocessing_distributed and args.gpu != 0:
        def print_pass(*args):
            pass
        builtins.print = print_pass

    print(vars(args))

    if args.distributed:
        if args.dist_url == "env://" and args.rank == -1:
            args.rank = int(os.environ["RANK"])
        if args.multiprocessing_distributed:
            # For multiprocessing distributed training, rank needs to be the
            # global rank among all the processes
            args.rank = args.rank * ngpus_per_node + gpu
        dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url,
                                world_size=args.world_size, rank=args.rank)

    if args.dataset == 'ucf101':
        class_num = 101
    elif args.dataset == 'hmdb51':
        class_num = 51

    if args.model == 'c3d':
        model = C3D(with_classifier=True, num_classes=class_num)
    elif args.model == 'r3d':
        model = R3DNet(layer_sizes=(3,4,6,3), with_classifier=True, num_classes=class_num)
    elif args.model == 'r21d':
        model = R2Plus1DNet(layer_sizes=(1,1,1,1), with_classifier=True, num_classes=class_num)

    if args.mode == 'train':  # ########## Train #############
        if args.ckpt:  # resume training
            pretrained_weights = load_pretrained_weights(args.ckpt)
            print(model)
            load_result = model.load_state_dict(pretrained_weights, strict=False)
            print(load_result)

    if args.distributed:
        torch.cuda.set_device(args.gpu)
        model.cuda(args.gpu)
        # When using a single GPU per process and per
        # DistributedDataParallel, we need to divide the batch size
        # ourselves based on the total number of GPUs we have
        args.bs = int(args.bs / ngpus_per_node)
        args.workers = int((args.workers + ngpus_per_node - 1) / ngpus_per_node)
        model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu])
        # vcopn.cuda()
        # # DistributedDataParallel will divide and allocate batch_size to all
        # # available GPUs if device_ids are not set
        # vcopn = torch.nn.parallel.DistributedDataParallel(vcopn)

        # log_dir = os.path.dirname(args.log)
        # print(log_dir)

        # else:
    writer = None
    if args.mode == 'train':
        if args.desp:
            exp_name = '{}_cl{}_{}_{}'.format(args.model, args.cl, args.desp, time.strftime('%m%d%H%M'))
        else:
            exp_name = '{}_cl{}_{}'.format(args.model, args.cl, time.strftime('%m%d%H%M'))
        log_dir = os.path.join(args.log, exp_name)
        print(log_dir)
        if args.gpu == 0:
            writer = SummaryWriter(log_dir)

        train_transforms = transforms.Compose([
            transforms.Resize((128, 171)),  # smaller edge to 128
            transforms.RandomCrop(112),
            transforms.RandomApply([
                transforms.ColorJitter(0.4, 0.4, 0.4, 0.1)  # not strengthened
            ], p=0.5),
            transforms.RandomGrayscale(p=0.2),
            # transforms.RandomApply([GaussianBlur([.1, 2.])], p=0.5),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor()
        ])
        if args.dataset == 'ucf101':
            train_dataset = UCF101Dataset_Classify('data/ucf101', args.cl, args.split, True, train_transforms)
            val_size = 800
        elif args.dataset == 'hmdb51':
            train_dataset = UCF101Dataset_Classify('data/hmdb51', args.cl, args.split, True, train_transforms)
            val_size = 400
             # split val for 800 videos
        train_dataset, val_dataset = random_split(train_dataset, (len(train_dataset) - val_size, val_size))
        print('TRAIN video number: {}, VAL video number: {}.'.format(len(train_dataset), len(val_dataset)))

        if args.distributed:
            train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset)
            val_sampler = torch.utils.data.distributed.DistributedSampler(val_dataset)

        # train_dataloader = DataLoader(train_dataset, batch_size=args.bs, shuffle=True,
        #                             num_workers=args.workers, pin_memory=True)
        train_dataloader = torch.utils.data.DataLoader(
            train_dataset, batch_size=args.bs, shuffle=(train_sampler is None),
            num_workers=args.workers, pin_memory=True, sampler=train_sampler)
        # val_dataloader = DataLoader(val_dataset, batch_size=args.bs, shuffle=False,
        #                             num_workers=args.workers, pin_memory=True)
        val_dataloader = torch.utils.data.DataLoader(
            val_dataset, batch_size=args.bs, shuffle=(val_sampler is None),
            num_workers=args.workers, pin_memory=True, sampler=val_sampler)

        # save graph and clips_order samples
        for data in train_dataloader:
            clips, idxs = data
            if args.gpu == 0:
                writer.add_video('train/clips', clips, fps=8)
                writer.add_text('train/idxs', str(idxs.tolist()))
            #  clips = clips
            clips = clips.to(args.gpu)
            if args.gpu == 0:
                writer.add_graph(model.module, clips)
            break
        # save init params at step 0
        for name, param in model.named_parameters():
            if args.gpu == 0:
                writer.add_histogram('params/{}'.format(name), param, 0)

        #  ## loss funciton, optimizer and scheduler ###
        criterion = nn.CrossEntropyLoss().cuda(args.gpu)
        optimizer = optim.SGD([
            {'params': [param for name, param in model.named_parameters() if
                        'linear' not in name and 'conv5' not in name and 'conv4' not in name]},
            {'params': [param for name, param in model.named_parameters() if
                        'linear' in name or 'conv5' in name or 'conv4' in name], 'lr': args.ft_lr}],
            lr=args.lr, momentum=args.momentum, weight_decay=args.wd)
        # optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.wd)
      #  scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', min_lr=1e-5, patience=50, factor=0.1)

        prev_best_val_loss = float('inf')
        prev_best_model_path = None
        for epoch in range(args.start_epoch, args.start_epoch+args.epochs):
            if args.distributed:
                train_sampler.set_epoch(epoch)
            time_start = time.time()
            adjust_learning_rate(optimizer, epoch, args)
            lr = optimizer.param_groups[0]['lr']
            # ft_lr = optimizer.param_groups[1]['lr']
            ft_lr = lr
            train(args, model, criterion, optimizer, args.gpu, train_dataloader, writer, epoch, lr, ft_lr)
            print('Epoch time: {:.2f} s.'.format(time.time() - time_start))
            val_loss = validate(args, model, criterion, args.gpu, val_dataloader, writer, epoch)
            # scheduler.step(val_loss)
            if args.gpu == 0:
                writer.add_scalar('train/lr', optimizer.param_groups[0]['lr'], epoch)
            # writer.add_scalar('train/ft_lr', optimizer.param_groups[1]['lr'], epoch)

            if not args.multiprocessing_distributed or (args.multiprocessing_distributed and args.rank % ngpus_per_node == 0):
                # save model every 20 epoches
                if epoch % 10 == 0 or epoch == args.epochs:
                    torch.save(model.state_dict(), os.path.join(log_dir, 'model_{}.pt'.format(epoch)))
                # save model for the best val
                if val_loss < prev_best_val_loss:
                    model_path = os.path.join(log_dir, 'best_model_{}.pt'.format(epoch))
                    torch.save(model.state_dict(), model_path)
                    prev_best_val_loss = val_loss
                    if prev_best_model_path:
                        os.remove(prev_best_model_path)
                    prev_best_model_path = model_path

            if epoch == args.start_epoch+args.epochs -1 or epoch % 10 == 0:
               # model.load_state_dict(torch.load(args.ckpt))
                test_transforms = transforms.Compose([
                    transforms.Resize((128, 171)),
                    transforms.CenterCrop(112),
                    transforms.ToTensor()
                ])
                if args.dataset == 'ucf101':
                    test_dataset = UCF101Dataset_Classify('data/ucf101', args.cl, args.split, False, test_transforms)
                elif args.dataset == 'hmdb51':
                    test_dataset = UCF101Dataset_Classify('data/hmdb51', args.cl, args.split, False, test_transforms)

                test_sampler = torch.utils.data.distributed.DistributedSampler(test_dataset)
                # test_dataloader = DataLoader(test_dataset, batch_size=args.bs, shuffle=False,
                #                         num_workers=args.workers, pin_memory=True)
                test_dataloader = torch.utils.data.DataLoader(
                    test_dataset, batch_size=args.bs, shuffle=False,
                    num_workers=args.workers, pin_memory=True, sampler=test_sampler)

                print('TEST video number: {}.'.format(len(test_dataset)))
                criterion = nn.CrossEntropyLoss().cuda(args.gpu)
                _, reduced_avg_acc = test(args, model, criterion, args.gpu, test_dataloader)
                if args.gpu == 0:
                    writer.add_scalar('test/Accuracy', reduced_avg_acc.item(), epoch)

    #  ########## Test #############
    elif args.mode == 'test':
        model.load_state_dict(torch.load(args.ckpt), strict=True)
        test_transforms = transforms.Compose([
            transforms.Resize((128, 171)),
            transforms.CenterCrop(112),
            transforms.ToTensor()
        ])
        if args.dataset == 'ucf101':
            test_dataset = UCF101Dataset_Classify('data/ucf101', args.cl, args.split, False, test_transforms)
        elif args.dataset == 'hmdb51':
            test_dataset = UCF101Dataset_Classify('data/hmdb51', args.cl, args.split, False, test_transforms)

        test_sampler = torch.utils.data.distributed.DistributedSampler(test_dataset)
        # test_dataloader = DataLoader(test_dataset, batch_size=args.bs, shuffle=False,
        #                         num_workers=args.workers, pin_memory=True)
        test_dataloader = torch.utils.data.DataLoader(
            test_dataset, batch_size=args.bs, shuffle=False,
            num_workers=args.workers, pin_memory=True, sampler=test_sampler)

        print('TEST video number: {}.'.format(len(test_dataset)))
        criterion = nn.CrossEntropyLoss().cuda(args.gpu)
        test(args, model, criterion, args.gpu, test_dataloader)

if __name__ == '__main__':
    main()


from PythonPrograms.Oblig2_A_star.Vertex import Vertex
from PythonPrograms.Oblig2_A_star.Edge import Edge
from PythonPrograms.Oblig2_A_star.Graph import Graph

import pygame as pg

'''
The AStar class inherits the Graph class
'''

class AStar(Graph):
    """
    #
    #  delay: seconds between each iteration when visualizing is turned on
    # visual: Turns pygame visualization on/off
    #
    """

    def __init__(self, delay=0.001, visual=True):
        super().__init__()
        self.pygame = visual
        self.background = None
        self.LIGHTGREY = (180, 180, 180)
        self.DARKGREEN = (0, 255, 0)
        self.PINK = (255, 200, 200)
        self.GREEN = (200, 255, 200)
        self.WHITE = (245, 245, 245)
        self.BLACK = (0, 0, 0)
        self.RED = (255, 0, 0)
        self.BLUE = (0, 0, 255)
        self.delay = delay
        # walls define obstacles in grid, e.g. walls, boxes etc, by defining each position in grid that is part of an
        # obstacle
        self.walls = []

    '''
    #
    # Defines obstacles provided in removevertecies from the graph by removing all edges
    # to vertecies that is defined as an obstacle
    #
    '''

    def removeVertecies(self, removevertecies=[]):
        err_v = []
        err_e = []
        # Removing the vertecies
        for r in removevertecies:
            self.walls.append(self.vertecies[r])
            self.vertecies.pop(r)
        # Checking the edges ...
        for v in self.vertecies:
            vtex = self.vertecies[v]
            for edge in vtex.adjecent:
                vertex = edge.vertex
                if vertex.name not in self.vertecies:
                    err_v.append(vtex)
                    err_e.append(edge)
        for i in range(0, len(err_v)):
            err_v[i].adjecent.remove(err_e[i])
        return removevertecies

    '''
    #
    # Read in the list of obstacles (defined by tag "remove"), the startnode from which traversal is defined,
    # and the targetnode
    #
    '''

    def readLimitations(self, filename):
        import pandas as pd
        from collections import namedtuple

        columnnames = ['item', 'valuelist']
        df = pd.read_csv(filename, error_bad_lines=False,
                         encoding='latin-1', warn_bad_lines=False,
                         names=columnnames, header=None, sep=':')
        for i in range(0, 3):
            if df.iat[i, 0] == 'startvertex':
                startVertex = df.iat[i, 1]
            elif df.iat[i, 0] == 'targetvertex':
                targetVertex = df.iat[i, 1]
            elif df.iat[i, 0] == 'remove':
                removed = self.removeVertecies(df.iat[i, 1].split(';'))
        return startVertex, targetVertex, removed

    '''
    # Initialize pygame visualization, if visualization has been defined
    # Creates a dynamic visual grid according to the number of columns and rows
    # read/defined during initialization. The visual limitations are defined by 1000x1000 pixels
    '''

    def initPygame(self):
        if self.pygame:
            xmin = ymin = xmax = ymax = 0
            for v in self.vertecies:
                vertex = self.vertecies[v]
                x, y = vertex.position()
                if x < xmin:
                    xmin = x
                elif x > xmax:
                    xmax = x
                if y < ymin:
                    ymin = y
                elif y > ymax:
                    ymax = y

            pg.init()
            w, h = 1000, 1000
            self.xboxsize = int(w / ((xmax + 1) - xmin))
            self.yboxsize = int(w / ((ymax + 1) - ymin))

            w = self.xboxsize * ((int(w / self.xboxsize)))
            h = self.yboxsize * ((int(h / self.yboxsize)))
            self.background = pg.display.set_mode((w, h))
            background = self.background

            self.clock = pg.time.Clock()

            self.clock.tick()

            for c in range(0, (int(w / self.xboxsize)) + 1):
                for l in range(0, (int(h / self.yboxsize)) + 1):
                    pg.draw.rect(background, self.WHITE,
                                 (c * self.xboxsize, l * self.yboxsize, self.xboxsize, self.yboxsize))
                    pg.draw.line(background, self.BLACK, (c * self.xboxsize, l * self.yboxsize),
                                 (c * self.xboxsize + self.xboxsize, l * self.yboxsize))
                    pg.draw.line(background, self.BLACK, (c * self.xboxsize, l * self.yboxsize),
                                 (c * self.xboxsize, l * self.yboxsize + self.yboxsize))
                    pg.draw.line(background, self.BLACK, (c * self.xboxsize + self.xboxsize, l * self.yboxsize),
                                 (c * self.xboxsize + self.xboxsize, l * self.yboxsize + self.yboxsize))
                    pg.draw.line(background, self.BLACK, (c * self.xboxsize, l * self.yboxsize + self.yboxsize),
                                 (c * self.xboxsize + self.xboxsize, l * self.yboxsize + self.yboxsize))
            for wall in self.walls:
                self.pygameState(wall, self.BLACK)
            pg.display.flip()

    '''
    # Draw a box, representing the current vertex in the position defined by the name of the vertex.
    # The color-parameter defines the (R,G,B)-value of the box
    # If no visualization is used (self.pygame == False), no box is visualized
    '''

    def pygameState(self, current, color):
        import time
        if self.pygame:
            background = self.background
            x, y = current.position()
            pg.draw.rect(background, color, (x * self.xboxsize, y * self.yboxsize, self.xboxsize, self.yboxsize))
            pg.draw.line(background, self.BLACK, (x * self.xboxsize, y * self.yboxsize),
                         (x * self.xboxsize + self.xboxsize, y * self.yboxsize))
            pg.draw.line(background, self.BLACK, (x * self.xboxsize, y * self.yboxsize),
                         (x * self.xboxsize, y * self.yboxsize + self.yboxsize))
            pg.draw.line(background, self.BLACK, (x * self.xboxsize + self.xboxsize, y * self.yboxsize),
                         (x * self.xboxsize + self.xboxsize, y * self.yboxsize + self.yboxsize))
            pg.draw.line(background, self.BLACK, (x * self.xboxsize, y * self.yboxsize + self.yboxsize),
                         (x * self.xboxsize + self.xboxsize, y * self.yboxsize + self.yboxsize))
            if color not in [self.BLUE, self.RED, self.BLACK]:
                time.sleep(self.delay)
                pass
            pg.display.flip()

    '''
    #
    # Defining the heuristics used to calculate the estimated distance between the node being handled (startVertexName),
    # and the targetnode (targetVertexName)
    # Please note that the name of the vertecies are passed, not the vertex itself. The name is used for lookup
    # in the list of vertecies in the graph.
    # Further, the name of the vertex has the syntax: xNNyMM, where NN and MM are numerical and indicates column and row.
    # E.g. x4y15 means column 4 of row 15.
    # By identifying the column and row of a vertex, the estimated shorted distance between two vertecies may be
    # calculated using the Manhatten distance
    #
    '''

    def heuristics(self, startVertexName=None, targetVertexName=None):
        if not startVertexName or not targetVertexName:
            raise KeyError("VertexLookup need the names of the Vertecies addressed.")
        if startVertexName not in self.vertecies:
            raise KeyError("Node/Vertex defined as FROM-vertex is not present in graph")
        if targetVertexName not in self.vertecies:
            raise KeyError("Node/Vertex defined as TO-vertex is not present in graph")

        xstart, ystart = self.vertecies[startVertexName].position()
        xend, yend = self.vertecies[targetVertexName].position()
        #
        # Manhatten heuristics
        #
        dx = abs(xstart - xend)
        dy = abs(ystart - yend)
        D = 1
        return D * (dx + dy)

    '''
    #
    # The Dijkstra's algorithm has been adapted to support visualization as defined by the graph
    # It has further been adopted to present a targetVetrx even though Dijkstra's has no use of
    # it during execution. The tragetVertex is used only for visualization purposes.
    '''

    def Dijkstra(self, startVertexName=None, targetVertexName=None):
        self.initPygame()

        # Check to see that startvertex is in Graph
        if startVertexName not in self.vertecies:
            raise KeyError("Start node not present in graph")

        # Reset visited and previous pointer before running algorithm
        vertex = self.vertecies[startVertexName]
        vertex.distance = distance = weight = 0
        previous_node = None
        startNode = self.vertecies[startVertexName]
        toNode = self.vertecies[targetVertexName]

        #
        # Create priority queue, priority = current weight on edge ...
        # No duplicate edges in queue allowed
        #

        edge = Edge(0, vertex)
        from queue import PriorityQueue
        priqueue = PriorityQueue()

        # Defines enqueue/dequeue methods on priqueue
        def enqueue(data):
            priqueue.put(data)

        def dequeue():
            return priqueue.get()

        enqueue(edge)

        while not priqueue.empty():
            # Get the element with lowest priority (i.e. weight on edge)
            edge = dequeue()
            eyeball = edge.vertex
            self.pygameState(eyeball, self.GREEN)
            self.pygameState(startNode, self.BLUE)
            self.pygameState(toNode, self.RED)

            # If not visited previously, we need to define the distance
            if not eyeball.known:
                eyeball.distance = distance
                eyeball.previous = previous_node
            eyeball.known = True

            # If the vertex pointed to by the edge has an adjecency list, we need to iterate on it
            for adjecentedge in eyeball.adjecent:
                if not adjecentedge.vertex.known:
                    adjecentedge.vertex.distance = eyeball.distance + adjecentedge.weight
                    adjecentedge.vertex.previous = eyeball
                    adjecentedge.vertex.known = True
                    enqueue(adjecentedge)
                    self.pygameState(adjecentedge.vertex, self.PINK)
                else:
                    if adjecentedge.vertex.distance > eyeball.distance + adjecentedge.weight:
                        adjecentedge.vertex.distance = eyeball.distance + adjecentedge.weight
                        adjecentedge.vertex.previous = eyeball
                        enqueue(adjecentedge)

            self.pygameState(eyeball, self.LIGHTGREY)
        for n in self.getPath(startVertexName, targetVertexName):
            self.pygameState(n, self.DARKGREEN)
        return self.getPath(startVertexName, targetVertexName)

    '''
    ###############################################################################
    #
    # def AStarSearch(self, startVertexName = None, targetVertexName = None)
    #
    # Implement your code below.
    # Please note that no other parts of this code or provided code should be altered
    #
    ###############################################################################
    '''

    """
    #################################################
    #
    # Karaktersatt Obligatorisk Oppgave #2, A* Pathfinding
    #
    # Halil Ibrahim Keser
    #
    #
    # DOKUMENTASJON:
    # Kode dokumentasjon er skrevet i selve koden som kommentarer.
    #
    # Denne koden bruker mindre noder enn i fasiten. Og finner en kortere vei.
    #
    # Implementasjons ide og pseudo kode er hentet fra to nettsider + forelesning:
    #
    # http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html
    # https://medium.com/@nicholas.w.swift/easy-a-star-pathfinding-7e6689c7f7b2
    #
    ##################################################
    """
    def AStarSearch(self, startVertexName=None, targetVertexName=None):
        """Returns vertex objects as a path from the given start to the given end in the given graph"""

        """
        The Initialization part
        """
        self.initPygame()  # initialize the game

        # Check to see that startvertex is in Grap.....................................................
        if startVertexName not in self.vertecies:
            raise KeyError("Start node not present in graph")

        # Reset visited and previous pointer before running algorithm..................................
        # Create start and end node as on Dijkstras funskjon
        vertex = self.vertecies[startVertexName]
        vertex.distance = 0
        start_node = self.vertecies[startVertexName]
        toNode = self.vertecies[targetVertexName]

        # Initialize open list, no need for closed list..................................................
        # Open_list as PriorityQueue for faster iteration of Big data
        from queue import PriorityQueue
        edge = Edge(0, vertex)  # Initialize the Edge with 0 weight
        open_list = PriorityQueue()
        closed_list = []

        def enqueue_open_list(data):
            open_list.put(data)

        def dequeue_open_list():
            return open_list.get()

        # Add the start node
        open_list.put((edge.vertex.g, edge.vertex))

        """
        The looping part, main part
        """
        # Loop until you find the end....................................................................
        while not open_list.empty():

            # Get the current node, current node here is the eyeball..................
            # Removing current from open list
            edge = dequeue_open_list()

            # Get the current index from edge, it is the first index of edge
            # current node that getting treated now
            # it contains edges of the current node.
            eyeball = edge[1]

            # Update the colours of eyeball, startnode and endnode...................
            self.pygameState(eyeball, self.GREEN)
            self.pygameState(start_node, self.BLUE)
            self.pygameState(toNode, self.RED)

            # Found the goal!.........................................................
            # if currentNode (eyeball) is the goal,
            # continue and make the eyeball.known as True
            # The path will be created later at the button. We have a function for it.
            if eyeball == toNode:
                break
            eyeball.known = True
            closed_list.append(eyeball)

            # Generate children. The children is the adjacents of the node ...........
            # If the vertex pointed to by the edge has an adjacent list,
            # we need to iterate on it
            # Loop through children
            for adjecentedge in eyeball.adjecent:
                # Loop through those we have not been through, and not known
                if not adjecentedge.vertex.known:  # not known, not in open list

                    if adjecentedge.vertex not in closed_list:  # not in closed_list
                        # Initialize the previous children
                        adjecentedge.vertex.previous = eyeball

                        # Create the f, g, and h values
                        # A - algorithm: F(n) = G(n) + H(n)
                        # We have a function for heuristics. We will use it to update vertex.h
                        adjecentedge.vertex.g = eyeball.g + adjecentedge.weight
                        adjecentedge.vertex.h = self.heuristics(adjecentedge.vertex.name, toNode.name)
                        adjecentedge.vertex.f = adjecentedge.vertex.g + adjecentedge.vertex.h

                        # Add the edge.node to open list as tuple
                        open_list.put((adjecentedge.vertex.f, adjecentedge.vertex))
                        adjecentedge.vertex.known = True

                        # Update the colours of known children.............................
                        self.pygameState(adjecentedge.vertex, self.PINK)

                    else:
                        # Update
                        # If edge.node weight greater than current.node weight + edge.node weight
                        if adjecentedge.vertex.g > (eyeball.g + adjecentedge.weight):
                            adjecentedge.vertex.previous = eyeball
                            adjecentedge.vertex.g = eyeball.g + adjecentedge.weight
                            adjecentedge.vertex.f = adjecentedge.vertex.g + adjecentedge.vertex.h
                        # Back-propagation
                        if not adjecentedge.vertex.known:
                            if adjecentedge.vertex in closed_list:
                                closed_list.remove(adjecentedge.vertex)
                                open_list.put((adjecentedge.vertex.f, adjecentedge.vertex))

            # Update the colours of known vertexes.....................................
            self.pygameState(eyeball, self.LIGHTGREY)

        # Get the path and, update the colours for path.....................................................
        # Return reversed path.
        # We will use the getPath function from the Graph class
        for n in self.getPath(startVertexName, targetVertexName):
            self.pygameState(n, self.DARKGREEN)
        return self.getPath(startVertexName, targetVertexName)

astar = AStar(delay=0, visual=True)

# astar.readFile('minigraf.txt')
# startVertexName, targetVertexName, removed = astar.readLimitations('minigraf_xtras.txt')
# astar.readFile('astjernegraf.txt')
# startVertexName, targetVertexName, removed = astar.readLimitations('xtras.txt')
astar.readFile('biggraph.txt')
startVertexName, targetVertexName, removed = astar.readLimitations('biggraph_xtras.txt')
# astar.readFile('AStarObligGraf.txt')
# startVertexName, targetVertexName, removed = astar.readLimitations('AStarObligGraf_xtras.txt')

# astar.Dijkstra(startVertexName, targetVertexName)
astar.AStarSearch(startVertexName, targetVertexName)

if astar.pygame:
    from pygame.locals import *

    while astar.pygame:
        for events in pg.event.get():
            if events.type == QUIT:
                exit(0)
    pg.quit()
else:
    print(astar.getPathAsString(startVertexName, targetVertexName))


import numpy as np

# Coordinates of the Anchor points (Sensors)
X = np.linspace(12,200-12,np.sqrt(16))
Y = np.linspace(12,200-12,np.sqrt(16))
sensors = np.zeros( (16,2) )
i=0
for x1 in X :
	for y1 in Y :
		sensors[i] = np.array( (x1,y1) )
		i += 1

# okumura hata rssi simulation
def get_rssi( pos , sensor = sensors):
	X=np.zeros([len(pos),len(sensor)])
	i=0
	for p in pos:
		j=0
		for sen in sensor:
			rssi = 100-100*4*np.log10(np.linalg.norm(p-sen))
			rssi += np.random.normal(0,abs(0.05*rssi),1)
			X[i,j] = rssi

			j += 1

		i += 1

	return X


import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

class clusteredData():
    def __init__(self,dataSet)-> None:
        self.__dataSet = dataSet
        self.__centriods,self.__belongsTo = elbowMethod(dataSet,8)
        #self.__centriods,self.__belongsTo = kmeans(3,dataSet)
        #self.plot()

    def plot(self) -> None:
        k = len(self.__centriods)
        colors = ['r', 'g','y']
        #color=iter(cm.rainbow(np.linspace(0,1,k)))
        __, ax = plt.subplots()

        for i in range(len(self.__dataSet)):
            x = self.__dataSet[i][0]
            y = self.__dataSet[i][1]
            ax.plot(x, y, (colors[int(self.__belongsTo[i])] + 'o'))

        for i in range(k):
            ax.plot(self.__centriods[i][0], self.__centriods[i][1], 'bo')
        plt.show()

    def getBelongsTo(self):
        return self.__belongsTo
    def getCentriods(self):
        return self.__centriods

def euclidian(a, b):
    return np.linalg.norm(a-b)

def kmeans(k: int, dataSet: np.array):
    numInstances, numFeatures = dataSet.shape

    # intializes the centriods at a random starting point
    startingIndexes = np.random.randint(0, numInstances - 1, size=k)
    unique = False
    while(not unique):
        indices = set()
        unique = True
        for index in startingIndexes:
            if index in indices:
                unique = False
                startingIndexes = np.random.randint(0, numInstances - 1, size=k)
                break
            else:
                indices.add(index)

    centriods = dataSet[startingIndexes]

    oldCentriods = np.zeros(centriods.shape)
    belongsTo = np.zeros((numInstances, 1))
    norm = euclidian(centriods,oldCentriods)

    while norm > 0:
        norm = euclidian(centriods,oldCentriods)

        for  dataIndex, data in enumerate(dataSet):
            distanceToCentriod = np.zeros((k,1))
            for centriodIndex, centroid in enumerate(centriods):
                distanceToCentriod[centriodIndex] = euclidian(centroid,data)

            belongsTo[dataIndex][0] = np.argmin(distanceToCentriod)

        tmpCentriods = np.zeros((k, numFeatures))
        for index in range(len(centriods)):
            # data points assigned to cluster
            assignedData = [i for i in range(len(belongsTo)) if belongsTo[i] == index]
            centriod = np.mean(dataSet[assignedData], axis=0)
            tmpCentriods [index, :] = centriod

        oldCentriods = centriods
        centriods = tmpCentriods

    return centriods, belongsTo

def getLine(x1,y1,x2,y2):
    m =(y2-y1)/(x2-x1)
    b = y2 - (m*x2)
    return m,b

def getClusters(centriods, belongsTo, dataSet):
    clusters = []
    for centriod in centriods:
        cluster = {}
        cluster["centriod"] = centriod
        cluster["dataPoints"] = []
        clusters.append(cluster)

    for itemIndex, item in enumerate(belongsTo):
        clusters[int(item)]["dataPoints"].append(dataSet[itemIndex])

    return clusters

def getClusterMean(cluster):
    distance = 0
    for datapoint in cluster["dataPoints"]:
        distance += euclidian(cluster["centriod"],datapoint)
    try:
        return distance/len(cluster["dataPoints"])
    except ZeroDivisionError:
        print("zero sized cluster")
        return None

def plotElbowMethod(datas):
    #color=iter(cm.rainbow(np.linspace(0,1,k)))
    __, ax = plt.subplots()

    for dataIndex, data in enumerate(datas):
        x = dataIndex
        y = data["sse"]
        ax.plot(x, y, "bo")
    plt.show()

def elbowMethod(dataSet, maxK = 8,plot =False):
    datas = []
    for i in range(1,maxK+1):
        data = {}
        centriods, belongsTo = kmeans(i,dataSet)
        data['centriods'] = centriods
        data['belongsTo'] = belongsTo
        data['sse'] = 0
        clusters = getClusters(centriods, belongsTo, dataSet)
        for cluster in clusters:
            mean = getClusterMean(cluster)
            for dataPoint in cluster["dataPoints"]:
                data['sse'] += (euclidian(dataPoint,cluster["centriod"]) - mean )**2
        datas.append(data)

    #plots elbow curve
    if plot:
        plotElbowMethod(datas)

    #gets line of starting and end points
    m,b = getLine(0,datas[0]['sse'],len(datas)-1,datas[len(datas)-1]['sse'])
    best = 0
    length = 0

    for i in range(len(datas)):
        y = m*i +b
        #print(y-datas[i]["sse"])
        if y - datas[i]["sse"] > length:
            best = i
            length = y - datas[i]["sse"]
    print(best+1)
    return datas[best]['centriods'], datas[best]['belongsTo']


############################################################################################
#
#  Cálculo do desvio padrão para as idades dos personagens dos Simpsons
#
# Cálculo do desvio padrão populacional e amostral para as idades dos personagens dos Simpsons:

# As regras básicas para os cálculos de desvios padrão são:

# * Calculamos o desvio padrão populaconal quando o conjunto  de dados é a população inteira.

# * Consideramos o desvio padrão amostral se nossos conjuntos de dados representarem uma amostra retirada de uma grande população (como é o caso para as idades dos personagems dos Simpsons).
#
# NOTA:
# O desvio padrão amostral sempre será maior que o desvio padrão populacional para
# o mesmo conjunto de dados porque há mais incerteza ao calcular o desvio padrão da amostra,
# assim nossa estimativa do desvio padrão será maior.
###########################################################################################

import statistics as stat
import numpy as np

grupo_1 = (1, 8, 10, 38, 39)

grupo_2 = (8, 10, 39, 45, 49)

# Cálculo do desvios padrões populacional e amostral usando a biblioteca Numpy
def Calcula_Desvio_Padrao_Populacional_Numpy(a):

    Population_STD_Numpy = np.std(a)

    return Population_STD_Numpy

def Calcula_Desvio_Padrao_da_Amostra_Numpy(a):

    Sample_STD_Numpy = np.std(a, ddof = 1)

    return Sample_STD_Numpy

# Cálculo do desvios padrões populacional e amostral usando a biblioteca Statistics
def Calcula_Desvio_Padrao_Populacional_Stat(a):

    Population_STD_Stat = stat.pstdev(a)

    return Population_STD_Stat

def Calcula_Desvio_Padrao_da_Amostra_Stat(a):

    Sample_STD_Stat = stat.stdev(a)

    return Sample_STD_Stat

print("\n Desvio Padrão populacional para o Grupo 1 (usando a biblioteca numpy): ", Calcula_Desvio_Padrao_Populacional_Numpy(grupo_1))

print("\n Desvio Padrão populacional para o Grupo 2 (usando a biblioteca numpy): ", Calcula_Desvio_Padrao_Populacional_Numpy(grupo_2))

print("\n Desvio Padrão amostral para o Grupo 1 (usando a biblioteca numpy): ", Calcula_Desvio_Padrao_da_Amostra_Numpy(grupo_1))

print("\n Desvio Padrão amostral para o Grupo 2 (usando a biblioteca numpy): ", Calcula_Desvio_Padrao_da_Amostra_Numpy(grupo_2))

print("\n Desvio Padrão populacional para o Grupo 1 (usando a biblioteca statistics): ", Calcula_Desvio_Padrao_Populacional_Stat(grupo_1))

print("\n Desvio Padrão populacional para o Grupo 2 (usando a biblioteca statistics): ", Calcula_Desvio_Padrao_Populacional_Stat(grupo_2))

print("\n Desvio Padrão amostral para o Grupo 1 (usando a biblioteca statistics): ", Calcula_Desvio_Padrao_da_Amostra_Stat(grupo_1))

print("\n Desvio Padrão amostral para o Grupo 2 (usando a biblioteca statistics): ", Calcula_Desvio_Padrao_da_Amostra_Stat(grupo_2))


class Solution:
    def maxArea(self, h: int, w: int,
                horizontalCuts: List[int], verticalCuts: List[int]) -> int:
        horizontalCuts.extend([0, h])
        horizontalCuts.sort()

        verticalCuts.extend([0, w])
        verticalCuts.sort()

        # print(horizontalCuts, verticalCuts)

        max_h = max_w = 0
        for idx in range(1, len(verticalCuts)):
            max_w = max(max_w, verticalCuts[idx] - verticalCuts[idx - 1])

        for idx in range(1, len(horizontalCuts)):
            max_h = max(max_h, horizontalCuts[idx] - horizontalCuts[idx - 1])

        return (max_w * max_h) % (10**9 + 7)


import tkinter as tk

class App:
    def __init__(self, root):
        frame = tk.Frame(root)
        frame.pack()
        self.hi_three = tk.Button(frame,text="say hello",bg="white",fg="red", command=self.say_hi)
        self.hi_three.pack(side=tk.LEFT)

    def say_hi(self):
        print("hello ,welcome to the real world")

root = tk.Tk()
app = App(root)
root.mainloop()


# 스포츠 기사 크롤링

import requests
from bs4 import BeautifulSoup as bs

from apps.resources.models import Article

def crawling_entertain_news():
    entertain_url = "https://entertain.naver.com"

    entertain_url_home = entertain_url + "/home"

    response = requests.get(entertain_url_home)
    soup = bs(response.text, "html.parser")

    ranking = soup.find("div", {"class": "rank_lst"})
    lis = ranking.findAll("li")

    entertain_news_list = []

    for li in lis:
        entertain_news_list.append(entertain_url + li.find("a")["href"])

    # 연예기사 넘 많아서 5개로 줄임
    entertain_news_list = entertain_news_list[:5]

    parsed_news = []

    for news in entertain_news_list:
        response = requests.get(news)
        soup = bs(response.text, "html.parser")
        title = (
            soup.select(".end_tit")[0]
            .text.replace("\n", "")
            .replace("\t", "")
            .replace("\r", "")
        )
        content = (
            soup.find("div", {"id": "articeBody"})
            .text.replace("\n", "")
            .replace("\t", "")
            .replace("\r", "")
        )
        url = news
        if len(content) > 500 & len(content) < 4000:
            parsed_news.append(
                {
                    "title": title,
                    "content": content,
                    "url": url,
                }
            )

    if len(parsed_news) > 0:
        for news in parsed_news:
            if Article.objects.filter(title=news["title"]).exists():
                continue
            else:
                Article.objects.create(
                    kind=Article.ArticleKindChoices.ENTERTAINMENT,
                    title=news["title"],
                    content=news["content"],
                    url=news["url"],
                )


import torch
print(torch.__version__)
torch.get_default_dtype()
torch.get_num_threads()
torch.set_default_dtype(torch.float64)
torch.get_default_dtype()
tensor_arr = torch.Tensor([[1, 2, 3], [4, 5, 6]])
torch.is_tensor(tensor_arr)
torch.numel(tensor_arr)  #Gives the number of elements in the tensor
tensor_uninitialized = torch.Tensor(2, 2)
tensor_uninitialized
tensor_initialized = torch.rand(2, 2)
tensor_initialized
temsor_cpu_int = torch.Tensor([5, 3]).type(torch.IntTensor)
tensor_cuda_int = torch.Tensor([5, 3]).type(torch.cuda.IntTensor)
torch_cpu_short = torch.ShortTensor([1.0, 2.0, 3.0])
tensor_cuda_short = torch.cuda.ShortTensor([1.0, 2.0, 3.0]) #cuda commands create tensor in GPU
tensor_float = torch.cuda.HalfTensor([1.0, 2.0, 3.0]) #takes half the memory
torch_cpu_fill = torch.full((2, 6), fill_value = 10)
torch_cuda_fill = torch.full((2, 6), fill_value = 5).type(torch.cuda.FloatTensor)
tensor_of_ones_cpu = torch.ones([2, 4], dtype = torch.int32)
tensor_of_ones_cuda = torch.ones([2, 4], dtype = torch.int32).type(torch.cuda.IntTensor)
tensor_of_zeros__cpu = torch.zeros_like(tensor_of_ones_cpu)
tensor_of_zeros_gpu = torch.zeros_like(tensor_of_ones_cuda)
tensor_eye_cpu = torch.eye(5)
tensor_eye_gpu = torch.eye(5).type(torch.cuda.FloatTensor)
non_zero_indices = torch.nonzero(tensor_eye_cpu)
non_zero_indices = torch.nonzero(tensor_of_ones_cuda)
i = torch.tensor([[0, 1, 1],
                 [2, 2, 0]])
v = torch.tensor([3, 4, 5], dtype = torch.float32)
sparse_tensor = torch.sparse_coo_tensor(i, v, [2, 5])
sparse_tensor.data

#Simple operations on Tensors
initial_tensor = torch.rand(2, 3)
initial_tensor.fill_(10)
new_tensor = initial_tensor.add(5)
initial_tensor.add_(8)
new_tensor.sqrt_()
x = torch.linspace(start = 0.1, end = 10, steps = 15)
tensor_chunk = torch.chunk(x, 3, 0)
tensor_1 = tensor_chunk[0]
tensor_2 = tensor_chunk[1]
tensor_3 = torch.tensor([3.0, 4.0, 5.0])
torch.cat((tensor_1, tensor_2, tensor_3), 0)
random_tensor = torch.Tensor([[20, 8, 40], [5, 43, 6], [78, 54, 6]])
random_tensor[0, 1]
random_tensor[1:, 1:]
random_tensor.size()
resized_tensor = random_tensor.view(9)
resized_tensor.size()
random_tensor[2, 2] = 200.0
resized_tensor

#Elementwise and Matrix Operations
tensor_unsqueeze = torch.unsqueeze(random_tensor, 2)
tensor_unsqueeze.size()
tensor_transpose = torch.transpose(initial_tensor, 0, 1)
sorted_tensor, sorted_indices = torch.sort(random_tensor)
tensor_float = torch.FloatTensor([-1.1, -2.2, -3.3])
tensor_abs = torch.abs(tensor_float)
rand1 = torch.abs(torch.rand(2,3))
rand2 = torch.abs(torch.rand(2,3))
add1 = rand1 + rand2
tensor_just = torch.Tensor([[1, 2, 3], [-1, -2, -3]])
tensor_div = torch.div(tensor_just, tensor_just + 0.3)
tensor_mul = torch.mul(tensor_just, tensor_just)
tensor_clamp = torch.clamp(tensor_just, min = -0.2, max = 2)
t1 = torch.Tensor([2, 3])
t2 = torch.Tensor([4, 5])
dot_product = torch.dot(t1, t2)
matrix = torch.Tensor([[1, 2, 3], [4, 5, 6]])
vector = torch.Tensor([0, 1, 2])
matrix_vector = torch.mv(matrix, vector)
another_matrix = torch.Tensor([[10,20], [30, 0], [0,50]])
matrix_mul = torch.mm(matrix, another_matrix)
torch.argmax(matrix_mul, dim = 1)
torch.argmin(matrix_mul, dim = 1)

#Converting between PyTorch and numpy tensors
import numpy as np
tensor = torch.rand(4,3)
type(tensor)
numpy_from_tensor = tensor.numpy()
type(numpy_from_tensor)
torch.is_tensor(tensor)
torch.is_tensor(numpy_from_tensor)
numpy_from_tensor[0, 0] = 100.0
numpy_from_tensor
tensor
numpy_arr = np.array([[1.0, 2.0, 3.0], [10.0, 20.0, 30.0], [100.0, 200.0, 300.0]])
tensor_from_numpy = torch.from_numpy(numpy_arr)
type(tensor_from_numpy)
torch.is_tensor(tensor_from_numpy)
tensor_from_numpy_arr = torch.as_tensor(numpy_arr)


from scout.build.disease import build_disease_term

def test_build_disease_term(adapter):
    ## GIVEN some disease info and a adapter with a gene
    disease_info = {
        'mim_number': 615349,
        'description': "EHLERS-DANLOS SYNDROME, PROGEROID TYPE, 2",
        'hgnc_symbols': set(['B3GALT6']),
        'inheritance': set(['AR']),
    }
    alias_genes = {}
    alias_genes['B3GALT6'] = {
            'true': 17978,
            'ids': [17978],
        }

    ## WHEN building the disease term
    disease_obj = build_disease_term(disease_info, alias_genes)

    ## THEN assert the term is on the correct format

    assert disease_obj['_id'] == disease_obj['disease_id'] == "OMIM:615349"
    assert disease_obj['inheritance'] == ['AR']
    assert disease_obj['genes'] == [17978]


import logging
from typing import Any, Callable, Dict, Iterable, Iterator, Optional, Tuple, Union

from swh.core.db import BaseDb
from swh.model.model import (
    BaseModel,
    Directory,
    DirectoryEntry,
    ExtID,
    RawExtrinsicMetadata,
    Release,
    Revision,
    Snapshot,
    SnapshotBranch,
    TargetType,
)
from swh.model.swhids import ExtendedObjectType
from swh.storage.postgresql.converters import (
    db_to_extid,
    db_to_raw_extrinsic_metadata,
    db_to_release,
    db_to_revision,
)
from swh.storage.replay import OBJECT_CONVERTERS
from swh.storage.writer import JournalWriter

logger = logging.getLogger(__name__)

PARTITION_KEY = {
    "content": "sha1",
    "skipped_content": "sha1",
    "directory": "id",
    "extid": "target",
    "metadata_authority": "type, url",
    "metadata_fetcher": "name, version",
    "raw_extrinsic_metadata": "target",
    "revision": "revision.id",
    "release": "release.id",
    "snapshot": "id",
    "origin": "id",
    "origin_visit": "origin_visit.origin",
    "origin_visit_status": "origin_visit_status.origin",
}

COLUMNS = {
    "content": [
        "sha1",
        "sha1_git",
        "sha256",
        "blake2s256",
        "length",
        "status",
        "ctime",
    ],
    "skipped_content": [
        "sha1",
        "sha1_git",
        "sha256",
        "blake2s256",
        "length",
        "ctime",
        "status",
        "reason",
    ],
    "directory": ["id", "dir_entries", "file_entries", "rev_entries", "raw_manifest"],
    "extid": ["extid_type", "extid", "extid_version", "target_type", "target"],
    "metadata_authority": ["type", "url"],
    "metadata_fetcher": ["name", "version"],
    "origin": ["url"],
    "origin_visit": [
        "visit",
        "type",
        ("origin.url", "origin"),
        "date",
    ],
    "origin_visit_status": [
        ("origin_visit_status.visit", "visit"),
        ("origin.url", "origin"),
        ("origin_visit_status.date", "date"),
        "type",
        "snapshot",
        "status",
        "metadata",
    ],
    "raw_extrinsic_metadata": [
        "raw_extrinsic_metadata.type",
        "raw_extrinsic_metadata.target",
        "metadata_authority.type",
        "metadata_authority.url",
        "metadata_fetcher.name",
        "metadata_fetcher.version",
        "discovery_date",
        "format",
        "raw_extrinsic_metadata.metadata",
        "origin",
        "visit",
        "snapshot",
        "release",
        "revision",
        "path",
        "directory",
    ],
    "revision": [
        ("revision.id", "id"),
        "date",
        "date_offset_bytes",
        "committer_date",
        "committer_date_offset_bytes",
        "type",
        "directory",
        "message",
        "synthetic",
        "metadata",
        "extra_headers",
        (
            "array(select parent_id::bytea from revision_history rh "
            "where rh.id = revision.id order by rh.parent_rank asc)",
            "parents",
        ),
        "raw_manifest",
        ("a.id", "author_id"),
        ("a.name", "author_name"),
        ("a.email", "author_email"),
        ("a.fullname", "author_fullname"),
        ("c.id", "committer_id"),
        ("c.name", "committer_name"),
        ("c.email", "committer_email"),
        ("c.fullname", "committer_fullname"),
    ],
    "release": [
        ("release.id", "id"),
        "date",
        "date_offset_bytes",
        "comment",
        ("release.name", "name"),
        "synthetic",
        "target",
        "target_type",
        ("a.id", "author_id"),
        ("a.name", "author_name"),
        ("a.email", "author_email"),
        ("a.fullname", "author_fullname"),
        "raw_manifest",
    ],
    "snapshot": ["id", "object_id"],
}

JOINS = {
    "release": ["person a on release.author=a.id"],
    "revision": [
        "person a on revision.author=a.id",
        "person c on revision.committer=c.id",
    ],
    "origin_visit": ["origin on origin_visit.origin=origin.id"],
    "origin_visit_status": [
        "origin on origin_visit_status.origin=origin.id",
    ],
    "raw_extrinsic_metadata": [
        "metadata_authority on "
        "raw_extrinsic_metadata.authority_id=metadata_authority.id",
        "metadata_fetcher on raw_extrinsic_metadata.fetcher_id=metadata_fetcher.id",
    ],
}

EXTRA_WHERE = {
    # hack to force the right index usage on table extid
    "extid": "target_type in ('revision', 'release', 'content', 'directory')"
}

def directory_converter(db: BaseDb, directory_d: Dict[str, Any]) -> Directory:
    """Convert directory from the flat representation to swh model
    compatible objects.

    """
    columns = ["target", "name", "perms"]
    query_template = """
    select %(columns)s
    from directory_entry_%(type)s
    where id in %%s
    """

    types = ["file", "dir", "rev"]

    entries = []
    with db.cursor() as cur:
        for type in types:
            ids = directory_d.pop("%s_entries" % type)
            if not ids:
                continue
            query = query_template % {
                "columns": ",".join(columns),
                "type": type,
            }
            cur.execute(query, (tuple(ids),))
            for row in cur:
                entry_d = dict(zip(columns, row))
                entry = DirectoryEntry(
                    name=entry_d["name"],
                    type=type,
                    target=entry_d["target"],
                    perms=entry_d["perms"],
                )
                entries.append(entry)

    (is_corrupt, dir_) = Directory.from_possibly_duplicated_entries(
        id=directory_d["id"],
        entries=tuple(entries),
        raw_manifest=directory_d["raw_manifest"],
    )
    if is_corrupt:
        logger.info("%s has duplicated entries", dir_.swhid())

    return dir_

def raw_extrinsic_metadata_converter(
    db: BaseDb, metadata: Dict[str, Any]
) -> RawExtrinsicMetadata:
    """Convert a raw extrinsic metadata from the flat representation to swh model
    compatible objects.

    """
    return db_to_raw_extrinsic_metadata(metadata)

def extid_converter(db: BaseDb, extid: Dict[str, Any]) -> ExtID:
    """Convert an extid from the flat representation to swh model
    compatible objects.

    """
    return db_to_extid(extid)

def revision_converter(db: BaseDb, revision_d: Dict[str, Any]) -> Revision:
    """Convert revision from the flat representation to swh model
    compatible objects.

    """
    revision = db_to_revision(revision_d)
    assert revision is not None, revision_d["id"]
    return revision

def release_converter(db: BaseDb, release_d: Dict[str, Any]) -> Release:
    """Convert release from the flat representation to swh model
    compatible objects.

    """
    release = db_to_release(release_d)
    assert release is not None, release_d["id"]
    return release

def snapshot_converter(db: BaseDb, snapshot_d: Dict[str, Any]) -> Snapshot:
    """Convert snapshot from the flat representation to swh model
    compatible objects.

    """
    columns = ["name", "target", "target_type"]
    query = """
    select %s
    from snapshot_branches sbs
    inner join snapshot_branch sb on sb.object_id=sbs.branch_id
    where sbs.snapshot_id=%%s
    """ % ", ".join(
        columns
    )
    with db.cursor() as cur:
        cur.execute(query, (snapshot_d["object_id"],))
        branches = {}
        for name, *row in cur:
            branch_d = dict(zip(columns[1:], row))
            if branch_d["target"] is not None and branch_d["target_type"] is not None:
                branch: Optional[SnapshotBranch] = SnapshotBranch(
                    target=branch_d["target"],
                    target_type=TargetType(branch_d["target_type"]),
                )
            else:
                branch = None
            branches[name] = branch

    return Snapshot(
        id=snapshot_d["id"],
        branches=branches,
    )

CONVERTERS: Dict[str, Callable[[BaseDb, Dict[str, Any]], BaseModel]] = {
    "directory": directory_converter,
    "extid": extid_converter,
    "raw_extrinsic_metadata": raw_extrinsic_metadata_converter,
    "revision": revision_converter,
    "release": release_converter,
    "snapshot": snapshot_converter,
}

def object_to_offset(object_id, numbits):
    """Compute the index of the range containing object id, when dividing
       space into 2^numbits.

    Args:
        object_id (str): The hex representation of object_id
        numbits (int): Number of bits in which we divide input space

    Returns:
        The index of the range containing object id

    """
    q, r = divmod(numbits, 8)
    length = q + (r != 0)
    shift_bits = 8 - r if r else 0

    truncated_id = object_id[: length * 2]
    if len(truncated_id) < length * 2:
        truncated_id += "0" * (length * 2 - len(truncated_id))

    truncated_id_bytes = bytes.fromhex(truncated_id)
    return int.from_bytes(truncated_id_bytes, byteorder="big") >> shift_bits

def byte_ranges(
    numbits: int, start_object: Optional[str] = None, end_object: Optional[str] = None
) -> Iterator[Tuple[Optional[bytes], Optional[bytes]]]:
    """Generate start/end pairs of bytes spanning numbits bits and
       constrained by optional start_object and end_object.

    Args:
        numbits: Number of bits in which we divide input space
        start_object: Hex object id contained in the first range
                            returned
        end_object: Hex object id contained in the last range
                          returned

    Yields:
        2^numbits pairs of bytes

    """
    q, r = divmod(numbits, 8)
    length = q + (r != 0)
    shift_bits = 8 - r if r else 0

    def to_bytes(i):
        return int.to_bytes(i << shift_bits, length=length, byteorder="big")

    start_offset = 0
    end_offset = 1 << numbits

    if start_object is not None:
        start_offset = object_to_offset(start_object, numbits)
    if end_object is not None:
        end_offset = object_to_offset(end_object, numbits) + 1

    for start in range(start_offset, end_offset):
        end = start + 1

        if start == 0:
            yield None, to_bytes(end)
        elif end == 1 << numbits:
            yield to_bytes(start), None
        else:
            yield to_bytes(start), to_bytes(end)

def raw_extrinsic_metadata_target_ranges(
    start_object: Optional[str] = None, end_object: Optional[str] = None
) -> Iterator[Tuple[Optional[str], Optional[str]]]:
    """Generate ranges of values for the `target` attribute of `raw_extrinsic_metadata`
    objects.

    This generates one range for all values before the first SWHID (which would
    correspond to raw origin URLs), then a number of hex-based ranges for each
    known type of SWHID (2**12 ranges for directories, 2**8 ranges for all other
    types). Finally, it generates one extra range for values above all possible
    SWHIDs.
    """
    if start_object is None:
        start_object = ""

    swhid_target_types = sorted(type.value for type in ExtendedObjectType)

    first_swhid = f"swh:1:{swhid_target_types[0]}:"

    # Generate a range for url targets, if the starting object is before SWHIDs
    if start_object < first_swhid:
        yield start_object, (
            first_swhid
            if end_object is None or end_object >= first_swhid
            else end_object
        )

    if end_object is not None and end_object <= first_swhid:
        return

    # Prime the following loop, which uses the upper bound of the previous range
    # as lower bound, to account for potential targets between two valid types
    # of SWHIDs (even though they would eventually be rejected by the
    # RawExtrinsicMetadata parser, they /might/ exist...)
    end_swhid = first_swhid

    # Generate ranges for swhid targets
    for target_type in swhid_target_types:
        finished = False
        base_swhid = f"swh:1:{target_type}:"
        last_swhid = base_swhid + ("f" * 40)

        if start_object > last_swhid:
            continue

        # Generate 2**8 or 2**12 ranges
        for _, end in byte_ranges(12 if target_type == "dir" else 8):
            # Reuse previous upper bound
            start_swhid = end_swhid

            # Use last_swhid for this object type if on the last byte range
            end_swhid = (base_swhid + end.hex()) if end is not None else last_swhid

            # Ignore out of bounds ranges
            if start_object >= end_swhid:
                continue

            # Potentially clamp start of range to the first object requested
            start_swhid = max(start_swhid, start_object)

            # Handle ending the loop early if the last requested object id is in
            # the current range
            if end_object is not None and end_swhid >= end_object:
                end_swhid = end_object
                finished = True

            yield start_swhid, end_swhid

            if finished:
                return

    # Generate one final range for potential raw origin URLs after the last
    # valid SWHID
    start_swhid = max(start_object, end_swhid)
    yield start_swhid, end_object

def integer_ranges(
    start: str, end: str, block_size: int = 1000
) -> Iterator[Tuple[Optional[int], Optional[int]]]:
    for range_start in range(int(start), int(end), block_size):
        if range_start == 0:
            yield None, block_size
        elif range_start + block_size > int(end):
            yield range_start, int(end)
        else:
            yield range_start, range_start + block_size

RANGE_GENERATORS: Dict[
    str,
    Union[
        Callable[[str, str], Iterable[Tuple[Optional[str], Optional[str]]]],
        Callable[[str, str], Iterable[Tuple[Optional[bytes], Optional[bytes]]]],
        Callable[[str, str], Iterable[Tuple[Optional[int], Optional[int]]]],
    ],
] = {
    "content": lambda start, end: byte_ranges(24, start, end),
    "skipped_content": lambda start, end: [(None, None)],
    "directory": lambda start, end: byte_ranges(24, start, end),
    "extid": lambda start, end: byte_ranges(24, start, end),
    "revision": lambda start, end: byte_ranges(24, start, end),
    "release": lambda start, end: byte_ranges(16, start, end),
    "raw_extrinsic_metadata": raw_extrinsic_metadata_target_ranges,
    "snapshot": lambda start, end: byte_ranges(16, start, end),
    "origin": integer_ranges,
    "origin_visit": integer_ranges,
    "origin_visit_status": integer_ranges,
}

def compute_query(obj_type, start, end):
    columns = COLUMNS.get(obj_type)
    join_specs = JOINS.get(obj_type, [])
    join_clause = "\n".join("left join %s" % clause for clause in join_specs)
    additional_where = EXTRA_WHERE.get(obj_type)

    where = []
    where_args = []
    if start:
        where.append("%(keys)s >= %%s")
        where_args.append(start)
    if end:
        where.append("%(keys)s < %%s")
        where_args.append(end)

    if additional_where:
        where.append(additional_where)

    where_clause = ""
    if where:
        where_clause = ("where " + " and ".join(where)) % {
            "keys": "(%s)" % PARTITION_KEY[obj_type]
        }

    column_specs = []
    column_aliases = []
    for column in columns:
        if isinstance(column, str):
            column_specs.append(column)
            column_aliases.append(column)
        else:
            column_specs.append("%s as %s" % column)
            column_aliases.append(column[1])

    query = """
select %(columns)s
from %(table)s
%(join)s
%(where)s
    """ % {
        "columns": ",".join(column_specs),
        "table": obj_type,
        "join": join_clause,
        "where": where_clause,
    }

    return query, where_args, column_aliases

def fetch(db, obj_type, start, end):
    """Fetch all obj_type's identifiers from db.

    This opens one connection, stream objects and when done, close
    the connection.

    Args:
        db (BaseDb): Db connection object
        obj_type (str): Object type
        start (Union[bytes|Tuple]): Range start identifier
        end (Union[bytes|Tuple]): Range end identifier

    Raises:
        ValueError if obj_type is not supported

    Yields:
        Objects in the given range

    """
    query, where_args, column_aliases = compute_query(obj_type, start, end)
    converter = CONVERTERS.get(obj_type)
    with db.cursor() as cursor:
        logger.debug("Fetching data for table %s", obj_type)
        logger.debug("query: %s %s", query, where_args)
        cursor.execute(query, where_args)
        for row in cursor:
            record = dict(zip(column_aliases, row))
            if converter:
                record = converter(db, record)
            else:
                record = OBJECT_CONVERTERS[obj_type](record)

            logger.debug("record: %s", record)
            yield record

def _format_range_bound(bound):
    if isinstance(bound, bytes):
        return bound.hex()
    else:
        return str(bound)

MANDATORY_KEYS = ["storage", "journal_writer"]

class JournalBackfiller:
    """Class in charge of reading the storage's objects and sends those
    back to the journal's topics.

    This is designed to be run periodically.

    """

    def __init__(self, config=None):
        self.config = config
        self.check_config(config)
        self._db = None
        self.writer = JournalWriter({"cls": "kafka", **self.config["journal_writer"]})
        assert self.writer.journal is not None

    @property
    def db(self):
        if self._db is None:
            self._db = BaseDb.connect(self.config["storage"]["db"])
        return self._db

    def check_config(self, config):
        missing_keys = []
        for key in MANDATORY_KEYS:
            if not config.get(key):
                missing_keys.append(key)

        if missing_keys:
            raise ValueError(
                "Configuration error: The following keys must be"
                " provided: %s" % (",".join(missing_keys),)
            )

        if "cls" not in config["storage"] or config["storage"]["cls"] not in (
            "local",
            "postgresql",
        ):
            raise ValueError(
                "swh storage backfiller must be configured to use a local"
                " (PostgreSQL) storage"
            )

    def parse_arguments(self, object_type, start_object, end_object):
        """Parse arguments

        Raises:
            ValueError for unsupported object type
            ValueError if object ids are not parseable

        Returns:
            Parsed start and end object ids

        """
        if object_type not in COLUMNS:
            raise ValueError(
                "Object type %s is not supported. "
                "The only possible values are %s"
                % (object_type, ", ".join(sorted(COLUMNS.keys())))
            )

        if object_type in ["origin", "origin_visit", "origin_visit_status"]:
            start_object = start_object or "0"
            end_object = end_object or str(100_000_000)  # hard-coded limit

        return start_object, end_object

    def run(self, object_type, start_object, end_object, dry_run=False):
        """Reads storage's subscribed object types and send them to the
        journal's reading topic.

        """
        start_object, end_object = self.parse_arguments(
            object_type, start_object, end_object
        )

        for range_start, range_end in RANGE_GENERATORS[object_type](
            start_object, end_object
        ):
            logger.info(
                "Processing %s range %s to %s",
                object_type,
                _format_range_bound(range_start),
                _format_range_bound(range_end),
            )

            objects = fetch(self.db, object_type, start=range_start, end=range_end)

            if not dry_run:
                self.writer.write_additions(object_type, objects)
            else:
                # only consume the objects iterator to check for any potential
                # decoding/encoding errors
                for obj in objects:
                    pass

if __name__ == "__main__":
    print('Please use the "swh-journal backfiller run" command')


import pandas as pd
import plot_likert as plot_likert
import numpy as np
import matplotlib.pyplot as plt
import pylab as p
import streamlit as st

st.set_option('deprecation.showPyplotGlobalUse', False)

st.title('Visualisierung der Seminarevaluation')
st.text('Hier kannst du deine Seminarevaluation in Histogrammen anzeigen lassen')

uploaded_file = st.file_uploader('Hier die CSV-Datei aus dem Moodle-Kurs hochladen:')

if uploaded_file:
    df2 = pd.read_csv(uploaded_file)
    df2 = df2.replace('Nicht beantwortbar', np.NaN)
    scale = ['Trifft nicht zu', 'Trifft eher nicht zu', 'Teils/Teils', 'Trifft eher zu', 'Trifft voll und ganz zu']
    data_1 = df2[['1.1) Aufbau und Gliederung der Veranstaltung waren klar. ',
                  '1.2) Die Lehrveranstaltung hat mir viele neue inhaltliche Erkenntnisse gebracht.',
                  '1.3) Die Leistungsanforderungen wurden transparent dargelegt.',
                  '1.4) Die zu Beginn der Veranstaltung beschriebenen Lernziele wurden bisher erfüllt.',
                  '1.5) Die veranstaltungsbegleitenden Materialien erleichterten das Verständnis des Seminarinhalts/-stoffes.',
                  '1.6) Die digitalen Formate unterstützten den Lernprozess.']]
    data_2 = df2[['2.1) Die/der Dozent_in war es wichtig, dass die Studierenden etwas lernen',
                  '2.2) Die Lehrveranstaltung/Aufgaben trugen zum Verständnis von Theorie und Praxis bei.',
                  '2.3) Die Lerninhalte wurden mit Beispielen aus der Praxis veranschaulicht.',
                  '2.4) Die/Der Dozent_in folgte immer einem klar nachvollziehbarem roten Faden.',
                  '2.5) Die/Der Dozent_in stellte Verbindungen zu bereits besprochenem Stoff aus der Veranstaltung her.',
                  '2.6) Die/Der Dozent_in hat klar und deutlich gesprochen.',
                  '2.7) Die/Der Dozent_in antwortete verständlich auf die Fragen der Studierenden.',
                  '2.8) Die Aufgaben trugen zum Verständnis der Veranstaltung bei.',
                  '2.9) Die Lehrformen waren abwechslungsreich gestaltet.']]
    data_3 = df2[[
        '3.1 Die/der Dozent_in schuf eine Atmosphäre, in der Studierende Fragen und Kommentare zum Stoff ohne Scheu äußerten.',
        '3.2) Die/der Dozent_in trug zu einem respektvollen Lehr-Lernklima in der Veranstaltung bei.',
        '3.3) Die Studierenden trugen zu einem respektvollen Lehr-Lernklima in der Veranstaltung bei.',
        '3.4) Die Studierenden wurden zur kritischen Auseinandersetzung mit den Inhalten der Veranstaltung angeregt.']]
    data_4 = df2[[
        '4.1) Die/Der Dozent_in achtete darauf, dass in ihren Ausführungen Menschen nicht in stereotypen/diskriminierenden Bildern beschrieben wurden.',
        '4.2) Wenn Inhalte erläutert wurden, wurde die Vielfalt der Erfahrungen der Studierenden berücksichtigt.']]

    #st.write(df2.head())

    plot_likert.plot_likert(data_1, scale, plot_percentage=True,
                            bar_labels=True, bar_labels_color="snow",
                            colors=plot_likert.colors.default_with_darker_neutral, figsize=(8, 11))

    st.pyplot()

    plot_likert.plot_likert(data_2, scale, plot_percentage=True, bar_labels=True, bar_labels_color="snow",
                            colors=plot_likert.colors.default_with_darker_neutral, figsize=(8, 13))

    st.pyplot()

    plot_likert.plot_likert(data_3, scale, plot_percentage=True,
                            bar_labels=True, bar_labels_color="snow",
                            colors=plot_likert.colors.default_with_darker_neutral, figsize=(8, 6))

    st.pyplot()

    plot_likert.plot_likert(data_4, scale, plot_percentage=True,
                           bar_labels=True, bar_labels_color="snow",
                           colors=plot_likert.colors.default_with_darker_neutral, figsize=(8, 3))

    st.pyplot()

    st.markdown('## Was hat Ihnen an der Lehrveranstaltung besonders gut gefallen?')

    df3 = df2['Was hat Ihnen an der Lehrveranstaltung besonders gut gefallen (Stichpunkte)'].dropna()
    st.markdown(df3.values)

    df4 = df2['Welche Verbesserungsvorschläge haben Sie? (Stichpunkte)'].dropna()

    st.markdown('## Welche Verbesserungsvorschläge haben Sie?')

    st.markdown(df4.values)


"""
class Dog:

    def __init__(self,name): #allows to instanciate the object
        self.name = name # Create an attribute of the class Dog wichs is the Name

    # method is a functions created inside of a Class
    def bark(self): #
        print("bark")

#d = Dog() # instance of my Class Dog

d = Dog("Tim") # Instance of my class Dog WITH A NAME

#d.bark() # calling the method of the object

"""

class student():

    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade

    def get_grade(self):
        return self.grade

class course():
    def __init__(self, name, max_students):
        self.name = name
        self.max_students = max_students
        self.students = []

    def set_maxS(self, max_students):
        self.max_students = max_students

    def add_student(self, student):
        if len(self.students) < self.max_students:
            self.students.append(student)
            return True
        return False

    def get_avg(self):
        value = 0
        for student in self.students :
            value += student.get_grade()
        avg =  value / len(self.students)
        return avg

s1 = student("Camilo", 27 , 100)
s2 = student("Lili" , 29 , 200 )

c1= course("AI", 1)

c1.add_student(s1)

c1.set_maxS(2)
c1.add_student(s2)

for student in c1.students:
    print(student.name)

print(c1.get_avg())


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('api', '0019_auto_20150121_1722'),
    ]

    operations = [
        migrations.AlterField(
            model_name='contestentry',
            name='jam_points',
            field=models.DecimalField(default=0, max_digits=9, decimal_places=1),
            preserve_default=True,
        ),
    ]


# -*- coding: utf-8 -*-
"""
Created on Sun Nov 20 11:25:42 2022

@author: ChrisZeThird
"""
# import sqlite3 as sql

""" Methods """

def create_table(db, list_name, item_name, item_price, item_url):
    cursor = db.cursor()
    cursor.execute('INSERT INTO Wishtable VALUES (?,?,?,?)',(list_name, item_name, item_price, item_url))
    db.commit()
    cursor.close()

def distinct(db,c='list_id'):
    cursor = db.cursor()
    cursor.execute(f"SELECT DISTINCT {c} FROM Wishtable ORDER BY {c}")

    data=cursor.fetchall()
    COLUMN = 0

    all_lists = [elt[COLUMN] for elt in data]
    # print(all_lists)
    cursor.close()
    return all_lists

def remove_element(db, item_name, list_name):
    cursor = db.cursor()
    cursor.execute(f"DELETE FROM Wishtable WHERE list_id = '{list_name}' AND name = '{item_name}'")
    db.commit()
    cursor.close()

def remove_list(db, list_name):
    cursor = db.cursor()
    cursor.execute(f"DELETE FROM Wishtable WHERE list_id = '{list_name}'")
    db.commit()
    cursor.close()

def fetch_items(db, list_name):
    cursor = db.cursor()
    cursor.execute(f'SELECT * FROM Wishtable WHERE list_id = "{list_name}"')
    items = cursor.fetchall()
    cursor.close()
    return items


from database import get_db
import settings

def set_air_temperature(airTemperature):
    db = get_db()
    db.execute("INSERT INTO airTemperature (value) VALUES (?)", (airTemperature,))
    db.commit()
    return get_air_temperature()

def update_temperature_auto(airTemperature):
    """
    -> update temperature of AC if mode = auto
    <0 : 27, 0 - 10: 25, 10-20: 23, 20-30: 21, 30+: 19
    """
    currentACMode = settings.get_ac_mode()
    if currentACMode.upper() == "AUTO":
        updatedACTemperature = 27
        if airTemperature >0:
            updatedACTemperature -=2
        if airTemperature >10:
            updatedACTemperature -=2
        if airTemperature >20:
            updatedACTemperature -=2
        if airTemperature >30:
            updatedACTemperature -=2
        settings.set_ac_temperature(updatedACTemperature)

def get_air_temperature():
    airTemperature = (
        get_db()
        .execute("SELECT timestamp, value FROM airTemperature ORDER BY timestamp DESC")
        .fetchone()
    )
    if airTemperature is None:
        return None
    else:
        return airTemperature["value"]

def set_air_humidity(humidity):
    db = get_db()
    db.execute("INSERT INTO airHumidity (value) VALUES (?)", (humidity,))
    db.commit()
    return get_air_humidity()

def get_air_humidity():
    airHumidity = (
        get_db()
        .execute("SELECT timestamp, value FROM airHumidity ORDER BY timestamp DESC")
        .fetchone()
    )
    if airHumidity is None:
        return None
    else:
        return airHumidity["value"]


#!/usr/bin/env python

import gtk, gobject

class MyWindow(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)
        self.connect("destroy", gtk.main_quit)

        self.box = gtk.VBox()
        self.add(self.box)

        self.push_button = gtk.Button(label='push')
        self.push_button.connect('clicked', self.on_button_clicked)
        self.box.pack_start(self.push_button, True, True, 0)

        self.sbar = gtk.Statusbar()
        self.box.pack_start(self.sbar, True, True, 0)
        self.sbar_context_id = self.sbar.get_context_id('printer')

        self.pbar = gtk.ProgressBar()
        self.box.pack_start(self.pbar, True, True, 0)
        self.progress = 0.0

        self.timer = gobject.timeout_add(1000, self.pbar_timeout)

        self.show_all()

    def pbar_timeout(self):
        self.progress += 0.1
        self.pbar.set_fraction(self.progress)
        if self.progress < 1:
            return True
        else:
            return False

    def on_button_clicked(self, widget):
        self.sbar.push(self.sbar_context_id, str(self.progress))
        return

if __name__ == '__main__':
    # UI initialization
    win = MyWindow()
    gtk.main()