import pandas as pd
import numpy as np
import copy

from .kernel import GaussKernel

class KernelMean(object):
    """[summary]
    The class of Kernel Mean.

    Args:
        data (array-like or pandas dataframe):
            Sample for calculating kernel mean.
            An array of shape should be (n_samples_x1, n_dim).

        kernel (class kernel):
            Specify an instance of kernel class as shown in Example.

        weights (array-like, optional): Defaults to None.
            Weight each sample when calculating the kernel mean.
            By default, all samples are treated as equal weight.
            Conditional kernel mean can be obtained by specifying the weights conditioned by kernel ABC.

    Examples:
        >>> kernel_gauss = GaussKernel(sigma=1.)
        >>> data = np.array([[0.],[1.],[3.]])
        >>> kernelmean = KernelMean(data=data,kernel=kernel_gauss,weights=None)
        >>> x = np.array([[-3],[-2.], [0.], [1.], [5.]])
        >>> np.array(kernelmean(x))
        array([[0.00381482],
               [0.048816  ],
               [0.53921322],
               [0.58062198],
               [0.04522482]])
    """

    def __init__(self, data, kernel, weights=None):
        if isinstance(data, pd.DataFrame):
            self._data = data
        else:
            self._data = pd.DataFrame(data)

        self._n_samples, self._n_features = self._data.shape
        self._weights = np.atleast_2d(np.full(self._n_samples, 1. / self._n_samples)).T
        if weights is not None:
            if not isinstance(weights, np.ndarray):
                raise ValueError('weights should be ndarray')
            if len(weights) != self._n_samples:
                raise ValueError(f'length of weights should be {self._n_samples, 1}')
            self._weights = weights.reshape(self._n_samples, 1)

        self.__kernel = kernel
        # if kernel=='Gauss':
        #     if 'covariance' not in kwargs:
        #         raise ValueError('GaussKernel requires a covariance matrix as "covariance"')
        #     cov = kwargs.get('covariance')
        #     self.__kernel = GaussKernel(cov)
        #
        # elif kernel=='Matern':
        #     a = kwargs.get('a',1.)
        #     l = kwargs.get('l')
        #     nu = kwargs.get('nu',1.5)
        #     self.__kernel = MaternKernel(a,l,nu)
        #
        # else:
        #     raise ValueError('kernel is not defined.')

    def __call__(self, val, **kwargs):
        """compute kernel density
        Args:
            val (ndarray): ndarray of shape (n_samples_val, n_dim).
            **kwargs :
                *kwargs* is used to specify the configuration for kernel mean calculation.

                normalize (bool, optional): Defaults to False.
                    Specify `True` when normalizing the kernel. Some kernels do not have a normalization option, so see the kernel's docstrings.

                weights_normalize (bool, optional): Defaults to False.
                    Specify `True` when normalizing kernel average weighting.

        Returns:
            KV (ndarray): return kernel mean value. ndarray of shape (n_samples_val,n_samples_data).
        """
        return self.kde(val, **kwargs)

    def __repr__(self):
        val = 'Class: KernelMean\n'
        val += f'The number of data: {self.n_samples}\n'
        val += f'Kernel: \n{self.kernel}\n'
        return val

    def copy(self):
        return copy.deepcopy(self)

    def kde(self, val, **kwargs):
        """compute kernel density
        Args:
            val (ndarray): ndarray of shape (n_samples_val, n_dim).
            **kwargs :
                *kwargs* is used to specify the configuration for kernel mean calculation.

                normalize (bool, optional): Defaults to False.
                    Specify `True` when normalizing the kernel. Some kernels do not have a normalization option, so see the kernel's docstrings.

                weights_normalize (bool, optional): Defaults to False.
                    Specify `True` when normalizing kernel average weighting.

        Returns:
            KV (ndarray): return kernel mean value. ndarray of shape (n_samples_val,n_samples_data).
        """
        weights_normalize = kwargs.get('weights_normalize', False)
        kde = self.kernel.kde(val, self._data.values, **kwargs)
        w = self._weights
        if weights_normalize:
            w = self.__weights_normalize()

        # return np.average(kde,weights=self._weights,axis=1)
        return np.dot(kde, w)

    def gradkde(self, val, **kwargs):
        """compute gradient of kernel density.
        Args:
            val (ndarray): ndarray of shape (n_samples_val, n_dim).
            **kwargs :
                *kwargs* is used to specify the configuration for kernel mean calculation.

                normalize (bool, optional): Defaults to False.
                    Specify `True` when normalizing the kernel.
                    Some kernels do not have a normalization option, so see the kernel's docstrings.

                weights_normalize (bool, optional): Defaults to False.
                    Specify `True` when normalizing kernel average weighting.

        Returns:
            KV (ndarray):
                return gradient value tensor.
                ndarray of shape (n_samples_val, n_samples_data, n_dim).
                Derivative value at val of kernel centered on data. dk(x,data)/dx (x=val).

        Raises:
            NotImplementedError:
                If the gradient cannot be calculated, it returns the error.
        """
        weights_normalize = kwargs.get('weights_normalize', False)
        grad = self.__calculate_grad(val, **kwargs)
        w = self._weights
        if weights_normalize:
            w = self.__weights_normalize()
        # return np.average(grad,weights=self._weights,axis=1)
        return np.einsum('ijd,jk->id', grad, w)

    def __calculate_grad(self, val, **kwargs):
        try:
            grad = self.kernel.gradkde(val, self._data.values, **kwargs)
        except:  # noqa
            raise NotImplementedError()
        return grad

    def __weights_normalize(self):
        return self._weights / np.sum(self._weights)

    @property
    def kernel(self):
        return self.__kernel

    @kernel.setter
    def kernel(self, kernel):
        self.__init__(data=self._data, kernel=kernel, weights=self._weights)

    @property
    def weights(self):
        return self._weights.copy()

    @property
    def data(self):
        return self._data.copy()

    @property
    def n_samples(self):
        return copy.copy(self._n_samples)

def test(data):
    plt.figure()
    plt.title('plot sample data')
    sns.scatterplot(x='0', y='1', data=data)
    plt.savefig('./pic/test/kernelmean/sample.png')
    plt.close()

    # bandwidth selection by scott
    bw = Bandwidth(data=data, method='scott', weights=None)
    print(bw)
    kernel = GaussKernel(sigma=bw.bandwidth)
    kernelmean = KernelMean(data=data, kernel=kernel, weights=None)

    x = np.arange(-10.0, 10, 0.5)
    y = np.arange(-10.0, 10, 0.5)
    xx, yy = np.meshgrid(x, y)
    z = kernelmean(np.array([xx.ravel(), yy.ravel()]).T)
    z_grad = kernelmean.gradkde(np.array([xx.ravel(), yy.ravel()]).T)
    zz = z.reshape(xx.shape[0], yy.shape[0])
    c = np.mean(np.square(z_grad), axis=1)

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.contour(xx, yy, zz)
    sns.scatterplot(x='0', y='1', data=data, ax=ax)
    plt.quiver(xx.ravel(), yy.ravel(), z_grad[:, 0], z_grad[:, 1], c)
    fig.savefig('./pic/test/kernelmean/kernelmean.png')

if __name__ == '__main__':
    import os

    import matplotlib.pyplot as plt
    import seaborn as sns
    import doctest

    from .data import testdata
    from .utils import Bandwidth

    if not os.path.exists('./pic/test/kernelmean/'):
        os.makedirs('./pic/test/kernelmean/')
    test(testdata)
    doctest.testmod()


import tkinter as tk
from tkinter import messagebox

from src.gestor_aplicacion.servicios.servicio import Servicio
from src.manejo_errores.error_aplicacion import DatosFalsos, TipoIncorrecto, CampoVacio, SinServicioSeleccionado
from src.ui_main.gestion.field_frame import FieldFrame

def imprimir_titulo(frame):
    # Limpia el frame
    for item in frame.winfo_children():
        item.destroy()

    # Imprime el titulo
    titulo = tk.Label(frame, text="Facturación", bg="white", font=("Helvetica", 16, "bold"))
    titulo.pack(pady=20)

def facturacion(hospital, frame):
    def validar_lista_servicios(paciente, lista_servicios, opcion):
        if len(lista_servicios) == 0:
            try:
                raise SinServicioSeleccionado()
            except SinServicioSeleccionado as e:
                e.enviar_mensaje()
        else:
            if opcion == 1:
                calcular_precio(paciente, lista_servicios)
            elif opcion == 2:
                confirmar_pago(paciente, lista_servicios)

    def confirmar_pago(paciente, servicios):
        respuesta = tk.messagebox.askyesno("Confirmar pago", "¿Desea realizar el pago?")
        if respuesta:
            for servicio in servicios:
                servicio.confirmar_pago(paciente, servicio.id_servicio)
            messagebox.showinfo("Pago realizado", "El pago se ha realizado exitosamente")
        else:
            messagebox.showinfo("Pago cancelado", "El pago ha sido cancelado")

        implementacion_default(frame)

    def calcular_precio(paciente, servicios):
        def calcular_precio_servicio(s, ls):
            # Permite editar la variable desde la funcion sin pasarla como argumento
            nonlocal precio_final

            if s in servicios:
                servicios.remove(s)
                ls.config(bg="white")
                precio_final -= paciente.calcular_precio(s)
            else:
                servicios.append(s)
                ls.config(bg="#d2d5f9")
                precio_final += paciente.calcular_precio(s)

            label_precio_final.config(text=f"Precio total: ${precio_final:,}")

        imprimir_titulo(frame)

        precio_final = 0

        label_servicios = tk.Label(frame, text="Confirme los servicios que va a pagar:", bg="white",
                                   font=("Helvetica", 10, "bold"))
        label_servicios.pack(pady=10)

        servicios.sort(key=lambda s: s.id_servicio)

        for servicio in servicios:
            precio_servicio = paciente.calcular_precio(servicio)
            precio_final += precio_servicio
            label_servicio = tk.Label(frame, text=f"{servicio.descripcion_servicio()} - "
                                                  f"Precio: ${precio_servicio:,}", bg="white")
            label_servicio.pack()
            label_servicio.config(bg="#d2d5f9")

            # Permite seleccionar el servicio al hacer click izquierdo (<Button-1>)
            label_servicio.bind(
                "<Button-1>",
                lambda event, s=servicio, ls=label_servicio: calcular_precio_servicio(s, ls))

            # Resalta el label donde se pone el cursor
            label_servicio.bind("<Enter>",
                                lambda event: event.widget.config(highlightthickness=2, highlightbackground="black"))
            label_servicio.bind("<Leave>", lambda event: event.widget.config(highlightthickness=0))

            # Seleccionado por defecto

        label_precio_final = tk.Label(frame, text=f"Precio total: ${precio_final:,}", bg="white")
        label_precio_final.pack(pady=10)

        boton_pagar = tk.Button(frame, text="Realizar pago",
                                command=lambda: validar_lista_servicios(paciente, servicios, 2))
        boton_pagar.pack(pady=10)

        boton_regresar = tk.Button(frame, text="Regresar",
                                   command=lambda: implementacion_default(frame))
        boton_regresar.pack()

    def obtener_servicios_sin_pagar(paciente):
        def seleccionar_servicio(s, ls):
            if s in servicios_seleccionados:
                servicios_seleccionados.remove(s)
                ls.config(bg="white")
            else:
                servicios_seleccionados.append(s)
                ls.config(bg="#d2d5f9")

        lista_servicios_sin_pagar = Servicio.obtener_servicios_sin_pagar(paciente)

        if len(lista_servicios_sin_pagar) == 0:
            tk.messagebox.showinfo("Sin servicios pendientes", "Usted no tiene ningún servicio pendiente de pago")
            implementacion_default(frame)
            return

        imprimir_titulo(frame)

        info_paciente = tk.Label(frame, text=f"{paciente.nombre} - CC: {paciente.cedula}", bg="white",
                                 font=("Helvetica", 12))
        info_paciente.pack(pady=10)

        label_servicios = tk.Label(frame, text="Lista de servicios sin pagar:", bg="white",
                                   font=("Helvetica", 10, "bold"))
        label_servicios.pack(pady=10)

        servicios_seleccionados = []

        # Imprime cada servicio en un label
        for servicio in lista_servicios_sin_pagar:
            label_servicio = tk.Label(frame, text=servicio.descripcion_servicio(), bg="white")
            label_servicio.pack()

            # Permite seleccionar el servicio al hacer click izquierdo (<Button-1>)
            label_servicio.bind(
                "<Button-1>",
                lambda event, s=servicio, ls=label_servicio: seleccionar_servicio(s, ls))

            # Resalta el label donde se pone el cursor
            label_servicio.bind("<Enter>",
                                lambda event: event.widget.config(highlightthickness=2, highlightbackground="black"))
            label_servicio.bind("<Leave>", lambda event: event.widget.config(highlightthickness=0))

        boton_precio = tk.Button(frame, text="Continuar",
                                 command=lambda: validar_lista_servicios(paciente, servicios_seleccionados, 1))
        boton_precio.pack(pady=10)

        boton_regresar = tk.Button(frame, text="Regresar",
                                   command=lambda: implementacion_default(frame))
        boton_regresar.pack()

    def buscar_paciente():
        cedula = ingreso_cedula.getValue(1)

        if len(cedula) != 0:
            try:
                paciente = hospital.buscar_paciente(int(cedula))
                obtener_servicios_sin_pagar(paciente)
            except DatosFalsos as e:
                e.enviar_mensaje()
            except (ValueError, TypeError):
                TipoIncorrecto().enviar_mensaje()
        else:
            try:
                raise CampoVacio()
            except CampoVacio as e:
                e.enviar_mensaje()

    imprimir_titulo(frame)

    # Pide la cedula del paciente
    label_ingreso_cedula = tk.Label(frame, text="Ingrese la cédula del paciente:", bg="white",
                                    font=("Helvetica", 10, "bold"))
    label_ingreso_cedula.pack()

    criterios = ["Cédula:"]
    ingreso_cedula = FieldFrame(frame, "", criterios, "", None, None)
    ingreso_cedula.pack()

    boton_buscar_paciente = tk.Button(frame, text="Buscar", command=buscar_paciente)
    boton_buscar_paciente.pack(pady=10)

    # Funcionalidad para regresar a la ventana principal
    # Se importa aca para evitar una referencia circular
    from src.ui_main.ventana_principal import implementacion_default

    boton_regresar = tk.Button(frame, text="Regresar",
                               command=lambda: implementacion_default(frame))
    boton_regresar.pack()


