import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from django.http import HttpResponse,JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
import neo4j
from neo4j import GraphDatabase, RoutingControl, exceptions

uri = 'neo4j+s://b35e138c.databases.neo4j.io'
auth = ("neo4j", 'VGfvQTk0VCkEzne79CGPXTKA_Eykhx0OwudLZUKG7sQ')

sem_zim = ["01","09","10","11","12"]
sem_let = ["02","03","04","05","06","07","08"]

def add_group(driver, ID):
    try:
        driver.execute_query(
        "Create (g:Group {ID: $name}) ",
        name=ID,database_="neo4j",
    )
    except neo4j.exceptions.ConstraintError:
        print(f"grupa {ID} jest juz w bazie danych")

def print_group(driver):
    records, _, _= driver.execute_query(
        "MATCH (g:Group)"
        "RETURN g.ID",
         database_="neo4j", routing_=RoutingControl.READ,
    )
    resoult = []
    for record in records:
        resoult.append(record['g.ID'])

    print(resoult)
    return resoult

def add_prac(driver, ID, name):

    try:
        driver.execute_query(
        "Create (p:Pracownik {ID: $ID, name: $name}) ",
        ID=ID,name=name,database_="neo4j",
    )
    except neo4j.exceptions.ConstraintError:
        print(f"Pracownik {name} jest juz w bazie danych")

def print_prac(driver):
    records, _, _ = driver.execute_query(
        "MATCH (p:Pracownik)"
        "RETURN p",
         database_="neo4j", routing_=RoutingControl.READ,
    )
    resoult = {}
    print(records[0].data())
    for record in records:
        data = record.data()
        resoult[data['p']['ID']] = data['p']['name']
    return resoult

def add_date(driver,all,group):

    for date in all.keys():

        for blok in all[date].keys():
            display = all[date][blok][0]
            full = all[date][blok][1]
            id_prow = display[3].split("[")[0]
            place = display[2]
            form = display[1]
            short = display[0]
            # print(id_prow, place, form, short, full)

            try:
                driver.execute_query(
                """Merge (b:Blok {id_prow: $id_prow, place: $place, date: $date, nr: $blok})

                On Create
                    SET
                    b.id_prow = $id_prow,
                    b.place = $place,
                    b.date = $date,
                    b.nr = $blok,
                    b.grups = [$group],
                    b.form = $form,
                    b.short = $short,
                    b.full = $full

                On match
                    SET b.grups = CASE
                        WHEN NOT $group IN b.grups THEN b.grups + $group
                        ELSE b.grups
               END
                """,
                date=date,group=group,blok=blok,id_prow=id_prow,place=place,form=form,short=short,full=full,database_="neo4j",
                )

            except neo4j.exceptions.ConstraintError:
                print(f"Zajecia dnia: {date} sa juz dodane do bloku {blok}")

            try:
                driver.execute_query(
                """match (b:Blok), (p:Pracownik)
                Where p.ID = b.id_prow
                and b.nr = $blok
                and b.date = $date
                MERGE (p)-[pz:prowadz_zajecia]->(b)
                """,
                date=date,group=group,blok=blok,database_="neo4j",
            )

                driver.execute_query(
                """match (g:Group), (b:Blok)
                Where  g.ID in b.grups
                and b.date = $date
                and b.nr = $blok
                MERGE (g)-[bz:blok_zajec]->(b)""",
                date=date,group=group,blok=blok,database_="neo4j",
            )
            except neo4j.exceptions.ConstraintError:
                print(f"Powiazania sa juz wykonane")
            except e as Exception:
                print(e)

            print(f"Zakonczono dodawanie dnia: {date} do bloku {blok}")

    return ("Zakonczono dodawanie")

class group_name(View):

    def get(self,request, **kwargs):

        try:
            key = request.META['HTTP_KEY']
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")
        with GraphDatabase.driver(uri,auth=auth) as driver:
            records = print_group(driver)
            driver.close()

        return JsonResponse(records, safe=False)

    def post(self,request,**kwargs):

        try:
            key = request.META['HTTP_KEY']
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        url = 'https://old.wcy.wat.edu.pl/pl/rozklad'
        response = requests.get(url, verify=False)
        soup = BeautifulSoup(response.text, 'html.parser')
        options = soup.find('select', {"class": "ctools-jump-menu-select form-select"}).find_all('option')
        with GraphDatabase.driver(uri, auth=auth) as driver:
            for option in options:
                try:
                    add_group(driver, option.text.strip())
                except Exception as e:
                    print(e)
                finally:
                    driver.close()
        print("pomyslnie zaaktualizowano nazwy grup")
        return HttpResponse(["pomyslnie zaaktualizowano nazwy grup"])

class prowadzacy(View):

    def get(self,request, **kwargs):

        try:
            key = str(request.read(),encoding='ascii')
            key = key.split("=")[1]
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        with GraphDatabase.driver(uri,auth=auth) as driver:
            records = print_prac(driver)

        return JsonResponse(records, safe=False)

    def post(self,request,**kwargs):

        try:
            key = str(request.read(),encoding='ascii')
            key = key.split("=")[1]
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        options = webdriver.ChromeOptions()
        options.add_argument('headless')
        driver = webdriver.Chrome(options)

        driver.get(f"https://old.wcy.wat.edu.pl/pl/planzajec")
        options = driver.find_element(By.TAG_NAME, "select").find_elements(By.TAG_NAME, "option")

        with GraphDatabase.driver(uri, auth=auth) as driver:
            for option in options:
                try:
                    name = option.get_property("label")
                    try:
                        id = option.get_property("value").split("id=")[1]
                    except IndexError:
                        id = 'brak'
                    add_prac(driver,id,name)

                except Exception as e:
                    print(e)
                finally:
                    driver.close()

        print("pomyslnie zaaktualizowano prowadzacych")
        return HttpResponse(["pomyslnie zaaktualizowano prowadzacych"])

class actuality_stud(View):
    def get(self, request, **kwargs):

        try:
            key = request.META['HTTP_KEY']
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        try:
            user_group = request.META['HTTP_GRP']
        except KeyError:
            return JsonResponse({"Podaj grupe idioto, group = nazwa_grupy"})

        if user_group != "":
            url = f'https://old.wcy.wat.edu.pl/pl/rozklad?grupa_id={user_group}'
            response = requests.get(url, verify=False)
            soup = BeautifulSoup(response.text, 'html.parser')

            date = soup.find('span', class_="head_info").text

            return JsonResponse(date, safe=False)
        else:
            return HttpResponse("Wpisz nazwe grupy")

class days(View):
    def get(self, request, **kwargs):

        try:
            key = kwargs.get('key')
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")
        url = f'https://old.wcy.wat.edu.pl/pl/rozklad?grupa_id=WCY20IK1S0'
        response = requests.get(url, verify=False)
        soup = BeautifulSoup(response.text, 'html.parser')

        plan = soup.find('div', class_="rozklad_container")
        week = {}
        day_name = 1
        for days in plan.find_all('div', class_="day_v1"):
            dates = []
            for day in days.find_all('div', class_="date"):
                dates.append([day.find('span', class_="date1").text, day.find('span', class_="date2").text])
            week[day_name] = dates
            day_name += 1
        return JsonResponse(week)

