import os
import pygame
from pygame.locals import *
import random
import array2d

def load_sound(name):
    class NoneSound:
        def play(self): pass
    if not pygame.mixer:
        return NoneSound()
    fullname = os.path.join('sound', name)
    sound = pygame.mixer.Sound(fullname)
    return sound

def random_color():
    return (random.randint(0,255), random.randint(0,255), random.randint(0,255))

def xor(a,b):
    if bool(a) != bool(b):
        return True
    else:
        return False

class Difficulty:
    def __init__(self, name, width, height, mines, cellsize):
        self.name = name
        self.width = width
        self.height = height
        self.mines = mines
        self.cell_size = cellsize

class Mines:
    def __init__(self):
        self.difficulty = 1
        self.difficulties = []
        self.difficulties.append(Difficulty("Easy"  ,  9,  9, 10, 35))
        self.difficulties.append(Difficulty("Medium", 16, 16, 40, 25))
        self.difficulties.append(Difficulty("Expert", 30, 16, 99, 20))
        self.difficulties.append(Difficulty("More Expert", 40, 25, 200, 18))
        self.difficulties.append(Difficulty("Narrow", 30, 5, 28, 20))
        self.difficulties.append(Difficulty("More Narrow", 40, 4, 35, 18))
        self.difficulties.append(Difficulty("Ridiculous", 50, 30, 320, 15))

        self.SW = 800
        self.SH = 600

        self.w = self.difficulties[self.difficulty].width
        self.h = self.difficulties[self.difficulty].height
        self.cell_size = self.difficulties[self.difficulty].cell_size
        self.mines = self.difficulties[self.difficulty].mines
        self.flagged = 0

        self.grid = list((array2d.Array2d(self.w,self.h,0),array2d.Array2d(self.w,self.h,0),array2d.Array2d(self.w,self.h,0)))

        self.xoff = (self.SW/2)-(self.cell_size*self.w/2)
        self.yoff = (self.SH/2)-(self.cell_size*self.h/2)+10

        self.gameover = False
        self.win = False

        self.small_font = pygame.font.Font(pygame.font.get_default_font(),12)
        self.medium_font = pygame.font.Font(pygame.font.get_default_font(),18)
        self.large_font = pygame.font.Font(pygame.font.get_default_font(),24)

        self.colors_bg = list(((50,55,65), (80,85,95), (70,75,85)))
        self.colors_text = (155,155,160)
        self.colors_text_win = (120,230,120)
        self.colors_text_lose = (230,120,120)
        self.colors_flag = list(((100,50,50),(200,100,100)))
        self.colors_flag2 = list(((50,50,100),(100,100,200)))
        self.colors_unrevealed = (165, 165, 165)
        self.colors_highlight = (180, 185, 190)
        self.colors_notouch = (148, 175, 190)
        self.colors_notouch_win = (175, 195, 178)
        self.colors_revealed = (185, 185, 185)
        self.colors_mine = (215, 185, 185)
        self.colors_back = (40,40,45)
        self.colors_back_win = (75,85,78)
        self.colors_back_lose = (85,78,75)
        self.colors_numbers = (0,0,0)

        self.background = pygame.Surface((self.SW,self.SH))
        self.background.fill((222,222,0))
        self.background.fill(self.colors_bg[0])

        self.sound_boom = load_sound('boomboomuhoh.wav')
        self.sound_boop = load_sound('boop.wav')
        self.sound_beeh = load_sound('beeh.wav')
        self.sound_wsh = load_sound('wsh.wav')
        self.sound_yes = load_sound('yes.wav')

        for x in range(int(self.SW/16)):
            for y in range(int(self.SH/12)):
                if xor(x%2,y%2):
                    pygame.draw.rect(self.background, self.colors_bg[1], (int(x*(self.SW/16))+1, int(y*(self.SH/12))+1, int(self.SW/16)-2, int(self.SH/12)-2) )
                else:
                    pygame.draw.rect(self.background, self.colors_bg[2], (int(x*(self.SW/16))+1, int(y*(self.SH/12))+1, int(self.SW/16)-2, int(self.SH/12)-2) )

        self.screen = pygame.display.set_mode((self.SW, self.SH))
        pygame.display.set_caption('Mines - ' + self.difficulties[self.difficulty].name)

    def surrounding(self, pos):
        count = 0

        if pos[0]:
            if pos[1]:
                if self.grid[0].v[pos[0]-1][pos[1]-1]:
                    count += 1
            if self.grid[0].v[pos[0]-1][pos[1]]:
                count += 1
            if pos[1] < self.h-1:
                if self.grid[0].v[pos[0]-1][pos[1]+1]:
                    count += 1

        if pos[0] < self.w-1:
            if pos[1] < self.h-1:
                if self.grid[0].v[pos[0]+1][pos[1]+1]:
                    count += 1
            if self.grid[0].v[pos[0]+1][pos[1]]:
                count += 1
            if pos[1]:
                if self.grid[0].v[pos[0]+1][pos[1]-1]:
                    count += 1

        if pos[1]:
            if self.grid[0].v[pos[0]][pos[1]-1]:
                count += 1
        if pos[1] < self.h-1:
            if self.grid[0].v[pos[0]][pos[1]+1]:
                count += 1

        return count

    def check_win(self):
        self.win = True
        for x in range(self.w):
            for y in range(self.h):
                if not self.grid[0].v[x][y] == self.grid[2].v[x][y]:
                    self.win = False

        if self.win:
            self.gameover = True
            for x in range(self.w):
                for y in range(self.h):
                    if not self.grid[0].v[x][y]:
                        self.grid[1].v[x][y] = 1

    def reveal(self,x,y):
        if not self.gameover and not self.grid[2].v[x][y]:
            self.grid[1].v[x][y] = 1
        else:
            return 0

        if self.grid[0].v[x][y]:
            self.sound_boom.play()
            self.gameover = True

        if not self.surrounding((x,y)) and not self.gameover:
            if x and y and not self.grid[1].v[x-1][y-1]:
                self.reveal(x-1,y-1)
            if x and not self.grid[1].v[x-1][y]:
                self.reveal(x-1,y)
            if x and y < self.h-1 and not self.grid[1].v[x-1][y+1]:
                self.reveal(x-1,y+1)

            if x < self.w-1 and y and not self.grid[1].v[x+1][y-1]:
                self.reveal(x+1,y-1)
            if x < self.w-1 and not self.grid[1].v[x+1][y]:
                self.reveal(x+1,y)
            if x < self.w-1 and y < self.h-1 and not self.grid[1].v[x+1][y+1]:
                self.reveal(x+1,y+1)

            if y and not self.grid[1].v[x][y-1]:
                self.reveal(x,y-1)
            if y < self.h-1 and not self.grid[1].v[x][y+1]:
                self.reveal(x,y+1)

    def flag(self,x,y):
        if not self.gameover and not self.win and not self.grid[1].v[x][y]:
            if self.grid[2].v[x][y] == 1:
                self.grid[2].v[x][y] = 2
                self.flagged -= 1
            elif self.grid[2].v[x][y] == 2:
                self.grid[2].v[x][y] = 0
            else:
                self.sound_yes.play()
                self.grid[2].v[x][y] = 1
                self.flagged += 1
            self.check_win()

    def set_mines(self):
        for x in range(self.w):
            for y in range(self.h):
                self.grid[0].v[x][y] = 0
                self.grid[1].v[x][y] = 0
                self.grid[2].v[x][y] = 0

        done = False
        minesset = 0

        while not done:
            x = random.randint(0,(self.w-1))
            y = random.randint(0,(self.h-1))
            if self.grid[0].v[x][y] == 0:
                self.grid[0].v[x][y] = 1
                minesset += 1
            if minesset == self.mines:
                done = True

    def update_bg(self):
        self.background.fill((222,222,0))
        self.background.fill(self.colors_bg[0])
        for x in range(int(self.SW/16)):
            for y in range(int(self.SH/12)):
                if xor(x%2,y%2):
                    pygame.draw.rect(self.background, self.colors_bg[1], (int(x*(self.SW/16))+1, int(y*(self.SH/12))+1, int(self.SW/16)-2, int(self.SH/12)-2) )
                else:
                    pygame.draw.rect(self.background, self.colors_bg[2], (int(x*(self.SW/16))+1, int(y*(self.SH/12))+1, int(self.SW/16)-2, int(self.SH/12)-2) )

    def reset(self):
        self.win = False
        self.gameover = False
        self.flagged = 0

        self.w = self.difficulties[self.difficulty].width
        self.h = self.difficulties[self.difficulty].height
        self.cell_size = self.difficulties[self.difficulty].cell_size
        self.mines = self.difficulties[self.difficulty].mines

        self.xoff = (self.SW/2)-(self.cell_size*self.w/2)
        self.yoff = (self.SH/2)-(self.cell_size*self.h/2)+10

        self.grid = list((array2d.Array2d(self.w,self.h,0),array2d.Array2d(self.w,self.h,0),array2d.Array2d(self.w,self.h,0)))
        self.set_mines()
        pygame.display.set_caption('Mines - ' + self.difficulties[self.difficulty].name)

    def randomize_colors(self):
        self.colors_bg = list((random_color(), random_color(), random_color()))
        self.colors_text = random_color()
        self.colors_text_win = random_color()
        self.colors_text_lose = random_color()
        self.colors_flag = list((random_color(),random_color()))
        self.colors_flag2 = list((random_color(),random_color()))
        self.colors_unrevealed = random_color()
        self.colors_highlight = random_color()
        self.colors_notouch = random_color()
        self.colors_notouch_win = random_color()
        self.colors_revealed = random_color()
        self.colors_mine = random_color()
        self.colors_back = random_color()
        self.colors_back_win = random_color()
        self.colors_back_lose = random_color()
        self.colors_numbers = random_color()
        self.update_bg()

    def load_colors(self, fn = "default.color"):
        if os.path.exists(fn):
            fin = open(fn,"r")
            lines = fin.readlines()
            for line in lines:
                exec("self.colors_"+line)
            self.update_bg()
        else:
            print(fn + " does not exist.\n")

    def print_colors(self):
        print("bg = " + str(self.colors_bg))
        print("text = " + str(self.colors_text))
        print("text_win = " + str(self.colors_text_win))
        print("text_lose = " + str(self.colors_text_lose))
        print("flag = " + str(self.colors_flag))
        print("flag2 = " + str(self.colors_flag2))
        print("unrevealed = " + str(self.colors_unrevealed))
        print("highlight = " + str(self.colors_highlight))
        print("notouch = " + str(self.colors_notouch))
        print("notouch_win = " + str(self.colors_notouch_win))
        print("revealed = " + str(self.colors_revealed))
        print("mine = " + str(self.colors_mine))
        print("back = " + str(self.colors_back))
        print("back_win = " + str(self.colors_back_win))
        print("back_lose = " + str(self.colors_back_lose))
        print("numbers = " + str(self.colors_numbers))
        print("")

    def draw(self):
        self.screen.blit(self.background,(0,0))

        if self.win:
            pygame.draw.rect(self.screen, self.colors_back_win,(self.xoff-2, self.yoff-2, self.cell_size*self.w+4, self.cell_size*self.h+4))
        elif self.gameover:
            pygame.draw.rect(self.screen, self.colors_back_lose,(self.xoff-2, self.yoff-2, self.cell_size*self.w+4, self.cell_size*self.h+4))
        else:
            pygame.draw.rect(self.screen, self.colors_back,(self.xoff-2, self.yoff-2, self.cell_size*self.w+4, self.cell_size*self.h+4))

        for x in range(self.w):
            for y in range(self.h):
                if self.grid[1].v[x][y]:
                    if self.grid[0].v[x][y]:
                        pygame.draw.rect(self.screen, self.colors_mine, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                    else:
                        if self.surrounding((x,y)):
                            pygame.draw.rect(self.screen, self.colors_revealed, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                            self.screen.blit(self.small_font.render(str(self.surrounding((x,y))),True, self.colors_numbers),
                                             (self.xoff+x*self.cell_size+(self.cell_size/2)-(self.small_font.size(str(self.surrounding((x,y))))[0]/2)-1,
                                              self.yoff+y*self.cell_size+(self.cell_size/2)-(self.small_font.size(str(self.surrounding((x,y))))[1]/2)-1))
                        else:
                            if self.win:
                                pygame.draw.rect(self.screen, self.colors_notouch_win, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                            else:
                                pygame.draw.rect(self.screen, self.colors_notouch, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                else:
                    p = pygame.mouse.get_pos()
                    if (int((p[0] - self.xoff)/self.cell_size) == x) and (int((p[1] - self.yoff)/self.cell_size) == y):
                        pygame.draw.rect(self.screen, self.colors_highlight, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                    else:
                        pygame.draw.rect(self.screen,self.colors_unrevealed, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                    if self.grid[0].v[x][y] and self.gameover and not self.win:
                        pygame.draw.rect(self.screen, self.colors_mine, (self.xoff+x*self.cell_size, self.yoff+y*self.cell_size, self.cell_size-1, self.cell_size-1))
                    if self.grid[2].v[x][y] == 1:
                        pygame.draw.rect(self.screen, self.colors_flag[0], (self.xoff+x*self.cell_size+4, self.yoff+y*self.cell_size+4, self.cell_size-1-8, self.cell_size-1-8))
                        pygame.draw.rect(self.screen, self.colors_flag[1], (self.xoff+x*self.cell_size+5, self.yoff+y*self.cell_size+5, self.cell_size-1-10, self.cell_size-1-10))
                    if self.grid[2].v[x][y] == 2:
                        pygame.draw.rect(self.screen, self.colors_flag2[0], (self.xoff+x*self.cell_size+4, self.yoff+y*self.cell_size+4, self.cell_size-1-8, self.cell_size-1-8))
                        pygame.draw.rect(self.screen, self.colors_flag2[1], (self.xoff+x*self.cell_size+5, self.yoff+y*self.cell_size+5, self.cell_size-1-10, self.cell_size-1-10))

        if self.gameover:
            if not self.win:
                self.screen.blit(self.medium_font.render("GAME OVER", True, self.colors_text_lose), ((self.SW/2)-(self.medium_font.size("GAME OVER")[0]/2),10))
            else:
                self.screen.blit(self.medium_font.render("YOU WIN!", True, self.colors_text_win), ((self.SW/2)-(self.medium_font.size("YOU WIN!")[0]/2),10))
        self.screen.blit(self.medium_font.render("Change Difficulty With +/-",True, self.colors_text), (10,self.SH-25))

        pygame.draw.line(self.screen, self.colors_text, (10, 8+self.medium_font.size("Minesweeper")[1]), (10+self.medium_font.size("Minesweeper")[0], 8+self.medium_font.size("Minesweeper")[1]))
        self.screen.blit(self.medium_font.render("Minesweeper", True, self.colors_text), (10,10))
        self.screen.blit(self.medium_font.render("Difficulty: "+self.difficulties[self.difficulty].name, True, self.colors_text), (10,35))
        self.screen.blit(self.medium_font.render("Mines Left:"+str(self.mines-self.flagged), True, self.colors_text), (self.xoff, self.yoff - 25))

        if pygame.mouse.get_pos()[0] >= self.SW-25 and pygame.mouse.get_pos()[1] <= 25:
            pygame.draw.rect(self.screen, self.colors_highlight, (self.SW-25,0,25,25))
        else:
            pygame.draw.rect(self.screen, self.colors_unrevealed, (self.SW-25,0,25,25))
        self.screen.blit(self.medium_font.render("R", True, (50,50,50)), (self.SW-18, 4))

        pygame.display.flip()

    def play(self):
        self.set_mines()

        running = True
        while running:
            for event in pygame.event.get():
                if event.type == QUIT:
                    running = False
                elif event.type == MOUSEBUTTONDOWN:
                    if event.pos[0] >= self.SW-20 and event.pos[1] <= 20:
                        self.reset()
                    if event.pos[0] >= self.xoff and event.pos[0] < self.xoff+(self.cell_size*self.w):
                        if event.pos[1] >= self.yoff and event.pos[1] < self.yoff+(self.cell_size*self.h):
                            if event.button == 1:
                                self.reveal(int((event.pos[0]-self.xoff)/self.cell_size), int((event.pos[1]-self.yoff)/self.cell_size))
                                if not self.gameover:
                                    self.sound_wsh.play()
                            elif event.button == 3:
                                self.flag(int((event.pos[0]-self.xoff)/self.cell_size), int((event.pos[1]-self.yoff)/self.cell_size))
                elif event.type == KEYDOWN:
                    if event.key == K_KP1:
                        self.load_colors("color/1.color")
                    if event.key == K_KP2:
                        self.load_colors("color/2.color")
                    if event.key == K_KP3:
                        self.load_colors("color/3.color")
                    if event.key == K_KP4:
                        self.load_colors("color/4.color")
                    if event.key == K_KP5:
                        self.load_colors("color/5.color")
                    if event.key == K_KP6:
                        self.load_colors("color/6.color")
                    if event.key == K_KP7:
                        self.load_colors("color/7.color")
                    if event.key == K_KP8:
                        self.load_colors("color/8.color")
                    if event.key == K_KP9:
                        self.load_colors("color/9.color")

                    if event.key == K_r:
                        pygame.mixer.stop()
                        self.reset()
                    if event.key == K_z:
                        self.randomize_colors()
                    if event.key == K_x:
                        self.load_colors()
                    if event.key == K_c:
                        self.print_colors()
                    if event.key == K_KP_PLUS:
                        if self.difficulty < len(self.difficulties) - 1:
                            self.difficulty += 1
                            self.reset()
                        else:
                            self.difficulty = 0
                            self.reset()
                    if event.key == K_KP_MINUS:
                        if self.difficulty > 0:
                            self.difficulty -= 1
                            self.reset()
                        else:
                            self.difficulty = len(self.difficulties)-1
                            self.reset()
            self.draw()

def main():
    pygame.init()
    game = Mines()
    game.play()
    pygame.quit()

if __name__ == '__main__':
    main()


'''Simple code to plot buoy trajectories'''

import sys
import os.path
from glob import glob
from datetime import datetime, timedelta
import argparse
from argparse import RawDescriptionHelpFormatter

import numpy as np
import matplotlib
#matplotlib.use('agg')
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors as colors
import matplotlib.lines as mlines
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from netCDF4 import Dataset
import cmocean

from grid_info import region_params, valid_regions

def parse_args():

    p = argparse.ArgumentParser("traj_plot",
                                formatter_class=RawDescriptionHelpFormatter)
    p.add_argument('trajinp',
                   help="Either a NetCDF file with selected trajectories or a directory with the individual NetCDF trajectory files")
    p.add_argument('-r', '--region', required=False, choices=valid_regions,
                   default='nh',
                   help="Region to plot")
    p.add_argument('-s', '--startdate', required=False, default='19910101',
                   help="Start date of the time range to plot")
    p.add_argument('-e', '--enddate', required=False, default='20201231',
                   help="End date of the timerange to plot")
    p.add_argument('-o', '--output', required=False, default='.',
                   help="Either an output directory or a full filepath")
    p.add_argument('-l', '--label_traj', action='store_true', default=False,
                   help="Label trajectories with buoy ID")
    p.add_argument('-c', '--col_time', action='store_true', default=False,
                   help="Color trajectory with time (default is by program)")

    args = p.parse_args()

    return args

def val_get_netw_color(netw):
    netw = netw.lower()
    # NH
    if netw in ['aari', 'AAR']:
        color = 'red'
    elif netw in ['argos', 'argo', 'ARG']:
        color = 'orange'
    elif netw in ['bbb', 'BBB']:
        color = 'yellow'
    elif netw in ['crrel', 'crre', 'CRR']:
        color = 'lime'
    elif netw in ['dtu', 'DTU']:
        color = 'mint'
    elif netw in ['huds', 'HUD']:
        color = 'olive'
    elif netw in ['iabp', 'IAB']:
        color = 'green'
    elif netw in ['itp', 'ITP']:
        color = 'blue'
    elif netw in ['sams', 'SAM']:
        color = 'cyan'
    elif netw in ['sedna', 'sedn', 'SED']:
        color = 'purple'
    elif netw in ['simba', 'simb', 'SIM']:
        color = 'lavendar'
    elif netw in ['sip', 'SIP']:
        color = 'magenta'
    elif netw in ['tara', 'TAR']:
        color = 'teal'
    # SH
    elif netw in ['antsid', 'ants', 'ANT']:
        color = 'maroon'
    # Undefined
    else:
        color = 'black'
        print("WARNING: Unsupported network value <{}>. Default to {}."
              "".format(netw, color))

    return color

def traj_read(ncfile, sdate, edate, hemi):

    ncdata = {}

    # Reading from the NetCDF file
    with Dataset(ncfile, 'r') as dataset:

        try:
            ncdata['tc_start'] = datetime.strptime(dataset.start_date_and_time,
                                                   '%Y-%m-%d %H:%M:%S')
        except:
            ncdata['tc_start'] = datetime.strptime(dataset.time_coverage_start,
                                                   '%Y-%m-%dT%H:%M:%SZ')
        try:
            ncdata['tc_end'] = datetime.strptime(dataset.end_date_and_time,
                                                 '%Y-%m-%d %H:%M:%S')
        except:
            ncdata['tc_end'] = datetime.strptime(dataset.time_coverage_end,
                                                 '%Y-%m-%dT%H:%M:%SZ')

        # Check that the file covers the correct hemisphere and return None
        # if not
        if dataset.area == 'Northern Hemisphere':
            area = 'nh'
        elif dataset.area == 'Southern Hemisphere':
            area = 'sh'
        else:
            raise ValueError('Unrecognised region in ncfile')
        if not area == hemi:
            return None

        # Check that the file covers the required time period and return None
        # if not
        if ncdata['tc_start'] >= edate or ncdata['tc_end'] <= sdate:
            return None

        # Read the variable data
        ncdata['name'] = dataset.variables['trajectory'].long_name
        timevarsec = dataset.variables['time'][:]
        latvar = np.array(dataset.variables['lat'][:])
        lonvar = np.array(dataset.variables['lon'][:])

        # Other useful data
        ncdata['id'] = dataset.id
        ncdata['network'] = dataset.network

    # Convert seconds since 1970-01-01 to a python datetime
    timevar = np.array([datetime.fromtimestamp(t) for t in timevarsec])

    # Select only the relevent trajectory data
    select = np.logical_and(timevar >= sdate, timevar <= edate)
    ncdata['time'] = timevar[select]
    ncdata['lat'] = latvar[select]
    ncdata['lon'] = lonvar[select]

    # Create a timedelta variable
    ncdata['timedelta'] = np.array([(t - sdate).total_seconds()
                                    for t in ncdata['time']])
    ncdata['period'] = (edate - sdate).total_seconds() / 86400.
#    ncdata['lapsed'] = np.array([t / period for t in ncdata['timedelta']])
    # Use lapsed in terms of number of days
    # Seconds in a day = 86400
    ncdata['lapsed'] = np.array([t / 86400 for t in ncdata['timedelta']])

    # Other useful data
    ncdata['filename'] = ncfile

    return ncdata

def comb_trajfile_read(trajinp):

    # Reading data from the NetCDF file
    with Dataset(trajinp, 'r') as dataset:

        sdate = datetime.strptime(dataset.start_date_and_time,
                                  '%Y-%m-%d %H:%M:%S')
        edate = datetime.strptime(dataset.end_date_and_time,
                                  '%Y-%m-%d %H:%M:%S')
        period = (edate - sdate).total_seconds() / 86400.
        secs_to_1970 = (sdate
                        - datetime.strptime('19700101000000',
                                            '%Y%m%d%H%M%S')).total_seconds()

        slen = dataset.dimensions['station'].size

        ids = dataset['id'][:]
        networks = dataset['network'][:]
        times_sec = dataset['time'][:]
        lats = dataset['lat'][:]
        lons = dataset['lon'][:]

    # Compiling the data into a dictionary
    tjs = {}
    for i in range(slen):
        tj = {}
        tj['id'] = ''.join([b.decode() for b in ids[i, :]]).strip()
        tj['network'] = ''.join([n.decode() for n in networks[i, :]]).strip()

        # Create a timedelta variable
        tj['timedelta'] = np.array(times_sec[i, :])
        tj['lapsed'] = np.array([t / 86400 for t in tj['timedelta']])
        tj['period'] = period

        # Convert seconds since start date to seconds since 1970-01-01
        # and then to a python datetime
        tj['time'] = np.array([datetime.fromtimestamp(t + secs_to_1970)
                               for t in tj['timedelta']])

        tj['lat'] = lats[i, :]
        tj['lon'] = lons[i, :]
        tjs[i] = tj

    return tjs, sdate, edate

def traj_plot(trajinp, region, startdate, enddate, output, label_traj,
              col_time):

    # Finding the region parameters
    rp = region_params(region)
    if rp['lllat'] > 0.:
        hemi = 'nh'
    else:
        hemi = 'sh'
    if region.startswith('ease'):
        gridtype = 'ease'
    elif region.startswith('pol'):
        gridtype = 'polstere'
    else:
        raise ValueError("Unrecognised grid type for region {} - "
                         "expected to start with 'ease' or 'pol'".format(region))
    # Define grid based on region
    if gridtype == 'polstere':
        if hemi == 'nh':
            plot_proj4_params = {'proj': 'stere',
                                 'lat_0': 90.,
                                 'lat_ts' : 70.,
                                 'lon_0': -45.0,
                                 'a': 6378273,
                                 'b': 6356889.44891}
            plot_globe = ccrs.Globe(semimajor_axis=plot_proj4_params['a'],
                                    semiminor_axis=plot_proj4_params['b'])
            plot_crs = ccrs.NorthPolarStereo(
                central_longitude=plot_proj4_params['lon_0'], globe=plot_globe)
        else:
            plot_proj4_params = {'proj': 'stere',
                                 'lat_0': -90.,
                                 'lat_ts' : -70.,
                                 'lon_0': 0.,
                                 'a': 6378273,
                                 'b': 6356889.44891}
            plot_globe = ccrs.Globe(semimajor_axis=plot_proj4_params['a'],
                                    semiminor_axis=plot_proj4_params['b'])
            plot_crs = ccrs.SouthPolarStereo(
                central_longitude=plot_proj4_params['lon_0'], globe=plot_globe)
    elif gridtype == 'ease':
        if hemi == 'nh':
            plot_crs = ccrs.LambertAzimuthalEqualArea(central_longitude=0,
                                                      central_latitude=90,
                                                      false_easting=0,
                                                      false_northing=0)
        else:
            plot_crs = ccrs.LambertAzimuthalEqualArea(central_longitude=0,
                                                      central_latitude=-90,
                                                      false_easting=0,
                                                      false_northing=0)
    else:
        raise ValueError("Unrecognised region {}".format(region))

    pc = ccrs.PlateCarree()

    # Defining the plot corners
    llx, lly = plot_crs.transform_point(rp['lllon'], rp['lllat'], src_crs=pc)
    urx, ury = plot_crs.transform_point(rp['urlon'], rp['urlat'], src_crs=pc)

    # The projection keyword determines how the plot will look
    plt.figure(figsize=(8, 6))
    ax = plt.axes(projection=plot_crs)

    # Setting the region
    ax.set_extent([llx, urx, lly, ury], crs=plot_crs)

    # Converting the start and end dates to Python datetimes (using the
    # entire day of the second date)
    sdate = datetime.strptime(startdate, '%Y%m%d')
    edate = datetime.strptime(enddate + '235959', '%Y%m%d%H%M%S')

    # Reading the trajectories
    if os.path.isdir(trajinp):

        index = 0
        tjs = {}
        flist = glob(os.path.join(trajinp, '*.nc'))
        #print("flist = ", flist)
        for fl in flist:
            print("Checking file: {}".format(fl))

            #try:
            trajdata = traj_read(fl, sdate, edate, hemi)
            #except:
            #    print("Problem reading trajectory from {}".format(fl))
            #    continue
            if trajdata is not None:
                tjs[index] = trajdata
                index += 1
                print("Data found in file...")

    elif os.path.isfile(trajinp):
        # Check if this is a single trajectory or a buoy collection
        single = False
        with Dataset(trajinp, 'r') as dataset:
            if 'source' in dataset.__dict__.keys():
                if dataset.source == "convert_buoy_txt2netcdf.py":
                    single = True
                    sdate = datetime.strptime(dataset.time_coverage_start,
                                              '%Y-%m-%dT%H:%M:%SZ')
                    edate = datetime.strptime(dataset.time_coverage_end,
                                              '%Y-%m-%dT%H:%M:%SZ')
        if single:
            tjs = {}
            tjs[0] = traj_read(trajinp, sdate, edate, hemi)
            #sdate = tjs[0]['tc_start']
            #edate = tjs[0]['tc_end']
        else:
            tjs, sdate, edate = comb_trajfile_read(trajinp)

    else:
        raise ValueError("{} is not a valid file or directory".format(trajinp))

    # Checking if there is valid data
    if len(tjs) == 0:
        print("No valid trajectory data to plot, exiting.")
        sys.exit(0)

    # Setting the colourmap
    cmap_time = cm.get_cmap('Spectral_r')
    period = tjs[0]['period']
    cb = ax.imshow(np.array([[0.0, 0.0], [0.0, 0.0]]), cmap=cmap_time,
                   vmin=0, vmax=period)

    # Plotting the trajectories
    netw_keys = []
    for i, tj in enumerate(tjs):

        # Default is to use network color
        netw = tjs[i]['network']
        col = val_get_netw_color(netw)

# PLOTTING VECTORS
#        for j in range(tjs[i]['lon'].size):
#
#            # If colouring by datetime, set this here per point
#            if col_time:
#                col = cmap_time(tjs[i]['lapsed'][j] / period)
#
#            x1, y1 = plot_crs.transform_point(tjs[i]['lon'][j],
#                                              tjs[i]['lat'][j], src_crs=pc)
#
#            # Label
#            if label_traj and j == 0:
#                label = '{}-{}'.format(tjs[i]['id'], tjs[i]['network'])
#                if ((x1 > llx) and (x1 < urx) and (y1 > lly) and (y1 < ury)):
#                    ax.text(x1, y1, label, fontsize='small')
#
#            # Plot the drift
#            if j > 0:
#                ax.plot([x0, x1], [y0, y1], color=col)
#                if not netw in netw_keys:
#                    netw_keys.append(netw)
#            x0 = x1
#            y0 = y1

        for j in range(tjs[i]['lon'].size):

            # If colouring by datetime, set this here per point
            if col_time:
                col = cmap_time(tjs[i]['lapsed'][j] / period)

            x0, y0 = plot_crs.transform_point(tjs[i]['lon'][j],
                                              tjs[i]['lat'][j], src_crs=pc)

            # Label
            if label_traj and j == 0:
                label = '{}-{}'.format(tjs[i]['id'], tjs[i]['network'])
                #if ((x1 > llx) and (x1 < urx) and (y1 > lly) and (y1 < ury)):
                ax.text(x0, y0, label, fontsize='small')

            # Plot the drift
            ax.plot(x0, y0, '-o', markersize=2, color=col)
            if not netw in netw_keys:
                netw_keys.append(netw)

    if col_time:
        # For the lines coloured by time, add the colourbar
        if period <= 10:
            cmap_lvl = np.arange(0, period)
        elif period > 10 and period <= 100:
            cmap_lvl = np.arange(0, period, 10)
        elif period > 100 and period <= 1000:
            cmap_lvl = np.arange(0, period, 100)
        else:
            cmap_lvl = np.arange(0, period, 1000)
        plt.colorbar(cb, ticks=cmap_lvl,
                     orientation='horizontal', pad=0.05, shrink=0.4
        ).set_label('Elapsed time (days)')
    else:
        # For the lines coloured by network, add the key
        handles = []
        for n in netw_keys:
            col = val_get_netw_color(n)
            handles.append(mlines.Line2D([], [], color=col, label=n))
        ax.legend(handles=handles)

    # Coastlines
#    ax.add_feature(cfeature.NaturalEarthFeature('physical', 'land',
#                                                '50m', edgecolor='black',
#                                                facecolor='white'))
    ax.add_feature(cfeature.NaturalEarthFeature('physical', 'land',
                                                '50m', edgecolor='grey',
                                                facecolor='grey'))
    ice_shelves = cfeature.NaturalEarthFeature(
        category='physical',
        name='antarctic_ice_shelves_polys',
        scale='10m',
        facecolor='grey',
        edgecolor='grey')
    ax.add_feature(ice_shelves

)

    # Title
    title = '{}-{} {}'.format(datetime.strftime(sdate, '%Y%m%d'),
                              datetime.strftime(edate, '%Y%m%d'),
                              rp['long_name'])
    plt.title(title, fontsize='small')

    # If the path given is only a directory, add a filename. Otherwise
    # we assume it is a valid filepath
    if os.path.isdir(output):
        name = 'trajectories.png'
        output = os.path.join(output,name)

    plt.savefig(output,bbox_inches='tight')
#    plt.show()
    plt.close()
    print("Figure is in {}".format(output))

def main():

    args = parse_args()
    trajinp = args.trajinp
    region = args.region
    startdate = args.startdate
    enddate = args.enddate
    output = args.output
    label_traj = args.label_traj
    col_time = args.col_time

    traj_plot(trajinp, region, startdate, enddate, output, label_traj, col_time)

if __name__ == '__main__':

    main()


from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index),
    url(r'^users$', views.users),
    url(r'^process$', views.process),
    url(r'^user_interests/(?P<user_id>\d+)$', views.user_interests)

]


import boto3

session = boto3.Session(region_name='us-east-1')
client = session.client(service_name='ec2')
all_regions = client.describe_regions()

region_list = ['us-east-1', ]
list_of_regions = []
for each_reg in all_regions['Regions']:
    list_of_regions.append(each_reg['RegionName'])
print(list_of_regions)

for each_reg in list_of_regions:
    session = boto3.Session(region_name=each_reg)
    resource = session.resource(service_name='ec2')
    if each_reg in region_list:
        print("List of services: ", each_reg)
        for each_in_vpc in resource.vpcs.all():
            print(client.describe_vpcs(VpcIds=[each_in_vpc.id]))
        print('-------------------------------------------------------------------')
        for each_in_subnet in resource.subnets.all():
            print(client.describe_subnets(SubnetIds=[each_in_subnet.id]))
        print('-------------------------------------------------------------------')
        for each_in_IGW in resource.internet_gateways.all():
            print(client.describe_internet_gateways(InternetGatewayIds=[each_in_IGW.id]))
        print('-------------------------------------------------------------------')
        for each_in_sg in resource.security_groups.all():
            print(client.describe_security_groups(GroupIds=[each_in_sg.id]))


import streamlit as st
import pandas as pd
from PIL import Image
import warnings
warnings.filterwarnings("ignore")
from pickle import load
import numpy as np

st.markdown("# :mushroom: Classifier : :fork_and_knife: or :skull:?")
image = Image.open("mushrooms.jpg")
st.image(image, caption="Mushrooms", use_column_width="auto")

mappings = load(open("mushroom_encoder.pkl", "rb"))

st.write('<style>div.row-widget.stRadio > div{flex-direction:row;justify-content: center;} </style>', unsafe_allow_html=True)
st.write('<style>div.st-bf{flex-direction:column;} div.st-ag{font-weight:bold;padding-right:30px;}</style>', unsafe_allow_html=True)

st.header("cap-shape: ")
if "cap-shape" not in st.session_state:
    st.session_state["cap-shape"] = ""
encoder_cap_shape = {v: int(k) for k,v in mappings[1].items()}
dico_cap_shape = {"bell": "b", "conical": "c", "convex": "x", "flat": "f", "knobbed": "k", "sunken": "s"}
st.session_state["cap-shape"] = encoder_cap_shape[dico_cap_shape[st.radio("",("bell", "conical", "convex", "flat", "knobbed", "sunken"))]]

st.header("cap-surface: ")
if "cap-surface" not in st.session_state:
    st.session_state["cap-surface"] = ""
encoder_cap_surface = {v: int(k) for k,v in mappings[2].items()}
dico_cap_surface = {"fibrous": "f","grooves":"g","scaly":"y","smooth":"s"}
st.session_state["cap-surface"] = encoder_cap_surface[dico_cap_surface[st.radio("",("fibrous","grooves","scaly", "smooth"))]]

st.header("cap-color: ")
if "cap-surface" not in st.session_state:
    st.session_state["cap-color"] = ""
encoder_cap_color = {v: int(k) for k,v in mappings[3].items()}
dico_cap_color = {"brown":"n","buff":"b","cinnamon":"c","gray":"g","green":"r","pink":"p","purple":"u","red":"e","white":"w","yellow":"y"}
st.session_state["cap-color"] = encoder_cap_color[dico_cap_color[st.radio("",("brown", "buff", "cinnamon", "gray", "green", "pink", "purple", "red", "white", "yellow"))]]

st.header("bruises?: ")
if "bruises?" not in st.session_state:
    st.session_state["bruises?"] = ""
encoder_bruises = {v: int(k) for k,v in mappings[4].items()}
dico_bruises = {"yes":"t","no":"f"}
st.session_state["bruises?"] = encoder_bruises[dico_bruises[st.radio("",("yes", "no"))]]

st.header("odor: ")
if "odor" not in st.session_state:
    st.session_state["odor"] = ""
encoder_odor = {v: int(k) for k,v in mappings[5].items()}
dico_odor = {"almond":"a","anise":"l","creosote":"c","fishy":"y","foul":"f","musty":"m","none":"n","pungent":"p","spicy":"s"}
st.session_state["odor"] = encoder_odor[dico_odor[st.radio("",("almond", "anise", "creosote", "fishy", "foul", "musty", "none", "pungent", "spicy"))]]

st.header("gill-attachment: ")
if "gill-attachment" not in st.session_state:
    st.session_state["gill-attachment"] = ""
encoder_gill_attachment = {v: int(k) for k,v in mappings[6].items()}
dico_gill_attachment = {"attached":"a","free":"f"}
st.session_state["gill-attachment"] = encoder_gill_attachment[dico_gill_attachment[st.radio("",("attached", "free"))]]

st.header("gill-spacing: ")
if "gill-spacing" not in st.session_state:
    st.session_state["gill-spacing"] = ""
encoder_gill_spacing = {v: int(k) for k,v in mappings[7].items()}
dico_gill_spacing = {"close": "c", "crowded": "w"}
st.session_state["gill-spacing"] = encoder_gill_spacing[dico_gill_spacing[st.radio("",("close", "crowded"))]]

st.header("gill-size: ")
if "gill-size" not in st.session_state:
    st.session_state["gill-size"] = ""
encoder_gill_size = {v: int(k) for k,v in mappings[8].items()}
dico_gill_size = {"broad":"b","narrow":"n"}
st.session_state["gill-size"] = encoder_gill_size[dico_gill_size[st.radio("",("broad", "narrow"))]]

st.header("gill-color: ")
if "gill-color" not in st.session_state:
    st.session_state["gill-color"] = ""
encoder_gill_color = {v: int(k) for k,v in mappings[9].items()}
dico_gill_color = {"black":"k","brown":"n","buff":"b","chocolate":"h","gray":"g","green":"r","orange":"o","pink":"p","purple":"u","red":"e","white":"w","yellow":"y"}
st.session_state["gill-color"] = encoder_gill_color[dico_gill_color[st.radio("",("black", "brown", "buff", "chocolate", "gray", "green", "orange", "pink", "purple", "red", "white", "yellow"))]]

st.header("stalk-shape: ")
if "stalk-shape" not in st.session_state:
    st.session_state["stalk-shape"] = ""
encoder_stalk_shape = {v: int(k) for k,v in mappings[10].items()}
dico_stalk_shape = {"enlarging": "e", "tapering": "t"}
st.session_state["stalk-shape"] = encoder_stalk_shape[dico_stalk_shape[st.radio("",("enlarging", "tapering"))]]

st.header("stalk-root: ")
if "stalk-root" not in st.session_state:
    st.session_state["stalk-root"] = ""
encoder_stalk_root = {v: int(k) for k,v in mappings[11].items()}
dico_stalk_root = {"bulbous":"b","club":"c","equal":"e","rooted":"r","missing":"?"}
st.session_state["stalk-root"] = encoder_stalk_root[dico_stalk_root[st.radio("",("bulbous", "club", "equal", "rooted", "missing"))]]

st.header("stalk-surface-above-ring: ")
if "stalk-surface-above-ring" not in st.session_state:
    st.session_state["stalk-surface-above-ring"] = ""
encoder_stalk_surface_above_ring = {v: int(k) for k,v in mappings[12].items()}
dico_stalk_surface_above_ring = {"fibrous":"f","scaly":"y","silky":"k","smooth":"s"}
st.session_state["stalk-surface-above-ring"] = encoder_stalk_surface_above_ring[dico_stalk_surface_above_ring[st.radio("",("fibrous", "scaly", "silky", "smooth"))]]

st.header("stalk-surface-below-ring: ")
if "stalk-surface-below-ring" not in st.session_state:
    st.session_state["stalk-surface-below-ring"] = ""
encoder_stalk_surface_below_ring = {v: int(k) for k,v in mappings[13].items()}
st.session_state["stalk-surface-below-ring"] = encoder_stalk_surface_below_ring[dico_stalk_surface_above_ring[st.radio(" ",("fibrous", "scaly", "silky", "smooth"))]]

st.header("stalk-color-above-ring: ")
if "stalk-color-above-ring" not in st.session_state:
    st.session_state["stalk-color-above-ring"] = ""
encoder_stalk_color_above_ring = {v: int(k) for k,v in mappings[14].items()}
dico_stalk_color_above_ring = {"brown":"n","buff":"b","cinnamon":"c","gray":"g","orange":"o","pink":"p","red":"e","white":"w","yellow":"y"}
st.session_state["stalk-color-above-ring"] = encoder_stalk_color_above_ring[dico_stalk_color_above_ring[st.radio("",("brown", "buff", "cinnamon", "gray", "orange", "pink", "red", "white", "yellow"))]]

st.header("stalk-color-below-ring: ")
if "stalk-color-below-ring" not in st.session_state:
    st.session_state["stalk-color-below-ring"] = ""
encoder_stalk_color_below_ring = {v: int(k) for k,v in mappings[15].items()}
st.session_state["stalk-color-below-ring"] = encoder_stalk_color_below_ring[dico_stalk_color_above_ring[st.radio(" ",("brown", "buff", "cinnamon", "gray", "orange", "pink", "red", "white", "yellow"))]]

st.header("veil-type: ")
if "veil-type" not in st.session_state:
    st.session_state["veil-type"] = ""
encoder_veil_type = {v: int(k) for k,v in mappings[16].items()}
dico_veil_type = {"partial":"p","universal":"u"}
st.session_state["veil-type"] = encoder_veil_type[dico_veil_type[st.radio("",("partial", "universal"))]]

st.header("veil-color: ")
if "veil-color" not in st.session_state:
    st.session_state["veil-color"] = ""
encoder_veil_color = {v: int(k) for k,v in mappings[17].items()}
dico_veil_color = {"brown":"n","orange":"o","white":"w","yellow":"y"}
st.session_state["veil-color"] = encoder_veil_color[dico_veil_color[st.radio("",("brown", "orange", "white", "yellow"))]]

st.header("ring-number: ")
if "ring-number" not in st.session_state:
    st.session_state["ring-number"] = ""
encoder_ring_number = {v: int(k) for k,v in mappings[18].items()}
dico_ring_number = {"none":"n","one":"o","two":"t"}
st.session_state["ring-number"] = encoder_ring_number[dico_ring_number[st.radio("",("none", "one", "two"))]]

st.header("ring-type: ")
if "ring-type" not in st.session_state:
    st.session_state["ring-type"] = ""
encoder_ring_type = {v: int(k) for k,v in mappings[19].items()}
dico_ring_type = {"evanescent":"e","flaring":"f","large":"l","none":"n","pendant":"p"}
st.session_state["ring-type"] = encoder_ring_type[dico_ring_type[st.radio("",("evanescent", "flaring", "large", "none", "pendant"))]]

st.header("spore-print-color: ")
if "spore-print-color" not in st.session_state:
    st.session_state["spore-print-color"] = ""
encoder_spore_print_color = {v: int(k) for k,v in mappings[20].items()}
dico_spore_print_color = {"black":"k","brown":"n","buff":"b","chocolate":"h","green":"r","orange":"o","purple":"u","white":"w","yellow":"y"}
st.session_state["spore-print-color"] = encoder_spore_print_color[dico_spore_print_color[st.radio("",("black", "brown", "buff", "chocolate", "green", "orange", "purple", "white", "yellow"))]]

st.header("population: ")
if "population" not in st.session_state:
    st.session_state["population"] = ""
encoder_population = {v: int(k) for k,v in mappings[21].items()}
dico_population = { "abundant":"a","clustered":"c","numerous":"n","scattered":"s","several":"v","solitary":"y"}
st.session_state["population"] = encoder_population[dico_population[st.radio("",("abundant", "clustered", "numerous", "scattered", "several", "solitary"))]]

st.header("habitat: ")
if "habitat" not in st.session_state:
    st.session_state["habitat"] = ""
encoder_habitat = {v: int(k) for k,v in mappings[22].items()}
dico_habitat = {"grasses":"g","leaves":"l","meadows":"m","paths":"p","urban":"u","waste":"w","woods":"d"}
st.session_state["habitat"] = encoder_habitat[dico_habitat[st.radio("",("grasses", "leaves", "meadows", "paths", "urban", "waste", "woods"))]]

image2 = Image.open("dish.jpg")
st.image(image2, caption="Bon appétit!", use_column_width="auto")

model = load(open("mushroom_pipeline.pkl", "rb"))

X_test = [
    st.session_state["cap-shape"],
    st.session_state["cap-surface"],
    st.session_state["cap-color"],
    st.session_state["bruises?"],
    st.session_state["odor"],
    st.session_state["gill-attachment"],
    st.session_state["gill-spacing"],
    st.session_state["gill-size"],
    st.session_state["gill-color"],
    st.session_state["stalk-shape"],
    st.session_state["stalk-root"],
    st.session_state["stalk-surface-above-ring"],
    st.session_state["stalk-surface-below-ring"],
    st.session_state["stalk-color-above-ring"],
    st.session_state["stalk-color-below-ring"],
    st.session_state["veil-type"],
    st.session_state["veil-color"],
    st.session_state["ring-number"],
    st.session_state["ring-type"],
    st.session_state["spore-print-color"],
    st.session_state["population"],
    st.session_state["habitat"]
]

if st.button("Make prediction!"):
    prediction = model.predict([np.asarray(X_test)])

    if prediction == 0:
        st.markdown("### :fork_and_knife: The mushroom is edible!")
    else:
        st.markdown("### :skull: The mushroom is poisonous be careful!")
else:
    pass

if st.button("Reset"):
    pass


# Test on new pics

import torch
from torchvision import models, datasets, transforms
#import argparse
import torchvision.transforms.functional as func
import torchvision.utils as utils
from PIL import Image
from data_loader import ClassLoader

'''
parser = argparse.ArgumentParser()
parser.add_argument('--loadfrom', default='', help='The model to load')
parser.add_argument('--image', default='', help='The image to classify')
parser.add_argument('--folder', default='', help='A whole folder of images to classify')
opt = parser.parse_args()

network = models.alexnet(pretrained=False, num_classes=185)
network.load_state_dict(torch.load(opt.loadfrom))
network.eval()
'''

class CustomImageFolder(datasets.ImageFolder):
    def __init__(self, location, transform=None):
        super(CustomImageFolder,self).__init__(location, transform=transform)

    def __getitem__(self, index):
        x, y = super(CustomImageFolder, self).__getitem__(index)
        return {'image':x, 'class':y, 'file':self.imgs[index][0]}

def test_on_new(network, opt):
    folder = opt['folder']
    image = opt['image']
    classes = ClassLoader()

    if folder != '':
        dset = CustomImageFolder(folder, transform=transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor()]))
        loader = torch.utils.data.DataLoader(dset, batch_size=1, shuffle=False, num_workers=1)
    #   iterator=loader.__iter__()
    #   sample = iterator.next()
    #   print(sample)
        allIms = []
        for i, data in enumerate(loader):
            image = data['image']
            allIms.append(image)
            file = data['file'][0]
    #       func.to_pil_image(image.squeeze()).show(title=file) #visualize the image #title doesn't work
            result = network(image).squeeze()
            _, arg = torch.max(result,0)
            arg = arg.item()
            print("File: {0}, Index: {1}, Classification: {2}".format(file, arg, classes.ind2str(arg).replace('_',' ')))
        imBatch = torch.cat(allIms)
        printIms = utils.make_grid(imBatch)
        utils.save_image(printIms, './collected_images.png')

    elif image != '':
        image = Image.open(image)
        image = func.resize(image, (224,224))
        image.show()
        image = func.to_tensor(image).unsqueeze(0)

        result = network(image).squeeze()

        _, arg = torch.max(result,0)
        arg = arg.item()

        print("Index: {0}, Classification: {1}".format(arg,classes.ind2str(arg).replace('_',' ')))

    else:
        print("Invalid image or folder")


import time
import serial

# Start the timer
start_time = time.time()

# Define the port and baudrate for the printer and the accelerometer
printer_port = '/dev/ttyUSB0'
accelerometer_port = '/dev/ttyACM0'
baudrate = 115200

# Open serial connections
printer_ser = serial.Serial(printer_port, baudrate, timeout=1)
accelerometer_ser = serial.Serial(accelerometer_port, baudrate, timeout=1)

# Allow some time for printer to initialize
time.sleep(2)

# Define different amplitudes
amplitudes = [0.1, 0.2, 0.5, 1, 2, 5, 10]

# Define speed
speed = 5000  # Maximum speed in mm/min

# Set relative positioning
printer_ser.write(b'G91\n')

# Shake the bed
for i, amplitude in enumerate(amplitudes):
    # so plot starts flat
    time.sleep(0.5)

    for _ in range(5):  # Repeat each amplitude 5 times
        # Move in positive Y direction
        printer_ser.write('G1 Y{} F{}\n'.format(amplitude, speed).encode())
        time.sleep(amplitude/speed*60)  # Wait for the movement to complete

        # Move in negative Y direction
        printer_ser.write('G1 Y-{} F{}\n'.format(amplitude, speed).encode())
        time.sleep(amplitude/speed*60)  # Wait for the movement to complete

    # Read accelerometer data
    end_time = time.time() + 3  # Define end time 3 seconds from now
    data = ""  # Initialize data string
    while time.time() < end_time:
        data += accelerometer_ser.read(accelerometer_ser.in_waiting).decode()

    # Write all data to a text file
    with open(f'accelerometer_data_{amplitude}.csv', 'w') as file:
        file.write(data)

# Set back to absolute positioning
printer_ser.write(b'G90\n')

# Close the printer serial connection
printer_ser.close()

# Close accelerometer serial connection
accelerometer_ser.close()


import setuptools

with open('README.md', 'r') as fh:
    long_description = fh.read()

setuptools.setup(
    name='res2desc',  # Replace with your own username
    version='0.1.0',
    author='Bonan Zhu',
    author_email='zhubonan@outlook.com',
    description=
    'Commandline interface for computing descriptors for AIRSS search results',
    long_description=long_description,
    long_description_content_type='text/markdown',
    url='https://github.com/zhubonan/res2desc',
    packages=setuptools.find_packages(),
    classifiers=[
        'Programming Language :: Python :: 3',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
    ],
    install_requires=['dscribe>=0.3.5', 'click>=7.0', 'tabulate', 'pandas'],
    extras_require={'testing': ['pytest>=5.0']},
    python_requires='>=3.6',
    entry_points='''
        [console_scripts]
        res2desc=res2desc.cli:cli
    ''')


# -*- coding: utf-8 -*-
"""
Created on Thu May 23 15:31:52 2019

@author: David
"""

#import mysql.connector
import requests
from bs4 import BeautifulSoup
import pandas as pd
import re

def convert_dms_to_decimal(dms):
    decimal = (((int(dms[2]) /60) + int(dms[1]))/60 + int(dms[0]))
    return decimal

url = 'https://de.wikipedia.org/wiki/Liste_der_Berliner_U-Bahnh%C3%B6fe'
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'lxml')

stationTable = soup.findAll('table',{"class":"wikitable sortable"})[1]
stationTable_rows = stationTable.find_all('tr')

stations_data = []

stationTable_rows = stationTable_rows[1:]

for num, element in enumerate(stationTable_rows, start =0):

    station_data = []
    td = stationTable_rows[num].find_all('td')

    for num, element in enumerate(td[0].find_all('a'), start=0):
        if num == 1:
            geodata = element.getText()
            geodata_split = geodata.split(',')
            geodata_split[0]
            latitude_dms = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", geodata_split[0])
            latitude = convert_dms_to_decimal(latitude_dms)
            longitude_dms = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", geodata_split[1])
            longitude = convert_dms_to_decimal(longitude_dms)
            station_data.append(latitude)
            station_data.append(longitude)
        else:
            station_name = element.getText()
            station_name = re.sub(r'\([^)]*\)', '', station_name)
            station_data.append(station_name.rstrip())

    #station_data.append(td[1]['data-sort-value'])
    station_data.append(td[4].getText())
    station_data.append('https://de.wikipedia.org' + td[0].find('a')['href'])
    stations_data.append(station_data)

#df=pd.DataFrame(stations_data ,columns=['station_name','latitude','longitude','line_name','district_name', 'station_link'])
df_allStations=pd.DataFrame(stations_data ,columns=['station_name','latitude','longitude','district_name', 'station_link'])
df_allStations = df_allStations.dropna()
df_allStations.drop_duplicates(subset ="station_name", keep = 'first', inplace = True)

df_allStations.to_csv ('C:/Users/David/Desktop/station_data.csv', index = None, header=True)


import contextlib
import io
import os
import shutil
import tempfile
import textwrap
import tokenize
import unittest
import unittest.mock as mock

from traits.api import HasTraits, Int
from traits.testing.optional_dependencies import sphinx, requires_sphinx

if sphinx is not None:
    from sphinx.ext.autodoc import Options
    from sphinx.ext.autodoc.directive import DocumenterBridge
    from sphinx.testing.path import path
    from sphinx.testing.util import SphinxTestApp
    from sphinx.util.docutils import LoggingReporter

    from traits.util.trait_documenter import (
        _get_definition_tokens,
        TraitDocumenter,
    )

class MyTestClass(HasTraits):
    """
    Class-level docstring.
    """
    #: I'm a troublesome trait with a long definition.
    bar = Int(42, desc=""" First line

        The answer to
        Life,
        the Universe,

        and Everything.
    """)

@requires_sphinx
class TestTraitDocumenter(unittest.TestCase):
    """ Tests for the trait documenter. """

    def setUp(self):
        self.source = """
    depth_interval = Property(Tuple(Float, Float),
                              depends_on="_depth_interval")
"""
        string_io = io.StringIO(self.source)
        tokens = tokenize.generate_tokens(string_io.readline)
        self.tokens = tokens

    def test_get_definition_tokens(self):
        src = textwrap.dedent(
            """\
        depth_interval = Property(Tuple(Float, Float),
                                  depends_on="_depth_interval")
        """
        )
        string_io = io.StringIO(src)
        tokens = tokenize.generate_tokens(string_io.readline)

        definition_tokens = _get_definition_tokens(tokens)

        # Check if they are correctly untokenized. This should not raise.
        string = tokenize.untokenize(definition_tokens)

        self.assertEqual(src.rstrip(), string)

    def test_add_line(self):

        src = textwrap.dedent(
            """\
        class Fake(HasTraits):

            #: Test attribute
            test = Property(Bool, label="ミスあり")
        """
        )
        mocked_directive = mock.MagicMock()

        documenter = TraitDocumenter(mocked_directive, "test", "   ")
        documenter.object_name = "Property"

        with mock.patch(
            "traits.util.trait_documenter.inspect.getsource", return_value=src
        ):
            with mock.patch(
                (
                    "traits.util.trait_documenter.ClassLevelDocumenter"
                    ".add_directive_header"
                )
            ):
                documenter.add_directive_header("")

        self.assertEqual(
            len(documenter.directive.result.append.mock_calls), 1)

    def test_abbreviated_annotations(self):
        # Regression test for enthought/traits#493.
        with self.create_directive() as directive:
            documenter = TraitDocumenter(
                directive, __name__ + ".MyTestClass.bar")
            documenter.generate(all_members=True)
            result = directive.result

        # Find annotations line.
        for item in result:
            if item.lstrip().startswith(":annotation:"):
                break
        else:
            self.fail("Didn't find the expected trait :annotation:")

        # Annotation should be a single line.
        self.assertIn("First line", item)
        self.assertNotIn("\n", item)

    @contextlib.contextmanager
    def create_directive(self):
        """
        Helper function to create a a "directive" suitable
        for instantiating the TraitDocumenter with, along with resources
        to support that directive, and clean up the resources afterwards.

        Returns
        -------
        contextmanager
            A context manager that returns a DocumenterBridge instance.
        """
        with self.tmpdir() as tmpdir:
            # The configuration file must exist, but it's okay if it's empty.
            conf_file = os.path.join(tmpdir, "conf.py")
            with io.open(conf_file, "w", encoding="utf-8"):
                pass

            app = SphinxTestApp(srcdir=path(tmpdir))
            app.builder.env.app = app
            app.builder.env.temp_data["docname"] = "dummy"

            # Backwards compatibility hack: for now, we need to be compatible
            # with both Sphinx < 2.1 (whose DocumenterBridge doesn't take
            # a state argument) and Sphinx >= 2.3 (which warns if the state
            # isn't passed). Eventually we should be able to drop support
            # for Sphinx < 2.1.
            kwds = {}
            if sphinx.version_info >= (2, 1):
                state = mock.Mock()
                state.document.settings.tab_width = 8
                kwds["state"] = state
            yield DocumenterBridge(
                app.env, LoggingReporter(''), Options(), 1, **kwds)

    @contextlib.contextmanager
    def tmpdir(self):
        """
        Helper function to create a temporary directory.

        Returns
        -------
        contextmanager
            Context manager that returns the path to a temporary directory.
        """
        tmpdir = tempfile.mkdtemp()
        try:
            yield tmpdir
        finally:
            shutil.rmtree(tmpdir)


import contextlib
import dataclasses
import functools
import inspect
import typing
import weakref

from torchgen.model import FunctionSchema, OperatorName, SchemaKind

import torch
import torch._C as _C
import torch.library as library

from .autograd import autograd_kernel_indirection, construct_autograd_kernel

"""
There are various APIs for defining custom-operator-like things in PyTorch:
- [user-facing] autograd.Function (Python)
- [user-facing] custom_op (Python)
- [for power users] torch.library (Python)
- [for power users] TORCH_LIBRARY (C++)

This file contains the implementation for a Simple Custom Operator API (CustomOp).
Using CustomOp, you are able to define a custom operator and implement interactions
between the CustomOp and various PyTorch subsystems, including all the subsystems
that are necessary for a custom operator to work with torch.compile (i.e.,
autograd, FakeTensor, functionalization).

CustomOp is positioned as being safer and easier to use than
torch.library/TORCH_LIBRARY, which require deep understanding of PyTorch internals.
In additional, it supports torch.compile better than and is in general more
comprehensive than autograd.Function, which only supports implementing gradient
computation and vmap rules.
"""

__all__ = ["custom_op", "CustomOp", "get_ctx", "AbstractImplCtx"]

SUPPORTED_DEVICE_TYPE_TO_KEY = {
    "cpu": "CPU",
    "cuda": "CUDA",
}

# We will not let users register CustomOps with anything that could look like
# PyTorch internals to avoid confusion.
RESERVED_NS = {
    "prim",
    "prims",
    "aten",
    "at",
    "torch",
    "pytorch",
}

def custom_op(
    qualname: str, manual_schema: typing.Optional[str] = None
) -> typing.Callable:
    r"""Creates a new CustomOp object.

    In PyTorch, defining an op (short for "operator") is a two step-process:
    - we need to define (create) the op
    - we need to implement behavior for how the operator interacts with
      various PyTorch subsystems, like CPU/CUDA Tensors, Autograd, etc.

    This entrypoint defines the CustomOp object (the first step);
    you must then perform the second step by calling various methods on
    the CustomOp object.

    This API is used as a decorator (see examples).

    Arguments:
        qualname (str): Should be a string that looks like
            "namespace::operator_name". Operators in PyTorch need a namespace to
            avoid name collisions; a given operator may only be created once.
            If you are writing a Python library, we recommend the namespace to
            be the name of your top-level module. The operator_name must be
            the same as the name of the function you pass to custom_op
            (see examples).
        manual_schema (Optional[str]): Each PyTorch operator needs a schema that
            tells PyTorch the types of the inputs/outputs. If None (default),
            we will infer the schema from the type annotations on the function
            (see examples). Otherwise, if you don't want to use type annotations,
            you may provide us the schema string.

    Example::
        >>> import numpy as np
        >>> from torch import Tensor
        >>>
        >>> # Step 1: define the CustomOp.
        >>> # We need to provide the decorator a "prototype function"
        >>> # (a function with Python ellipses as the body).
        >>> @custom_op("mylibrary::numpy_sin")
        >>> def numpy_sin(x: Tensor) -> Tensor:
        >>>     ...
        >>>
        >>> # numpy_sin is now an instance of class CustomOp
        >>> print(type(numpy_sin))
        >>>
        >>> # Step 2: Register an implementation for various PyTorch subsystems
        >>>
        >>> # Register an implementation for CPU tensors
        >>> @numpy_sin.impl('cpu'):
        >>> def numpy_sin_impl_cpu(x):
        >>>     return torch.from_numpy(np.sin(x.numpy()))
        >>>
        >>> # Register an implementation for CUDA tensors
        >>> @numpy_sin.impl('cuda'):
        >>> def numpy_sin_impl_cuda(x):
        >>>     return torch.from_numpy(np.sin(x.cpu().numpy())).to(x.device)
        >>>
        >>> x = torch.randn(3)
        >>> numpy_sin(x)  # calls numpy_sin_impl_cpu
        >>>
        >>> x_cuda = x.cuda()
        >>> numpy_sin(x)  # calls numpy_sin_impl_cuda

    """

    def inner(func):
        if not inspect.isfunction(func):
            raise ValueError(
                f"custom_op(...)(func): Expected `func` to be a Python "
                f"function, got: {type(func)}"
            )

        ns, name = parse_namespace(qualname)
        validate_namespace(ns)
        if func.__name__ != name:
            raise ValueError(
                f"custom_op(qualname='{qualname}', ...)(func): expected `func` "
                f"to have name '{name}' but got '{func.__name__}'. "
                f"Please either change the name of `func` or the qualname that "
                f"is passed to `custom_op`"
            )

        schema = infer_schema(func) if manual_schema is None else manual_schema
        schema_str = f"{name}{schema}"
        function_schema = FunctionSchema.parse(schema_str)
        validate_schema(function_schema)
        if manual_schema is not None:
            validate_function_matches_schema(function_schema, func)

        lib = library.Library(ns, "FRAGMENT")
        lib.define(schema_str)
        ophandle = find_ophandle_or_throw(ns, function_schema.name)
        result = CustomOp(lib, ns, function_schema, function_schema.name, ophandle, _private_access=True)

        result.__name__ = func.__name__
        result.__module__ = func.__module__
        result.__doc__ = func.__doc__

        library.impl(lib, result._opname, "Autograd")(
            autograd_kernel_indirection(weakref.proxy(result))
        )

        torch._C._dispatch_set_report_error_callback(
            ophandle, functools.partial(report_error_callback, weakref.proxy(result))
        )

        return result

    return inner

# Global dictionary holding references to all CustomOp objects
# Yes, it keeps all CustomOps alive (see NOTE [CustomOp lifetime])
# Used to query the CustomOp associated with a specific C++ dispatcher operator.
# An example usage is FakeTensor: FakeTensor checks if a specific operator
# has an implementation registered via the CustomOp API.
# Indexed by qualname (e.g. aten::foo)
global_registry: typing.Dict[str, "CustomOp"] = {}

class CustomOp:
    r"""Class for custom operators in PyTorch.

    Use the CustomOp API to create user-defined custom operators that behave
    just like regular PyTorch operators (e.g. torch.sin, torch.mm) when it
    comes to various PyTorch subsystems (like torch.compile).

    To construct a `CustomOp`, use `custom_op`.
    """

    def __init__(self, lib, cpp_ns, schema, operator_name, ophandle, *, _private_access=False):
        super(CustomOp, self).__init__()
        if not _private_access:
            raise RuntimeError(
                "The CustomOp constructor is private and we do not guarantee "
                "BC for it. Please use custom_op(...) to create a CustomOp object"
            )
        name = f"{cpp_ns}::{str(operator_name.name)}"
        self._schema = schema
        self._cpp_ns = cpp_ns
        self._lib: library.Library = lib
        self._ophandle: _C._DispatchOperatorHandle = ophandle
        # Has the name of the op, e.g. "foo". We cache here for convenience.
        self._opname: str = str(operator_name)
        # this is _opname but with namespace. e.g. "custom::foo"
        self._qualname: str = name
        self.__name__ = None  # mypy requires this
        # NB: Some of these impls are registered as kernels to DispatchKeys.
        # Modifying the _impls dict directly won't do anything in that case.
        self._impls: typing.Dict[str, typing.Optional[FuncAndLocation]] = {}

        global_registry[self._qualname] = self

    # Records the impl and the source location in self._impls
    # Note that this doesn't cause torch.library to use the impl, that
    # needs to be done in a separate self._lib.impl call.
    def _register_impl(self, kind, func, stacklevel=2):
        if self._has_impl(kind):
            func_and_location = self._impls[kind]
            assert func_and_location is not None  # Pacify mypy
            location = func_and_location.location
            raise RuntimeError(
                f"Attempting to register a {kind} impl for operator {self._qualname} "
                f"that already has a {kind} impl registered from Python at "
                f"{location}. This is not supported."
            )
        frame = inspect.stack()[stacklevel]
        location = f"{frame.filename}:{frame.lineno}"
        self._impls[kind] = FuncAndLocation(func, location)

    def _get_impl(self, kind):
        return self._impls[kind]

    def _has_impl(self, kind):
        return kind in self._impls

    def _destroy(self):
        # NOTE: [CustomOp lifetime]
        # A CustomOp, once created, lives forever. The mechanism is that the
        # global registry holds a reference to it. However, to make testing
        # easier, we want to be able to destroy CustomOp objects.
        # CustomOp._destroy does the job, though it leaves the CustomOp
        # in a garbage state.
        del self._lib

        opnamespace = getattr(torch.ops, self._cpp_ns)
        if hasattr(opnamespace, self._opname):
            delattr(opnamespace, self._opname)

        del global_registry[self._qualname]

    def __repr__(self):
        return f'<CustomOp(op="{self._qualname}")>'

    def __call__(self, *args, **kwargs):
        # Bypass torch.ops.* and directly do OperatorHandle::callBoxed.
        # Using torch.ops.* is a bit of a pain (it can be slow and it has lifetime
        # issues from caching operators that make testing CustomOp difficult).
        result = _C._dispatch_call_boxed(self._ophandle, *args, **kwargs)
        return result

    def impl(
        self, device_types: typing.Union[str, typing.Iterable[str]]
    ) -> typing.Callable:
        r"""Register an implementation for a device type for this CustomOp object.

        If the CustomOp is passed multiple Tensor inputs with different device
        types, it will dispatch to the registered implementation for the highest
        priority device type among those present.
        The supported device types, in order of priority, are {'cuda', 'cpu'}.

        This API is used as a decorator (see examples).

        Arguments:
            device_types (str or Iterable[str]): the device type(s) to register the function for.

        Examples::
            >>> import numpy as np
            >>> from torch import Tensor
            >>>
            >>> @custom_op("mylibrary::numpy_sin")
            >>> def numpy_sin(x: Tensor) -> Tensor:
            >>>     ...
            >>>
            >>> # Register an implementation for CPU Tensors
            >>> @numpy_sin.impl('cpu'):
            >>> def numpy_sin_impl_cpu(x):
            >>>     return torch.from_numpy(np.sin(x.numpy()))
            >>>
            >>> # Register an implementation for CUDA Tensors
            >>> @numpy_sin.impl('cuda'):
            >>> def numpy_sin_impl_cuda(x):
            >>>     return torch.from_numpy(np.sin(x.cpu().numpy())).to(x.device)
            >>>
            >>> x = torch.randn(3)
            >>> numpy_sin(x)  # calls numpy_sin_impl_cpu
            >>>
            >>> x_cuda = x.cuda()
            >>> numpy_sin(x)  # calls numpy_sin_impl_cuda

        """
        if isinstance(device_types, str):
            device_types = [device_types]
        for device_type in device_types:
            validate_device_type(device_type)

        def inner(f):
            for device_type in set(device_types):
                self._register_impl(device_type, f)
                dispatch_key = SUPPORTED_DEVICE_TYPE_TO_KEY[device_type]
                library.impl(self._lib, self._opname, dispatch_key)(f)
            return f

        return inner

    def impl_factory(self) -> typing.Callable:
        r"""Register an implementation for a factory function."""

        def inner(f):
            self._register_impl("factory", f)
            library.impl(self._lib, self._opname, "BackendSelect")(f)
            return f

        return inner

    def impl_abstract(self) -> typing.Callable:
        r"""Register an abstract implementation for this operator.

        An "abstract implementation" specifies the behavior of this operator on
        Tensors that carry no data. Given some input Tensors with certain properties
        (sizes/strides/storage_offset/device), it specifies what the properties of
        the output Tensors are.

        The abstract implementation has the same signature as the operator.
        It is run for both FakeTensors and meta tensors. To write an abstract
        implementation, assume that all Tensor inputs to the operator are
        regular CPU/CUDA/Meta tensors, but they do not have storage, and
        you are trying to return regular CPU/CUDA/Meta tensor(s) as output.
        The abstract implementation must consist of only PyTorch operations
        (and may not directly access the storage or data of any input or
        intermediate Tensors).

        This API is used as a decorator (see examples).

        Examples::
            >>> import numpy as np
            >>> from torch import Tensor
            >>>
            >>> # Example 1: an operator without data-dependent output shape
            >>> @custom_op('mylibrary::custom_linear')
            >>> def custom_linear(x: Tensor, weight: Tensor, bias: Tensor):
            >>>     ...
            >>>
            >>> @custom_linear.impl_abstract():
            >>> def custom_linear_abstract(x, weight):
            >>>     assert x.dim() == 2
            >>>     assert weight.dim() == 2
            >>>     assert bias.dim() == 1
            >>>     assert x.shape[1] == weight.shape[1]
            >>>     assert weight.shape[0] == bias.shape[0]
            >>>     assert x.device == weight.device
            >>>
            >>>     return (x @ weight.t()) + bias
            >>>
            >>> # Example 2: an operator with data-dependent output shape
            >>> @custom_op('mylibrary::custom_nonzero')
            >>> def custom_nonzero(x: Tensor) -> Tensor:
            >>>     ...
            >>>
            >>> @custom_nonzero.impl_abstract():
            >>> def custom_nonzero_abstract(x):
            >>>     # Number of nonzero-elements is data-dependent.
            >>>     # Since we cannot peek at the data in an abstract impl,
            >>>     # we use the ctx object to construct a new symint that
            >>>     # represents the data-dependent size.
            >>>     ctx = torch._custom_op.get_ctx()
            >>>     nnz = ctx.create_unbacked_symint()
            >>>     shape = [x.dim(), nnz]
            >>>     result = x.new_empty(shape, dtype=torch.long)
            >>>     return result
            >>>
            >>> @numpy_nonzero.impl(['cpu', 'cuda'])
            >>> def custom_nonzero_impl(x):
            >>>     x_np = to_numpy(x)
            >>>     res = np.stack(np.nonzero(x_np), axis=1)
            >>>     # unbacked symbolic ints in PyTorch must be >= 2, so we
            >>>     # constrain the range to at least 2
            >>>     if res.shape[0] <= 1:
            >>>         raise RuntimeError("not supported")
            >>>     return torch.tensor(res, device=x.device)

        """

        def inner(f):
            frame = inspect.stack()[1]
            self._register_impl("abstract", f)
            location = self._get_impl("abstract").location

            qualname = self._qualname

            # Handle DispatchKey.Meta registration
            @functools.wraps(f)
            def f_with_ctx(*args, **kwargs):
                def error_on_ctx():
                    raise RuntimeError(
                        f"Attempted to call get_ctx() for the meta implementation "
                        f"for {qualname}."
                        f"You have presumably called get_ctx() because the operator "
                        f"has a data-dependent output shape; if so, there is no "
                        f"such meta implementation and this error is the correct "
                        f"behavior. Otherwise, please remove the call to get_ctx() "
                        f"in the implementation registered with impl_abstract "
                        f"at {location}"
                    )

                with set_ctx_getter(error_on_ctx):
                    return f(*args, **kwargs)

            self._lib.impl(self._opname, f_with_ctx, "Meta")
            return f

        return inner

    # NOTE ["backward", "save_for_backward", and "autograd"]
    # As a part of the explicit autograd API, a user must provide us
    # a "save_for_backward" function and a "backward" function.
    # When both of these have been provided, then we automatically
    # construct the "autograd" kernel.
    def _register_autograd_kernel(self):
        assert self._has_impl("backward")
        assert self._has_impl("save_for_backward")
        kernel = construct_autograd_kernel(
            self._schema,
            self._output_differentiability,
            self,
            self._get_impl("save_for_backward").func,
            self._get_impl("backward").func)
        self._register_impl("autograd", kernel)

    def impl_save_for_backward(self):
        r"""Register a function that tells us what to save for backward.

        Please see impl_backward for more details.
        """
        def inner(f):
            self._register_impl("save_for_backward", f)
            if self._has_impl("backward"):
                self._register_autograd_kernel()
        return inner

    def impl_backward(self, output_differentiability=None):
        r"""Registers a backward formula.

        In order for the CustomOp to work with autograd, you need to register
        a backward formula. There are two pieces to this:
        1. You must give us a function to specify what to save for backward.
           Call this the "save for backward" function.
        2. You must give us a function that computes gradients. Call this the
           "backward" function.

        Use `impl_save_for_backward` to define a "save for backward" function
        that specifies what gets saved for backward. The function should accept
        two arguments ``(inputs, output)`` and return the quantities to be saved
        for backward.

        During runtime, when you call the CustomOp, PyTorch will invoke the
        "save for backward" function with the inputs and output of the CustomOp.

        Use `impl_backward` to define the "backward" function. The backward
        function must accept ``(ctx, saved, *grads)``:
        - ``ctx`` is a context object where we may provide information
        - ``saved`` is exactly what gets returned from the "save for backward"
          function
        - ``grads`` is one or more gradients. The number of gradients matches
          the number of outputs of the CustomOp.

        The backward function must return a dict that maps the name of
        an input to the CustomOp to its corresponding gradient. All inputs that
        were declared to be Tensors in the CustomOp definition must be accounted
        for in the dict. The gradient may be a Tensor or None.

        TODO(rzou): Add example when this PR is closer to landing.

        """
        if output_differentiability is not None:
            def yell():
                raise RuntimeError(
                    f"impl_backward(output_differentiability): expected "
                    f"output_differentiability to be a list of bools with "
                    f"length equal to the number of outputs of this CustomOp "
                    f"got: {output_differentiability}")

            if not isinstance(output_differentiability, list):
                yell()
            for diff in output_differentiability:
                if not isinstance(diff, bool):
                    yell()
            if len(self._schema.returns) != len(output_differentiability):
                yell()

        def inner(f):
            self._register_impl("backward", f)
            self._output_differentiability = output_differentiability
            if self._has_impl("save_for_backward"):
                self._register_autograd_kernel()
        return inner

@dataclasses.dataclass
class FuncAndLocation:
    func: typing.Callable
    location: str

def find_ophandle_or_throw(cpp_ns: str, operator_name: OperatorName):
    overload_name = (
        "" if operator_name.overload_name is None else operator_name.overload_name
    )
    return _C._dispatch_find_schema_or_throw(
        f"{cpp_ns}::{str(operator_name.name)}", overload_name
    )

def validate_namespace(ns: str) -> None:
    if "." in ns:
        raise ValueError(
            f'custom_op(..., ns="{ns}"): expected ns to not contain any . (and be a '
            f"valid variable name)"
        )
    if ns in RESERVED_NS:
        raise ValueError(
            f"custom_op(..., ns='{ns}'): '{ns}' is a reserved namespace, "
            f"please choose something else. "
        )

def validate_schema(schema: FunctionSchema) -> None:
    # Coming in the future. Requires us to have correct logic for
    # the ADInplaceOrView key
    if schema.kind() != SchemaKind.functional:
        raise ValueError(
            f"custom_op does not support non-functional function schema. Got: {schema}"
        )

    rets = schema.returns
    is_non_mutating_view = len(rets) > 0 and any(
        r.annotation is not None and not r.annotation.is_write for r in rets
    )
    if is_non_mutating_view:
        raise ValueError(f"custom_op does not support view functions. Got: {schema}")

    # Just seems weird so banning for now
    if not schema.returns:
        raise ValueError(
            f"custom_op does not support function schema with no outputs. Got: {schema}"
        )

    # For simplicity: don't allow self arguments
    if schema.arguments.self_arg is not None:
        raise ValueError(
            f"custom_op does not support arguments named 'self'. Please "
            f"rename your argument. Got: {schema}"
        )

def parse_namespace(namespaced_entity: str) -> typing.Tuple[str, str]:
    names = namespaced_entity.split("::", 1)
    if len(names) != 2:
        raise ValueError(f"Expected there to be a namespace in {namespaced_entity}.")
    return names[0], names[1]

def validate_device_type(device_type: str) -> None:
    if device_type not in SUPPORTED_DEVICE_TYPE_TO_KEY:
        raise ValueError(
            f"CustomOp.impl(device_types=[{device_type}, ...]): we only support device_type "
            f"in {SUPPORTED_DEVICE_TYPE_TO_KEY.keys()}."
        )

def supported_param(param: inspect.Parameter) -> bool:
    return param.kind in (
        inspect.Parameter.POSITIONAL_OR_KEYWORD,
        inspect.Parameter.KEYWORD_ONLY,
    )

def validate_function_matches_schema(
    schema: FunctionSchema, func: typing.Callable
) -> None:
    sig = inspect.signature(func)

    if not all(supported_param(p) for _, p in sig.parameters.items()):
        raise ValueError(
            f"custom_op(..., manual_schema)(func): positional-only args, "
            f"varargs, and kwargs are not supported. Please rewrite `func` "
            f"to not have them. Got `func` with signature: {sig}"
        )

    if (
        any(
            p.annotation is not inspect.Parameter.empty
            for _, p in sig.parameters.items()
        )
        or sig.return_annotation is not inspect.Signature.empty
    ):
        raise ValueError(
            f"custom_op(..., manual_schema)(func): When passing in a manual "
            f"schema, we expect `func` to have no type annotations to avoid "
            f"ambiguity. Got `func` with signature: {sig}"
        )

    positional = [
        (name, param)
        for name, param in sig.parameters.items()
        if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
    ]
    kwargonly = [
        (name, param)
        for name, param in sig.parameters.items()
        if param.kind == inspect.Parameter.KEYWORD_ONLY
    ]

    def error():
        raise ValueError(
            f"custom_op(..., manual_schema)(func): When passing in a manual "
            f"schema, we expect `func`'s signature to match `manual_schema` "
            f"(aside from type annotations). "
            f"func's signature: {sig}, manual_schema: {schema}"
        )

    def error_default_args():
        raise ValueError(
            f"custom_op(..., manual_schema)(func): "
            f"neither func nor manual_schema should have default "
            f"arguments. Got "
            f"func's signature: {sig}, manual_schema: {schema}"
        )

    def compare(sig_args, schema_args):
        if len(sig_args) != len(schema_args):
            error()
        for (name, param), arg in zip(sig_args, schema_args):
            if name != arg.name:
                error()
            if param.default is not inspect.Parameter.empty or arg.default is not None:
                error_default_args()

    compare(positional, schema.arguments.flat_positional)
    compare(kwargonly, schema.arguments.flat_kwarg_only)

def get_none():
    return None

global_ctx_getter: typing.Callable = get_none

# NOTE [ctx inside the fake implementation]
# If a user has an operator with data-dependent output shape, then when writing
# a fake implementation they must query the current ctx and use methods on the
# ctx to construct a new unbacked symint.
#
# This is done via us setting the global_ctx_getter function every time a fake
# implementation is invoked.
def get_ctx() -> "AbstractImplCtx":
    """get_ctx() returns the current AbstractImplCtx object.

    Calling ``get_ctx()`` is only valid inside of an abstract implementation.
    """
    return global_ctx_getter()

@contextlib.contextmanager
def set_ctx_getter(ctx_getter):
    global global_ctx_getter
    prev = global_ctx_getter
    try:
        global_ctx_getter = ctx_getter
        yield
    finally:
        global_ctx_getter = prev

class AbstractImplCtx:
    """
    Context object for writing abstract implementations for custom operators.
    """

    def __init__(self, _shape_env, _op):
        self._shape_env = _shape_env
        self._op = _op

    def create_unbacked_symint(self, *, min=2, max=None) -> torch.SymInt:
        """Constructs a new symint (symbolic int) representing a data-dependent value.

        This is useful for writing the abstract implementation (which is necessary
        for torch.compile) for a CustomOp where an output Tensor has a size
        that depends on the data of the input Tensors.

        Args:
            min (int): A statically known inclusive lower bound for this symint.
                min must be at least 2 due to implementation details of
                torch.compile. Default: 2.
            max (Optional[int]): A statically known inclusive upper bound for this
                symint. Default: None

        .. warning:

            It is important that the ``min`` and ``max`` (if not None) values are set
            correctly, otherwise, there will be undefined behavior under
            torch.compile. The default value of ``min`` is 2 due to torch.compile
            specializing on 0/1 sizes.

            You must also verify that your implementation on concrete Tensors
            (e.g. CPU/CUDA) only returns Tensors where the size that corresponds
            to the symint also has respects these constraint.
            The easiest way to do this is to add an assertion in the CPU/CUDA/etc
            implementation that the size follows these bounds.

        Example::

            >>> # an operator with data-dependent output shape
            >>> @custom_op("mylibrary::custom_nonzero")
            >>> def custom_nonzero(x: Tensor) -> Tensor:
            >>>     ...
            >>>
            >>> @custom_nonzero.impl_abstract():
            >>> def custom_nonzero_abstract(x):
            >>>     # Number of nonzero-elements is data-dependent
            >>>     ctx = torch._custom_op.get_ctx()
            >>>     nnz = ctx.create_unbacked_symint()
            >>>     shape = [x.dim(), nnz]
            >>>     result = x.new_empty(shape, dtype=torch.long)
            >>>     return result
            >>>
            >>> @numpy_nonzero.impl(['cpu', 'cuda'])
            >>> def custom_nonzero_impl(x):
            >>>     x_np = to_numpy(x)
            >>>     res = np.stack(np.nonzero(x_np), axis=1)
            >>>     # the size associated with ctx.create_unbacked_symint()
            >>>     # must be constrained in the same way, so we add an assertion here.
            >>>     if res.shape[0] < 2 or res.shape[0] > x.numel():
            >>>         raise RuntimeError("not supported")
            >>>     return torch.tensor(res, device=x.device)

        """
        if (
            self._shape_env is None
            or not self._shape_env.allow_dynamic_output_shape_ops
        ):
            raise torch._subclasses.fake_tensor.DynamicOutputShapeException(self._op)

        if isinstance(min, torch.SymInt) or isinstance(max, torch.SymInt):
            raise ValueError(
                f"ctx.create_unbacked_symint(min={min}, max={max}): expected "
                f"min and max to be statically known ints but got SymInt. "
                f"This is not supported."
            )

        if min < 2:
            raise ValueError(
                f"ctx.create_unbacked_symint(min={min}, ...): expected min to be "
                f"greater than or equal to 2. PyTorch only supports new "
                f"data-dependent sizes of >= 2"
            )

        result = self._shape_env.create_unbacked_symint()
        torch.fx.experimental.symbolic_shapes.constrain_range(result, min=2, max=max)
        return result

def infer_schema(prototype_function: typing.Callable) -> str:
    sig = inspect.signature(prototype_function)

    def error_fn(what):
        raise ValueError(
            f"custom_op(...)(func): {what} " f"Got func with signature {sig})"
        )

    params = [
        parse_param(name, param, error_fn) for name, param in sig.parameters.items()
    ]
    ret = parse_return(sig.return_annotation, error_fn)
    return f"({', '.join(params)}) -> {ret}"

def parse_param(name, param, error_fn):
    if not supported_param(param):
        error_fn("We do not support positional-only args, varargs, or varkwargs.")

    if param.annotation is inspect.Parameter.empty:
        error_fn(f"Parameter {name} must have a type annotation.")

    if param.annotation not in SUPPORTED_PARAM_TYPES.keys():
        error_fn(
            f"Parameter {name} has unsupported type {param.annotation}. "
            f"The valid types are: {SUPPORTED_PARAM_TYPES.keys()}."
        )

    if param.default is not inspect.Parameter.empty:
        error_fn(
            f"Parameter {name} has a default value; this is not supported. "
            f"If you want to use default values then create a function with "
            f"default values that calls the CustomOp"
        )

    return f"{SUPPORTED_PARAM_TYPES[param.annotation]} {name}"

def derived_types(
    base_type, cpp_type, list_base, optional_base_list, optional_list_base
):
    result = [
        (base_type, cpp_type),
        (typing.Optional[base_type], f"{cpp_type}?"),
    ]
    if list_base:
        result.append((typing.Sequence[base_type], f"{cpp_type}[]"))  # type: ignore[valid-type]
    if optional_base_list:
        result.append((typing.Sequence[typing.Optional[base_type]], f"{cpp_type}?[]"))  # type: ignore[valid-type]
    if optional_list_base:
        result.append((typing.Optional[typing.Sequence[base_type]], f"{cpp_type}[]?"))  # type: ignore[valid-type]
    return result

def get_supported_param_types():
    data = [
        # (python type, schema type, type[] variant, type?[] variant, type[]? variant
        (torch.Tensor, "Tensor", True, True, False),
        (int, "SymInt", True, False, True),
        (float, "float", True, False, True),
        (bool, "bool", True, False, True),
        (str, "str", False, False, False),
        (torch.types.Number, "Scalar", True, False, False),
        (torch.dtype, "ScalarType", False, False, False),
        (torch.device, "Device", False, False, False),
    ]
    result = []
    for line in data:
        result.extend(derived_types(*line))
    return dict(result)

def parse_return(annotation, error_fn):
    if annotation is torch.Tensor:
        return "Tensor"
    origin = typing.get_origin(annotation)
    if origin is not tuple:
        error_fn(
            "Expected output of func to be type annotated as either Tensor "
            "or a Tuple of known size of one or more tensors"
        )
    args = typing.get_args(annotation)
    for arg in args:
        if arg is not torch.Tensor:
            error_fn(
                "Expected output of func to be type annotated as either Tensor "
                "or a Tuple of known size of one or more tensors"
            )
    return "(" + ", ".join(["Tensor"] * len(args)) + ")"

SUPPORTED_PARAM_TYPES = get_supported_param_types()

def report_error_callback(custom_op: typing.Any, key: str) -> None:
    if key == "Undefined":
        raise NotImplementedError(
            f"{custom_op}: There were no Tensor inputs to this operator "
            f"(e.g. you passed an empty list of Tensors). If your operator is a "
            f"factory function (that is, it takes no Tensors and constructs "
            f"a new one), then please use CustomOp.impl_factory to register "
            f"an implementation for it"
        )
    if key == "Meta":
        raise NotImplementedError(
            f"{custom_op}: when running with device='Meta' tensors: there is no "
            f"abstract impl registered for this CustomOp. Please register one via "
            f"CustomOp.impl_abstract to get this CustomOp to work with Meta tensors"
        )
    if key in ("CPU", "CUDA"):
        device = key.lower()
        raise NotImplementedError(
            f"{custom_op}: when running with device='{device}' tensors: there is no "
            f"{device} impl registered for this CustomOp. Please register one via "
            f"CustomOp.impl(device_type='{device}')"
        )
    raise NotImplementedError(
        f"{custom_op}: No implementation for dispatch key {key}. It is likely "
        f"that we have not added this functionality yet, please either open an "
        f"issue or if you're feeling adventurous, use the low-level "
        f"torch.library API"
    )


import lxml.etree as ET
import os
from textwrap import dedent

# run from top-level uwlswd folder
# changes DOIs in UWLSWD resource rdf:Description @rdf:about attributes to lowercase

def process_file(filepath):
    edited = False
    tree = ET.parse(filepath)
    root = tree.getroot()

    for child in root:
        if child.tag == '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description':
            if '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about' in child.attrib:
                if "https://doi.org/" in child.attrib['{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about']:
                    old_doi = child.attrib['{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about']
                    if '#' in old_doi:
                        lc_doi = old_doi.split('#')
                        if lc_doi[0].islower() == False:
                            lc_doi[0] = lc_doi[0].lower()
                            final_doi = lc_doi[0] + '#' + lc_doi[1]
                            child.attrib['{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about'] = final_doi
                            edited = True
                    else:
                        if old_doi.islower() == False:
                            final_doi = old_doi.lower()
                            child.attrib['{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about'] = final_doi
                            edited = True
    if edited == True:
        tree.write(filepath, xml_declaration=True, encoding="UTF-8")
        print("DOI changed to lowercase")

# get file path
def prompt_user():
    file_prompt = dedent("""Enter the path of the folder or file relative to the working directory.
    The file must have the extenstion ".rdf",
    if entering the path of a folder, each '.rdf' file within the directory will be serialized
    For example: ../uwlswd_vocabs or ../uwlswd_vocabs/linked_data_platforms.rdf
> """)
    file_path = input(file_prompt)

    if os.path.exists(file_path):
        return file_path

    else:
        print(dedent("\nError: file could not be found. Please re-enter file name or press CTRL+C to cancel.\n"))
        return prompt_user()

file_path = prompt_user()
if os.path.isfile(file_path):
    if file_path.endswith('.rdf'):
        print("\nprocessing " + file_path)
        process_file(file_path)
    else:
        print("Input must be an rdf file or a directory containing rdf files")
        exit()

elif os.path.isdir(file_path):
    complete_files = []

    for root, dir_names, file_names in os.walk(file_path):
        for f in file_names:
            if f.endswith('.rdf'):
                complete_files.append(os.path.join(root, f))

    for f in complete_files:
        print("\nprocessing " + f)
        process_file(f)