# number = input('enter a number: ')
# print(number[0])
# print(number[1])
# print(number[2])

# با روش بالا برنامه ای بنویس که یک عدد از ورودی بگیرد و تمامی ارقام آن را نمایش دهد
# 23478
# "1th digit is 2"
# "2th digit is 3"
# .....
# در نهایت مجموع رقم های آن عدد را محاسبه و پرینت کن
# همینطور مجموع رفم های فرد عدد را محاسبه و نمایش بده

# با روش ریاضی هم انجام

# number = int(input('enter a number: '))
# i = 1
# while number != 0:
#     print(i, number % 10)
#     number = number // 10
#     i = i + 1

import random

# def

# print("hi",1,1+2,"hello")

# def add(n1,n2,n3):
#     print(n1+n2+n3)

# add(2,3,4)

# def my(name,n1,n2,n3):
#     if (0<= n1 <=100) and (0<= n2 <=100) and (0<= n3 <=100):
#         ave = (n1 + n2 + n3)/3
#         return f'{name}, your average is {ave:.2f}'

#     return 'scores are not correct'

# name = input('enter your name: ')
# number1 = int(input('Enter first number: '))
# number2 = int(input('Enter second number: '))
# number3 = int(input('Enter third number: '))
# result = my(name,number1,number2,number3)

# print(result)

def generate_secrent_number():
    numbers = list('0123456789')
    random.shuffle(numbers)
    return''.join(numbers[:3])

def check_user_guess(user_guess, secret_number):
    if user_guess == secret_number:
        return 'You won!'
    help = []
    for i in range(len(user_guess)):
        if user_guess[i] == secret_number[i]:
            help.append("Fermi")
        elif user_guess[i] in secret_number:
            help.append("Pico")

    if len(help) == 0:
        return 'Bagels'
    return help

secret_number = generate_secrent_number()

for i in range(10):
    user_guess = input('Enter your guess: ')
    print(secret_number)
    result = check_user_guess(user_guess, secret_number)
    if user_guess == secret_number:
        print(result)
        break
    else:
        print(result)


class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        num_counts = collections.Counter(nums)  # Count the frequencies of numbers in the list
        max_sequence_length = 0  # Variable to store the length of the longest consecutive sequence

        for num in nums:
            if num - 1 not in num_counts:  # Check if the previous number is not in the list
                current_num = num  # Start a new sequence with the current number
                current_sequence_length = 1  # Initialize the length of the current sequence

                while current_num + 1 in num_counts:  # Continue the sequence by checking the next number
                    current_num += 1  # Increment the current number
                    current_sequence_length += 1  # Increment the length of the current sequence

                max_sequence_length = max(max_sequence_length, current_sequence_length)  # Update the maximum sequence length if necessary

        return max_sequence_length  # Return the length of the longest consecutive sequence


import math
from angle import Angle

class Point(object):
    """ class to handle 2D points
        :param e: easting of point (float)
        :param n: northing of point (float)
    """

    def __init__(self, e=0, n=0):
        self.e = e
        self.n = n

    def __str__(self):
        """ print helper method
        """
        return "Point(%.2f, %.2f)" % (self.e, self.n)

    def __add__(self, p):
        """ add the coordinates of two points
            :param p: point to add
            :returns: a new point or child class
        """
        return self.__class__(self.e + p.e, self.n + p.n)   # create instance of self

    def __iadd__(self, p):
        """ increment coordinates by an other point
            :param p: point to add
            :returns: the incremented instance
        """
        self.e += p.e
        self.n += p.n
        return self

    def __sub__(self, p):
        """ substract coordinates of another point
            :param p: point to add (Point)
            :returns: a new point or child class instance
        """
        return self.__class__(self.e - p.e, self.n - p.n)

    def dist(self):
        """ distance from origin
            :returns: distance from origin (float)
        """
        return math.hypot(self.e, self.n)

    def wcb(self):
        """ whole circle bearing from the origin
            :returns: whole circle bearing (Angle)
        """
        return Angle(math.atan2(self.e, self.n))

    def polar(self, a, d):
        """ Polar point calculation
            :param a: polar angle from north clockwise (Angle)
            :param d: polar distance (float)
            :returns: a new point
        """
        return self.__class__(self.e + math.sin(a.val) * d,
                              self.n + math.cos(a.val) * d)

class Circle(Point):
    """ class to handle circles
        :param e: easting of point (float)
        :param n: northing of point (float)
        :param r: radius (float)
    """

    def __init__(self, e=0, n=0, r=1):
        self.r = r
        super(Circle, self).__init__(e, n)  # call parent's constructor

    def __str__(self):
        """ print helper method
        """
        return "Circle(%.2f, %.2f R=%.2f)" % (self.e, self.n, self.r)

    def area(self):
        """ area of circle
            :returns: area of circle
        """
        return math.pi * self.r ** 2.0

    def perimeter(self):
        """ perimeter of circle
            :returns: perimeter of circle
        """
        return math.pi * self.r * 2.0

    def center(self):
        """ get center as a Point object
            :returns: center of circle (Point)
        """
        return Point(self.e, self.n)

    def __mul__(self, c):
        """ intersections of circles using '*' operator
            :param c: another circle (Circle)
            :returns: tuple of intersection points (Point)
        """
        p0 = self.center()
        d = (p0 - c.center()).dist()
        if d > self.r + c.r:
            # no intersection empty tuple
            return ()
        a = (self.r ** 2 - c.r ** 2 + d ** 2) / 2.0 / d
        h = math.sqrt(self.r ** 2 - a ** 2)
        de = c.e - self.e
        dn = c.n - self.n
        p2 = p0 + Point(a * de / d, a * dn / d)
        w1 = h * dn / d
        w2 = h * de / d
        pp1 = p2 + Point(w1, -w2)
        pp2 = p2 + Point(-w1, w2)
        return (pp1, pp2)

class Line(Point):
    """ class to handle infinite lines
        :param e: easting of point (float)
        :param n: northing of point (float)
        :param angle: angle from north clockwise (Angle)
    """

    def __init__(self, e=0, n=0, angle=0):
        if isinstance(angle, Angle):
            self.angle = angle
        else:
            self.angle = Angle(angle)
        super(Line, self).__init__(e, n)  # call parent's constructor

    def rotate(self, a):
        """ rotate
            :param a: rotation angle clockwise (Angle)
        """
        self.angle += a

    def equation(self):
        """ calculate line equation coefficients a * e + b * n + c = 0
            :returns: tuple of a, b, c
        """
        a = math.cos(self.angle.val)
        b = -math.sin(self.angle.val)
        c = - a * self.e - b * self.n
        return (a, b, c)

    def __mul__(self, l):
        """ intersection of two lines using '*' operator
            :param l: line to intersect with (Line)
            :returns: intersection point (Point)
        """
        l1 = self.equation()
        l2 = l.equation()
        w = l1[0] * l2[1] - l1[1] * l2[0]
        if abs(w) > 1e-5:                # check for paralel lines
            e = (l1[1] * l2[2] - l1[2] * l2[1]) / w
            n = (l1[2] * l2[0] - l1[0] * l2[2]) / w
            return Point(e, n)
        return None

    def __str__(self):
        """ print helper method
        """
        return "Line(%.2f, %.2f %s)" % (self.e, self.n, self.angle)

class Text(Line):
    """ class to handle text
        :param e: easting of point (float)
        :param n: northing of point (float)
        :param angle: angle from north clockwise (Angle)
        :param txt: text (string)
    """

    def __init__(self, e=0, n=0, angle=0, txt='hello'):
        self.txt = txt
        super(Text, self).__init__(e, n, angle)  # call parent's constructor

    def __str__(self):
        """ print helper method
        """
        return "Text(%.2f, %.2f %s %s)" % (self.e, self.n, self.angle, self.txt)