class plan_stud(View):
    def get(self,request,**kwargs):

        try:
            key = request.META['HTTP_KEY']
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        try:
            user_group = request.META['HTTP_GRP']
        except KeyError:
            return JsonResponse({"Podaj grupe idioto, group = nazwa_grupy"})

        confirm = request.META['HTTP_CONFIRM']
        if confirm != "tak":
            return HttpResponse["uzyj get"]

        options = webdriver.ChromeOptions()
        options.add_argument('headless')
        driver = webdriver.Chrome(options)
        driver.implicitly_wait(1)
        resoult = {}

        driver.get(f"https://old.wcy.wat.edu.pl/pl/rozklad?grupa_id={user_group}")
        lessons = driver.find_elements(By.CLASS_NAME, "lesson")
        current_date = driver.find_element(By.CLASS_NAME, "head_info").get_attribute("innerHTML").split("-")[1]

        if current_date in sem_zim:
            sem = sem_zim
        else:
            sem = sem_let

        for index in lessons:
            date = index.find_element(By.CLASS_NAME, "date").get_attribute("innerHTML")

            month = date.split("_")[1]

            if month in sem:
                full_info = index.find_element(By.CLASS_NAME, "info").get_attribute("innerHTML")
                display = index.find_element(By.CLASS_NAME, "name").get_attribute("innerHTML").split("
")
                block = index.find_element(By.CLASS_NAME,"block_id").get_attribute("innerHTML")[-1:]
            else:
                continue

            if date not in resoult:
                resoult[date] = {}

            resoult[date][block] = [display, full_info]
        driver.quit()

        return JsonResponse(resoult)

    def post(self,request,**kwargs):

        try:
            key = request.META['HTTP_KEY']
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        try:
            user_group = request.META['HTTP_GRP']
        except KeyError:
            return JsonResponse({"Podaj grupe idioto, group = nazwa_grupy"})

        confirm = request.META['HTTP_CONFIRM']
        if confirm != "tak":
            return HttpResponse["uzyj get"]

        options = webdriver.ChromeOptions()
        options.add_argument('headless')
        options.add_argument('--remote-debugging-port=443')

        driver = webdriver.Chrome(options)
        driver.implicitly_wait(1)
        resoult = {}

        driver.get(f"https://old.wcy.wat.edu.pl/pl/rozklad?grupa_id={user_group}")
        lessons = driver.find_elements(By.CLASS_NAME, "lesson")
        current_date = driver.find_element(By.CLASS_NAME, "head_info").get_attribute("innerHTML").split("-")[1]

        if current_date in sem_zim:
            sem = sem_zim
        else:
            sem = sem_let

        for index in lessons:
            date = index.find_element(By.CLASS_NAME, "date").get_attribute("innerHTML")

            month = date.split("_")[1]

            if month in sem:
                full_info = index.find_element(By.CLASS_NAME, "info").get_attribute("innerHTML")
                display = index.find_element(By.CLASS_NAME, "name").get_attribute("innerHTML").split("
")
                block = index.find_element(By.CLASS_NAME,"block_id").get_attribute("innerHTML")[-1:]

            else:
                continue

            if date not in resoult:
                resoult[date] = {}

            resoult[date][block] = [display, full_info]

        driver.quit()

        with GraphDatabase.driver(uri,auth=auth) as bd:
                add_date(bd,resoult,user_group)

        return HttpResponse("zakonczono")

class plan_prow(View):
    def get(self,request,**kwargs):

        try:
            key = request.META['HTTP_KEY']
        except KeyError:
            return JsonResponse({"Podaj klucz"})
        if key!="karzel":
            return HttpResponse("bledny klucz")

        try:
            prow = request.META['HTTP_PROW']
        except KeyError:
            return JsonResponse({"Podaj prowadzacego cepie, prow = nazwa_grupy zazwyczaj 2 pierwsze litery PS BZYKU CHUJ"})

        options = webdriver.ChromeOptions()
        options.add_argument('headless')
        driver = webdriver.Chrome(options)
        driver.implicitly_wait(1)
        resoult = {}

        driver.get(f"https://old.wcy.wat.edu.pl/pl/rozklad?grupa_id={prow}")
        lessons = driver.find_elements(By.CLASS_NAME, "lesson")
        current_date = driver.find_element(By.CLASS_NAME, "head_info").get_attribute("innerHTML").split("-")[1]

        if current_date in sem_zim:
            sem = sem_zim
        else:
            sem = sem_let

        for index in lessons:
            date = index.find_element(By.CLASS_NAME, "date").get_attribute("innerHTML")
            month = date.split("_")[1]

            if month in sem:
                full_info = index.find_element(By.CLASS_NAME, "info").get_attribute("innerHTML")
                display = index.find_element(By.CLASS_NAME, "name").get_attribute("innerHTML").split("
")
                block = index.find_element(By.CLASS_NAME,"block_id").get_attribute("innerHTML")[-1:]
            else:
                continue

            if date not in resoult:
                resoult[date] = {}

            resoult[date][block] = [display, full_info]
        driver.quit()

        return JsonResponse(resoult)

class help(View):
    def get(self,request):

    #     try:
    #         with GraphDatabase.driver(uri, auth) as driver:
    #             driver.execute_query(
    #     "MERGE (a:Person {name: $name}) "
    #     "MERGE (friend:Person {name: $friend_name}) "
    #     "MERGE (a)-[:KNOWS]->(friend)",
    #     name="name", friend_name="friend_name", database_="Plan",
    # )

    #     finally:
    #         driver.close()

        help = """zawsze i wszedzie bzyku jebany bedzie

 DO KAZDEGO ZAPYTANIA GET, POST ITP POTRZEBNY JEST KLUCZ DOSTEPU

 taki klucz to bedzie karzel

 przyklad: response = request.get(url, key="karzel")

 spis wszystkich URL i szybkie objasnienie:

                       <dfn>https://planwat.azurewebsites.net/polls/ </dfn>
 -- to jest strona domyslna dalej bede pisal tylko rozszerzenia

<dfn>/prowadzacy </dfn>                              -- spis wszystkich prowadzacych w formacie:

 {"Ad.P.": "Adamczyk Piotr"}, gdzie kluczem jest id prowadzacego a wartoscia imie i nazwisko

                            Zeby byly polskie znaki musicie przy kazdym odbiorze JSON dopisac:

 with open(wasz_request, encoding="UTF-8):

 Nie, nie moglem tego zrobic tutaj

<dfn>/actuality/stud                          </dfn> -- wypluwa ostatnia aktualizacje planu danej grupy

 "Data aktualizacji:2023-10-25 00:25:35" </p

<dfn>/days                                    </dfn> -- zwraca dni kalendarzowe jakby sie wam nie chcialo kalendarza wrzucac

 {"1": [["25", "IX"], ["02", "X"],...], "2": [...]} zwraca slownik gdzie id to 1,2,3... dni tygodnia od poniedzialku a wartosci to [dzien,miesiac]

<dfn>/plan/stud                              </dfn>  -- zwraca plan grupy zajec, przy GET wymaga podania grupy w formie group=nazwa_grupy i KLUCZA key=karzel

 zwraca slownik gdzie kluczem jest data, ktory zawiera slownik gdzie kluczem jest blok, ktory zawiera przedmiot. Dane o przedmiocie to lista gdzie 0 index to display a 1 to pelne info

 {"dd-mm-rrrr": {"1": [display,full_info ], "2":...}, ...} max jest do 7 bloku </p/

<dfn>/grp                                     </dfn> -- zwraca wszystkie grupy szkoleniowe, prosta lista

 ["CHUJ", "chuj", "ChUj", "Bzyku", "To", "Kurwa"]

<dfn>/plan/prow                               </dfn> -- dziala i zwraca to samo co plan dla studentow

tyle ze trzeba dac id prowadzacego przy GET, prow=id_prow

jeszcze nie ma zapisywania do BD wiec wszystko robcie na requestach

zrobie tak zeby zawsze JSON byly takie same to bez pierdolenia ze sie zmieni

jak skonczycie front to podeslijcie i wstawie gotutaj

formas czytaj juz o testach jednostkowych, dam ci dostep do serwera jak nauczysz sie cos robic

zebys przypadkiem czegos nie rozjebala

                """
        return HttpResponse(help)


from django.forms.forms import Form
from django import forms
from glide.core.models import City

class SearchForm(Form):
	query = forms.ChoiceField()

	def __init__(self, *args, **kwargs):
		try:
			default = kwargs.pop('query', None)
		except:
			default = False
		super(SearchForm, self).__init__(*args, **kwargs)
		cities = City.objects.filter(has_local=True).exclude(name=u'').order_by('name')
		#cities = [city for city in cities if city.name]
		self.fields['query'].choices = [(city.pk, city) for city in cities]
		if not default:
			self.fields['query'].choices.insert(0, ('',"I'm looking for a guide in..." ) )
		else:
			self.fields['query'].initial = default


import filecmp, os, shutil, datetime, argparse
from time import sleep

# Check if it's directory
def dir_path(path):
    if os.path.isdir(path):
        return path
    else:
        raise argparse.ArgumentTypeError(f"readable_dir:{path} is not a valid path")

# Set flags
def flags():
    msg='''Periodically synchronizes a user defined directory to a
    defined destination directory. Outputs logs to console and creates a
    file at a user defined location.'''
    parser=argparse.ArgumentParser(description=msg)
    parser.add_argument("-s", "--source", help="Set a Source Directory for synchronization", type=dir_path, required=True)
    parser.add_argument("-d", "--destination", help="Set a Destination Directory for synchronization", type=dir_path, required=True)
    parser.add_argument("-l", "--log", help="Set the path for the log.txt file",type=dir_path, required=True)
    parser.add_argument("-t", "--timeout", help="Set a synchronization frequency, in minutes", type=int, required=True)
    return parser.parse_args()

# Use flags in script
sourcePath=flags().source
destinationPath=flags().destination
logs=os.path.join(flags().log,"logs.txt")
timeout=flags().timeout*60

# Get time
def timestmp():
    now = datetime.datetime.now()
    return now.strftime('%d-%m-%Y %H:%M:%S')

# Write Logs to file
def writeLogs(path, time, fileSource, fileDestination, action ):
    file=open(path, "a")
    temp=time+"    Source: "+fileSource+"        Action: "+action+"        Destination: "+fileDestination+"\n"
    file.write(temp)
    file.close()

# Print to console with color
def printConsole(time, source, destination, action):
    if os.name != 'nt':
        if "Removing" in action:
            print(time+"    \033[1mSource:\033[0m "+source+"    \033[1mAction:\033[0m \033[1;31m"+action+"\033[0m"+"    \033[1mDestination:\033[0m "+destination)
        elif "Modified" in action:
            print(time+"    \033[1mSource:\033[0m "+source+"    \033[1mAction:\033[0m \033[1;33m"+action+"\033[0m"+"    \033[1mDestination:\033[0m "+destination)
        elif "Created" in action:
            print(time+"    \033[1mSource:\033[0m "+source+"    \033[1mAction:\033[0m \033[1;32m"+action+"\033[0m"+"    \033[1mDestination:\033[0m "+destination)
    else:
        if "Removing" in action:
            print(time+"    Source: "+source+"    Action: "+action+"    Destination: "+destination)
        elif "Modified" in action:
            print(time+"    Source: "+source+"    Action: "+action+"    Destination: "+destination)
        elif "Created" in action:
            print(time+"    Source: "+source+"    Action: "+action+"    Destination:\ "+destination)

# Verbose copy2 for copytree
def copy2_verbose(src, dst):
    shutil.copy2(src,dst)
    writeLogs(logs, timestmp(), src,dst, "Created In")
    printConsole(timestmp(), src, dst, "Created In")

def diff_port(src,dst):
    compare=filecmp.dircmp(src,dst)

    common=list(set(os.listdir(src)) & set(os.listdir(dst)))
    common_files=[f for f in common if os.path.isfile(os.path.join(src, f))]

    # Search differences in Source directory
    inSource=list(set(os.listdir(src)) - set(os.listdir(dst)))

    # Search differences in Destination directory
    inDestination=list(set(os.listdir(dst)) - set(os.listdir(src)))

    # Find mismatched files
    match, mismatched, errors = filecmp.cmpfiles(src,dst,common_files,shallow=False)

    # No files and dirs in Destination
    if len(compare.right_list)==0 and len(compare.left_list)>0:
        # If destination is empty Copy from source to destination
        for noDestination in compare.left_list:
            srcPath=os.path.join(src,os.path.basename(noDestination))
            dstPath=os.path.join(dst,os.path.basename(noDestination))
            try:
                shutil.copy2(srcPath,dstPath)
            except:
                shutil.copytree(srcPath, dstPath, symlinks=False, ignore=None, copy_function=shutil.copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)
            writeLogs(logs, timestmp(), srcPath,dstPath, "Created In")
            printConsole(timestmp(), srcPath, dstPath, "Created In")
    elif len(inSource)>0:
        # If destination not empty Copy from source to destination
        for destNotEmpty in inSource:
            sPath=os.path.join(src,destNotEmpty)
            dPath=os.path.join(dst,destNotEmpty)
            try:
                shutil.copy2(sPath,dPath)
            except:
                shutil.copytree(sPath, dPath, symlinks=False, ignore=None, copy_function=copy2_verbose, ignore_dangling_symlinks=False, dirs_exist_ok=True)
            writeLogs(logs, timestmp(), sPath,dPath, "Created In")
            printConsole(timestmp(), sPath, dPath, "Created In")

    # No files and dirs in Source
    if len(compare.left_list)==0 and len(compare.right_list)>0:
        # if Source is empty Remove from destination
        for noSource in compare.right_list:
            destRemove=os.path.join(dst,noSource)
            try:
                os.remove(destRemove)
            except:
                shutil.rmtree(destRemove)
            writeLogs(logs, timestmp(), "Item doesn't exit",destRemove, "Removing From")
            printConsole(timestmp(), "Item doesn't exit", destRemove, "Removing From")
    elif len(inDestination)>0:
        # If list destination has extra remove extra
        for extraDestination in inDestination:
            dstRemove=os.path.join(dst,extraDestination)
            try:
                os.remove(dstRemove)
            except:
                shutil.rmtree(dstRemove)
            writeLogs(logs, timestmp(), "Item doesn't exit",dstRemove, "Removing From")
            printConsole(timestmp(), "Item doesn't exit", dstRemove, "Removing From")

    # Common files that differ
    if len(mismatched) != 0:
        # Copy mismatched from Source to Destination
        for mismatch in mismatched:
            srcFile=os.path.join(src,mismatch)
            dstFile=os.path.join(dst,mismatch)
            shutil.copy2(srcFile, dstFile)
            writeLogs(logs, timestmp(), srcFile,dstFile, "Modified In")
            printConsole(timestmp(), srcFile, dstFile, "Modified In")

    for common_dir in compare.common_dirs:
        newSrc = os.path.join(src, common_dir)
        newDst = os.path.join(dst, common_dir)
        if not diff_port(newSrc, newDst):
            return False
    return True # checks all directories from the same level

while True:
    diff_port(sourcePath, destinationPath)
    sleep(timeout)


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import random
import os
import sys
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets import base
from tensorflow.python.framework import random_seed
import glob2

distributions = tf.contrib.distributions

def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.01)

  return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.0, shape=shape)

    return tf.Variable(initial)

#We map past locations (mean_arr) to some distribution over potential locations at the current time step
#and return the log likelihood of these possible samples
def loglikelihood(mean_arr, sampled_arr, sigma):
    mu = tf.stack(mean_arr)  # mu = [timesteps, batch_size, loc_dim]
    sampled = tf.stack(sampled_arr)  # same shape as mu
    gaussian = distributions.Normal(mu, sigma)
    logll = gaussian.log_prob(sampled)  # [timesteps, batch_size, loc_dim]
    logll = tf.reduce_sum(logll, 2)
    logll = tf.transpose(logll)  # [batch_size, timesteps]

    return logll

class DataSet(object):
    def __init__(self,
               images,
               labels,
               reshape=True,
               seed=None):
        seed1, seed2 = random_seed.get_seed(seed)
        # If op level seed is not set, use whatever graph level seed is returned
        np.random.seed(seed1 if seed is None else seed2)
        assert images.shape[0] == labels.shape[0], (
            'images.shape: %s labels.shape: %s' % (images.shape, labels.shape))
        self._num_examples = images.shape[0]

        # Convert shape from [num examples, rows, columns, depth]
        # to [num examples, rows*columns]
        if reshape:
            images = images.reshape(images.shape[0],
                                    images.shape[1] * images.shape[2])

        self._images = images
        self._labels = labels
        self._epochs_completed = 0
        self._index_in_epoch = 0

    @property
    def images(self):
        return self._images

    @property
    def labels(self):
        return self._labels

    @property
    def num_examples(self):
        return self._num_examples

    @property
    def epochs_completed(self):
        return self._epochs_completed

    def next_batch(self, batch_size, shuffle=True):
        """Return the next `batch_size` examples from this data set."""
        start = self._index_in_epoch
        # Shuffle for the first epoch
        if self._epochs_completed == 0 and start == 0 and shuffle:
            perm0 = np.arange(self._num_examples)
            np.random.shuffle(perm0)
            self._images = self.images[perm0]
            self._labels = self.labels[perm0]
        # Go to the next epoch
        if start + batch_size > self._num_examples:
            # Finished epoch
            self._epochs_completed += 1
            # Get the rest examples in this epoch
            rest_num_examples = self._num_examples - start
            images_rest_part = self._images[start:self._num_examples]
            labels_rest_part = self._labels[start:self._num_examples]
            # Shuffle the data
            if shuffle:
                perm = np.arange(self._num_examples)
                np.random.shuffle(perm)
                self._images = self.images[perm]
                self._labels = self.labels[perm]
            # Start next epoch
            start = 0
            self._index_in_epoch = batch_size - rest_num_examples
            end = self._index_in_epoch
            images_new_part = self._images[start:end]
            labels_new_part = self._labels[start:end]
            return np.concatenate(
                (images_rest_part, images_new_part), axis=0), np.concatenate(
                    (labels_rest_part, labels_new_part), axis=0)
        else:
            self._index_in_epoch += batch_size
            end = self._index_in_epoch
            return self._images[start:end], self._labels[start:end]

def extract_pc_and_labels(data_dir,
                          num_points_per_pc,
                          all_examples,
                          all_labels,
                          examples_indices):
    len_data = len(examples_indices)
    data = np.empty((len_data, num_points_per_pc, 3))
    labels = np.zeros((len_data,))

    for i, index in enumerate(examples_indices):
        curr_example = all_examples[index]

        curr_file = os.path.join(data_dir, curr_example)
        curr_pc = open(curr_file, 'r').read().split('\n')
        rows = [i for i in curr_pc if i != '']
        all_points = [i.split(',')[:3] for i in rows]
        all_points = np.array(all_points, dtype=np.float32)

        data[i] = all_points

        curr_label = curr_example.split('/')[0]
        one_hot_pos = np.where(all_labels == curr_label)
        labels[i] = one_hot_pos[0][0]

    return data, labels

def read_data_sets(data_dir,
                   num_points_per_pc,
                   reshape=True,
                   seed=None):
    try:
        print('Trying to load existing ModelNet40 datasets...')
        options = dict(reshape=reshape, seed=seed)

        train_pc = np.load('train_pc.npy')
        train_labels = np.load('train_labels.npy')
        val_pc = np.load('val_pc.npy')
        val_labels = np.load('val_labels.npy')
        test_pc = np.load('test_pc.npy')
        test_labels = np.load('test_labels.npy')

        print('Building train, validation and test dataset objects...')

        train = DataSet(train_pc, train_labels, **options)
        validation = DataSet(val_pc, val_labels, **options)
        test = DataSet(test_pc, test_labels, **options)

        return base.Datasets(train=train, validation=validation, test=test)
    except:
        print('No existing ModelNet40 train, validation and test sets found...Rebuilding')

        example_list = open(data_dir + '/filelist.txt', 'r')
        all_examples = example_list.read().split('\n')
        all_examples = [i for i in all_examples if i != '']
        all_examples = np.array(all_examples)
        example_list.close()

        num_examples = len(all_examples)
        range_examples = np.arange(0, num_examples)
        random.Random(0).shuffle(range_examples)

        label_list = open(data_dir + '/modelnet40_shape_names.txt', 'r')
        all_labels = label_list.read().split('\n')
        all_labels = [i for i in all_labels if i != '']
        all_labels = np.array(all_labels)
        label_list.close()

        X_train, X_test, _, _ = train_test_split(range_examples,
                                                range_examples,
                                                test_size=0.2,
                                                random_state=42)

        X_val, X_test, _, _ = train_test_split(X_test,
                                            X_test,
                                            test_size=0.5,
                                            random_state=42)

        train_pc, train_labels = extract_pc_and_labels(data_dir,
                                                        num_points_per_pc,
                                                        all_examples,
                                                        all_labels,
                                                        X_train)
        val_pc, val_labels = extract_pc_and_labels(data_dir,
                                                    num_points_per_pc,
                                                    all_examples,
                                                    all_labels,
                                                    X_val)
        test_pc, test_labels = extract_pc_and_labels(data_dir,
                                                        num_points_per_pc,
                                                        all_examples,
                                                        all_labels,
                                                        X_test)

        options = dict(reshape=reshape, seed=seed)

        np.save('train_pc', train_pc)
        np.save('train_labels', train_labels)
        np.save('val_pc', val_pc)
        np.save('val_labels', val_labels)
        np.save('test_pc', test_pc)
        np.save('test_labels', test_labels)

        print('Building train, validation and test dataset objects...')

        train = DataSet(train_pc, train_labels, **options)
        validation = DataSet(val_pc, val_labels, **options)
        test = DataSet(test_pc, test_labels, **options)

        return base.Datasets(train=train, validation=validation, test=test)


import numpy as np

from six.moves import xrange

# ------------------------------------------------------------
# Tensorflow ops

def tf_get_shape_as_list(x):

    return [_s if _s is not None else - 1 for _s in x.get_shape().as_list()]

def tf_quaternion_from_matrix(M):

    import tensorflow as tf

    m00 = M[:, 0, 0][..., None]
    m01 = M[:, 0, 1][..., None]
    m02 = M[:, 0, 2][..., None]
    m10 = M[:, 1, 0][..., None]
    m11 = M[:, 1, 1][..., None]
    m12 = M[:, 1, 2][..., None]
    m20 = M[:, 2, 0][..., None]
    m21 = M[:, 2, 1][..., None]
    m22 = M[:, 2, 2][..., None]
    # symmetric matrix K
    zeros = tf.zeros_like(m00)
    K = tf.concat(
        [m00 - m11 - m22, zeros, zeros, zeros,
         m01 + m10, m11 - m00 - m22, zeros, zeros,
         m02 + m20, m12 + m21, m22 - m00 - m11, zeros,
         m21 - m12, m02 - m20, m10 - m01, m00 + m11 + m22],
        axis=1)
    K = tf.reshape(K, (-1, 4, 4))
    K /= 3.0
    # quaternion is eigenvector of K that corresponds to largest eigenvalue
    w, V = tf.self_adjoint_eig(K)

    q0 = V[:, 3, 3][..., None]
    q1 = V[:, 0, 3][..., None]
    q2 = V[:, 1, 3][..., None]
    q3 = V[:, 2, 3][..., None]
    q = tf.concat([q0, q1, q2, q3], axis=1)
    sel = tf.reshape(tf.to_float(q[:, 0] < 0.0), (-1, 1))
    q = (1.0 - sel) * q - sel * q

    return q

def tf_matrix_from_quaternion(q, eps=1e-10):

    import tensorflow as tf

    # Make unit quaternion
    q_norm = q / (eps + tf.norm(q, axis=1, keep_dims=True))
    q_norm *= tf.constant(2.0 ** 0.5, dtype=tf.float32)
    qq = tf.matmul(
        tf.reshape(q_norm, (-1, 4, 1)),
        tf.reshape(q_norm, (-1, 1, 4))
    )
    M = tf.stack([
        1.0 - qq[:, 2, 2] - qq[:, 3, 3], qq[:, 1, 2] - qq[:, 3, 0],
        qq[:, 1, 3] + qq[:, 2, 0], qq[:, 1, 2] + qq[:, 3, 0],
        1.0 - qq[:, 1, 1] - qq[:, 3, 3], qq[:, 2, 3] - qq[:, 1, 0],
        qq[:, 1, 3] - qq[:, 2, 0], qq[:, 2, 3] + qq[:, 1, 0],
        1.0 - qq[:, 1, 1] - qq[:, 2, 2]
    ], axis=1)

    return M

def tf_skew_symmetric(v):

    import tensorflow as tf

    zero = tf.zeros_like(v[:, 0])

    M = tf.stack([
        zero, -v[:, 2], v[:, 1],
        v[:, 2], zero, -v[:, 0],
        -v[:, 1], v[:, 0], zero,
    ], axis=1)

    return M

def tf_unskew_symmetric(M):

    import tensorflow as tf

    v = tf.stack([
        0.5 * (M[:, 7] - M[:, 5]),
        0.5 * (M[:, 2] - M[:, 6]),
        0.5 * (M[:, 3] - M[:, 1]),
    ], axis=1)

    return v

# ------------------------------------------------------------
# Architecture related

def bn_act(linout, perform_gcn, perform_bn, activation_fn, is_training,
           data_format):

    import tensorflow as tf

    """ Perform batch normalization and activation """
    if data_format == "NHWC":
        axis = -1
    else:
        axis = 1

    # Global Context normalization on the input
    if perform_gcn:
        # Epsilon to be used in the tf.nn.batch_normalization
        var_eps = 1e-3
        # get mean variance for single sample (channel-wise, note that we omit
        # axis=1 since we are expecting a size of 1 in that dimension)
        mean, variance = tf.nn.moments(linout, axes=[2], keep_dims=True)
        # Use tensorflow's nn.batchnorm
        linout = tf.nn.batch_normalization(
            linout, mean, variance, None, None, var_eps)

    if perform_bn:
        with tf.variable_scope("bn"):
            linout = tf.layers.batch_normalization(
                inputs=linout,
                center=False, scale=False,
                training=is_training,
                trainable=True,
                axis=axis,
            )

    if activation_fn is None:
        output = linout
    else:
        output = activation_fn(linout)

    return output

def pad_cyclic(tensor, paddings):

    import tensorflow as tf

    ndim = len(paddings)
    for _dim, _pad in zip(xrange(ndim), paddings):

        pad_list = []
        if _pad[0] > 0:
            # Padding to put at front
            slice_st = [slice(None, None)] * ndim
            slice_st[_dim] = slice(-_pad[0], None)
            pad_list += [tensor[tuple(slice_st)]]

        # Original
        pad_list += [tensor]

        if _pad[1] > 0:
            # Padding to put at back
            slice_ed = [slice(None, None)] * ndim
            slice_ed[_dim] = slice(None, _pad[1])
            pad_list += [tensor[tuple(slice_ed)]]

        if len(pad_list) > 1:
            # Concatenate to do padding
            tensor = tf.concat(pad_list, axis=_dim)

    return tensor

def conv1d_pad_cyclic(inputs, ksize, numconv, data_format="NCHW"):
    in_shp = tf_get_shape_as_list(inputs)
    ksize = 2 * (ksize // 2 * numconv) + 1

    if data_format == "NCHW":
        assert (ksize < in_shp[-1]) or (in_shp[-1] == -1)
        if np.mod(ksize, 2) == 0:
            paddings = [
                [0, 0], [0, 0], [0, 0], [ksize // 2 - 1, ksize // 2]
            ]
        else:
            paddings = [
                [0, 0], [0, 0], [0, 0], [ksize // 2, ksize // 2]
            ]
    else:
        assert (ksize < in_shp[-2]) or (in_shp[-2] == -1)
        if np.mod(ksize, 2) == 0:
            paddings = [
                [0, 0], [0, 0], [ksize // 2 - 1, ksize // 2], [0, 0]
            ]
        else:
            paddings = [
                [0, 0], [0, 0], [ksize // 2, ksize // 2], [0, 0]
            ]
    inputs = pad_cyclic(inputs, paddings)

    return inputs

def get_W_b_conv1d(in_channel, out_channel, ksize, dtype=None):

    import tensorflow as tf

    if dtype is None:
        dtype = tf.float32

    fanin = in_channel * ksize
    W = tf.get_variable(
        "weights", shape=[1, ksize, in_channel, out_channel], dtype=dtype,
        initializer=tf.truncated_normal_initializer(stddev=2.0 / fanin),
        # initializer=tf.random_normal_initializer(stddev=0.02),
    )
    b = tf.get_variable(
        "biases", shape=[out_channel], dtype=dtype,
        initializer=tf.zeros_initializer(),
    )
    # tf.summary.histogram("W", W)
    # tf.summary.histogram("b", b)

    return W, b

def conv1d_layer(inputs, ksize, nchannel, activation_fn, perform_bn,
                 perform_gcn, is_training, perform_kron=False,
                 padding="CYCLIC", data_format="NCHW",
                 act_pos="post"):

    import tensorflow as tf

    assert act_pos == "pre" or act_pos == "post"

    # Pad manually
    if padding == "CYCLIC":
        if ksize > 1:
            inputs = conv1d_pad_cyclic(
                inputs, ksize, 1, data_format=data_format)
        cur_padding = "VALID"
    else:
        cur_padding = padding

    in_shp = tf_get_shape_as_list(inputs)
    if data_format == "NHWC":
        in_channel = in_shp[-1]
        ksizes = [1, 1, ksize, 1]
    else:
        in_channel = in_shp[1]
        ksizes = [1, 1, 1, ksize]

    assert len(in_shp) == 4

    # # Lift with kronecker
    # if not is_first:
    #     inputs = tf.concat([
    #         inputs,
    #         kronecker_layer(inputs),
    #     ], axis=-1)

    pool_func = None
    self_ksize = ksize
    do_add = False

    # If pre activation
    if act_pos == "pre":
        inputs = bn_act(inputs, perform_gcn, perform_bn, activation_fn,
                        is_training, data_format)

    # Normal convolution
    with tf.variable_scope("self-conv"):
        W, b = get_W_b_conv1d(in_channel, nchannel, self_ksize)
        # Convolution in the valid region only
        linout = tf.nn.conv2d(
            inputs, W, [1, 1, 1, 1], cur_padding, data_format=data_format)
        linout = tf.nn.bias_add(linout, b, data_format=data_format)
    # Pooling Convolution for the summary route
    if pool_func is not None:
        with tf.variable_scope("neigh-conv"):
            if not do_add:
                linout = pool_func(
                    linout,
                    ksize=ksizes,
                    strides=[1, 1, 1, 1],
                    padding=cur_padding,
                    data_format=data_format,
                )
            else:
                W_n, b_n = get_W_b_conv1d(in_channel, nchannel, 1)
                # Convolution in the valid region only
                linout_n = tf.nn.conv2d(
                    inputs, W_n, [1, 1, 1, 1], "VALID", data_format=data_format
                )
                linout_n = tf.nn.bias_add(
                    linout_n, b_n, data_format=data_format)
                linout_n = pool_func(
                    linout_n,
                    ksize=ksizes,
                    strides=[1, 1, 1, 1],
                    padding=cur_padding,
                    data_format=data_format,
                )
                # # Crop original linout
                # if ksize > 1:
                #     if np.mod(ksize, 2) == 0:
                #         crop_st = ksize // 2 - 1
                #     else:
                #         crop_st = ksize // 2
                #         crop_ed = ksize // 2
                #     linout = linout[:, :, :, crop_st:-crop_ed]
                # Add to the original output
                linout = linout + linout_n

    # If post activation
    output = linout
    if act_pos == "post":
        output = bn_act(linout, perform_gcn, perform_bn, activation_fn,
                        is_training, data_format)

    return output

def conv1d_resnet_block(inputs, ksize, nchannel, activation_fn, is_training,
                        midchannel=None, perform_bn=False, perform_gcn=False,
                        padding="CYCLIC", act_pos="post", data_format="NCHW"):

    import tensorflow as tf

    # In case we want to do a bottleneck layer
    if midchannel is None:
        midchannel = nchannel

    # don't activate conv1 in case of midact
    conv1_act_fn = activation_fn
    if act_pos == "mid":
        conv1_act_fn = None
        act_pos = "pre"

    # Pass branch
    with tf.variable_scope("pass-branch"):
        # passthrough to be used when num_outputs != num_inputs
        in_shp = tf_get_shape_as_list(inputs)
        if data_format == "NHWC":
            in_channel = in_shp[-1]
        else:
            in_channel = in_shp[1]
        if in_channel != nchannel:
            cur_in = inputs
            # Simply change channels through 1x1 conv
            with tf.variable_scope("conv"):
                cur_in = conv1d_layer(
                    inputs=inputs, ksize=1,
                    nchannel=nchannel,
                    activation_fn=None,
                    perform_bn=False,
                    perform_gcn=False,
                    is_training=is_training,
                    padding=padding,
                    data_format=data_format,
                )
            orig_inputs = cur_in
        else:
            orig_inputs = inputs

    # Conv branch
    with tf.variable_scope("conv-branch"):
        cur_in = inputs
        # Do bottle neck if necessary (Linear)
        if midchannel != nchannel:
            with tf.variable_scope("preconv"):
                cur_in = conv1d_layer(
                    inputs=cur_in, ksize=1,
                    nchannel=nchannel,
                    activation_fn=None,
                    perform_bn=False,
                    perform_gcn=False,
                    is_training=is_training,
                    padding=padding,
                    data_format=data_format,
                )
                cur_in = activation_fn(cur_in)

        # Main convolution
        with tf.variable_scope("conv1"):
            # right branch
            cur_in = conv1d_layer(
                inputs=cur_in, ksize=ksize,
                nchannel=nchannel,
                activation_fn=conv1_act_fn,
                perform_bn=perform_bn,
                perform_gcn=perform_gcn,
                is_training=is_training,
                padding=padding,
                act_pos=act_pos,
                data_format=data_format,
            )

        # Main convolution
        with tf.variable_scope("conv2"):
            # right branch
            cur_in = conv1d_layer(
                inputs=cur_in, ksize=ksize,
                nchannel=nchannel,
                activation_fn=activation_fn,
                perform_bn=perform_bn,
                perform_gcn=perform_gcn,
                is_training=is_training,
                padding=padding,
                act_pos=act_pos,
                data_format=data_format,
            )

        # Do bottle neck if necessary (Linear)
        if midchannel != nchannel:
            with tf.variable_scope("postconv"):
                cur_in = conv1d_layer(
                    inputs=cur_in, ksize=1,
                    nchannel=nchannel,
                    activation_fn=None,
                    perform_bn=False,
                    perform_gcn=False,
                    is_training=is_training,
                    padding=padding,
                    data_format=data_format,
                )
                cur_in = activation_fn(cur_in)

    # Crop lb or rb accordingly
    if padding == "VALID" and ksize > 1:
        # Crop pass branch results
        if np.mod(ksize, 2) == 0:
            crop_st = ksize // 2 - 1
        else:
            crop_st = ksize // 2
            crop_ed = ksize // 2
            if data_format == "NHWC":
                orig_inputs = orig_inputs[:, :,  crop_st:-crop_ed, :]
            else:
                orig_inputs = orig_inputs[:, :, :, crop_st:-crop_ed]

    return cur_in + orig_inputs

#
# ops.py ends here


import zipfile
import os
import shutil

home = os.path.expanduser('~')
def unzip(name):
    zname = home + '/files/' + name + '.zip'
    fname = home + '/files/' + name
    fantasy_zip = zipfile.ZipFile(zname)
    fantasy_zip.extractall(fname)
    fantasy_zip.close()


import bs4 as bs
import urllib.request
import csv
import pandas as pd
source = urllib.request.urlopen('https://untappd.com/beer/top_rated?type=adambier').read()
soup = bs.BeautifulSoup(source,'lxml')
f = csv.writer(open('beers.csv','w'))
body = soup.body
table = soup.find_all("div",{"class":"beer-item"})
#table_rows = table.find_all('span',{"class":"num"})
for t in table:
    beer_rating = []
    rating = t.select('span',{"class":"num"})
    name = t.select('a',{"class":"name"})
    row_rating = [i.text for i in rating]
    row_name = [i.text for i in name]
    #f.writerow(rating)
    #print(row_name[1])
    #beer_rating = beer_rating.append([[row_name[1],row_rating[1]]])
    print(row_name[1],row_rating[1])


import pygame
from snake import Snake
from cherry import Cherry

size = 20
xlim = 21
ylim = 15
width = size * xlim
height = size * ylim + 50

pygame.init()                       # Initialization
black = (0,0,0)
gray = (180,180,180)
white = (255,255,255)
blue = (0,0,255)
green = (0,255,0)
red = (255,0,0)
screen  = pygame.display.set_mode((width, height))
pygame.display.set_caption('Snake')
gameClose = False
gameOver = False
score = 0
speed = 20
multi = 1
clock = pygame.time.Clock()
snake = Snake((xlim-1)/2, (ylim-1)/2, xlim-1, ylim-1, 0.5, 0)
cherry = Cherry(snake, xlim-1, ylim-1)

msg_font = pygame.font.SysFont("bahnschrift", 20)
score_font = pygame.font.SysFont("comicsansms", 20)

def background(screen):
    screen.fill(white)
    for i in range(0,xlim*ylim,2):
        x = i % xlim
        y = i // xlim
        pygame.draw.rect(screen,gray,[x*size,y*size,size,size])

while not gameClose:
    if gameOver:
        msg1 = msg_font.render("Game Over", True, black)
        screen.blit(msg1, [width / 3 +20, height / 3])
        msg2 = msg_font.render("Press 'Spacebar' to restart", True, black)
        screen.blit(msg2, [width / 6, height / 3 + 20])
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                gameClose = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    gameOver = False
                    snake = Snake((xlim-1)/2, (ylim-1)/2, xlim-1, ylim-1, 0.5, 0)
                    cherry = Cherry(snake, xlim-1, ylim-1)
                    score = 0
                    speed = 20
                    multi = 1
    else:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                gameClose = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP and snake.yd == 0:
                    snake.moveQueue.append('up')
                elif event.key == pygame.K_DOWN and snake.yd == 0:
                    snake.moveQueue.append('down')
                elif event.key == pygame.K_LEFT and snake.xd == 0:
                    snake.moveQueue.append('left')
                elif event.key == pygame.K_RIGHT and snake.xd == 0:
                    snake.moveQueue.append('right')
        if snake.update(cherry.x, cherry.y):
            cherry = Cherry(snake,xlim-1,ylim-1)
        if snake.check():
            gameOver = True
        background(screen)
        pygame.draw.line(screen,black,(0,height-50),(width,height-50),3)
        pygame.draw.line(screen,black,(0,0),(width,0),3)
        pygame.draw.line(screen,black,(0,0),(0,height-50),3)
        pygame.draw.line(screen,black,(width,0),(width,height-50),3)
        pygame.draw.circle(screen, blue, (snake.body[0][0]*size + size/2, snake.body[0][1]*size + size/2), size/2)
        for i in range(1,snake.len-1):
            a = snake.body[i]
            if snake.body[i-1][0] != snake.body[i+1][0] and snake.body[i-1][1] != snake.body[i+1][1]:
                pygame.draw.circle(screen, blue, (a[0]*size + size/2, a[1]*size + size/2), size/2)
            else:
                pygame.draw.rect(screen,blue, [a[0]*size,a[1]*size,size,size])
        pygame.draw.circle(screen,red,(cherry.x*size + size/2, cherry.y*size + size/2), size/4)
        pygame.draw.circle(screen, green, (snake.x*size + size/2, snake.y*size + size/2), size/2)
        value = score_font.render("Score: " + str((snake.len-3)*10), True, black)
        if snake.len - score > 20*multi+3:
            score = snake.len
            speed += 3
            multi += 0.2
        screen.blit(value,[10, height - 50])
        pygame.display.update()
        clock.tick(speed)

pygame.quit()
quit()


#importing necessary packages
import streamlit as st
import pandas as pd
from fpdf import FPDF
import base64

#setting page header
st.header("DasKasas Tag Tool")

#requesting tag sizes and csv file from user in the sidebar
with st.sidebar:
    csv = st.file_uploader("Please Upload Updated CSV Export from DEAR Inventory.")
    portrait_landscape = st.radio("Please select the orientation of the tags.", ("Portrait", "Landscape"))
    if portrait_landscape == "Portrait":
        portrait_landscape = "P"
    else:
        portrait_landscape = "L"
    auto_max_characters = st.checkbox("Auto Max Characters")
    tag_height = st.number_input("Tag Height (mm)", min_value=0, max_value=100, value=34)/3
    tag_width = st.number_input("Tag Width (mm)", min_value=0, max_value=100, value=60)
    font_size = st.number_input("Font Size", min_value=8, max_value=100, value=8)
    if not auto_max_characters:
        max_characters = st.number_input("Max Characters", min_value=18, max_value=60, value=37)
    else:
        # set max characters so that text fits on tag
        max_characters = int(tag_width / (font_size* 0.20))

#if a csv has been uploaded load the csv as a pandas df and display it for verification by user
if csv:
    st.subheader("CSV file uploaded, please confirm below:")
    df = pd.read_csv(csv)
    st.dataframe(df)

    #getting list of price columns
    price_columns = df.filter(regex='Price').columns

    #allowing the user to change the price columns to be used for the tag if need be
    with st.sidebar:
        price_chosen = st.selectbox("Select Price Regime", price_columns,4)

    #search box for filtering by item name and including only those in the pdf to be printed
    selected_products = st.multiselect("Select Products", df.ProductCode.unique())

    #creating and displaying a df with only selected products (filter)
    df = df.loc[df["ProductCode"].isin(selected_products)]
    df = df.reset_index(drop=True)
    #Allow the description to only have a length of 40 characters
    df["Name"] = df["Name"].str[:max_characters]

    #displaying the new dataframe with only selected products for verification by user
    st.dataframe(df)

    #button to go ahead and create the pdf with the tags
    button = st.button("Process Tags")

    if button:
        # instantiation of inherited class
        pdf = FPDF(unit= "mm", format = "A4", orientation = portrait_landscape)

        #creating a blank page and setting font
        pdf.add_page(portrait_landscape)
        pdf.set_font('Arial', 'B', font_size)

        #getting initial x coordinate
        x_initial = pdf.get_x()

        for index in df.index:
            #if current x position is within the useable width of the page create new tags
            if pdf.get_x() < (pdf.w - pdf.l_margin - tag_width):
                pdf.cell(w = tag_width, h = tag_height, txt = df.iloc[index, 0], border = 0)
                pdf.set_xy(pdf.get_x()-tag_width, pdf.get_y() + tag_height)
                pdf.cell(w = tag_width, h = tag_height, txt = df.iloc[index, 1], border = 0)
                pdf.set_xy(pdf.get_x()-tag_width, pdf.get_y() + tag_height)
                pdf.cell(w = tag_width, h = tag_height, txt = "R" + "{:0.2f}".format(df.at[index, price_chosen]*1.15), border = 0)
                pdf.set_xy(pdf.get_x(),pdf.get_y()-2*tag_height)
                #create border around all three cells
                pdf.rect(pdf.get_x()-tag_width, pdf.get_y(), tag_width, 3*tag_height)

            else:
            #if not create new tags on new full tag line (tag_height * 3)
                pdf.set_xy(x_initial, pdf.get_y() + tag_height*3)
                pdf.cell(w = tag_width, h = tag_height, txt = df.iloc[index, 0], border = 0)
                pdf.set_xy(pdf.get_x()-tag_width, pdf.get_y() + tag_height)
                pdf.cell(w = tag_width, h = tag_height, txt = df.iloc[index, 1], border = 0)
                pdf.set_xy(pdf.get_x()-tag_width, pdf.get_y() + tag_height)
                pdf.cell(w = tag_width, h = tag_height, txt = "R" + "{:0.2f}".format(df.at[index, price_chosen]*1.15), border = 0)
                pdf.set_xy(pdf.get_x(),pdf.get_y()-2*tag_height)
                #create border around all three cells
                pdf.rect(pdf.get_x()-tag_width, pdf.get_y(), tag_width, 3*tag_height)

        #store output pdf in memory
        final_pdf = pdf.output("test.pdf")

        #reading the saved pdf file and storing it as PDFbyte
        with open("test.pdf", "rb") as pdf_file:
            PDFbyte = pdf_file.read()

        #creating a download button to download above pdf
        st.download_button(label="Download Printable PDF",
                data=PDFbyte,
                file_name="PriceTags.pdf",
                mime='application/octet-stream')


from django.contrib import admin

from .models import Donor, Currency, Donation

#repeat for other models.
class DonationAdmin(admin.ModelAdmin):
    list_display = ('donor', 'datetime_of_donation',
                    'currency_type', 'amount','notified','section18a_issued',)
    #list_editable = ('donor', 'datetime_of_donation',
     #               'currency_type', 'amount','notified','section18a_issued',)

admin.site.register(Donor)
admin.site.register(Currency)
admin.site.register(Donation, DonationAdmin)


from setuptools import setup, find_packages
import pathlib
import re

# this will come in handy, probably
cwd = pathlib.Path(__file__).parent.resolve()

# Parse long description from README.md file
with open("README.md") as f:
    lines, capture = [], False
    for line in f:
        s = line.strip()
        if re.match(r"#\s*[Aa]bout", s):
            capture = True
        elif re.match("^#", s):
            break
        elif capture is True:
            lines.append(s)
    if lines:
        long_description = " ".join(lines)
    else:
        long_description = "DISPATCHES project"

def read_requirements(input_file):
    """Build list of requirements from a requirements.txt file
    """
    req = []
    for line in input_file:
        s = line.strip()
        c = s.find("#")  # look for comment
        if c != 0:  # no comment (-1) or comment after start (> 0)
            if c > 0:  # strip trailing comment
                s = s[:c]
            req.append(s)
    return req

class SpecialDependencies:
    """
    The following packages require special treatment, as they change rapidly between release cycles.
    Two separate lists of dependencies are kept:
    - for_release: to be used when cutting a release of DISPATCHES
    - for_prerelease: to be used for the prerelease version of DISPATCHES (i.e. the `main` branch, and all PRs targeting it)
    """
    # idaes-pse: for IDAES DMF -dang 12/2020
    for_release = [
        # NOTE: this will fail until this idaes-pse version is available on PyPI
        "idaes-pse==2.0.*",
        "pyomo==6.5.*",
    ]
    for_prerelease = [
        "idaes-pse==2.0.*",
        "pyomo==6.5.*",
    ]

SPECIAL_DEPENDENCIES = SpecialDependencies.for_prerelease

########################################################################################

setup(
    name="dispatches",
    url="https://github.com/gmlc-dispatches/dispatches",
    version="1.3.dev0",
    description="GMLC DISPATCHES software tools",
    long_description=long_description,
    long_description_content_type="text/plain",
    author="DISPATCHES team",
    # Classifiers help users find your project by categorizing it.
    #
    # For a list of valid classifiers, see https://pypi.org/classifiers/
    classifiers=[
        #   3 - Alpha
        #   4 - Beta
        #   5 - Production/Stable
        "Development Status :: 3 - Alpha",
        "Intended Audience :: End Users/Desktop",
        "Intended Audience :: Science/Research",
        "License :: OSI Approved :: BSD License",
        "Natural Language :: English",
        "Operating System :: MacOS",
        "Operating System :: Microsoft :: Windows",
        "Operating System :: Unix",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: Implementation :: CPython",
        "Topic :: Scientific/Engineering :: Mathematics",
        "Topic :: Scientific/Engineering :: Chemistry",
        "Topic :: Software Development :: Libraries :: Python Modules",
        "Programming Language :: Python :: 3 :: Only",
    ],
    keywords="market simulation, chemical engineering, process modeling, hybrid power systems",
    packages=find_packages(),
    python_requires=">=3.8, <4",
    install_requires=[
        "pytest",
        # we use jupyter notebooks
        "jupyter",
        # for visualizing DMF provenance
        "graphviz",
        "gridx-prescient>=2.2.2",
        "nrel-pysam",
        "utm",
        "dispatches-data-packages >= 23.3.19",
        "dispatches-rts-gmlc-data",
        *SPECIAL_DEPENDENCIES
    ],
    extras_require={
        "teal": [
            "raven-framework == 2.3 ; python_version <= '3.9' and platform_system != 'Linux'",
            "teal-ravenframework == 0.4 ; python_version <= '3.9' and platform_system != 'Linux'",
            "dispatches-synthetic-price-data >= 23.4.4",
        ],
        "surrogates": [
            "tslearn >= 0.5.2",  # not needed for steady-state surrogates
            "scikit-learn == 1.2.1",  # used by RE steady-state surrogate (static_clustering_wind_pmax.pkl)
            "tensorflow == 2.10.0",  # to match Tensorflow version used to train RE steady-state surrogates Keras models
            "tables >= 3.6.1",
            "matplotlib",
            "dispatches-dynamic-sweep-data >= 23.4.4",
        ],
    },
    package_data={
        "": ["*.json"],
        "dispatches.tests.data.prescient_5bus": ["*.csv"],
        "dispatches.case_studies.renewables_case.tests": [
            "rts_results_all_prices.npy",
        ],
        "dispatches.case_studies.renewables_case.data": [
           "Wind_Thermal_Dispatch.csv",
           "309_WIND_1-SimulationOutputs.csv",
            "44.21_-101.94_windtoolkit_2012_60min_80m.srw"
        ],
        "dispatches.case_studies.renewables_case.data.steady_state_surrogate.dispatch_frequency":[
            "ss_surrogate_param_wind_pmax.json",
            "static_clustering_wind_pmax.pkl"
        ],
        "dispatches.case_studies.renewables_case.data.steady_state_surrogate.dispatch_frequency.ss_surrogate_model_wind_pmax":[
            "keras_metadata.pb",
            "saved_model.pb"
        ],
        "dispatches.case_studies.renewables_case.data.steady_state_surrogate.dispatch_frequency.ss_surrogate_model_wind_pmax.variables":[
            "variables.data-00000-of-00001",
            "variables.index"
        ],
        "dispatches.case_studies.renewables_case.data.steady_state_surrogate.revenue":[
            "RE_revenue_params_2_25.json"
        ],
        "dispatches.case_studies.renewables_case.data.steady_state_surrogate.revenue.RE_revenue_2_25":[
            "keras_metadata.pb",
            "saved_model.pb"
        ],
        "dispatches.case_studies.renewables_case.data.steady_state_surrogate.revenue.RE_revenue_2_25.variables":[
            "variables.data-00000-of-00001",
            "variables.index"
        ],
        "dispatches.case_studies.fossil_case.ultra_supercritical_plant": [
            "pfd_ultra_supercritical_pc.svg",
        ],
        "dispatches.workflow.train_market_surrogates.dynamic.tests.data":[
            "inputdatatest.h5",
            "revdatatest.csv",
            "simdatatest.csv",
            "sample_clustering_model.json"
        ],
    },
)


#Given a gff of regions you are interested in, a fasta file of the genome sequence, and a .bed file
#of phastcons values (sorted.phastcons.mm10.bed.gz), look for kmers within the exonic regions and
#get phastcons values from kmerstart - x to kmerend + x

from Bio import SeqIO
import sys
import pysam
import gffutils
import os
import gzip
import numpy as np

def indexgenome(genomefasta):
	sys.stderr.write('Indexing genome sequence...\n')
	seq_dict = SeqIO.to_dict(SeqIO.parse(genomefasta, 'fasta'))
	sys.stderr.write('{0} chromosomes indexed.\n'.format(len(seq_dict)))

	return seq_dict

def getSequence(seq_dict, region):
    #region = chrm;start;stop;strand
	chrm = region.split(';')[0]
	start = int(region.split(';')[1])
	stop = int(region.split(';')[2])
	strand = region.split(';')[3]

	if strand == '+':
		seq = seq_dict[chrm].seq[start:stop].upper().transcribe()
	elif strand == '-':
		seq = seq_dict[chrm].seq[start:stop].upper().reverse_complement().transcribe()

	seq = str(seq)

	return seq

def getkmerpos(featureseq, featurecoords, kmers):
	kmerindexes = []
	ntsinwindow = [] #single nt positions (in genome space) covered by a kmerofinterest
	#coords in ntinwindow are 0-based for easy interaction with a 0-based bed
	k = len(kmers[0]) #all kmers have to be the same length
	for i in range(len(featureseq) - k + 1):
		kmer = featureseq[i:i+k]
		if kmer in kmers:
			kmerindexes.append(i) # i is start of kmer; kmerofinterest goes from i to i + len(kmerofinterest)

	for ind in kmerindexes:
		for j in list(range(k)): #can add window around kmer here
			ntsinwindow.append(featurecoords[ind + j])

	return list(set(ntsinwindow))

def getscores(phastconstabix, chrom, ntsinwindow):
	#Get score for each nt in ntsinwindow
	phastconsscores = []
	for nt in ntsinwindow:
		for bed in phastconstabix.fetch(chrom, nt, nt + 1, parser = pysam.asTuple()):
			score = bed[4]
			phastconsscores.append(float(score))

	return phastconsscores

def iterategff(gff, seq_dict, kmers, windowsize, featureofinterest, phastconsbed, outfile):
	kmers = kmers.split(',')

	#Feature of interest is 2nd field of gff
	#Make gff database
	print('Indexing gff...')
	gff_fn = gff
	db_fn = os.path.abspath(gff_fn) + '.db'
	if os.path.isfile(db_fn) == False:
		gffutils.create_db(gff_fn, db_fn, merge_strategy = 'merge', verbose = True)

	db = gffutils.FeatureDB(db_fn)
	print('Done indexing!')

	phastconstabix = pysam.Tabixfile(phastconsbed)

	features = db.features_of_type(featureofinterest)

	with open(outfile, 'w') as outfh:
		outfh.write('Gene' + '\t' + 'medianmotifscore' + '\n')
		featurecounter = 0
		for feature in features:
			featurecounter +=1
			if featurecounter % 500 == 0:
				print('Feature {0}...'.format(featurecounter))
			featureseq = ''
			featurecoords = [] #0-based exon coords for interaction with phastcons bed
			if feature.strand == '+':
				for exon in db.children(feature, featuretype = 'exon', order_by = 'start'):
					#gff is 1-based, seq_dict is 0-based
					region = exon.chrom + ';' + str(exon.start - 1) + ';' + str(exon.end) + ';' + exon.strand
					exonseq = getSequence(seq_dict, region)
					featureseq += exonseq
					featurecoords += list(range(exon.start - 1, exon.end))

			elif feature.strand == '-':
				for exon in db.children(feature, featuretype = 'exon', order_by = 'start', reverse = True):
					#gff is 1-based
					region = exon.chrom + ';' + str(exon.start - 1) + ';' + str(exon.end) + ';' + exon.strand
					exonseq = getSequence(seq_dict, region)
					featureseq += exonseq
					featurecoords += reversed(list(range(exon.start - 1, exon.end)))

			ntsinwindow = getkmerpos(featureseq, featurecoords, kmers)
			if ntsinwindow:
				phastconsscores = getscores(phastconstabix, str(feature.chrom), ntsinwindow)
				if phastconsscores:
					score = np.median(phastconsscores)
					outfh.write(str(feature.id).split(':')[1] + '\t' + str(score) + '\n')
				elif not phastconsscores:
					outfh.write(str(feature.id).split(':')[1] + '\t' + 'NA' + '\n')
			elif not ntsinwindow:
				outfh.write(str(feature.id).split(':')[1] + '\t' + 'NA' + '\n')

seq_dict = indexgenome(sys.argv[1])
iterategff(sys.argv[2], seq_dict, 'UGUGU,GUGUG', None, 'UTR3', sys.argv[3], sys.argv[4])
#iterategff(sys.argv[2], seq_dict, 'GGGUU,UUGGG,UUUGG,GGUUU,AUUGG,GGUUA,AGGUU,UUGGA', None, 'UTR3', sys.argv[3], sys.argv[4])


import pytest

from leetcode344 import Solution

@pytest.mark.parametrize("s,expected", [
    ([], []),
    (["a"], ["a"]),
    (["a", "b", "c"], ["c", "b", "a"]),
    (["a", "b", "c", "d"], ["d", "c", "b", "a"]),
])
def testSolutionReverseString(s, expected):
    assert Solution().reverseString(s) == expected


# Django
from django.core.handlers.wsgi import WSGIRequest
from django.http.response import HttpResponse

# Python
from typing import Callable

class SecureMiddleware:
    """
    Middleware для обеспечения безопасности.

    Устанавливает заголовки безопасности, такие как X-Frame-Options и X-Content-Type-Options.
    Также управляет установкой и удалением httponly cookie с токеном обновления (refresh_token).

    Attributes:
        LOGIN_REFERER (str): Ссылка для проверки Referer при логине.
        LOGOUT_REFERER (str): Ссылка для проверки Referer при логауте.

    Methods:
        __init__(self, get_response: Callable): Конструктор middleware.

        set_refresh_token_cookie(self, response: HttpResponse, token: str):
        -> Устанавливает httponly cookie с токеном обновления.

        def delete_refresh_token_cookie(self, response: HttpResponse):
        -> Удаляет токен обновления при выходе пользователя с аккаунта.

        __call__(self, request: WSGIRequest) -> HttpResponse: Обработчик middleware.
    """

    # Ссылки для проверки Referer
    LOGIN_REFERER = 'http://127.0.0.1:8000/login/'
    LOGOUT_REFERER = 'http://127.0.0.1:8000/logout/'

    def __init__(self, get_response: Callable) -> None:
        self.get_response = get_response

    def set_refresh_token_cookie(self, response: HttpResponse, token: str) -> None:
        # Параметры для установки cookie
        params = {
            'httponly': True,
            'samesite': 'Strict',
            'path': '/'
        }

        # Определение срока действия токена
        expiration = 60 * 60 * 24 * 7  # 1 неделя
        expiration -= 60  # Вычесть одну минуту на всякий случай
        params['max_age'] = expiration

        # Установка cookie refresh_token
        response.set_cookie('refresh_token', token, **params)

    def delete_refresh_token_cookie(self, response: HttpResponse) -> None:
        """
        Удаление куки refresh_token из response.
        """
        # Параметры для удаления cookie
        params = {
            'path': '/',
        }

        # Удаление cookie refresh_token
        response.delete_cookie('refresh_token', **params)

    def __call__(self, request: WSGIRequest) -> HttpResponse:
        response = self.get_response(request)

        # Установка заголовков для защиты от XSS
        response['X-Frame-Options'] = 'SAMEORIGIN'
        response['X-Content-Type-Options'] = 'nosniff'

        # Установка httponly cookie refresh_token
        if request.META.get('HTTP_REFERER') == self.LOGIN_REFERER:
            # Если есть данные и ответ успешен (статус код 2xx)
            if hasattr(response, 'data') and 200 <= response.status_code < 300:
                refresh_token = response.data.get('refresh')
                # Если токен успешно получен
                if refresh_token:
                    self.set_refresh_token_cookie(response, refresh_token)

        # Удаление refresh_token из cookie
        elif request.META.get('HTTP_REFERER') == self.LOGOUT_REFERER:
            # Если есть данные и ответ успешен (статус код 200)
            if hasattr(response, 'data') and response.status_code == 200:
                self.delete_refresh_token_cookie(response)

        return response


# -*- coding: utf-8 -*-
"""
Created on Mon Mar 20 22:15:56 2017

@author: S
"""

import json
import requests
import urllib
import os
import threading

class download_thread(threading.Thread):
    def __init__(self, ID, folder_name, unit):
        threading.Thread.__init__(self)
        self.ID = ID
        self.folder_name = folder_name
        self.unit = unit
    def run(self):

        ID = self.ID
        unit = self.unit
        folder_name = self.folder_name

        new_file = "./" + folder_name + "/" + unit["id"] + ".jpg"

        if not os.path.isfile(new_file):
            try:
                urllib.request.urlretrieve(unit["source"], new_file)
                print("%s: %s downloaded" % (ID, unit["id"]))
            except:
                print("%s: %s failed" % (ID, unit["id"]))
                pass
        else:
            print("%s: %s exists" % (ID, unit["id"]))

class download_album_thread(threading.Thread):
    def __init__(self, album, root_folder):
        threading.Thread.__init__(self)
        self.album = album
        self.root_folder = root_folder
    def run(self):
        download_album(self.album,self.root_folder)

class download_page_thread(threading.Thread):
    def __init__(self, page, access_token):
        threading.Thread.__init__(self)
        self.page = page
        self.access_token = access_token
    def run(self):
        scrape_photos_from_page(self.page, self.access_token)

def remove_illegal_characters(folder_name):
    result = folder_name
    result = ''.join(character for character in folder_name if (character.isalnum() or character in " []") and character != "./\\|<>:?*\"\'")
    result = result.strip()
    return result

def download_from_dictionary(dictionary, folder_name):

    print("BEGIN DOWNLOAD for %s" % folder_name)

    """
    i = 1
    active_threads = []

    for unit in dictionary:

        thread = download_thread(i, folder_name, unit)
        thread.start()
        active_threads.append(thread)

        i += 1

    for t in active_threads:
        t.join()

    """

    i = 0
    active_threads = []

    while(i < len(dictionary)):

        if(threading.active_count() < 50):

            unit = dictionary[i]

            thread = download_thread(i, folder_name, unit)
            thread.start()
            active_threads.append(thread)
            i += 1

    for t in active_threads:
        t.join()

    print("END DOWNLOAD \n")

def download_album(album, root_folder_name):

    folder_name = album["name"]
    folder_name = remove_illegal_characters(folder_name)
    new_path = root_folder_name + "/" + folder_name
    number_of_existing_files = 0

    if not os.path.isdir(new_path):

        try:
            os.makedirs(new_path)
            print("%s folder created." % new_path)
        except:
            print("Error occured when trying to create %s" % new_path)

    else:

        print("%s folder exists." % new_path)

    number_of_existing_files = len([name for name in os.listdir(new_path) if os.path.isfile(os.path.join(new_path, name))])

    if number_of_existing_files == album["count"]:
        print("Duplicate detected! Download skipped.")
    else:

        print("%s Photo Count: %s \n" % (new_path, album["count"]))

        #Acquire list of photo by id/source dictionaries

        current_list = []
        photo_set = album["photos"]

        while(True):

            photos_data = photo_set["data"]

            for photo in photos_data:

                unit = {"id" : photo["id"], "source" : photo["source"]}
                current_list.append(unit)

            print("Photos scraped: %s" % len(current_list))

            try:

                try:
                    next_url = photo_set["paging"]["next"]
                except:
                    next_url = photo_set["next"]

                request = requests.get(next_url).text
                photo_set = json.loads(request)

            except KeyError:
                #Reached the end of album
                break

        print("Number of photo sources scraped: %s \n" % (len(current_list)))
        download_from_dictionary(current_list, root_folder_name + "/" + folder_name)

def scrape_photos_from_page(pg, access_token_code):

    # HTTP Stuff

    host = "https://graph.facebook.com/"
    version = "v2.8/"
    page_id = pg
    modifiers = "?fields=name,albums{name,count,photos{id,source}}"
    access_token = "&access_token=%s" % access_token_code

    get_json= host + version + page_id + modifiers + access_token

    # Page JSON

    page = json.loads(requests.get(get_json).text)

    # Albums JSON

    albums = page["albums"]
    albums_data = albums["data"]
    page_id = page["id"]
    page_name = remove_illegal_characters(page["name"])

    #Create main folder

    main_folder_name = ("[%s]%s" % (page_id,page_name))
    print(main_folder_name + "\n")

    page_folder_exists = os.path.isdir(main_folder_name)

    if not page_folder_exists:
        try:
            os.makedirs(main_folder_name)
        except Exception as e:
            print("Something went wrong with the page folder creation.")
            print(e)
        print("\"%s\" folder created!" % main_folder_name)

    else:
        print("\"%s\" folder already exists." % main_folder_name)

    #Iterates album by album

    while(True):

        #Iterates photo by photo per album

        #threads = []

        for alb in albums_data:

            if alb["count"] > 0:
                download_album(alb,main_folder_name)
                """
                thread = download_album_thread(alb,main_folder_name)
                thread.start()
                threads.append(thread)

        for t in threads:
            t.join()
        """

        #Get next album

        try:
            next_json =  albums["paging"]["next"]
            albums = json.loads(requests.get(next_json).text)
            albums_data = albums["data"]
        except KeyError:
            print("== END OF ALBUM LIST ==")
            break

def scrape_photos_from_pages(pg_list, access_token_code):

    for p in pg_list:

        scrape_photos_from_page(p, access_token_code)

    """"
    threads = []

    for p in pg_list:
        thread = download_page_thread(p, access_token_code)
        thread.start()
        threads.append(thread)

    for t in threads:
        t.join()
    """


# Definition for a binary tree node.
from typing import Optional

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution1:
    def traversal(self, node, node_list):
        if node is None:
            return

        node_list.append(node)

        self.traversal(node.left, node_list)
        self.traversal(node.right, node_list)

    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Time O(n)
        Space O(n) 需要额外的list空间来记录前序遍历顺序下的node
        先前序遍历存储下来所有node顺序，再通过loop list重新给每个node分配right和left指针
        """
        node_list = []
        self.traversal(root, node_list)

        for i in range(len(node_list) - 1):
            cur_node = node_list[i]
            next_node = node_list[i + 1]
            cur_node.right = next_node
            cur_node.left = None

        return root

class Solution2:
    def traversal(self, node):
        if node is None:
            return

        # 后续遍历，需要先左右节点先处理完再处理当前节点
        self.traversal(node.left)
        self.traversal(node.right)

        # 后序遍历位置
        # 1、此时左右子树已经被拉平成一条链表
        left = node.left
        right = node.right

        # 2、将左子树作为右子树
        node.left = None
        node.right = left

        # 3、将原先的右子树接到当前右子树(也就是之前的左子树)的末端，需要while loop一直走到当前右子树的底端先
        p = node
        while p.right:
            p = p.right
        p.right = right

    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Time O(n)
        Space O(1)  不需要额外list空间存储顺序
        分解问题，分别解决左右子树，则解决了整个树。当前节点左节点变成右节点，然后原先的右节点接入左节点的右节点。
        """

        self.traversal(root)


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

urlpatterns = [
    url(r'^getAll/$', views.get_all, name='fetch'),
    url(r'^add/$', views.create_reminder, name='add'),
    url(r'^save/(?P<id>\d+)/$', views.save_reminder, name='create'),
    url(r'^edit/(?P<id>\d+)/$', views.edit_reminder, name='edit'),
    url(r'^delete/(?P<id>\d+)/$', views.delete_reminder, name='delete'),
    url(r'^$', views.index),
]


import vk_api.vk_api
from vk_api.bot_longpoll import VkBotLongPoll
from vk_api.bot_longpoll import VkBotEventType
from vk_api.keyboard import VkKeyboard, VkKeyboardColor
from vk_api.upload import VkUpload
import requests
import datetime
from io import BytesIO

now = datetime.datetime.now().year

class VK_Bot_personal:

    def __init__(self, personal_token):
        self.personal_vk = vk_api.VkApi(token=personal_token)
        self.personal_vk_api = self.personal_vk.get_api()
        self.offset = 0
        self.response = None

    def search_users(self, sex, hometown, age_from, age_to):
        return self.personal_vk_api.users.search(count=1, offset=self.offset, sort=0,
                                                 age_from=age_from,
                                                 age_to=age_to,
                                                 sex=sex,
                                                 has_photo=1,
                                                 friend_status=0,
                                                 city_id=hometown,
                                                 #is_closed=False,
                                                 fields="bdate, photo_max, city")

    def top_photos(self, owner_id):
        try:
            self.response = self.personal_vk_api.photos.get(owner_id=owner_id,
                                                            album_id="profile",
                                                            extended=1,
                                                            fields="likes")
        except vk_api.exceptions.ApiError:
            print("Приватный профиль, загрузка фото невозможна")
            return None

class VK_Bot:

    def __init__(self, group_token, group_id, server_name: str = "Empty"):
        self.server_name = server_name
        self.vk = vk_api.VkApi(token=group_token)
        self.long_poll = VkBotLongPoll(self.vk, group_id, wait=20)
        self.vk_api = self.vk.get_api()
        self.user_id = 0
        self.user_info = {}
        self.bdate = 0
        self.sex = 0
        self.city = ""
        self.status = 0
        self.search_sex = 0
        self.age_from = self.bdate - 2
        self.age_to = self.bdate + 2
        self.search_status = 0
        self.search_city = ""
        self.search_result = {}
        self.upload = VkUpload(self.vk)
        self.http = requests.Session()
        self.list_of_most_liked_photos = []
        self.searching_id = 1
        self.hometown = 0
        self.photos_links = []
        self.attachment = []
        self.peer_id = 0

    def start(self, s_engine, db_engine):
        for event in self.long_poll.listen():
            if event.type == VkBotEventType.MESSAGE_NEW:
                if event.message["text"].lower() == "привет":
                    self.user_info = self.vk_api.users.get(user_id=event.object["message"]["from_id"],
                                                           fields="sex, bdate, city, status")[0]
                    self.peer_id = event.object["message"]["peer_id"]
                    self.user_id = self.user_info["id"]
                    username = self.user_info["first_name"]
                    self.sex = self.user_info["sex"]
                    try:
                        self.hometown = int(self.user_info["city"]["id"])
                    except KeyError:
                        self.hometown = 1
                    if "bdate" in self.user_info.keys():
                        if int(self.user_info["bdate"].split(sep=".")[-1]) < 100:
                            if int(self.user_info["bdate"].split(sep=".")[-1]) < 30:
                                self.bdate = now - int(self.user_info["bdate"].split(sep=".")[-1]) - 1900
                            else:
                                self.bdate = now - int(self.user_info["bdate"].split(sep=".")[-1]) - 2000
                        else:
                            self.bdate = now - int(self.user_info["bdate"].split(sep=".")[-1])
                    s_engine.offset = db_engine.search_number_definition(self.user_id)
                    self.status = self.user_info["status"]
                    self.age_from = self.bdate - 2
                    self.age_to = self.bdate + 2
                    self.send_message(event.object["message"]["peer_id"], f"{username}, привет!")
                    if self.sex == 1:
                        self.send_message(event.object["message"]["peer_id"], "Поищем парня?",
                                          keyboard=self.create_callback_keyboard("Не угадал", "Давай",
                                                                                "search_sex_1", "choose_age"))
                        self.search_sex = 2
                    elif self.sex == 2:
                        self.send_message(event.object["message"]["peer_id"], "Поищем девушку?",
                                          keyboard=self.create_callback_keyboard("Не угадал", "Давай",
                                                                                "search_sex_2", "choose_age"))
                        self.search_sex = 1
                    else:
                        self.send_message(event.object["message"]["peer_id"], "Кого будем искать??",
                                          keyboard=self.create_callback_keyboard("Парня", "Девушку", "search_sex_2",
                                                                                "search_sex_1"))
                    db_engine.database_user_record(self.user_id, self.user_info)
                else:
                    self.send_message(event.object["message"]["peer_id"], "Напиши, привет")
            elif event.type == VkBotEventType.MESSAGE_EVENT:
                if event.object.payload.get('type') == "search_sex_2":
                    self.search_sex = 2
                    self.choosing_age(event)
                elif event.object.payload.get('type') == "search_sex_1":
                    self.search_sex = 1
                    self.choosing_age(event)
                elif event.object.payload.get('type') == "choose_age":
                    self.choosing_age(event)
                elif event.object.payload.get('type') == "choose_age_2":
                    self.send_message(event.object.peer_id, "Введи диапазон возрастов через дефис, "
                                                            "например 25-28")
                    for event in self.long_poll.listen():
                        if event.type == VkBotEventType.MESSAGE_NEW:
                            self.age_from = event.message["text"].split(sep="-")[0]
                            self.age_to = event.message["text"].split(sep="-")[1]
                            break
                    if self.age_from >= self.age_to:
                        keyboard = vk_api.keyboard.VkKeyboard(one_time=True)
                        keyboard.add_callback_button("Еще разок", color=VkKeyboardColor.NEGATIVE,
                                                     payload={"type": "choose_age_2"})
                        self.send_message(event.object["message"]["peer_id"], "Что-то пошло не так. Давай еще раз",
                                          keyboard=keyboard.get_keyboard())
                    else:
                        keyboard = vk_api.keyboard.VkKeyboard(one_time=True)
                        keyboard.add_callback_button("Поехали", color=VkKeyboardColor.POSITIVE,
                                                     payload={"type": "start_search"})
                        self.send_message(event.object["message"]["peer_id"], "Поехали",
                                          keyboard=keyboard.get_keyboard())
                elif event.object.payload.get('type') == "start_search":
                    self.searching(event.object.peer_id, s_engine)
                elif event.object.payload.get('type') == "like" or event.object.payload.get('type') == "dislike":
                    if event.object.payload.get('type') == "like":
                        #print("like")
                        db_engine.database_inserting(True, self.search_result, self.user_id, s_engine.offset)
                    else:
                        #print("dislike")
                        db_engine.database_inserting(False, self.search_result, self.user_id, s_engine.offset)
                    self.searching(event.object.peer_id, s_engine)

    def choosing_age(self, event):
        self.send_message(event.object.peer_id, f"Я вижу, что тебе {self.bdate} лет (года). "
                                                f"Поищем в диапазоне {self.bdate - 2} - {self.bdate + 2}?",
                          keyboard=self.create_callback_keyboard("Нет, я введу возраст сам", "Давай",
                                                                 "choose_age_2",
                                                                 "start_search"))

    def send_photo(self, vk, peer_id, message, keyboard, s_engine):
        self.top_photos_links(self.search_result['items'][0]['id'], s_engine)
        if self.search_result['items'][0]['is_closed'] == True:
            vk.messages.send(
                random_id=0,
                peer_id=peer_id,
                attachment=self.upload_photo(self.search_result['items'][0]['photo_max']),
                message=f"{message} (приватный профиль)",
                keyboard=keyboard
            )
        else:
            vk.messages.send(
                random_id=0,
                peer_id=peer_id,
                attachment=self.photos_links,
                message=message,
                keyboard=keyboard
            )

    def top_photos_links(self, id, s_engine):
        self.photos_links = []
        list_of_most_liked_photos = []
        dict_of_photos_for_likes = {}
        s_engine.top_photos(id)
        # print(Search_engine.response)
        if s_engine.response is None:
            self.photos_links = None
            return None
        else:
            # print(f"response {Search_engine.response}")
            for _ in s_engine.response["items"]:
                # print(_["likes"]["count"])
                list_of_most_liked_photos.append(_["likes"]["count"])
                dict_of_photos_for_likes[
                    _["likes"]["count"]] = f'photo{s_engine.response["items"][0]["owner_id"]}_{_["id"]}'
            list_of_most_liked_photos.sort(reverse=True)
            if int(s_engine.response["count"]) >= 3:
                for i in range(0, 3):
                    for k, v in dict_of_photos_for_likes.items():
                        if k == list_of_most_liked_photos[i]:
                            self.photos_links.append(v)
            else:
                for i in range(0, s_engine.response["count"]):
                    for k, v in dict_of_photos_for_likes.items():
                        if k == list_of_most_liked_photos[i]:
                            self.photos_links.append(v)
            return self.photos_links

    def send_message(self, peer_id, message, keyboard=None, attachment=None):
        self.vk.get_api().messages.send(peer_id=peer_id, message=message, random_id=0, keyboard=keyboard,
                                        attachment=attachment)

    def create_keyboard(self, btn_text_no, btn_text_yes):
        keyboard = vk_api.keyboard.VkKeyboard(one_time=True)
        keyboard.add_button(btn_text_no, color=VkKeyboardColor.NEGATIVE)
        keyboard.add_button(btn_text_yes, color=VkKeyboardColor.POSITIVE)
        return keyboard.get_keyboard()

    def create_callback_keyboard(self, btn_text_no, btn_text_yes, payload_no, payload_yes):
        keyboard = vk_api.keyboard.VkKeyboard(one_time=True)
        keyboard.add_callback_button(btn_text_no, color=VkKeyboardColor.NEGATIVE, payload={"type": payload_no})
        keyboard.add_callback_button(btn_text_yes, color=VkKeyboardColor.POSITIVE, payload={"type": payload_yes})
        return keyboard.get_keyboard()

    def create_empty_keyboard(self):
        keyboard = vk_api.keyboard.VkKeyboard.get_empty_keyboard()
        return keyboard

    def searching(self, peer_id, s_engine):
        self.attachment = []
        self.search_result = s_engine.search_users(self.search_sex, self.hometown, self.age_from, self.age_to)
        # pprint(self.search_result)
        s_engine.offset += 1
        message = f"Как тебе {self.search_result['items'][0]['first_name']} " \
                  f"{self.search_result['items'][0]['last_name']}? " \
                  f"https://vk.com/id{self.search_result['items'][0]['id']}"
        keyboard = self.create_callback_keyboard("Не нравится", "Нравится", "dislike",
                                                 "like")
        self.send_photo(self.vk_api, peer_id, message, keyboard, s_engine)

    def upload_photo(self, url):
        img = requests.get(url).content
        f = BytesIO(img)
        response = VkUpload(self.vk_api).photo_messages(f)[0]
        owner_id = response['owner_id']
        photo_id = response['id']
        return f'photo{owner_id}_{photo_id}'

    def send_error_message(self):
        self.vk.get_api().messages.send(peer_id=self.peer_id, message="Хмм, что-то пошло не так. Напиши привет еще раз", random_id=0)