class Rectangle(Line):
    """ class to handle rotated rectangle
        :param e: easting of point (float)
        :param n: northing of point (float)
        :param angle: angle from north clockwise (Angle)
        :param width: width of rectangel (float)
        :param height: height of rectngle (float)
    """
    def __init__(self, e=0, n=0, angle=0, width=1, height=1):
        self.width = width
        self.height = height
        super(Rectangel, self).__init__(e, n, angle)  # call parent's constructor

    def area(self):
        """ area of rectangle
            :returns: area (float)
        """
        return self.width * self.height

    def perimeter(self):
        """ perimeter of rectangle
            :returns: perimeter
        """
        return (self.width + self.height) * 2.0

    def __str__(self):
        """ print helper method
        """
        return "Rectangle(%.2f, %.2f %s %s)" % (self.e, self.n, self.angle, self.txt)

if __name__ == "__main__":
    # tests for point class
    p1 = Point(5, 8)
    print(p1)
    p2 = p1 + Point(1, 2)
    print(p2)
    a2 = (p2-p1).wcb().normalize()      # bearing from p1 to p2
    print(a2)
    print((p2-p1).dist())               # distance between p1 and p2
    p3 = p1 + Point(1, -2)
    print(p3)
    a3 = (p3-p1).wcb().normalize()      # bearing from p1 to p3
    print(a3)
    p4 = p1 - Point(1, 2)
    print(p4)
    a4 = (p4-p1).wcb().normalize()      # bearing from p1 to p4
    print(a4)
    p5 = p1 + Point(-1, 2)
    print(p5)
    a5 = (p5-p1).wcb().normalize()      # bearing from p1 to p5
    print(a5)
    print(p1.polar(Angle('12-34-56'), 2.456))   # a polar point
    # tests for line
    print("-------------------")
    l1 = Line(p2.e, p2.n, (p1-p2).wcb().normalize())
    l2 = Line(p3.e, p3.n, (p1-p3).wcb().normalize())
    l3 = Line(p4.e, p4.n, (p1-p4).wcb().normalize())
    l4 = Line(p5.e, p5.n, (p1-p5).wcb().normalize())
    print(l1 * l2)                       # intersection of lines
    print(l1 * l4)                       # intersection of lines
    print(l2 * l3)                       # intersection of lines
    print(l3 * l4)                       # intersection of lines
    # test for circle
    print("-------------------")
    c1 = Circle(6, 8, 4)
    print(c1)
    print(c1.area())
    print(c1.perimeter())
    c2 = Circle(8, 5, 5)
    print(c2)
    for p in (c1 * c2):
        print(p)


from flask import Flask,g
from db import DB
import json
from datetime import datetime

app = Flask(__name__)

@app.route('/student/<key>')
def get_expreesion_duration(key):
    db = DB()
    return_data = {
        'happy': 0,
        'neutral': 0,
        'sleepy': 0
    }
    querys = list(db.query(f"select * from record where s_id = '{key}'").get_points())
    # query = (key:char(6), expression: text, eye_dir: text, time: datatime) - tuple
    before_time = datetime.strptime(querys[0]["time"], "%Y-%m-%dT%H:%M:%SZ")
    expression = querys[0]["expression"]
    for query in querys[1:]:
        current_time = datetime.strptime(query["time"], "%Y-%m-%dT%H:%M:%SZ")
        duration = (current_time-before_time).total_seconds()

        return_data[expression] += duration

        before_time = current_time
        expression = query["expression"]
    return json.dumps(return_data)

@app.route('/classroom')
def classroom():
    db = DB()
    # query_db return value : tuple
    keys = [q['distinct'] for q in list(db.query('select distinct(s_id) from record').get_points())]
    ret = []
    for key in keys:
        data_form = {}
        data_form['key'] = key
        data_form['exp_duration'] = json.loads(get_expreesion_duration(key))
        ret.append(data_form)
    return json.dumps(ret)

if __name__ == '__main__':
    #app.run()
    app.run(host='0.0.0.0', debug=False)


'''This scripts downloads GIFs from Twitter.

Required packages:
```pip install pandas tqdm requests twitter```

Example usage:
```
python3 download_twitter_gif.py \
    /path/to/downloaded/gif-reply-dataset.csv \
    /path/to/twitter_credential.json \
    /path/to/cache/child-tweets.json \
    /path/to/store/downloaded/gifs/ # this is the $GIF_PATH of your choice
```
'''
import argparse
import logging
import os
import sys
from pathlib import Path

import pandas as pd
import requests
from prepare_gif_reply_dataset import crawl_tweets_to_file
from tqdm import tqdm

logging.basicConfig(
    stream=sys.stdout,
    format='%(asctime)s.%(msecs)03d %(levelname)s %(name)s %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO,
)

def gif_id_to_filepath(gif_id: str, gif_dir, ext='.mp4') -> str:
    def _gif_id_to_structured_path(gif_id):
        return os.path.join(gif_id[0], gif_id[1], gif_id[2], gif_id[3:])
    return os.path.join(gif_dir, _gif_id_to_structured_path(gif_id)+ext)

def download_gif_file(gif_id: str, download_url: str, gif_dir: str):
    # Try to download gif, If success, add filepath to database
    gif_filepath = gif_id_to_filepath(gif_id, gif_dir=gif_dir)
    # Make dir if not exists
    Path(os.path.dirname(gif_filepath)).mkdir(parents=True, exist_ok=True)

    if os.path.exists(gif_filepath):
        # skipping gif if already downloaded
        return gif_filepath

    try:
        img_file = requests.get(download_url)
        # If download success
        if img_file.status_code == 200:
            # Store the gif file
            with open(gif_filepath, 'wb') as f:
                f.write(img_file.content)
        else:
            gif_filepath = None
    except Exception as e:
        logging.error(f'Exception occurred for {twitter_url}')
        logging.exception(e)
        gif_filepath = None
        try:
            os.remove(gif_filepath)
        except:
            pass

    return gif_filepath

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'gif_reply_dataset_filepath',
        help='filepath of the downloaded gif-reply dataset.',
    )
    parser.add_argument(
        'twitter_credential_file',
        help='filepath for twitter credentials.',
    )
    parser.add_argument(
        'tweets_output_file',
        help='filepath for intermediate tweets json.',
    )
    parser.add_argument(
        'output_gif_dir', help='filepath to store the downloaded GIFs.',
    )
    args = parser.parse_args()

    # Read reply dataset and build child_gif_id -> [child_id1, child_id2, ...] mapping
    dataset = pd.read_csv(args.gif_reply_dataset_filepath)
    dataset['parent_id'] = dataset['parent_id'].apply(str)
    dataset['child_id'] = dataset['child_id'].apply(str)
    gif_id_with_child_id = dataset[['child_gif_id', 'child_id']].groupby(
        'child_gif_id',
    )['child_id'].apply(list).reset_index()

    # Crawl child tweets using Twitter Offical API
    all_child_ids = list(dataset['child_id'].unique())
    crawl_tweets_to_file(
        all_child_ids, args.tweets_output_file, args.twitter_credential_file,
    )

    # Read crawled child tweets and build tweet_id -> gif_url mapping
    child_tweets = pd.read_json(
        args.tweets_output_file, orient='records', lines=True,
    )
    child_tweets['id'] = child_tweets['id'].apply(str)

    def _get_url(media):
        try:
            return media[0]['video_info']['variants'][0]['url']
        except:
            return None

    child_tweets['gif_url'] = child_tweets['media'].apply(_get_url)
    tweet_id_to_animated_gif_url = dict(
        child_tweets[['id', 'gif_url']].to_numpy(),
    )

    # Download every GIF
    for i, row in tqdm(gif_id_with_child_id.iterrows(), total=len(gif_id_with_child_id)):
        gif_id = row['child_gif_id']  # hash id
        child_ids = row['child_id']

        res = ''
        # attempt for all available child gif id
        for child_id in child_ids:
            twitter_url = tweet_id_to_animated_gif_url.get(child_id)
            if twitter_url is None:
                continue
            res = download_gif_file(gif_id, twitter_url, args.output_gif_dir)
            if res is not None:
                break
        if res is None:
            # Note that some of the GIFs hosted on Twitter might be removed by the original poster.
            logging.error(f'Failed to download GIF file for ID {gif_id}')


# code for updating gene expression from ..2018

from scipy.io import loadmat
import pandas as pd
import os

#raw_dir = './RAW'
raw_dir = '../../../obsbrain_RAW/Arnatkeviciute2018/RAW/'
mat = loadmat(os.path.join(raw_dir, 'ROIxGene_aparcaseg.mat'))
mat.keys()

new_dir = './obs.DAT'
if not os.path.exists(new_dir):
    os.mkdir(new_dir)

gene_list = [gene[0][0] for gene in mat['probeInformation'][0][0][1]]
"""
for i, gene in enumerate(gene_list):
    col_idx = i+1
    gene_dat = mat['parcelExpression'][:,col_idx]
    out_file = os.path.join(new_dir, gene+'.obs')
    gene_dat_df = pd.DataFrame()
    gene_dat_df['X'] = gene_dat
    gene_dat_df.to_csv(out_file, index=False, header=False)
"""
# save gene list in the database
gene_list_pf = pd.DataFrame()
gene_list_pf['X'] = gene_list
gene_list_pf.to_csv('obs.gene_list.csv', index=False, header=False)

# save hemi list
hemi_list = ['lh']*34
hemi_list_pd = pd.DataFrame()
hemi_list_pd['Hemi'] = hemi_list
hemi_list_pd.to_csv('obs.hemi_list.csv', index=False, header=False)

gene_dat = mat['parcelExpression'][:,1:]
out_file = os.path.join(new_dir, 'obsDat.csv')
gene_dat_df = pd.DataFrame(data=gene_dat, columns=gene_list)
gene_dat_df.to_csv(out_file, index=False)

out_file = os.path.join(new_dir, 'obsDat.feather')
gene_dat_df.reset_index(inplace=True)
gene_dat_df['index']=u'r' + gene_dat_df['index'].astype(str)
gene_dat_df.columns = gene_dat_df.columns.map(str)
gene_dat_df.to_feather(out_file)


import logging
from http import HTTPStatus

from awd.cli import cli

logger = logging.getLogger(__name__)

def test_deposit_success(
    caplog,
    doi_list_success,
    mocked_web,
    mocked_dynamodb,
    mocked_s3,
    mocked_ses,
    mocked_sqs_input,
    s3_client,
    sqs_client,
    submission_message_body,
    runner,
):
    with caplog.at_level(logging.DEBUG):
        s3_client.put_file(
            file_content=doi_list_success,
            bucket="awd",
            key="doi_success.csv",
        )
        assert len(s3_client.client.list_objects(Bucket="awd")["Contents"]) == 1
        result = runner.invoke(cli, ["deposit"])
        assert result.exit_code == 0
        uploaded_metadata = s3_client.client.get_object(
            Bucket="awd", Key="10.1002-term.3131.json"
        )
        assert uploaded_metadata["ResponseMetadata"]["HTTPStatusCode"] == HTTPStatus.OK
        s3_client.client.get_object(Bucket="awd", Key="10.1002-term.3131.pdf")
        assert uploaded_metadata["ResponseMetadata"]["HTTPStatusCode"] == HTTPStatus.OK
        sqs_client.queue_name = "mock-input-queue"
        messages = sqs_client.receive()
        for message in messages:
            assert message["Body"] == submission_message_body
        assert (
            len(s3_client.client.list_objects(Bucket="awd")["Contents"])
            == 3  # noqa: PLR2004
        )
        assert "Submission process has completed" in caplog.text
        assert "Logs sent to" in caplog.text

def test_deposit_insufficient_metadata(
    caplog,
    doi_list_insufficient_metadata,
    mocked_web,
    mocked_dynamodb,
    mocked_s3,
    mocked_ses,
    mocked_sqs_input,
    s3_client,
    runner,
):
    with caplog.at_level(logging.DEBUG):
        s3_client.put_file(
            file_content=doi_list_insufficient_metadata,
            bucket="awd",
            key="doi_insufficient_metadata.csv",
        )
        result = runner.invoke(cli, ["deposit"])
        assert result.exit_code == 0
        assert (
            "Insufficient metadata for 10.1002/nome.tadata, missing title or URL"
            in caplog.text
        )
        assert len(s3_client.client.list_objects(Bucket="awd")["Contents"]) == 1
        assert "Submission process has completed" in caplog.text
        assert "Logs sent to" in caplog.text

def test_deposit_pdf_unavailable(
    caplog,
    doi_list_pdf_unavailable,
    mocked_web,
    mocked_dynamodb,
    mocked_s3,
    mocked_ses,
    mocked_sqs_input,
    s3_client,
    runner,
):
    with caplog.at_level(logging.DEBUG):
        s3_client.put_file(
            file_content=doi_list_pdf_unavailable,
            bucket="awd",
            key="doi_pdf_unavailable.csv",
        )
        result = runner.invoke(cli, ["deposit"])
        assert result.exit_code == 0
        assert "A PDF could not be retrieved for DOI: 10.1002/none.0000" in caplog.text
        assert len(s3_client.client.list_objects(Bucket="awd")["Contents"]) == 1
        assert "Submission process has completed" in caplog.text
        assert "Logs sent to" in caplog.text

def test_deposit_s3_nonexistent_bucket(
    caplog,
    monkeypatch,
    mocked_web,
    mocked_dynamodb,
    mocked_s3,
    mocked_ses,
    mocked_sqs_input,
    s3_client,
    runner,
):
    with caplog.at_level(logging.DEBUG):
        monkeypatch.setenv("BUCKET", "not-a-bucket")
        result = runner.invoke(cli, ["deposit"])
        assert result.exit_code == 0
        assert (
            "Error accessing bucket: not-a-bucket, The specified bucket does not exist"
        ) in caplog.text

def test_deposit_wrong_dynamodb_table_exits(
    caplog,
    monkeypatch,
    doi_list_success,
    mocked_web,
    mocked_dynamodb,
    mocked_s3,
    mocked_ses,
    mocked_sqs_input,
    s3_client,
    runner,
):
    with caplog.at_level(logging.INFO):
        monkeypatch.setenv("DOI_TABLE", "not-a-table")
        s3_client.put_file(
            file_content=doi_list_success,
            bucket="awd",
            key="doi_success.csv",
        )
        result = runner.invoke(cli, ["deposit"])
        assert result.exit_code == 0
        assert ("Unable to read DynamoDB table") in caplog.text

def test_listen_success(
    caplog,
    mocked_dynamodb,
    mocked_s3,
    mocked_ses,
    mocked_sqs_output,
    sample_doiprocessattempt,
    sqs_client,
    result_message_attributes_error,
    result_message_attributes_success,
    result_message_body_error,
    result_message_body_success,
    runner,
):
    with caplog.at_level(logging.DEBUG):
        sqs_client.send(
            result_message_attributes_error,
            result_message_body_error,
        )
        sqs_client.send(
            message_attributes=result_message_attributes_success,
            message_body=result_message_body_success,
        )
        sample_doiprocessattempt.add_item("111.1/1111")
        sample_doiprocessattempt.add_item("222.2/2222")
        result = runner.invoke(cli, ["listen"])
        assert result.exit_code == 0
        assert str(result_message_body_error) in caplog.text
        assert str(result_message_body_success) in caplog.text
        assert "Messages received and deleted from output queue" in caplog.text
        messages = sqs_client.receive()
        assert next(messages, None) is None
        assert "Logs sent to" in caplog.text

def test_listen_message_error(
    caplog, mocked_dynamodb, mocked_ses, mocked_sqs_output, runner, sqs_client
):
    with caplog.at_level(logging.DEBUG):
        sqs_client.send(
            message_attributes={},
            message_body={},
        )
        result = runner.invoke(cli, ["listen"])
        assert result.exit_code == 0
        assert "Error while processing SQS message:" in caplog.text
        assert "Logs sent to" in caplog.text


import discord
from sqlalchemy import and_, or_, exists

import db
import discord_bot
from logger import logger

def has_user_reached(level, user_id):
    if level.unlocks:
        return db.session.query(db.UserUnlock).where(
            and_(db.UserUnlock.level_id == level.id, db.UserUnlock.user_id == str(user_id))
        ).scalar() is not None
    for parent_level in level.parent_levels:
        if parent_level.solutions:
            has_solved = db.session.query(db.UserSolve).where(
                and_(db.UserSolve.level_id == parent_level.id, db.UserSolve.user_id == str(user_id))
            ).scalar() is not None
            if not has_solved:
                return False
        elif not has_user_reached(parent_level, user_id):
            return False
    return True

def has_user_solved(level, user_id):
    return db.session.query(db.UserSolve).where(
        and_(db.UserSolve.level_id == level.id, db.UserSolve.user_id == str(user_id))
    ).scalar() is not None

def get_solvable_levels(user_id):
    levels = []
    for level in db.session.query(db.Level).all():
        if can_user_solve(level, user_id):
            if level.solutions:
                levels.append(level)
            elif not level.child_levels:
                levels.append(level)
            elif not any(not l.unlocks and has_user_reached(l, user_id) for l in level.child_levels):
                levels.append(level)
    return levels

def get_solved_levels(user_id, name=None, start='', limit=None):
    query = db.session.query(db.Level).where(
        and_(
            db.Level.name == name if name else db.Level.name.startswith(start),
            exists().where(and_(db.UserSolve.user_id == str(user_id), db.Level.id == db.UserSolve.level_id))
        )
    )
    if limit is not None:
        query = query.limit(limit)
    return query.all()

def get_solved_or_unlocked_levels(user_id, name=None, start='', limit=None):
    query = db.session.query(db.Level).where(
        and_(
            db.Level.name == name if name else db.Level.name.startswith(start),
            or_(
                exists().where(and_(db.UserSolve.user_id == str(user_id), db.Level.id == db.UserSolve.level_id)),
                exists().where(and_(db.UserUnlock.user_id == str(user_id), db.Level.id == db.UserUnlock.level_id))
            )
        )
    )
    if limit is not None:
        query = query.limit(limit)
    return query.all()

def get_user_level_suffixes(user_id):
    levels = list(get_solvable_levels(user_id))
    levels.sort(key=lambda l: ((l.category.ordinal or 0) if l.category else -1, l.name))
    user_level_suffixes = []
    for level in levels:
        if level.nickname_suffix and (not level.nickname_merge or level.nickname_suffix not in user_level_suffixes):
            user_level_suffixes.append(level.nickname_suffix)
    return user_level_suffixes

def has_user_solved_everything(user_id):
    unsolved_level = db.session.query(db.Level).where(
        and_(
            exists().where(db.Level.id == db.Solution.level_id),
            ~exists().where(and_(db.UserSolve.user_id == str(user_id), db.Level.id == db.UserSolve.level_id)),
        )
    ).first()
    locked_level = db.session.query(db.Level).where(
        and_(
            exists().where(db.Level.id == db.Unlock.level_id),
            ~exists().where(and_(db.UserUnlock.user_id == str(user_id), db.Level.id == db.UserUnlock.level_id)),
        )
    ).first()
    return unsolved_level is None and locked_level is None

def get_used_role_ids():
    roles = set()
    for level in db.session.query(db.Level).all():
        if level.discord_role:
            roles.add(level.discord_role)
    completionist_role = db.get_setting('completionist_role')
    if completionist_role:
        roles.add(completionist_role)
    return roles

def get_starting_levels():
    return db.session.query(db.Level).where(~db.Level.parent_levels.any())

def is_member_admin(member):
    admin_role_id = db.get_setting('admin_role')
    if not admin_role_id:
        return False
    return member.get_role(int(admin_role_id)) is not None

async def update_user_nickname(user_id):
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    member = guild.get_member(int(user_id)) or await guild.fetch_member(int(user_id))
    if not member:
        logger.error('member %s not found in guild %s', user_id, guild.name)
        return
    if member.bot:
        return
    user = db.session.get(db.User, str(user_id))
    if user is None:
        user = db.User(id=str(user_id))
        db.session.add(user)
    if db.get_setting('admin_enable') == 'true' and is_member_admin(member):
        name_suffix = db.get_setting('admin_badge', '')
    elif db.get_setting('completionist_enable_nickname') == 'true' and has_user_solved_everything(user_id):
        name_suffix = db.get_setting('completionist_badge', '*')
    elif db.get_setting('nickname_enable') == 'true':
        level_suffixes = get_user_level_suffixes(user_id)
        prefix = db.get_setting('nickname_prefix', ' [')
        separator = db.get_setting('nickname_separator', ', ')
        suffix = db.get_setting('nickname_suffix', ']')
        name_suffix = f'{prefix}{separator.join(level_suffixes)}{suffix}' if level_suffixes else ''
    else:
        name_suffix = None
    user.nick = user.name or member.name
    if name_suffix:
        user.nick = user.nick[:32 - max(0, len(name_suffix))] + name_suffix[:32]
    db.session.commit()
    if member.nick == user.nick:
        return
    logger.info(f'updating nickname for %s (%s) to %s in %s', member.name, member.id, user.nick, guild.name)
    try:
        await member.edit(nick=user.nick)
    except discord.Forbidden:
        logger.error(f'missing permission to update nickname for %s (%s)', member.name, member.id)

async def update_all_user_nicknames():
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    for i, member in enumerate(guild.members):
        if not member.bot:
            await update_user_nickname(str(member.id))
        yield f'{i}/{guild.member_count}'

async def update_user_roles(user_id, used_role_ids=None):
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    if used_role_ids is None:
        used_role_ids = get_used_role_ids()

    member = guild.get_member(int(user_id)) or await guild.fetch_member(user_id)
    if member is None or member.bot:
        return
    roles_user_has = set(map(lambda r: str(r.id), member.roles)) & used_role_ids
    roles_user_should_have = set()

    if not (db.get_setting('admin_enable') == 'true' and is_member_admin(member)):
        for level in db.session.query(db.Level).all():
            if has_user_reached(level, user_id):
                if any(not l.unlocks and has_user_reached(l, user_id) for l in level.child_levels):
                    continue
                for parent_level in get_parent_levels_until_role_or_unlock(level):
                    roles_user_should_have.add(parent_level.discord_role)

        if db.get_setting('completionist_enable_role') == 'true' and has_user_solved_everything(user_id):
            completionist_role = db.get_setting('completionist_role')
            if completionist_role:
                roles_user_should_have.add(completionist_role)

    role_ids_to_add = roles_user_should_have - roles_user_has
    roles_to_add = list(filter(lambda r: r is not None, map(lambda r: guild.get_role(int(r)), role_ids_to_add)))
    role_ids_to_remove = roles_user_has - roles_user_should_have
    roles_to_remove = list(filter(lambda r: r is not None, map(lambda r: guild.get_role(int(r)), role_ids_to_remove)))
    await member.add_roles(*roles_to_add)
    await member.remove_roles(*roles_to_remove)

async def update_all_user_roles():
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    used_role_ids = get_used_role_ids()
    for i, member in enumerate(guild.members):
        if not member.bot:
            await update_user_roles(str(member.id), used_role_ids=used_role_ids)
        yield f'{i}/{guild.member_count}'

def can_user_solve(level, user_id):
    if db.session.query(db.UserSolve).where(
            and_(db.UserSolve.level_id == level.id, db.UserSolve.user_id == str(user_id))
    ).scalar():
        return False
    return has_user_reached(level, user_id)

def can_user_unlock(level, user_id):
    if db.session.query(db.UserUnlock) \
            .where(and_(db.UserUnlock.level_id == level.id, db.UserUnlock.user_id == str(user_id))).scalar():
        return False
    for parent_level in level.parent_levels:
        if not has_user_reached(parent_level, user_id):
            return False
    return True

async def add_role_to_user(user_id, role_id):
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id)
    if guild is None:
        raise Exception(f'guild not set or wrong: {guild_id}')
    member = guild.get_member(int(user_id)) or await guild.fetch_member(int(user_id))
    if member is None:
        raise Exception(f'failed to find member')
    role = guild.get_role(int(role_id))
    logger.info('assigning role %s to user %s (%s)', role.name, member.name, member.id)
    await member.add_roles(role)

def get_parent_levels_until_role_or_unlock(level):
    if level.discord_role:
        return {level}
    res = set()
    if not level.unlocks:
        for parent_level in level.parent_levels:
            res.update(get_parent_levels_until_role_or_unlock(parent_level))
    return res

def get_child_ids_recursively(level):
    child_ids = {level.id}
    for child_level in level.child_levels:
        if child_level.id not in child_ids:
            child_ids |= get_child_ids_recursively(child_level)
    return child_ids

async def move_level_to_category(level):
    if level.discord_channel and level.category and level.category.discord_category:
        discord_channel = discord_bot.client.get_channel(int(level.discord_channel)) \
                          or await discord_bot.client.fetch_channel(level.discord_channel)
        if discord_channel.category_id != int(level.category.discord_category):
            discord_category = discord_bot.client.get_channel(int(level.category.discord_category)) \
                               or await discord_bot.client.fetch_channel(level.category.discord_category)
            if discord_category and discord_category.type == discord.ChannelType.category:
                child_ids = get_child_ids_recursively(level)
                position = None
                found_child = False
                for other_channel in discord_category.channels:
                    for other_level in db.session.query(db.Level).where(
                            db.Level.discord_channel == str(other_channel.id)):
                        if other_level.id in child_ids:
                            position = other_channel.position
                            found_child = True
                    if found_child:
                        break
                    position = other_channel.position + 1
                if position is not None:
                    await discord_channel.edit(category=discord_category, position=position)
                else:
                    await discord_channel.edit(category=discord_category)

async def move_all_levels_to_categories():
    for level in db.session.query(db.Level).all():
        await move_level_to_category(level)

def get_parent_levels_recursively(level, initial_level=None):
    if initial_level is None:
        initial_level = level
    parent_levels = {level}
    for parent_level in level.parent_levels:
        if parent_level.id == initial_level.id:
            raise ValueError('loop in level dependencies')
        parent_levels |= get_parent_levels_recursively(parent_level, initial_level)
    return parent_levels

def check_loops():
    for level in db.session.query(db.Level).all():
        get_parent_levels_recursively(level)

async def update_role_permissions():
    levels = [level for level in db.session.query(db.Level).all()]
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    if guild is None:
        raise Exception(f'guild not set or wrong: {guild_id}')
    channel_permissions = {level.discord_channel: {
        guild.default_role: discord.PermissionOverwrite(read_messages=False)
    } for level in levels if level.discord_channel}
    for level in db.session.query(db.Level).all():
        role = guild.get_role(int(level.discord_role)) if level.discord_role else None
        if role is None:
            continue
        for parent_level in get_parent_levels_recursively(level):
            if parent_level.discord_channel and parent_level.discord_channel in channel_permissions.keys():
                parent_child = parent_level.discord_channel
                channel_permissions[parent_child][role] = discord.PermissionOverwrite(read_messages=True)
    progress = 0
    for channel_id, permissions in channel_permissions.items():
        channel = guild.get_channel(int(channel_id))
        if channel is not None:
            permissions_as_list = list(permissions.items())
            current_permissions = {chid: overwrite for chid, overwrite in channel.overwrites.items() if
                                   overwrite.read_messages}
            roles_to_add = set(permissions.keys()) - set(current_permissions.keys())
            roles_to_remove = set(current_permissions.keys()) - set(permissions.keys())
            if len(roles_to_add) == 0 and len(roles_to_remove) == 0:
                continue
            if len(roles_to_add) + len(roles_to_remove) < len(permissions_as_list) - 100:
                for role in roles_to_remove:
                    await channel.set_permissions(role, overwrite=None)
                for role in roles_to_add:
                    await channel.set_permissions(role, overwrite=permissions[role])
                continue
            first_batch = dict(permissions_as_list[:100])
            await channel.edit(overwrites=first_batch)
            for role, overwrite in permissions_as_list[100:]:
                await channel.set_permissions(role, overwrite=overwrite)
        yield f'{progress}/{len(channel_permissions)}'

async def skip_user_to_level(user_id, level, include_self=False):
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    member = guild.get_member(int(user_id)) or await guild.fetch_member(int(user_id))
    if not member:
        raise Exception('invalid author')
    solved_level_names = []
    unlocked_level_names = []
    for parent_level in get_parent_levels_recursively(level):
        if parent_level.unlocks and not db.session.query(db.UserUnlock).where(
                and_(db.UserUnlock.level_id == str(parent_level.id), db.UserUnlock.user_id == str(member.id))).scalar():
            unlocked_level_names.append(parent_level.name)
            db.session.add(db.UserUnlock(user_id=str(member.id), level=parent_level))
        if level.unlocks and level in parent_level.child_levels:
            continue
        if parent_level == level and not include_self:
            continue
        if parent_level.solutions and not db.session.query(db.UserSolve).where(
                and_(db.UserSolve.level_id == parent_level.id, db.UserSolve.user_id == str(member.id))).scalar():
            solved_level_names.append(parent_level.name)
            db.session.add(db.UserSolve(user_id=str(member.id), level=parent_level))
    db.session.commit()
    await update_user_roles(str(member.id))
    await update_user_nickname(str(member.id))
    message_parts = []
    if solved_level_names:
        message_parts.append(f'{len(solved_level_names)} solves ({", ".join(reversed(solved_level_names))})')
    if unlocked_level_names:
        message_parts.append(f'{len(unlocked_level_names)} unlocks ({", ".join(reversed(unlocked_level_names))})')
    return ('Added ' + ' and '.join(message_parts)) if message_parts else 'Nothing to do'

def get_invalid_user_solves():
    return db.session.query(db.UserSolve).where(
        ~exists(db.Solution).where(db.UserSolve.level_id == db.Solution.level_id)).all()

def get_leaderboard(categories=None):
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id)
    if categories is not None:
        levels = db.session.query(db.Level).where(db.Level.category_id.in_(categories))
    else:
        levels = db.session.query(db.Level).all()
    scores = {}
    for level in levels:
        if not level.solutions:
            continue
        for user_solve in level.user_solves:
            scores[user_solve.user_id] = scores.get(user_solve.user_id, 0) + 1
    groups = {}
    for uid, score in scores.items():
        user = db.session.get(db.User, uid)
        member = guild.get_member(int(user.id))
        if member and is_member_admin(member):
            continue
        if score not in groups:
            groups[score] = []
        groups[score].append(user)
    return sorted(groups.items(), reverse=True)

def get_users_dict():
    users = {}
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id)
    for user in db.session.query(db.User).all():
        member = guild.get_member(int(user.id))
        users[user.id] = {
            'nick': user.nick,
            'avatar': user.avatar,
            'admin': member and is_member_admin(member)
        }
    return users

def get_scores_dict():
    scores = {}
    for solve in db.session.query(db.UserSolve).all():
        if solve.user_id not in scores:
            scores[solve.user_id] = {}
        scores[solve.user_id][solve.level.category_id] = scores[solve.user_id].get(solve.level.category_id, 0) + 1
    return scores

def update_avatar(member):
    user = db.session.get(db.User, str(member.id)) or db.User(id=member.id)
    avatar_url = member.display_avatar.url if user is not None else None
    if avatar_url != user.avatar:
        user.avatar = avatar_url
        db.session.commit()

async def update_all_avatars():
    guild_id = int(db.get_setting('guild'))
    guild = discord_bot.client.get_guild(guild_id) or await discord_bot.client.fetch_guild(guild_id)
    for user in db.session.query(db.User).all():
        member = guild.get_member(int(user.id))
        if member is not None:
            update_avatar(member)
        else:
            user.avatar = None
            db.session.commit()

def get_used_categories():
    used_categories = []
    for category in db.session.query(db.Category).order_by('ordinal').all():
        for level in category.levels:
            if level.solutions:
                break
        else:
            continue
        used_categories.append(category)
    return used_categories


import socket # for networking
import pickle # for sending/receiving objects

# import the game
from tic_tac_toe import TicTacToe

HOST = '127.0.0.1' # this address is the "local host"
PORT = 12783       # port to listen on for clients

# set up the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)

# accept a connection from the client
client_socket, client_address = s.accept()
print(f"\nConnnected to {client_address}!")

# set up the game
player_x = TicTacToe("X")

# allow the player to suggest playing again
rematch = True

while rematch == True:
    # a header for an intense tic-tac-toe match!
    print(f"\n\n T I C - T A C - T O E ")

    # the rest is in a loop; if either player has won, it exits
    while player_x.did_win("X") == False and player_x.did_win("O") == False and player_x.is_draw() == False:
        # draw grid, ask for coordinate
        print(f"\n       Your turn!")
        player_x.draw_grid()
        player_coord = input(f"Enter coordinate: ")
        player_x.edit_square(player_coord)

        # draw the grid again
        player_x.draw_grid()

        # pickle the symbol list and send it
        x_symbol_list = pickle.dumps(player_x.symbol_list)
        client_socket.send(x_symbol_list)

        # if the player won with the last move or it's a draw, exit the loop
        if player_x.did_win("X") == True or player_x.is_draw() == True:
            break

        # wait to receive the symbol list and update it
        print(f"\nWaiting for other player...")
        o_symbol_list = client_socket.recv(1024)
        o_symbol_list = pickle.loads(o_symbol_list)
        player_x.update_symbol_list(o_symbol_list)

    # end game messages
    if player_x.did_win("X") == True:
        print(f"Congrats, you won!")
    elif player_x.is_draw() == True:
        print(f"It's a draw!")
    else:
        print(f"Sorry, the client won.")

    # ask for a rematch
    host_response = input(f"\nRematch? (Y/N): ")
    host_response = host_response.capitalize()
    temp_host_resp = host_response
    client_response = ""

    # pickle response and send it to the client
    host_response = pickle.dumps(host_response)
    client_socket.send(host_response)

    # if the host doesn't want a rematch, we're done here
    if temp_host_resp == "N":
        rematch = False

    # if the host does want a rematch, we ask the client for their opinion
    else:
        # receive client's response
        print(f"Waiting for client response...")
        client_response = client_socket.recv(1024)
        client_response = pickle.loads(client_response)

        # if the client doesn't want a rematch, exit the loop
        if client_response == "N":
            print(f"\nThe client does not want a rematch.")
            rematch = False

        # if both the host and client want a rematch, restart the game
        else:
            player_x.restart()

spacer = input(f"\nThank you for playing!\nPress enter to quit...\n")

client_socket.close()


import multiprocessing

from tensorflow.python.data.ops import dataset_ops
from tensorflow.python.data.ops import structured_function
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import tensor_spec
from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops

def _snapshot(input_dataset,  # pylint: disable=unused-private-name
              path,
              compression="AUTO",
              reader_func=None,
              shard_func=None,
              name=None):
  """See `Dataset.snapshot()` for details."""

  project_func = None
  if shard_func is None:
    input_dataset = input_dataset.enumerate(name=name)
    # This sets the amount of parallelism based on the number of CPU cores on
    # the machine where this Python code is executed, which may differ from
    # the number of CPU cores where the input pipeline graph is actually
    # executed (e.g. remote Cloud TPU workers).
    local_shard_func = lambda index, _: index % multiprocessing.cpu_count()
    project_func = lambda _, elem: elem
  else:
    local_shard_func = shard_func
  dataset = _SnapshotDataset(
      input_dataset=input_dataset,
      path=path,
      compression=compression,
      reader_func=reader_func,
      # This will not do the right thing where the graph is built on a
      # different machine than the executor (e.g. Cloud TPUs).
      shard_func=local_shard_func,
      name=name)
  if project_func is not None:
    dataset = dataset.map(project_func, name=name)
  return dataset

class _SnapshotDataset(dataset_ops.UnaryUnchangedStructureDataset):
  """A dataset that allows saving and re-use of already processed data."""

  def __init__(self,
               input_dataset,
               path,
               shard_func,
               compression=None,
               reader_func=None,
               pending_snapshot_expiry_seconds=None,
               use_legacy_function=False,
               name=None):

    if reader_func is None:
      reader_func = lambda datasets: datasets.interleave(  # pylint:disable=g-long-lambda
          lambda x: x,
          cycle_length=multiprocessing.cpu_count(),
          num_parallel_calls=dataset_ops.AUTOTUNE)

    self._input_dataset = input_dataset
    self._path = path
    self._compression = compression

    self._reader_func = structured_function.StructuredFunctionWrapper(
        reader_func,
        self._transformation_name() + ".reader_func",
        # Dataset of datasets of input elements
        input_structure=dataset_ops.DatasetSpec(
            dataset_ops.DatasetSpec(input_dataset.element_spec)),
        use_legacy_function=use_legacy_function)
    self._shard_func = structured_function.StructuredFunctionWrapper(
        shard_func,
        self._transformation_name() + ".shard_func",
        dataset=input_dataset,
        use_legacy_function=use_legacy_function)

    if ((not self._shard_func.output_structure.is_compatible_with(
        tensor_spec.TensorSpec([], dtypes.int32))) and
        (not self._shard_func.output_structure.is_compatible_with(
            tensor_spec.TensorSpec([], dtypes.int64)))):
      raise TypeError(f"Invalid `shard_func`. `shard_func` must return "
                      f"`tf.int64` scalar tensor but its return type is "
                      f"{self._shard_func.output_structure}.")

    self._name = name
    variant_tensor = ged_ops.snapshot_dataset_v2(
        input_dataset._variant_tensor,  # pylint: disable=protected-access
        path,
        self._reader_func.function.captured_inputs,
        self._shard_func.function.captured_inputs,
        compression=compression,
        reader_func=self._reader_func.function,
        shard_func=self._shard_func.function,
        **self._common_args)
    super().__init__(input_dataset, variant_tensor)

  def _functions(self):
    return [self._reader_func, self._shard_func]

  def _transformation_name(self):
    return "Dataset.snapshot()"


import json
import sys

def writeJSONtoFile(data, file):
    with open(file, 'w', encoding='utf8') as f:
        json.dump(data, f, indent=4, ensure_ascii=False)

def readJSONtoDict(file):
    with open(file, 'r', encoding='utf8') as f:
        return json.load(f)

EXEMPTED_NAMES = [] # Place hkn-rails names of people who are exempted

#purely for readability, this is a one-line job
def mergeCourseNames(web, rails):
    return rails["courseName"]

def mergeTutors(web, rails):
    #add all hkn-web tutors + info
    webNames = {}
    empty_timeslots_onlyinhknweb = set()
    for tutor in web["tutors"]:
        cache = {}
        for element in ["name", "timeSlots", "officePrefs", "adjacentPref", "numAssignments"]:
            if element == "timeSlots":
                total_sum = sum(abs(number) for number in tutor["timeSlots"])
                if total_sum == 0:
                    empty_timeslots_onlyinhknweb.add(tutor["name"])
            cache[element] = tutor[element]
        webNames[cache["name"]] = cache

    #add all hkn-rails tutors + info
    railNames = {}
    for tutor in rails["tutors"]:
        cache = {}
        for element in ["name", "tid", "courses", "numAssignments"]:
            cache[element] = tutor[element]
        railNames[cache["name"]] = cache

    not_matched_rails = []
    assignment_mismatches = []
    for name in webNames.keys():
        if name in railNames:
            keys = railNames[name].keys()
            for key in keys:
                if key == "numAssignments" and webNames[name][key] != railNames[name][key]:
                    assignment_mismatches.append((name, webNames[name][key], railNames[name][key]))
                else:
                    webNames[name][key] = railNames[name][key]
        else:
            not_matched_rails.append(name)
            if name in empty_timeslots_onlyinhknweb:
                empty_timeslots_onlyinhknweb.remove(name)

    not_matched_web = []
    for name in railNames.keys():
        if not name in webNames:
            not_matched_web.append(name)

    if True:
        for name in not_matched_rails:
            if name in webNames:
                del webNames[name]

    for exempt_name in EXEMPTED_NAMES:
        del webNames[exempt_name]
        if exempt_name in empty_timeslots_onlyinhknweb:
            empty_timeslots_onlyinhknweb.remove(exempt_name)
        print("Exempted", exempt_name)

    mergedTutors = list(webNames.values())
    return mergedTutors, not_matched_rails, not_matched_web, empty_timeslots_onlyinhknweb, assignment_mismatches

SPECIAL_DAY_CONVERSION = {"Tuesday" : "Tues", "Thursday" : "Thus"}

def shorten_day_of_week(day_of_week):
    return SPECIAL_DAY_CONVERSION.get(day_of_week, day_of_week[:3])

def hash_rails_element(element):
    return (element["office"], element["hour"], shorten_day_of_week(element["day"]))

def hash_web_element(element):
    return (element["office"].replace("Hybrid/", ""), element["hour"], element["day"])

def mergeSlots(web, rails):
    #create dictionary for both web and rails that maps the tuple
    # (office, hour, day) to the corresponding data in each database
    webDict = {}
    for element in web["slots"]:
        cache = {}
        for key in element:
            if key not in ["office", "hour", "day"]:
                cache[key] = element[key]
        webDict[hash_web_element(element)] = cache

    railsDict = {}
    for element in rails["slots"]:
        cache = {}
        for key in element:
            if key not in ["office", "hour", "day"]:
                cache[key] = element[key]
        railsDict[hash_rails_element(element)] = cache

    #map web slot ids to rails slot ids
    webIDtorailsID = {}
    for key in webDict:
        webID = webDict[key]["sid"]
        if key in railsDict:
            railsID = railsDict[key]["sid"]
            webIDtorailsID[webID] = railsID

    mergedList = []
    web_errors = []
    for key in webDict:
        if key in railsDict:
            merge = {}
            merge["hour"] = key[1]
            merge["name"] = railsDict[key]["name"]
            merge["day"] = key[2]
            merge["office"] = key[0] #webDict[key]["office"]
            merge["courses"] = railsDict[key]["courses"]
            merge["adjacentSlotIDs"] = webDict[key]["adjacentSlotIDs"] if key[0] != "Online" else [1] * len(webDict[key]["adjacentSlotIDs"])
            merge["sid"] = webDict[key]["sid"]
            mergedList += [merge]
        else:
            web_errors.append(key)
    return mergedList, web_errors

if __name__ == "__main__":
    if len(sys.argv) == 1:
        outfile = "DESTINATION_NAME.json"
        railsfile = "hkn-rails-tutoring.json"
        webfile = "hknweb-tutoring.json"
    elif len(sys.argv) == 4:
        outfile = sys.argv[3]
        railsfile = sys.argv[2]
        webfile = sys.argv[1]
    else:
        print("Error - please specify three file names in order of web JSON, rails JSON, and destination JSON")

    webJSON = readJSONtoDict(webfile)
    railsJSON = readJSONtoDict(railsfile)
    outJSON = {}
    outJSON["courseName"] = mergeCourseNames(webJSON, railsJSON)
    outJSON["tutors"], notInRails, notInWeb, empty_timeslots_onlyinhknweb, assignment_mismatches = mergeTutors(webJSON, railsJSON)
    outJSON["slots"], errors = mergeSlots(webJSON, railsJSON)
    # """ Uncomment or comment for error output
    print("Assignment mismatches (name, hknweb reported hours, hkn-rails position hours)")
    for mismatch in assignment_mismatches:
        print(" ".join(str(m) for m in mismatch))
    print("----------------")
    print("Tutors exist in hkn-web but not in hkn-rails")
    print(notInRails)
    print("----------------")
    print("Tutors exist in hkn-rails but not in hkn-web")
    print(notInWeb)
    print("----------------")
    print("(Office, Hour, Day) entries in hkn-web but not in hkn-rails")
    print(errors)
    print("----------------")
    print("Empty TimeSlot Preferences AND in hkn-rails:")
    print(empty_timeslots_onlyinhknweb)
    # """
    writeJSONtoFile(outJSON, outfile)


from fastapi import FastAPI, Body
from fastapi.encoders import jsonable_encoder

from app.model import RecipeSchema, UpdateRecipeSchema
from app.database import save_recipe, get_all_recipes, get_single_recipe, update_recipe_data, remove_recipe

app = FastAPI()

@app.get("/", tags=["Root"])
def get_root() -> dict:
    return {
        "message": "Welcome to your Okteto app."
    }

@app.get("/recipe", tags=["Recipe"])
def get_recipes() -> dict:
    recipes = get_all_recipes()
    return {
        "data": recipes
    }

@app.get("/recipe/{id}", tags=["Recipe"])
def get_recipe(id: str) -> dict:
    recipe = get_single_recipe(id)
    if recipe:
        return {
            "data": recipe
        }
    return {
        "error": "No such recipe with ID {} exist".format(id)
    }

@app.post("/recipe", tags=["Recipe"])
def add_recipe(recipe: RecipeSchema = Body(...)) -> dict:
    new_recipe = save_recipe(recipe.dict())
    return new_recipe
    return {
        "message": "Recipe added successfully."
    }

@app.put("/recipe", tags=["Recipe"])
def update_recipe(id: str, recipe_data: UpdateRecipeSchema)  -> dict:
    if not get_single_recipe(id):
        return {
            "error": "No such recipe exist"
        }

    update_recipe_data(id, recipe_data.dict())

    return {
        "message": "Recipe updated successfully."
    }

@app.delete("/recipe/{id}", tags=["Recipe"])
def delete_recipe(id: str) -> dict:
    if not get_single_recipe(id):
        return {
            "error": "Invalid ID passed"
        }

    remove_recipe(id)
    return {
        "message": "Recipe deleted successfully."
    }


import requests, re
from urlextract import URLExtract
#from lipoja https://github.com/lipoja/URLExtract
from bs4 import BeautifulSoup
import csv,time
from datetime import datetime

artes = 0
written = False
printables=""
timetaken = datetime.now()
printables += f"\nData Taken at {timetaken}\n"
yearz = str(datetime.now().year)
montz = str(datetime.now().month)
dias = str(datetime.now().day)
hrs = str(datetime.now().hour)
minz = str(datetime.now().minute)
timetaken2 = f'{yearz}{montz}{dias}{hrs}{minz}'
pagenums=1

questionable_a = 0
straight_nsfw_a = 0

keyword = ''
keyword = input('Type a deviantart user to see their works!>')
keyword = keyword.lower()

url = 'https://www.deviantart.com/{}/gallery/all?page={}'.format(keyword,pagenums)
headers = {
    'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36'
}
req = requests.get(url,headers=headers)

nd = str(req)
soup = BeautifulSoup(req.content,'html.parser')

#To print entire HTML Page. Used for maintenance.
def print_txt(keyword):
    with open(f'{keyword}.txt','w',encoding='utf-8',newline='') as fyle:
        fyle.write(soup.prettify())
        fyle.close()

#To determine an active account. An active account should return Response 200 at all times.
if nd == '<Response [200]>':
    print(' ')
    print('This user is available!\n')

else:
    print(' ')
    deact = str(soup.prettify())
    if "DeviantArt: Deactivated Account" in deact:
        print('Deactivated Account!\n')
    elif "DeviantArt: 404" in deact:
        print('Account has never existed!\n')
    print('This user is not available! Please run this program again.\n')
    exit()

#Saving in .csv
def save_csv(keyword, lists,favelists,arttype,maturelevel,linklists):
    global written,timetaken,timetaken2
    csv_header = ['Title', 'Faves','Type','Artist','Mature','Link']
    keyword=keyword.lower()
    with open(f'{keyword}_{timetaken2}.csv','a+',encoding='utf-8',newline='') as f:
        arts = csv.writer(f)
        if written == False:
            arts.writerow(csv_header)
            written = True
        for i in range(len(lists)):
            arts.writerow([lists[i],favelists[i],arttype[i],keyword,maturelevel[i],linklists[i]])
    f.close()

def save_csv2(keyword):
    global timetaken,timetaken2
    keyword=keyword.lower()
    csv_header = [f'Timestamp taken at {timetaken}']
    with open(f'{keyword}_{timetaken2}.csv','a+',encoding='utf-8',newline='') as f:
        arts = csv.writer(f)
        arts.writerow(csv_header)
    f.close()

#Saving in .txt
def printprintables(printables_a):
    global timetaken2
    with open(f'{keyword.lower()}_written_data_{timetaken2}.txt','w',encoding='utf-8',newline='') as dataz:
        dataz.write(printables_a)
        dataz.close()

#Differ Visual and Literature arts and then
#Determining the contents
def addappend(lists,sensiti,maturelebels):
    for i in range (len(sensiti)):
        x = str(sensiti[i])
        if 'visual art' in x:
            for q in range (10):
                if lists[q] in x:
                    arttype[q] = 'Visual Art'
                    break
        elif 'literature' in x:
            for q in range (10):
                if lists[q] in x:
                    arttype[q] = 'Literature'
                    break
        if '
May contain sensitive content
' in x:
            for p in range (10):
                if lists[p] in x:
                    maturelebels[p] = 'Suggestive'
                    break

        elif '
Sensitive content
' in x:
            for p in range (10):
                if lists[p] in x:
                    maturelebels[p] = 'Mature'
                    break

        else:
            for p in range (10):
                if lists[p] in x:
                    maturelebels[p] = 'Safe'
                    break

#To determine name change of an user
name = soup.find_all('span',{'class':'_2UI2c'})
username = str(name[0])
del name
username = username[21:]
username = username[:-7]

if keyword.lower() != username.lower():
    printables+=f'\nThis user used to be around Deviantart with the name of {keyword.lower()},\nand now their name is {username}\n'
    print(printables)
    nowname = input("Would you like to use their current name (Y/N)?>")
    if nowname == 'Y' or nowname == 'y':
        keyword = username
    else:
        keyword = keyword

keyword = keyword.upper()
#print_txt(keyword)
#exit()
printme = input("\nPrint .csv proof (Y/N)?>")

printme2 = input("\nPrint .txt proof (Y/N)?>")

printables += '\nLIST OF ALL '+ keyword +' WORKS'
printables += '\n\n'

totaldevs = soup.find_all('span',{'class':'_2USdI'})
if len(totaldevs) > 0:
    devsnumber = str(totaldevs[0])
    devsnumber = devsnumber[21:]
    devsnumber = devsnumber[:-7]
    devsnumber = int(devsnumber)
    numofdeviation = devsnumber
else:
    devsnumber = 0

if devsnumber == 0:
    print("They got no posts for now.\n")
    printables += '\nThey got no posts for now.\n'
    printprintables(printables)
    exit()

#Printing their recent art titles
while devsnumber > 0:
    url = 'https://www.deviantart.com/{}/gallery/all?page={}'.format(keyword,pagenums)
    req = requests.get(url,headers=headers)
    nd = str(req)
    soup = BeautifulSoup(req.content,'html.parser')
    questionable = 0
    straight_nsfw = 0
    maturelebels = ['']*10
    arttype = ['']*10
    #saving number of faves
    dicts = {}

    items = soup.find_all('div',{'class':'_22J_R'})
    lists = []
    for i in items:
        i=str(i)
        i = i[20:]
        i = i[:-6]
        lists.append(i)

    #For visual arts
    sensiti1 = soup.find_all('a',{'class':'_1mCeE'})
    for i in range (len(sensiti1)):
        addappend(lists,sensiti1,maturelebels)

    #For literature arts
    sensiti2 = soup.find_all('div',{'class':'_1mCeE'})
    for i in range (len(sensiti2)):
        addappend(lists,sensiti2,maturelebels)

    #Extracting URL
    linkss= soup.find_all('a',{'data-hook':'deviation_link'})
    linklists = []
    a = 0
    for i in range (len(linkss)):
        if i % 2 == 0:
            linksone = str(linkss[i])
            extractor = URLExtract()
            linksone = extractor.find_urls(linksone)
            linklists.append(linksone[0])

    #Counting Mature and Suggestive Arts
    for i in range (len(maturelebels)):
        if maturelebels[i] == 'Suggestive':
            questionable += 1
        elif maturelebels[i] == 'Mature':
            straight_nsfw += 1
        else:
            continue

    #Counting faves and storing it on a dictionary
    faves = soup.find_all('button',{'class':'_3Vvhk x48yz'})
    favelists = []

    a = 0

    for i in faves:
        try:
            faveone = faves[a].find_all('span')
        except IndexError as e:
            print("This user needs to draw more art!")
        favecount = str(faveone[1])
        favecount = favecount.strip('')
        favecount = favecount.strip('')

        if "." in favecount:
            favecount=favecount.replace(".","")
            if "K" in favecount:
                favecount=favecount.replace("K","00")
        else:
            if "K" in favecount:
                favecount=favecount.replace("K","000")

        favecountint = int(favecount)
        favelists.append(favecount)
        dicts.update({lists[a]:favecountint})
        a = a+1

    #Collecting results
    for i in range(len(favelists)):
        artes += 1
        printables+=f'\n{artes}. '+lists[i]+" - "+favelists[i]+" favorites,\n"+"Art Type: "+arttype[i]+",\nMature Level: "+maturelebels[i]+",\nLink: "+linklists[i]+"\n"
    if printme == 'Y' or printme == 'y':
        save_csv(keyword, lists,favelists,arttype,maturelebels,linklists)

    devsnumber -= 10
    pagenums += 1
    questionable_a += questionable
    straight_nsfw_a += straight_nsfw
    totaldone=numofdeviation-devsnumber
    totaldone = (totaldone*100)/numofdeviation
    totaldonewritten = "%.2f" % totaldone
    if totaldone < 100:
        print (f'\nCalculation {totaldonewritten}% done!')
    else:
        print('\nCalculation done!')
    #To give requests some delay
    time.sleep(1)

questionable_arts = questionable_a/numofdeviation
questionable_arts = questionable_arts*100
questionable_arts = "%.2f" % questionable_arts
nsfw_arts = straight_nsfw_a/numofdeviation
nsfw_arts=nsfw_arts*100
nsfw_arts = "%.2f" % nsfw_arts
printables_a = ''
printables_a += f'\nFrom {numofdeviation} total arts, \n{questionable_a} ({questionable_arts}%) are questionable, \nand {straight_nsfw_a} ({nsfw_arts}%) are not safe for work.\n'
printables_a += printables

del printables

keyword = keyword.lower()
#Displaying results
printmea = input("\nDisplay results in terminal (Y/N)?>")
if printmea == 'Y' or printmea == 'y':
    print(printables_a)

if printme == 'Y' or printme == 'y':
    save_csv2(keyword)

if printme2 == 'Y' or printme2 == 'y':
    #print_txt(keyword) #--> Only uncomment in maintenances or mods.
    printprintables(printables_a)

print("\nThank you for using this checker!\n")
exit()


import traceback

from sqlalchemy.exc import IntegrityError

from dao.DaoUtil import DaoUtil
from exceptions import NotFoundException, AlreadyExists
from model import UserInfoModel, CheckingUserInfoModel, UserModel
from run import session, db
from utils.converter import UserInfoConverter

class UserInfoDao(DaoUtil):

    def get_user_info(self, user_id, is_stable):
        try:
            if is_stable:
                user_info = session.query(UserInfoModel).filter(UserInfoModel.id == user_id).first()
            else:
                user_info = session.query(CheckingUserInfoModel).filter(CheckingUserInfoModel.id == user_id).first()
            if not user_info:
                raise NotFoundException
            return user_info
        except NotFoundException:
            raise NotFoundException
        except:
            traceback.print_exc()
        finally:
            session.close()

    def update_user_info(self, userInfoModel: UserInfoModel):
        try:
            self.session = db.session
            user_info: UserInfoModel = session.query(UserInfoModel).filter(
                UserInfoModel.userID == userInfoModel.userID).first()
            """先设置成删除然后去增加的，后面再修改"""
            if not user_info:
                raise NotFoundException
            print(user_info.__dict__)
            print(userInfoModel.__dict__)
            self.delete(user_info)
            session.add(userInfoModel)
            session.commit()
        except NotFoundException:
            raise NotFoundException
        except IntegrityError:
            raise AlreadyExists
        finally:
            session.close()

    def get_un_checking_list(self):
        user_list = session.query(UserModel).join(CheckingUserInfoModel).all()
        session.close()
        return user_list

    def check_user_info(self, user_id, result):
        if result:
            """把一个数据库中的数据直接拿到另一个数据库中"""
            user_info = session.query(CheckingUserInfoModel).filter(CheckingUserInfoModel.id == user_id).first()
            if not user_info:
                raise NotFoundException
            current_info = session.query(UserInfoModel).filter(UserInfoModel.id == user_id).first()
            if current_info:
                self.delete(current_info)
            self.insert(UserInfoConverter().mockToReal(user_info))
            self.delete(user_info)
            self.session.commit()
        else:
            session.query(CheckingUserInfoModel).filter(CheckingUserInfoModel.id == user_id) \
                .update(dict(isReject=True))
            # user_info.isRejected = True
            self.session.commit()
        user_info = [i[0] for i in session.query(CheckingUserInfoModel.id).all()]
        session.close()
        return user_info

    def get_all(self):
        try:
            user_info = session.query(UserInfoModel).all()
            return user_info
        except:
            traceback.print_exc()
        finally:
            session.close()


# 使用OpenCV进行验证码缺口识别
# 阈值与图片本身有直接关系
import cv2

GAUSSIAN_BLUR_KERNEL_SIZE = (5, 5)
GAUSSIAN_BLUR_SIGMA_X = 0
CANNY_THRESHOLD1 = 200
CANNY_THRESHOLD2 = 450

def get_gaussian_blur_image(image):  # 对图片进行高斯滤波处理并返回
    return cv2.GaussianBlur(image, GAUSSIAN_BLUR_KERNEL_SIZE, GAUSSIAN_BLUR_SIGMA_X)

def get_canny_image(image):  # 返回边缘检测后图片信息
    return cv2.Canny(image, CANNY_THRESHOLD1, CANNY_THRESHOLD2)

def get_contours(image):  # 返回提取的轮廓信息
    contours, _ = cv2.findContours(image, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    return contours

def get_contour_area_threshold(image_width, image_height):  # 计算轮廓面积上下限
    contour_area_min = (image_width * 0.15) * (image_height * 0.25) * 0.8
    contour_area_max = (image_width * 0.15) * (image_height * 0.25) * 1.2
    return contour_area_min, contour_area_max

def get_arc_length_threshold(image_width, image_height):  # 计算轮廓周长上下限
    arc_length_min = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 0.8
    arc_length_max = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 1.2
    return arc_length_min, arc_length_max

def get_offset_threshold(image_width):  # 计算缺口位置偏移量上下限
    offset_min = 0.2 * image_width
    offset_max = 0.85 * image_width
    return offset_min, offset_max

def main():
    image_row = cv2.imread(r'E:\Mydata\picture\captcha.png')
    image_height, image_width, _ = image_row.shape
    # print(image_height, image_width)
    image_gaussian_blur = get_gaussian_blur_image(image_row)
    image_canny = get_canny_image(image_gaussian_blur)
    contours = get_contours(image_canny)
    cv2.imwrite(r'E:\Mydata\picture\image_canny.png', image_canny)
    cv2.imwrite(r'E:\Mydata\picture\image_gaussian_blur.png', image_gaussian_blur)
    contour_area_min, contour_area_max = get_contour_area_threshold(image_width, image_height)
    arc_length_min, arc_length_max = get_arc_length_threshold(image_width, image_height)
    offset_min, offset_max = get_offset_threshold(image_width)
    offset = None
    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)
        if contour_area_min < cv2.contourArea(contour) < contour_area_max and \
                arc_length_min < cv2.arcLength(contour, True) < arc_length_max and \
                offset_min < x < offset_max:
            cv2.rectangle(image_row, (x, y), (x + w, y + h), (0, 0, 255), 2)
            offset = x
            # print(offset)
    cv2.imwrite(r'E:\Mydata\picture\image_label.png', image_row)
    print('offset', offset)

if __name__ == "__main__":
    main()


from connect_db import connect_db_on, connect_db_off
from humans_friends import Animals

print('Меню:')
print('1. Добавление животного\n2. Определение класса\n3. Просмотр команд\n4. Обучение команде\n0. Выход ')

animal = Animals
print(connect_db_on())
flag = True
while flag:
    n = int(input("Введите номер меню: "))
    if n == 1:
        animal.add_animals()
    elif n == 2:
        animal.define_class()
    elif n == 3:
        animal.view_commands()
    elif n == 4:
        animal.train_command()
    elif n == 0:
        print(connect_db_off())
        flag = False
    else:
        print('Неверно введен номер меню')


from __future__ import annotations

import json
import math
from typing import List, Dict, Tuple

import settings
from objects.ast.models import SlcoModel, Transition, StateMachine, State
from rendering.java.renderer import JavaModelRenderer

class MeasurementsModelRenderer(JavaModelRenderer):
    """
    Create a subclass of the Java model renderer that provides the required information to perform measurements.
    """

    def __init__(self):
        super().__init__()

        # Support variables.
        self.abbreviation_mapping: Dict[Tuple, str] = dict()

    # LOG STATEMENTS.
    def get_decision_structure_identifier(self, model: State):
        """Get a string with which decision structures can be identified in the logs."""
        return self.abbreviation_mapping[(self.current_class, self.current_state_machine, model)]

    def get_transition_identifier(self, model: Transition):
        """Get a string with which transitions can be identified in the logs."""
        return self.abbreviation_mapping[(self.current_class, self.current_state_machine, model)]

    def render_measurement_statement(self, model: str) -> str:
        """Render a statement that performs the measurement action."""
        return ""

    def get_transition_call_opening_body(self, model: Transition) -> str:
        result = super().get_transition_call_opening_body(model)
        return self.join_with_strip([
                result,
                self.render_measurement_statement(f"{self.get_transition_identifier(model)}.O")
            ])

    def get_transition_call_success_closing_body(self, model: Transition) -> str:
        result = super().get_transition_call_success_closing_body(model)
        return self.join_with_strip([
                result,
                self.render_measurement_statement(f"{self.get_transition_identifier(model)}.S"),
                self.render_measurement_statement(f"{self.get_decision_structure_identifier(model.source)}.S")
            ])

    def get_transition_call_failure_closing_body(self, model: Transition) -> str:
        result = super().get_transition_call_failure_closing_body(model)
        return result
        # TODO: Removed, since failure data can be derived from the other two entries. Furthermore, it creates an
        #  imbalance in the number of log actions each path through the program has to take.
        # return self.join_with_strip([
        #         result,
        #         self.render_measurement_statement(f"{self.get_transition_identifier(model)}.F")
        #     ])

    def get_decision_structure_opening_body(self, model: StateMachine, state: State) -> str:
        result = super().get_decision_structure_opening_body(model, state)
        return self.join_with_strip([
                result,
                self.render_measurement_statement(f"{self.get_decision_structure_identifier(state)}.O")
            ])

    def get_decision_structure_closing_body(self, model: StateMachine, state: State) -> str:
        result = super().get_decision_structure_closing_body(model, state)
        return result
        # TODO: Removed, since failure data can be derived from the other two entries. Furthermore, it creates an
        #  imbalance in the number of log actions each path through the program has to take.
        # return self.join_with_strip([
        #         result,
        #         self.render_measurement_statement(f"{self.get_decision_structure_identifier(state)}.F")
        #     ])

    def add_support_data(self, model: SlcoModel) -> None:
        """Add the data needed to render log messages in an uniform and equally impactful manner."""
        # The goal is to have all log messages within the model be of the same length, with the primary aim being that
        # all transitions should be impacted to an equal degree by the logging.
        transition_ids = dict()
        decision_structure_ids = dict()
        for c in model.classes:
            for sm in c.state_machines:
                for state in sm.states:
                    decision_structure_ids[(c, sm, state)] = f"{len(decision_structure_ids)}"
                    for t in sm.state_to_transitions[state]:
                        transition_ids[(c, sm, t)] = f"{len(transition_ids)}"

        # Ensure that all abbreviations are of equal length.
        length = max([len(v) for v in transition_ids.values()] + [len(v) for v in decision_structure_ids.values()])
        for key, value in transition_ids.items():
            self.abbreviation_mapping[key] = "T" + value.zfill(length)
        for key, value in decision_structure_ids.items():
            self.abbreviation_mapping[key] = "D" + value.zfill(length)

    def get_model_information(self, model: SlcoModel) -> str:
        """Represent the model as a json object."""
        # Create a structure of dictionaries and convert it to json.
        classes = dict()
        for c in model.classes:
            state_machines = dict()
            for sm in c.state_machines:
                decision_structures = dict()
                for state in sm.states:
                    transitions = dict()
                    for t in sm.state_to_transitions[state]:
                        transitions[t.id] = {
                            "name": str(t),
                            "id": self.abbreviation_mapping[(c, sm, t)],
                            "source": t.source.name,
                            "target": t.target.name,
                            "priority": t.priority,
                            "is_excluded": t.is_excluded
                        }
                    decision_structures[state.name] = {
                        "source": state.name,
                        "id": self.abbreviation_mapping[(c, sm, state)],
                        "transitions": transitions
                    }
                state_machines[sm.name] = {
                    "name": sm.name,
                    "states": [s.name for s in sm.states],
                    "decision_structures": decision_structures
                }
            classes[c.name] = {
                "name": c.name,
                "state_machines": state_machines
            }
        data = {
            "name": model.name,
            "settings": settings.original_arguments,
            "classes": classes
        }

        return json.dumps(data).replace("\"", "\\\"")

    def render_model(self, model: SlcoModel) -> str:
        # Add the data needed to abbreviate object names.
        self.add_support_data(model)
        return super().render_model(model)


import json
from argparse import ArgumentParser

if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument("in_file")
    args = parser.parse_args()

    any_masks = 0
    total_read = 0
    mask_lens = []
    tok_lens = []
    mask_prop = []

    with open(args.in_file) as f:
        for line in f:
            instance = json.loads(line)
            total_read += 1

            tok_lens.append(len(instance["original_claim"].split()))
            if len(instance["master_explanation"]):
                any_masks += 1
                mask_lens.append(len(instance["master_explanation"]))
                mask_prop.append(mask_lens[-1] / tok_lens[-1])

        print(f"Read {total_read} instances, of which {any_masks} have masks")
        print("Average mask length is: ", sum(mask_lens) / len(mask_lens))
        print("Average mask prop is: ", sum(mask_prop) / len(mask_prop))
        print("Average tok length is: ", sum(tok_lens) / len(tok_lens))

        print()


# This sketch draws a grid over the window, spaced by 100 pixels
# Youtube Playlist:
# https://www.youtube.com/playlist?list=PL5eh956CtlEQxk52EOq53pjz8qWZjcvWk

size(1400, 900)
x = 0
y = 0

while x <= width:
    line(x, 0, x, height)
    x = x + 100

while y <= height:
    line(0, y, width, y)
    y = y + 100


from __future__ import unicode_literals
from functools import wraps
from prompt_toolkit.filters import CLIFilter, Always

def create_handle_decorator(registry, filter=Always()):
    """
    Create a key handle decorator, which is compatible with `Registry.handle`
    but has a `save_before` option, which will make sure that undo changes are
    saved to the undo stack of the `Buffer` object before every key press
    event.
    """
    assert isinstance(filter, CLIFilter)

    def handle(*keys, **kw):
        safe_before = kw.pop('save_before', True)

        # Chain the given filter to the filter of this specific binding.
        if 'filter' in kw:
            kw['filter'] = kw['filter'] & filter
        else:
            kw['filter'] = filter

        def decorator(handler_func):
            @registry.add_binding(*keys, **kw)
            @wraps(handler_func)
            def wrapper(event):
                if safe_before:
                    event.cli.current_buffer.save_to_undo_stack()
                handler_func(event)
            return handler_func
        return decorator
    return handle