import pygame
import random
import numpy as np

class MainGame():
    window= None
    screenheight=600
    screenwidth=800
    color=pygame.Color(0,0,0)
    fontcolor=pygame.Color(255,0,0)
    tankp1=None
    enemytank_list=[]
    enemytank_count=10
    Bullet_list=[]
    enemybullet_list=[]
    explode_list=[]
    wall_list=[]
    sensor_b=None
    sensor_s=None
    sensor_r=None
    sensor_l=None
    def __init__(self):
        pygame.display.init()
        MainGame.window=pygame.display.set_mode([MainGame.screenwidth,MainGame.screenheight])
        self.reset()
        music = Music("tankimages/music.mp3")
        #music.play(-1)
        pygame.display.set_caption("TAMO V1.0")

#divide between init and play step
    def play_step(self,action):
        self.frame_iteration+=1
        self.getevent()
        MainGame.window.fill(MainGame.color)
        MainGame.window.blit(self.gettextsurface("the remaining tank number is %d"%len(MainGame.enemytank_list)),(5,5))
        self.blitwalls()
        self.blitmytank()
        self.blitsensors()
        MainGame.tankp1.move(action)
        self.blitenemytank()
        self.score = 10 - len(MainGame.enemytank_list)
        reward=0
        game_over=False
        self.blitbullet()
        self.blitenemybullet()
        self.displayexplodes()
        if len(MainGame.enemytank_list)==0:
            game_over=True
        for ebullet in MainGame.enemybullet_list:
            if ebullet.hitmytank():
                game_over=True
                reward=-10
                return reward,game_over,self.score
        for bullet in MainGame.Bullet_list:
            if bullet.hitenemytank():
                reward=10
        pygame.display.update()
        return reward,game_over,self.score

    def reset(self):
        for etank in MainGame.enemytank_list:
            etank.live = False
        for walls in MainGame.wall_list:
            walls.live = False
        for enemybullet in MainGame.enemybullet_list:
            enemybullet.live= False
        self.create_mytank()
        self.create_enemytank()
        self.create_walls()
        self.score=0
        self.frame_iteration = 0
        self.create_sensors()

    def create_enemytank(self):
        top=100
        for i in range(MainGame.enemytank_count):
            speed = random.randint(3, 6)
            left = random.randint(100, 700)
            etank=EnemyTank(left,top,speed)
            MainGame.enemytank_list.append(etank)
    def create_walls(self):
        for i in range(1,7):
            wall=Wall(120*i,300)
            MainGame.wall_list.append(wall)
    def create_sensors(self):
        MainGame.sensor_b=Sensor_back()
        MainGame.sensor_s=Sensor_straight()
        MainGame.sensor_r=Sensor_right()
        MainGame.sensor_l=Sensor_left()

    def blitwalls(self):
        for wall in MainGame.wall_list:
            if wall.live:
                wall.displaywall()
            else:
                MainGame.wall_list.remove(wall)
    def blitmytank(self):
        if MainGame.tankp1 and MainGame.tankp1.live:
            MainGame.tankp1.displaytank()
            MainGame.tankp1.hitwalls()
            MainGame.tankp1.hitenemytank()
            bullet=Bullet(MainGame.tankp1)
            if len(MainGame.Bullet_list) < 5:
                MainGame.Bullet_list.append(bullet)
        else:
            del MainGame.tankp1
            MainGame.tankp1=None
    def create_mytank(self):
        MainGame.tankp1 = Mytank(600, 200)
        #music=Music('tankimages/music.mp3')
        #music.play()

    def blitenemytank(self):
        for etank in MainGame.enemytank_list:
            if etank.live:
                etank.displaytank()
                etank.randmove()
                etank.hitwalls()
                etank.hitmytank()
                ebullet=etank.shot()
                if ebullet and len(MainGame.enemybullet_list)<5*self.enemytank_count:
                    MainGame.enemybullet_list.append(ebullet)
            else:
                MainGame.enemytank_list.remove(etank)
    def blitbullet(self):
        for bullet in MainGame.Bullet_list:
            if bullet.live:
                bullet.displaybullet()
                bullet.bulletmove()
                bullet.hitenemytank()
                bullet.hitwalls()
            else:
                MainGame.Bullet_list.remove(bullet)
    def blitenemybullet(self):
        for ebullet in MainGame.enemybullet_list:
            if ebullet.live:
                ebullet.displaybullet()
                ebullet.bulletmove()
                ebullet.hitwalls()
                #ebullet.hitsensors()
                if MainGame.tankp1 and MainGame.tankp1.live:
                    ebullet.hitmytank()
            else:
                MainGame.enemybullet_list.remove(ebullet)
    def displayexplodes(self):
        for explode in MainGame.explode_list:
            if explode.live:
                explode.displayexplode()
            else:
                MainGame.explode_list.remove(explode)

    def blitsensors(self):
        MainGame.sensor_b.displaysensor()
        MainGame.sensor_l.displaysensor()
        MainGame.sensor_r.displaysensor()
        MainGame.sensor_s.displaysensor()

    def is_collision_stright(self):
        if len(MainGame.enemybullet_list) >= 1:
            for enemy_bullet in MainGame.enemybullet_list:
                if pygame.sprite.collide_rect(MainGame.sensor_s, enemy_bullet):
                    return True
        return False

    def is_collision_back(self):
        if len(MainGame.enemybullet_list) >= 1:
            for enemy_bullet in MainGame.enemybullet_list:
                if pygame.sprite.collide_rect(MainGame.sensor_b, enemy_bullet):
                    return True
        return False

    def is_collision_left(self):
        if len(MainGame.enemybullet_list)>=1:
            for enemy_bullet in MainGame.enemybullet_list:
                if pygame.sprite.collide_rect(MainGame.sensor_l, enemy_bullet):
                    return True
        return False

    def is_collision_right(self):
        if len(MainGame.enemybullet_list) >= 1:
            for enemy_bullet in MainGame.enemybullet_list:
                if pygame.sprite.collide_rect(MainGame.sensor_r, enemy_bullet):
                    return True
        return False

    def determine_enemy_up(self):
        if len(MainGame.enemytank_list) >= 1:
            for etank in MainGame.enemytank_list:
                if etank.rect.top>MainGame.tankp1.rect.top:
                    return True
        return False

    def determine_enemy_down(self):
        if len(MainGame.enemytank_list) >= 1:
            for etank in MainGame.enemytank_list:
                if etank.rect.top<MainGame.tankp1.rect.top:
                    return True
        return False

    def determine_enemy_left(self):
        if len(MainGame.enemytank_list)>=1:
            for etank in MainGame.enemytank_list:
                if etank.rect.left<MainGame.tankp1.rect.left:
                    return True
        return False

    def determine_enemy_right(self):
        if len(MainGame.enemytank_list) >= 1:
            for etank in MainGame.enemytank_list:
                if etank.rect.left>MainGame.tankp1.rect.left:
                    return True
        return False
    def endgame(self):
        print("thanks for playing")
        exit()
    def getevent(self):
        eventlist = pygame.event.get()
        for event in eventlist:
            if event.type == pygame.QUIT:
                self.endgame()

    def gettextsurface(self,text):
        pygame.font.init()
        font=pygame.font.SysFont("laoui",18)
        textsurface=font.render(text,True,MainGame.fontcolor)
        return textsurface
class Baseitem(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__()

class Tank(Baseitem):
    def __init__(self,left,top):
        self.images = {
            "U":pygame.image.load('tankimages/tankup.jpg'),
            "D":pygame.image.load('tankimages/tankdown.jpg'),
            "L":pygame.image.load('tankimages/tankleft.jpg'),
            "R":pygame.image.load('tankimages/tankright.jpg')
        }
        self.direction = "U"
        self.image =self.images[self.direction]
        self.rect=self.image.get_rect()
        self.rect.left=left
        self.rect.top=top
        self.speed=5
        self.stop=True
        self.live=True
        self.oldleft=self.rect.left
        self.oldtop=self.rect.top

    def move(self,action):
        self.oldleft = self.rect.left
        self.oldtop = self.rect.top
        clock_wise = ["R", "D", "L", "U"]
        idx = clock_wise.index(self.direction)

        if np.array_equal(action, [1, 0, 0, 0]):
            new_dir = clock_wise[idx]  # no change
        elif np.array_equal(action, [0, 1, 0, 0]):
            next_idx = (idx + 1) % 4
            new_dir = clock_wise[next_idx]  # right turn r -> d -> l -> u
        elif np.array_equal(action, [0, 0, 1, 0]):
            next_idx = (idx - 1) % 4
            new_dir = clock_wise[next_idx]  # left turn r -> u -> l -> d
        elif np.array_equal(action, [0, 0, 0, 1]):
            next_idx = (idx + 2) % 4
            new_dir = clock_wise[next_idx]

        self.direction = new_dir
        if self.direction=="L":
            if self.rect.left>0:
                self.rect.left-=self.speed
        elif self.direction=="R":
            if self.rect.left+self.rect.height<MainGame.screenwidth:
                self.rect.left+=self.speed
        elif self.direction=="U":
            if self.rect.top>0:
                self.rect.top-=self.speed
        elif self.direction=="D":
            if self.rect.top+self.rect.height<MainGame.screenheight:
                self.rect.top+=self.speed
    def stay(self):
        self.rect.left=self.oldleft
        self.rect.top=self.oldtop
    def hitwalls(self):
        for wall in MainGame.wall_list:
            if pygame.sprite.collide_rect(wall,self):
                self.stay()

    def shot(self):
        num=random.randint(1,100)
        if num>=10:
            return Bullet(self)
    def displaytank(self):
        self.image = self.images[self.direction]
        MainGame.window.blit(self.image,self.rect)

class Mytank(Tank):
    def __init__(self,left,top):
        super(Mytank,self).__init__(left,top)
    def hitenemytank(self):
        for etank in MainGame.enemytank_list:
            if pygame.sprite.collide_rect(etank,self):
                self.stay()

class Sensor_straight():
    def __init__(self):
        self.direction=MainGame.tankp1.direction
        self.sensorimages={
            "U":pygame.image.load('tankimages/36x216.png'),
            "D":pygame.image.load('tankimages/36x216.png'),
            "L":pygame.image.load('tankimages/210x35.png'),
            "R":pygame.image.load('tankimages/210x35.png')
        }
        self.sensorimage = self.sensorimages[self.direction]
        self.rect = self.sensorimage.get_rect()

    def displaysensor(self):
        if self.direction=="L":
            self.rect.left=MainGame.tankp1.rect.left-210
            self.rect.top=MainGame.tankp1.rect.top
        elif self.direction=="R":
            self.rect.left = MainGame.tankp1.rect.left + 36
            self.rect.top = MainGame.tankp1.rect.top
        elif self.direction=="U":
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top-216
        else:
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top +38
        MainGame.window.blit(self.sensorimage, self.rect)

class Sensor_back():
    def __init__(self):
        self.direction = MainGame.tankp1.direction
        self.sensorimages = {
            "U": pygame.image.load('tankimages/36x216.png'),
            "D": pygame.image.load('tankimages/36x216.png'),
            "L": pygame.image.load('tankimages/210x35.png'),
            "R": pygame.image.load('tankimages/210x35.png')
        }
        self.sensorimage = self.sensorimages[self.direction]
        self.rect = self.sensorimage.get_rect()

    def displaysensor(self):
        if self.direction=="L":
            self.rect.left=MainGame.tankp1.rect.left+36
            self.rect.top=MainGame.tankp1.rect.top
        elif self.direction=="R":
            self.rect.left = MainGame.tankp1.rect.left - 210
            self.rect.top = MainGame.tankp1.rect.top
        elif self.direction=="U":
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top+38
        else:
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top -216
        MainGame.window.blit(self.sensorimage, self.rect)

class Sensor_left():
    def __init__(self):
        self.direction = MainGame.tankp1.direction
        self.sensorimages = {
            "U": pygame.image.load('tankimages/228x38.png'),
            "D": pygame.image.load('tankimages/228x38.png'),
            "L": pygame.image.load('tankimages/36x216.png'),
            "R": pygame.image.load('tankimages/36x216.png')
        }
        self.sensorimage = self.sensorimages[self.direction]
        self.rect = self.sensorimage.get_rect()

    def displaysensor(self):
        if self.direction == "L":
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top - 35
        elif self.direction == "R":
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top + 216
        elif self.direction == "U":
            self.rect.left = MainGame.tankp1.rect.left + 228
            self.rect.top = MainGame.tankp1.rect.top
        else:
            self.rect.left = MainGame.tankp1.rect.left - 36
            self.rect.top = MainGame.tankp1.rect.top
            MainGame.window.blit(self.sensorimage, self.rect)

class Sensor_right():
    def __init__(self):
        self.direction = MainGame.tankp1.direction
        self.sensorimages = {
            "U": pygame.image.load('tankimages/228x38.png'),
            "D": pygame.image.load('tankimages/228x38.png'),
            "L": pygame.image.load('tankimages/36x216.png'),
            "R": pygame.image.load('tankimages/36x216.png')
        }
        self.sensorimage = self.sensorimages[self.direction]
        self.rect = self.sensorimage.get_rect()

    def displaysensor(self):
        if self.direction == "L":
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top + 216
        elif self.direction == "R":
            self.rect.left = MainGame.tankp1.rect.left
            self.rect.top = MainGame.tankp1.rect.top - 35
        elif self.direction == "U":
            self.rect.left = MainGame.tankp1.rect.left - 36
            self.rect.top = MainGame.tankp1.rect.top
        else:
            self.rect.left = MainGame.tankp1.rect.left + 228
            self.rect.top = MainGame.tankp1.rect.top
            MainGame.window.blit(self.sensorimage, self.rect)

class EnemyTank(Tank):
    def __init__(self,left,top,speed):
        self.images = {
            "U": pygame.image.load('tankimages/enemytankup.jpg'),
            "D": pygame.image.load('tankimages/enemytankdown.jpg'),
            "L": pygame.image.load('tankimages/enemytankleft.jpg'),
            "R": pygame.image.load('tankimages/enemytankright.jpg')
        }
        self.direction = self.randirection()
        self.image = self.images[self.direction]
        self.rect = self.image.get_rect()
        self.rect.left = left
        self.rect.top = top
        self.speed = speed
        self.stop = True
        self.step=20
        self.live=True
    def randirection(self):
        num=random.randint(1,4)
        if num==1:
            return"U"
        elif num==2:
            return"D"
        elif num==3:
            return"L"
        elif num==4:
            return"R"
    def randmove(self):
        if self.step <=0:
            self.direction=self.randirection()
            self.step=20
        else:
            self.move()
            self.step-=1
    def move(self):
        self.oldleft = self.rect.left
        self.oldtop = self.rect.top
        if self.direction=="L":
            if self.rect.left>0:
                self.rect.left-=self.speed
        elif self.direction=="R":
            if self.rect.left+self.rect.height<MainGame.screenwidth:
                self.rect.left+=self.speed
        elif self.direction=="U":
            if self.rect.top>0:
                self.rect.top-=self.speed
        elif self.direction=="D":
            if self.rect.top+self.rect.height<MainGame.screenheight:
                self.rect.top+=self.speed
    def shot(self):
        num=random.randint(1,100)
        if num <=10:
            return Bullet(self)
    def hitmytank(self):
        if MainGame.tankp1 and MainGame.tankp1.live:
            if pygame.sprite.collide_rect(self,MainGame.tankp1):
                self.stay()

class Bullet(Baseitem):
    def __init__(self,tank):
        self.image=pygame.image.load("tankimages/bullet.jpg")
        self.direction=tank.direction
        self.rect=self.image.get_rect()
        self.speed=15
        if self.direction=="U":
            self.rect.left=tank.rect.left+tank.rect.width/2-self.rect.width/2
            self.rect.top=tank.rect.top-self.rect.height
        elif self.direction=="D":
            self.rect.left = tank.rect.left + tank.rect.width / 2  -self.rect.width/2
            self.rect.top=tank.rect.top + tank.rect.height

        elif self.direction=="L":
            self.rect.left=tank.rect.left-self.rect.width
            self.rect.top = tank.rect.top + tank.rect.width / 2  -self.rect.width/2

        elif self.direction=="R":
            self.rect.left = tank.rect.left + tank.rect.width
            self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2
        self.live=True

    def bulletmove(self):
        if self.direction== "U":
           if self.rect.top>0:
               self.rect.top-=self.speed
           else:
               self.live=False
        elif self.direction=="D":
            if self.rect.top<=MainGame.screenheight-self.rect.height:
                self.rect.top+=self.speed
            else:
                self.live = False

        elif self.direction=="L":
            if self.rect.left>0:
                self.rect.left-=self.speed
            else:
                self.live = False
        elif self.direction=="R":
            if self.rect.left<=MainGame.screenwidth-self.rect.width:
                self.rect.left+=self.speed
            else:
                self.live = False
    def displaybullet(self):
        MainGame.window.blit(self.image,self.rect)
    def hitenemytank(self):
        for etank in MainGame.enemytank_list:
            if pygame.sprite.collide_rect(etank,self):
                explode=Explode(etank)
                MainGame.explode_list.append(explode)
                self.live=False
                etank.live=False
                return True
    def hitmytank(self):
        if pygame.sprite.collide_rect(MainGame.tankp1, self):
            explode=Explode(MainGame.tankp1)
            MainGame.explode_list.append(explode)
            self.live=False
            MainGame.tankp1.live=False
            return True
    def hitwalls(self):
        for wall in MainGame.wall_list:
            if pygame.sprite.collide_rect(wall,self):
                self.live=False
                wall.hp-=1
                if wall.hp<=0:
                    wall.live=False
    #def hitsensors(self):
        #if pygame.sprite.collide_rect(MainGame.sensor_s,self):
            #explode = Explode(MainGame.sensor_s)
            #MainGame.explode_list.append(explode)
        #if pygame.sprite.collide_rect(MainGame.sensor_b,self):
            #explode = Explode(MainGame.sensor_b)
            #MainGame.explode_list.append(explode)
        #if pygame.sprite.collide_rect(MainGame.sensor_l,self):
            #explode = Explode(MainGame.sensor_l)
            #MainGame.explode_list.append(explode)
        #if pygame.sprite.collide_rect(MainGame.sensor_r,self):
            #explode = Explode(MainGame.sensor_r)
            #MainGame.explode_list.append(explode)

class Explode():
    def __init__(self,tank):
        self.rect=tank.rect
        self.step=0
        self.images=[                   pygame.image.load('tankimages/blast0.jpg'),
                                        pygame.image.load('tankimages/blast1.jpg'),
                                        pygame.image.load('tankimages/blast2.jpg'),
                                        pygame.image.load('tankimages/blast3.jpg'),
                                        pygame.image.load('tankimages/blast4.jpg')
                                        ]
        self.image=self.images[self.step]
        self.live=True
    def displayexplode(self):
        if self.step<len(self.images):
            MainGame.window.blit(self.image,self.rect)
            self.image=self.images[self.step]
            self.step+=1
        else:
            self.live=False
            self.step=0
class Wall():
    def __init__(self,left,top):
        self.image=pygame.image.load("tankimages/REDAIWALL.png")
        self.rect=self.image.get_rect()
        self.rect.left=left
        self.rect.top=top
        self.live=True
        self.hp=50
    def displaywall(self):
        MainGame.window.blit(self.image,self.rect)
class Music():
    def __init__(self,filename):
        pygame.mixer.init()
        self.filename=filename
        pygame.mixer.music.load(self.filename)
    def play(self,loops):
        pygame.mixer.music.play(loops)


from functools import lru_cache
from typing import Literal, Dict

import orjson
from django.conf import settings
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from rest_framework import generics
from rest_framework.exceptions import PermissionDenied
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from check import models
from check.logging import log
from check.permissions import profile_permission
from devicemanager.device import Interfaces
from devicemanager.remote.exceptions import InvalidMethod
from net_tools.models import VlanName, DevicesInfo
from ..decorators import except_connection_errors
from ..permissions import DevicePermission
from ..serializers import (
    InterfacesCommentsSerializer,
    ADSLProfileSerializer,
    PortControlSerializer,
    PoEPortStatusSerializer,
)
from ..swagger import schemas

@method_decorator(schemas.port_control_api_doc, name="post")  # API DOC
@method_decorator(profile_permission(models.Profile.REBOOT), name="dispatch")
class PortControlAPIView(generics.GenericAPIView):
    permission_classes = [IsAuthenticated, DevicePermission]
    serializer_class = PortControlSerializer

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.port_desc = ""

    @except_connection_errors
    def post(self, request, device_name: str):
        """
        ## Изменяем состояние порта оборудования
        """

        # Проверяем данные, полученные в запросе, с помощью сериализатора.
        serializer: PortControlSerializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        port_status: Literal["up", "down", "reload"] = serializer.validated_data["status"]
        port_name: str = serializer.validated_data["port"]
        save_config: bool = serializer.validated_data["save"]

        model_dev = get_object_or_404(models.Devices, name=device_name)

        # Есть ли у пользователя доступ к группе данного оборудования
        self.check_object_permissions(request, model_dev)

        # Если оборудование Недоступно
        if not model_dev.available:
            return Response({"error": "Оборудование недоступно!"}, status=500)

        # Теперь наконец можем подключиться к оборудованию :)
        session = model_dev.connect()
        # Перезагрузка порта
        if port_status == "reload":
            port_change_status = session.reload_port(
                port=port_name,
                save_config=save_config,
            )

        # UP and DOWN
        else:
            port_change_status = session.set_port(
                port=port_name,
                status=port_status,
                save_config=save_config,
            )

        if "Неверный порт" in port_change_status:
            return Response({"error": port_change_status}, status=400)

        # Логи
        log(
            request.user,
            model_dev,
            f"{port_status} port {port_name} ({self.port_desc}) \n{port_change_status}",
        )

        return Response(serializer.validated_data, status=200)

    def check_object_permissions(self, request, device) -> None:
        # Смотрим интерфейсы, которые сохранены в БД
        dev_info, _ = DevicesInfo.objects.get_or_create(dev=device)

        if dev_info.interfaces is None:
            # Собираем интерфейсы с оборудования.
            interfaces = Interfaces(device.connect().get_interfaces())
        else:
            # Преобразовываем JSON строку с интерфейсами в класс `Interfaces`
            interfaces = Interfaces(orjson.loads(dev_info.interfaces))

        # Далее смотрим описание на порту, так как от этого будет зависеть, может ли пользователь управлять им
        self.port_desc: str = interfaces[self.request.data["port"]].desc

        # Если не суперпользователь, то нельзя изменять состояние определенных портов
        if not request.user.is_superuser and settings.PORT_GUARD_PATTERN.search(self.port_desc):
            raise PermissionDenied(
                detail="Запрещено изменять состояние данного порта!",
            )

        # Если недостаточно привилегий для изменения статуса порта
        if request.user.profile.perm_level < 2 and request.data["status"] in ["up", "down"]:
            # Логи
            log(
                request.user,
                device,
                f"Tried to set port {request.data['port']} ({self.port_desc}) "
                f"to the {request.data['status']} state, but was refused \n",
            )

            raise PermissionDenied(
                detail="У вас недостаточно прав, для изменения состояния порта!",
            )

@method_decorator(profile_permission(models.Profile.BRAS), name="dispatch")
class ChangeDescriptionAPIView(APIView):
    """
    ## Изменяем описание на порту у оборудования
    """

    permission_classes = [IsAuthenticated, DevicePermission]

    @except_connection_errors
    def post(self, request, device_name: str):
        """
        ## Меняем описание на порту оборудования

        Требуется передать JSON:

            {
                "port": "порт оборудования",
                "description": "новое описание порта"
            }

        Если указанного порта не существует на оборудовании, то будет отправлен ответ со статусом `400`

            {
                "detail": "Неверный порт {port}"
            }

        Если описание слишком длинное, то будет отправлен ответ со статусом `400`

            {
                "detail": "Слишком длинное описание! Укажите не более {max_length} символов."
            }

        """

        if not self.request.data.get("port"):
            raise ValidationError({"detail": "Укажите порт!"})

        dev = get_object_or_404(models.Devices, name=device_name)

        # Проверяем права доступа пользователя к оборудованию
        self.check_object_permissions(request, dev)

        new_description = self.request.data.get("description", "")
        port = self.request.data.get("port")

        set_description_status = dev.connect().set_description(port=port, desc=new_description)

        if set_description_status.max_length:
            # Если есть данные, что описание слишком длинное.
            raise ValidationError(
                {
                    "detail": "Слишком длинное описание! "
                    f"Укажите не более {set_description_status.max_length} символов."
                }
            )

        log(request.user, dev, str(set_description_status))

        if set_description_status.error:
            return Response(
                {"detail": f"{set_description_status.error}, port={port}"},
                status=400 if set_description_status.error == "Неверный порт" else 500,
            )
        # Логи

        return Response(
            {
                "description": set_description_status.description,
                "port": set_description_status.port,
                "saved": set_description_status.saved,
            }
        )

class MacListAPIView(APIView):
    permission_classes = [IsAuthenticated, DevicePermission]

    @except_connection_errors
    def get(self, request, device_name):
        """
        ## Смотрим MAC-адреса на порту оборудования

        Для этого необходимо передать порт в параметре URL `?port=eth1`

        Если порт верный и там есть MAC-адреса, то будет вот такой ответ:

            {
                "count": 47,
                "result": [
                    {
                        "vlanID": "1051",
                        "mac": "00-04-96-51-AD-3D",
                        "vlanName": ""
                    },
                    {
                        "vlanID": "1051",
                        "mac": "00-04-96-52-A5-FB",
                        "vlanName": ""
                    },
                    ...
                ]
            }

        """

        port = self.request.GET.get("port")
        if not port:
            raise ValidationError({"detail": "Укажите порт!"})

        device = get_object_or_404(models.Devices, name=device_name)
        self.check_object_permissions(request, device)

        macs = []  # Итоговый список
        for vid, mac in device.connect().get_mac(port):  # Смотрим VLAN и MAC
            macs.append(
                {
                    "vlanID": vid,
                    "mac": mac,
                    "vlanName": self.get_vlan_name(vid),
                }
            )

        return Response({"count": len(macs), "result": macs})

    @staticmethod
    @lru_cache(maxsize=35)
    def get_vlan_name(vlan_id: int) -> str:
        try:
            return VlanName.objects.get(vid=int(vlan_id)).name
        except (ValueError, VlanName.DoesNotExist):
            return ""

class CableDiagAPIView(APIView):
    permission_classes = [IsAuthenticated, DevicePermission]

    @except_connection_errors
    def get(self, request, device_name):
        """
        ## Запускаем диагностику кабеля на порту

        Для этого необходимо передать порт в параметре URL `?port=eth1`

        Функция возвращает данные в виде словаря.
        В зависимости от результата диагностики некоторые ключи могут отсутствовать за ненадобностью.

            {
                "len": "-",         # Длина кабеля в метрах, либо "-", когда не определено
                "status": "",       # Состояние на порту (Up, Down, Empty)
                "pair1": {
                    "status": "",   # Статус первой пары (Open, Short)
                    "len": "",      # Длина первой пары в метрах
                },
                "pair2": {
                    "status": "",   # Статус второй пары (Open, Short)
                    "len": "",      # Длина второй пары в метрах
                }
            }

        """

        if not request.GET.get("port"):
            raise ValidationError({"detail": "Неверные данные"})

        # Находим оборудование
        device = get_object_or_404(models.Devices, name=device_name)
        self.check_object_permissions(request, device)

        # Если оборудование недоступно
        if not device.available:
            return Response({"detail": "Device unavailable"}, status=500)

        try:
            cable_test = device.connect().virtual_cable_test(request.GET["port"])
        except InvalidMethod:
            return Response({"detail": "Unsupported for this device"}, status=400)
        else:
            return Response(cable_test)

@method_decorator(profile_permission(models.Profile.BRAS), name="dispatch")
class SetPoEAPIView(generics.GenericAPIView):
    permission_classes = [IsAuthenticated, DevicePermission]
    serializer_class = PoEPortStatusSerializer

    @except_connection_errors
    def post(self, request, device_name):
        """
        ## Устанавливает PoE статус на порту
        """

        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # Находим оборудование
        device = get_object_or_404(models.Devices, name=device_name)
        self.check_object_permissions(request, device)

        # Если оборудование недоступно
        if not device.available:
            return Response({"detail": "Device unavailable"}, status=500)

        poe_status = serializer.validated_data["status"]
        port_name = serializer.validated_data["port"]

        try:
            _, err = device.connect().set_poe_out(port_name, poe_status)
        except InvalidMethod:
            return Response({"detail": "Unsupported for this device"}, status=400)
        else:
            if not err:
                return Response({"status": poe_status})
            return Response(
                {"detail": f"Invalid data ({poe_status})"},
                status=400,
            )

class InterfaceInfoAPIView(APIView):
    permission_classes = [IsAuthenticated, DevicePermission]

    @except_connection_errors
    def get(self, request, device_name):
        """
        ## Общая информация об определенном порте оборудования

        В зависимости от типа оборудования информация будет совершенно разной

        Поле `portDetailInfo.type` указывает тип данных, которые могут быть строкой, JSON, или HTML кодом.

            {
                "portDetailInfo": {
                    "type": "text",  - Тип данных для детальной информации о порте
                    "data": ""       - Сами данные
                },
                "portConfig":   "Конфигурация порта (из файла конфигурации)",
                "portType":     "COPPER"    - (SFP, COMBO),
                "portErrors":   "Ошибки на порту",
                "hasCableDiag": true        - Имеется ли на данном типе оборудования возможность диагностики порта
            }

        """

        port = self.request.GET.get("port")
        if not port:
            raise ValidationError({"detail": "Укажите порт!"})

        device = get_object_or_404(models.Devices, name=device_name)
        self.check_object_permissions(request, device)

        # Если оборудование недоступно
        if not device.available:
            return Response({"detail": "Device unavailable"}, status=500)

        result = {}
        session = device.connect()

        result["portDetailInfo"] = session.get_port_info(port)
        result["portConfig"] = session.get_port_config(port)
        result["portType"] = session.get_port_type(port)
        result["portErrors"] = session.get_port_errors(port)
        result["hasCableDiag"] = True

        if result["portDetailInfo"].get("type") == "gpon":
            # Ищем возможные комментарии только для GPON типа
            result["portDetailInfo"]["data"]["onts_lines"] = self.create_onts_lines_with_comments(
                result["portDetailInfo"]["data"].get("onts_lines", []),
                gpon_port=port,
                device=device,
            )

        return Response(result)

    @staticmethod
    def create_onts_lines_with_comments(
            onts_lines: list, gpon_port: str, device: models.Devices
    ) -> list:
        """
        Находит комментарии созданные на ONT для порта `gpon_port` оборудования `device`.

        :param onts_lines: Текущий список данных ONT.
        :param gpon_port: Основной GPON порт.
        :param device: Оборудование, на котором надо искать комментарии.
        :return: Список данных ONT с добавлением в конец списка возможных комментариев.
        """
        if not onts_lines:
            return []

        interfaces_comments = device.interfacescomments_set.select_related("user")

        ont_interfaces_dict: Dict[str, list] = {}

        for comment in interfaces_comments:
            comment_data = {
                "text": comment.comment,
                "user": comment.user.username,
                "id": comment.id,
            }
            if ont_interfaces_dict.get(comment.interface):
                ont_interfaces_dict[comment.interface].append(comment_data)
            else:
                ont_interfaces_dict[comment.interface] = [comment_data]

        new_onts_lines = []

        for line in onts_lines:
            # Соединяем порт GPON и ONTid
            ont_full_port = f"{gpon_port}/{line[0]}"
            # Добавляем комментарии либо пустой список в конец
            new_onts_lines.append(
                line
                + [
                    ont_interfaces_dict.get(ont_full_port, []),
                ]
            )
        return new_onts_lines

@method_decorator(profile_permission(models.Profile.BRAS), name="dispatch")
class ChangeDSLProfileAPIView(APIView):
    permission_classes = [IsAuthenticated, DevicePermission]

    @except_connection_errors
    def post(self, request, device_name: str):
        """
        ## Изменяем профиль xDSL порта на другой

        Возвращаем `{ "status": status }` или `{ "error": error }`

        """

        serializer = ADSLProfileSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        device = get_object_or_404(models.Devices, name=device_name)
        self.check_object_permissions(request, device)

        # Если оборудование недоступно
        if not device.available:
            return Response({"detail": "Device unavailable"}, status=500)

        # Подключаемся к оборудованию
        session = device.connect()
        try:
            status = session.change_profile(
                serializer.validated_data["port"],
                serializer.validated_data["index"],
            )
        except InvalidMethod:
            # Нельзя менять профиль для данного устройства
            return Response({"error": "Device can't change profile"}, status=400)
        else:
            return Response({"status": status})

class CreateInterfaceCommentAPIView(generics.CreateAPIView):
    serializer_class = InterfacesCommentsSerializer

    def perform_create(self, serializer):
        dev = get_object_or_404(models.Devices, name=self.request.data.get("device"))
        serializer.save(user=self.request.user, device=dev)

class InterfaceCommentAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = models.InterfacesComments.objects.all()
    serializer_class = InterfacesCommentsSerializer
    lookup_field = "pk"
    lookup_url_kwarg = "pk"


#!/usr/bin/python3
"""Send a request to http://0.0.0.0:5000/search_user"""
import requests
import sys

def search_user(letter):
    url = "http://0.0.0.0:5000/search_user"
    data = {"q": letter}
    response = requests.post(url, data=data)
    try:
        if response.json():
            print("[{}] {}".format(response.json().get("id"), response.json().get("name")))
        else:
            print("No result")
    except ValueError:
        print("Not a valid JSON")

if __name__ == "__main__":
    try:
        letter = sys.argv[1]
    except IndexError:
        letter = ""
    search_user(letter)


#!/usr/bin/env python

"""
Created by Cyber Security Diskominfo Kota Serang (https://cybersec.serangkota.go.id):
- malwareslayer
- ahdihaikal
- gagaltotal
- Sorong6etar
Dinas Komunikasi Dan Informatika Kota Serang (https://kominfo.serangkota.go.id)
"""

import asyncio
import re
import os
import sys
import json
import pytesseract

from aiohttp import ClientSession
from typing import (
    AnyStr,
    Dict,
    Optional
)

from lib.core.enums import PRIORITY
from lib.core.data import logger

class OCRTamper:
    __default_image_path = "/tmp/captcha.png"
    __default_headers_path = "/tmp/headers"
    __results = None

    def __init__(self, data: json.loads):
        self.images: AnyStr = data["images"]
        self.parameters: AnyStr = data["parameters"]
        self.headers: Dict[AnyStr] = data["headers"]
        self.mode: AnyStr = data["mode"]

        try:
            self.config: Optional[Dict[AnyStr]] = data["config"]
        except json.decoder.JSONDecodeError:
            logger.debug("Something error in config, ignore")
            self.config: Optional[Dict[AnyStr]] = None

        asyncio.get_event_loop().run_until_complete(self.run())

    def __del__(self):
        os.remove(self.__default_image_path)

    async def run(self):
        await self.downloadimage()
        if self.mode == 'math':
            expressions = await self.tesseractloads
            self.__results = await self.calculate(expressions)
        elif self.mode == 'character':
            self.__results = await self.preview
        elif self.mode == 'easy':
            self.__results = await self.tesseractloads
        else:
            logger.critical("input either math, character or easy")
            exit(1)

    async def calculate(self, expressions) -> AnyStr:
        expressions = re.search(r'\d+[-+x*/]\d+', expressions)

        try:
            expressions = re.findall(r'(\d+|[^ 0-9])', expressions.group(0))
        except AttributeError:
            logger.debug("cannot detected by tesseract trying manual")
            return await self.preview

        if expressions[1] == 'x':
            expressions[1] = '*'
        elif expressions[1] == '/':
            expressions[1] = '//'

        expressions = ''.join(expressions)
        results = str(eval(expressions))

        logger.info(f"{expressions} = {results}")

        return "&" + self.parameters + "=" + results

    async def downloadimage(self) -> None:
        async with ClientSession(headers=self.headers) as Session:
            async with Session.get(self.images) as Response:
                image = await Response.read()
                with open(self.__default_image_path, "wb+") as file:
                    file.write(image)

    @property
    def result(self) -> AnyStr:
        return self.__results

    @property
    async def preview(self) -> AnyStr:
        catimg = await asyncio.create_subprocess_shell(
            f"catimg {self.__default_image_path}",
            stdin=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)

        stdout, stderr = await catimg.communicate()

        if stderr:
            raise stderr
        else:
            return "&" + self.parameters + "=" + input("Real Captcha ? : ")

    @property
    async def tesseractloads(self) -> pytesseract.image_to_string:

        try:
            if self.config is None:
                convert = "-colorspace Gray -blur 3x1 -morphology Erode Diamond:1x4 -level 20%"
            else:
                convert = self.config["convert"]
        except KeyError:
            convert = "-colorspace Gray -blur 3x1 -morphology Erode Diamond:1x4 -level 20%"

        imagick = await asyncio.create_subprocess_shell(
            f"""
            convert {self.__default_image_path} \
            {convert}                           \
            {self.__default_image_path}
            """,
            stdin=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)

        stdout, stderr = await imagick.communicate()

        try:
            if self.config is None:
                tesseract = None
            else:
                tesseract = self.config["tesseract"]
        except KeyError:
            tesseract = None

        if stderr:
            raise stderr
        else:
            return pytesseract.image_to_string(self.__default_image_path, config=tesseract)

__priority__ = PRIORITY.HIGHEST

baseargv = sys.argv.index(os.path.basename(__file__).split(".")[0])

filejsonpath: str = ""
filejson: str = ""
datajson: json.loads = None

try:
    filejsonpath: str = sys.argv[baseargv + 1]
except IndexError:
    logger.critical("Please input argument json file into tamper or make --tamper securimagecaptcha in last argument")
    exit(1)

logger.debug("Load: " + filejsonpath)

try:
    filejson = open(filejsonpath, "r+").read()
except FileNotFoundError:
    logger.critical("Cannot find the path you input")
    exit(1)

try:
    datajson = json.loads(filejson)
except json.decoder.JSONDecodeError:
    logger.critical("Check your json file")
    exit(1)

def depedencies():
    pass

def tamper(payload, **kwargs):

    ocr = OCRTamper(datajson)

    return payload + ocr.result


from django.shortcuts import render
from first_app.forms import StudentForm
from first_app.models import Teacher,Student

# Create your views here.
def home(request):
    std = StudentForm()
    if request.method == "POST":
        form = StudentForm(request.POST)
        if form.is_valid():
            form.save()
            print(form.cleaned_data)
    else:
        form = StudentForm()
    return render(request, 'home.html', {'form' : form})

def showData(request):
    # students list for one teacher
    teacher = Teacher.objects.get(name = 'Tarek')
    students = teacher.student.all()         # class name Student choto hat er lekhte hobe
    for stud in students:
        print(stud.name, stud.roll, stud.class_name)
    # teachers list for one student
    stu = Student.objects.get(name = 'Rifat')
    teachers = stu.teachers.all()    # models e related_name ja dibo ta use korte hobe
    # teachers = stu.teacher_set.all()        # jekhane manytomany use kora hou oikhane class_name_set use korte hoy r class er nam chotohat er hoy
    for tec in teachers:
        print(f"{tec.name} {tec.subject} {tec.mobile_num}")
    return render(request, 'show_data.html')


#importanto bibliotecas
from tkinter import *
from tkinter import ttk
from tkcalendar import Calendar, DateEntry
#cores

cor1 = '#12171c'
cor2 = '#1b1c21'
cor3 = '#f5f6f7' #branco
cor4 = '#cf9913' #laranja

#inicio / start
janela = Tk()
janela.title('CALCULADORA DE IDADE')
janela.geometry('310x400+400+200')

frame_tela = Frame(janela, width=310, height=140, pady=0, padx=0, relief=FLAT, bg = cor1)
frame_tela.grid(row=0, column=0)

#corpo é onde será atribuido os botões
frame_corpo = Frame(janela, width=310, height=260, pady=0, padx=0, relief=FLAT, bg = cor2)
frame_corpo.grid(row=1, column=0)

#conteudo do frame tela

tela_calc_cima = Label(frame_tela, text = 'Calculadora', width = 25, height = 1, pady = 3, relief = FLAT, anchor = 'center', font = ('Helvética 15 bold'), bg = cor1, fg = cor3 )
tela_calc_cima.place(x=0, y = 20)

tela_calc_baixo = Label(frame_tela, text = 'DE IDADE', width = 15, height = 1, pady = 3, relief = FLAT, anchor = 'center', font = ('Helvética 25 bold'), bg = cor1, fg = cor3 )
tela_calc_baixo.place(x=0, y = 60)

#conteudo do frame corpo

data_inicial = Label(frame_corpo, text = 'Data Inicial', width = 13, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 12 bold'), bg = cor2, fg = cor3)
data_inicial.place(x=0, y=20)

calendario_1 = DateEntry(frame_corpo, width = 13, bg = cor3, fg = cor4, borderwidth = 2,locale = 'pt_BR', data_patter= 'dd-mm-y', y = 2022, tooltipbackground = cor2, foreground = cor3)
calendario_1.place(x=143, y = 20)

data_nascimento = Label(frame_corpo, text = 'Nascimento', width = 13, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 12 bold'), bg = cor2, fg = cor3)
data_nascimento.place(x=0, y = 60)

calendario_2 = DateEntry(frame_corpo, width = 13, bg = cor3, fg = cor4, borderwidth = 2,locale = 'pt_BR', data_patter= 'dd-mm-y', y = 2022)
calendario_2.place(x=143, y = 60)

ano = Label(frame_corpo, text = '22', width = 5, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 25 bold'), bg = cor2, fg = cor3)
ano.place(x=0, y=110)

ano_t = Label(frame_corpo, text = 'anos', width = 5, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 15 bold'), bg = cor2, fg = cor3)
ano_t.place(x=19, y=150)

mes = Label(frame_corpo, text = '22', width = 5, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 25 bold'), bg = cor2, fg = cor3)
mes.place(x=95, y=110)

mes_t = Label(frame_corpo, text = 'meses', width = 5, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 15 bold'), bg = cor2, fg = cor3)
mes_t.place(x=114, y=150)

dia = Label(frame_corpo, text = '22', width = 5, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 25 bold'), bg = cor2, fg = cor3)
dia.place(x=190, y=110)

dia_t = Label(frame_corpo, text = 'dias', width = 5, height = 1,  pady = 3, relief = FLAT, font = ('Helvética 15 bold'), bg = cor2, fg = cor3)
dia_t.place(x=210, y=150)

janela.mainloop()


import pymc3 as pm
import numpy as np
from statsmodels.stats.moment_helpers import corr2cov
from theano import tensor as tt

def linear():
    np.random.seed(0)
    size = 200
    true_intercept = 1
    true_slope = 2

    x = np.linspace(0, 1, size)
    # y = a + b*x
    true_regression_line = true_intercept + true_slope * x
    # add noise
    y = true_regression_line + np.random.normal(scale=.1, size=size)

    data = dict(x=x, y=y)

    with pm.Model() as model:  # model specifications in PyMC3 are wrapped in a with-statement
        pm.glm.GLM.from_formula('y ~ x', data)
    return model, {'Intercept': (true_intercept, 0.1), 'x': (true_slope, 0.1)}

def read_test_data():
    """
    :return: Z vector array, S covariance matrix array
    """
    mu = np.arange(3)
    corr_coeffs = np.array([-0.3, 0.1, 0.7])
    ndim = int(np.sqrt(1 + (4 * len(corr_coeffs) * 2)) + 1) // 2
    corr = np.zeros((ndim, ndim))
    corr[np.triu_indices(ndim, k=1)] = corr_coeffs
    corr[np.diag_indices(ndim)] = 1
    corr = corr + corr.T - np.diag(corr.diagonal())

    std = np.array([1, 2, 0.1])
    cov = np.dot(corr, np.dot(np.diag(std), corr))
    Z = np.random.multivariate_normal(mu, cov, 7000)
    S = np.tile(np.eye(3), (7000, 1, 1)) * 0.01
    return Z, S, (mu, corr_coeffs, corr, cov, std)

def correlation():
    np.random.seed(0)
    values, S, (true_mu, true_corr_coeffs, true_corr, true_cov, true_std) = read_test_data()
    nmeas, ndim = values.shape
    with pm.Model() as model:
        packed_L = pm.LKJCholeskyCov('packed_L', n=ndim, eta=1, sd_dist=pm.HalfCauchy.dist(2.5))
        L = pm.expand_packed_triangular(ndim, packed_L)
        cov = pm.Deterministic('cov', L.dot(L.T))

        std = pm.Deterministic('std', tt.sqrt(tt.diag(cov)))
        diag = tt.diag(std)
        diaginv = tt.nlinalg.matrix_inverse(diag)
        corr = tt.dot(diaginv, tt.dot(cov, diaginv))

        indices = np.triu_indices(ndim, k=1)
        pm.Deterministic('corr_coeffs', corr[indices])

        mu = pm.Normal('mu', mu=values.mean(axis=0), sd=values.std(axis=0), shape=ndim)  # centre of gaussian
        pm.MvNormal('like', mu=mu, chol=L, observed=values, shape=ndim)
    return model, {'mu': (true_mu, 0.1), 'cov': (true_cov, 0.1)}


#author:hanshiqiang365

import pandas as pd
from pyecharts.charts import Graph
from pyecharts import options as opts
import jieba
import jieba.posseg as pseg

def process_data():
    with open("水浒传.txt", encoding='gb18030') as f:
        book = f.readlines()

    jieba.load_userdict("characters_list.txt")
    characters_data = pd.read_csv("characters_list.txt")

    mylist = [k[0].split(" ")[0] for k in characters_data.values.tolist()]
    tmpnames = []
    characters_names = {}
    characters_relationships = {}

    for b in book:
        b.replace("晁天王", "晁盖")
        b.replace("晁保正", "晁盖")
        b.replace("宋押司", "宋江")
        b.replace("宋头领", "宋江")
        b.replace("宋公明", "宋江")
        b.replace("黑旋风", "李逵")
        b.replace("武二郎", "武松")
        b.replace("武都头", "武松")
        b.replace("林教头", "林冲")
        b.replace("鲁提辖", "鲁达")
        b.replace("鲁智深", "鲁达")
        b.replace("花和尚", "鲁达")
        b.replace("青面兽", "杨志")
        b.replace("柴大官人", "柴进")
        b.replace("小李广", "花荣")
        b.replace("高太尉", "高俅")
        b.replace("一丈青", "扈三娘")
        b.replace("玉麒麟", "卢俊义")
        b.replace("入云龙", "公孙胜")
        b.replace("一清", "公孙胜")
        poss = pseg.cut(b)
        tmpnames.append([])
        for w in poss:
            if w.flag != 'nr' or len(w.word) != 2 or w.word not in mylist:
                continue
            tmpnames[-1].append(w.word)
            if characters_names.get(w.word) is None:
                characters_names[w.word] = 0
            characters_relationships[w.word] = {}
            characters_names[w.word] += 1

    #print(characters_relationships)
    #print(tmpnames)

    for name, times in characters_names.items():
        print(name, times)

    for name in tmpnames:
        for name1 in name:
            for name2 in name:
                if name1 == name2:
                    continue
                if characters_relationships[name1].get(name2) is None:
                    characters_relationships[name1][name2] = 1
                else:
                    characters_relationships[name1][name2] += 1

    #print(characters_relationships)

    with open("characters_relationship.csv", "w", encoding='utf-8') as f:
        f.write("Source,Target,Weight\n")
        for name, edges in characters_relationships.items():
            for v, w in edges.items():
                f.write(name + "," + v + "," + str(w) + "\n")

    with open("characters_namenode.csv", "w", encoding='utf-8') as f:
        f.write("ID,Label,Weight\n")
        for name, times in characters_names.items():
            f.write(name + "," + name + "," + str(times) + "\n")

def process_graph():
    characters_relationship_data = pd.read_csv('characters_relationship.csv')
    characters_namenode_data = pd.read_csv('characters_namenode.csv')
    characters_relationship_data_list = characters_relationship_data.values.tolist()
    characters_namenode_data_list = characters_namenode_data.values.tolist()

    nodes = []
    for node in characters_namenode_data_list:
        if node[0] == "宋江":
            node[2] = node[2]/2
        nodes.append({"name": node[0], "symbolSize": node[2]/30})

    links = []
    for link in characters_relationship_data_list:
        links.append({"source": link[0], "target": link[1], "value": link[2]})

    g = (
        Graph()
        .add("", nodes, links, repulsion=8000)
        .set_global_opts(title_opts=opts.TitleOpts(title="《水浒传》人物关系"))
    )
    return g

if __name__ == '__main__':
    process_data()
    g = process_graph()
    g.render()


import decimal
import math
from flask import Flask, request, jsonify

app = Flask(__name__)

def calculate_pi(digits):
    decimal.getcontext().prec = digits + 2  # Imposta la precisione dei calcoli decimali

    a = decimal.Decimal(1)
    b = decimal.Decimal(1) / decimal.Decimal(2).sqrt()
    t = decimal.Decimal(0.25)
    p = decimal.Decimal(1)

    for _ in range(digits):
        a_next = (a + b) / 2
        b = (a * b).sqrt()
        t -= p * (a - a_next) * (a - a_next)
        a = a_next
        p *= 2

    pi = (a + b) * (a + b) / (4 * t)

    return str(pi)[:-1]  # Restituisci π senza l'ultimo cifra 3, che è fissa

@app.route('/esegui-funzione4', methods=['POST'])
def esegui_funzione():
    print("funzione in esecuzione")
    try:
        # Ricevi i dati dalla richiesta HTTP come JSON
        data = request.get_json()

        # Estrai la stringa di input dalla richiesta
        input_string = data['input']

        # Esegui la tua funzione
        risultato = calculate_pi(int(input_string))

        # Restituisci il risultato come JSON
        return jsonify({'risultato': risultato})

    except Exception as e:
        return jsonify({'errore': str(e)})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=6000)
#print(f"π con {num_cifre} cifre decimali:\n{pi_calcolato}")


# -*- coding: utf-8 -*-
"""
Created on Tue Oct 15 23:11:43 2019

@author: may
"""

from pre_train import collect_data
import math
import torch.nn as nn
import torch
from sklearn.externals import joblib
from sklearn.preprocessing import StandardScaler

def get_numeralData(inp, img):
    numeral_list = []
    for i in range(inp.shape[0]):
       data  = collect_data(int(inp[i,0]), int(inp[i,1]), math.ceil(inp[i,2]), math.ceil(inp[i,3]), img)

       if data[6] == 0:
            print("pop")
       else:
            data = list(map(lambda x: math.log(x), data))
       numeral_list.append(data)

    return numeral_list

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.input = nn.Linear(8, 16)
        self.hidden1 = nn.Linear(16, 8)
        self.hidden2 = nn.Linear(8, 4)
        self.output = nn.Linear(4, 1)
    def forward(self, data):
        pred = nn.functional.relu(self.input(data))
        pred = nn.functional.relu(self.hidden1(pred))
        pred = nn.functional.relu(self.hidden2(pred))
        out = self.output(pred)
        return out

def predict_weight(prediction_data):
    #weight_path = "/workspace/weight_dataset/chicken_weight_first.pth"
    weight_path = 'svr_first.pkl'
    prediction = []
    svr = joblib.load(weight_path)

    ss = StandardScaler()
    prediction_data = ss.fit_transform(prediction_data)

    prediction = svr.predict(prediction_data)

    '''
    model_net = Net()
    model_net.load_state_dict(torch.load(weight_path)) #load checkpoint
    if(torch.cuda.is_available()):
        model_net.cuda()

    model_net.eval()  # Set in evaluation mode

    prediction_data = torch.Tensor(prediction_data)
    if(torch.cuda.is_available()):
        prediction_data = prediction_data.cuda()
    with torch.no_grad():
        prediction = model_net(prediction_data)
    '''

    return prediction


#coding:utf-8

"""
ID:          issue-1921
ISSUE:       1921
TITLE:       Server crash with isc_dsql_execute_immediate and zero length string
DESCRIPTION:
JIRA:        CORE-1506
FBTEST:      bugs.core_1506
"""

import pytest
from firebird.qa import *
from firebird.driver import DatabaseError

db = db_factory()

act = python_act('db', substitutions=[('- SQL error code.*', '')])

@pytest.mark.version('>=3')
def test_1(act: Action):
    with act.db.connect() as con:
        with pytest.raises(DatabaseError, match='.*-Unexpected end of command.*'):
            con.execute_immediate('')


from django import forms
from django.forms import ModelForm

from authapp.forms import UserRegistrationForm, UserProfileForm
from authapp.models import User
from mainapp.models import Product, ProductCategory

class UserAdminRegistrationForm(UserRegistrationForm):
    class Meta:
        model = User
        fields = ('username', 'email', 'first_name', 'last_name', 'password1',
                  'password2', 'age', 'image')

    def __init__(self, *args, **kwargs):
        super(UserAdminRegistrationForm, self).__init__(*args, **kwargs)

        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control py-4'
        self.fields['image'].widget.attrs['class'] = 'custom-file-input'

class UserAdminProfileForm(UserProfileForm):
    email = forms.EmailField(widget=forms.EmailInput(
        attrs={'class': 'form-control py-4', 'readonly': False}))
    username = forms.EmailField(widget=forms.EmailInput(
        attrs={'class': 'form-control py-4', 'readonly': False}))

class ProductAdminRegistrationForm(ModelForm):
    class Meta:
        model = Product
        fields = (
            'name', 'description', 'price', 'quantity', 'category', 'image')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs[
            'placeholder'] = 'Введите имя продукта'
        self.fields['description'].widget.attrs[
            'placeholder'] = 'Введите описание товара'
        self.fields['price'].widget.attrs['placeholder'] = 'Введите цену'
        self.fields['quantity'].widget.attrs[
            'placeholder'] = 'Введите количество'
        self.fields['category'].widget.attrs[
            'placeholder'] = 'Введите категорию товара'
        self.fields['image'].widget.attrs['placeholder'] = 'Добавьте картинку'
        for field_name, field in self.fields.items():
            if field_name == 'image' or field_name == 'category':
                field.widget.attrs['class'] = 'form-control'
            else:
                field.widget.attrs['class'] = 'form-control py-4'

class ProductAdminProfileForm(ProductAdminRegistrationForm):
    image = forms.ImageField(widget=forms.FileInput(), required=False)

    def __init__(self, *args, **kwargs):
        super(ProductAdminRegistrationForm, self).__init__(*args, **kwargs)
        self.fields['quantity'].widget.attrs['readonly'] = True

        for field_name, field in self.fields.items():
            if field_name == 'image' or field_name == 'category':
                field.widget.attrs['class'] = 'form-control'
            else:
                field.widget.attrs['class'] = 'form-control py-4'

class CategoryUpdateFormAdmin(forms.ModelForm):
    discount = forms.IntegerField(widget=forms.NumberInput, label='Скидка',
                                  required=False, max_value=90,
                                  initial=0)

    class Meta:
        model = ProductCategory
        fields = ('name', 'description', 'discount')

    def __init__(self, *args, **kwargs):
        super(CategoryUpdateFormAdmin, self).__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control py-4'


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Apr  7 11:18:31 2020

@author: toddlillian
"""

#originally based on: https://matplotlib.org/3.1.1/gallery/widgets/slider_demo.html
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
from scipy import signal
import control

Time = np.arange(0.0, 20.0, 0.001)#time range of interest
U=Time*0+1#command input of interest
Omega=np.logspace(-2,2,1000)#frequency range of interest

plantsys=control.tf(1,[1,2*.01,1])#system of interest

#control system definition/initialize gains
kp0 = 1
ki0 = 1
kd0 = 1
sysp=kp0#proportional
sysi=control.tf(ki0,[1,0])#integral
sysd=control.tf([kd0,0],1)#derivative
sys=control.feedback(plantsys*(sysp+sysi+sysd),1)#assembled closed loop system

#set up the plot for the response in the time domain
fig1, ax1 = plt.subplots(num=1)
ax1.margins(x=0)
plt.subplots_adjust(left=0.15, bottom=0.3)
plt.ylim((0,1.5))

#plot the command input
plt.plot(Time,U,'r')

#solve for and plot the response in the time domain using initilized gains
Y=control.forced_response(sys,Time,U)[1]
l, = plt.plot(Time, Y, lw=2)

#set up sliders for the gains
axcolor = 'lightgoldenrodyellow'
axkp	 = plt.axes([0.15, 0.2, 0.75, 0.03], facecolor=axcolor)
axki = plt.axes([0.15, 0.15, 0.75, 0.03], facecolor=axcolor)
axkd = plt.axes([0.15, 0.1, 0.75, 0.03], facecolor=axcolor)

skp = Slider(axkp, 'kp', 0, 10, valinit=1, valstep=.1)
ski = Slider(axki, 'ki', 0, 10, valinit=1, valstep=.1)
skd = Slider(axkd, 'kd', 0, 10, valinit=1, valstep=.1)

#set up the bode plots
fig2, ax2 = plt.subplots(2,1,num=2)

#solve for and plot the bode data
mag,phase,omega=control.bode_plot(sys,Plot=False,omega=Omega)
plt.subplot(2,1,1)
m,=plt.semilogx(omega,20*np.log10(mag))
plt.subplot(2,1,2)
p,=plt.semilogx(omega,np.degrees(phase))

#function to update data upon moving sliders
def update(val):
	#control system definition
	kp = skp.val#proportional gain
	ki = ski.val#integral gain
	kd = skd.val#derivative gain
	sysp=kp#proportional
	sysi=control.tf(ki,[1,0])#integral
	sysd=control.tf([kd,0],1)#derivative
	sys=control.feedback(plantsys*(sysp+sysi+sysd),1)#assembled closed loop system

	#solve for and update the plot of the response in the time domain
	Y=control.forced_response(sys,Time,U)[1]
	l.set_ydata(Y)
	fig1.canvas.draw_idle()

	#solve for and update the plot of the bode data

	mag,phase,omega=control.bode_plot(sys,Plot=False,omega=Omega)
	m.set_ydata(20*np.log10(mag))
	p.set_ydata(np.degrees(phase))
	fig2.canvas.draw_idle()

#call the update function upon movine a slider
skp.on_changed(update)
ski.on_changed(update)
skd.on_changed(update)

plt.figure(fig1.number)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')

#define reset button
def reset(event):
	skp.reset()
	ski.reset()
	skd.reset()
button.on_clicked(reset)

# rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
# radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)

# def colorfunc(label):
# 	l.set_color(label)
# 	fig.canvas.draw_idle()
# radio.on_clicked(colorfunc)

plt.show()


"""
Module containing all lark internal_transformer classes
"""
from __future__ import annotations

from collections import defaultdict
import re
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    List,
    Optional,
    Pattern,
    Set,
    Tuple,
    Union,
)

import ibis
from ibis.expr.operations import CrossJoin
from ibis.expr.types import AnyColumn, TableExpr
from lark import Token, Tree, v_args

from sql_to_ibis.exceptions.sql_exception import (
    InvalidQueryException,
    NeedsAggOrGroupQueryException,
    TableExprDoesNotExist,
)
from sql_to_ibis.parsing.transformers import InternalTransformer, TransformerBaseClass
from sql_to_ibis.query_info import OrderByInfo, QueryInfo
from sql_to_ibis.sql.sql_clause_objects import LimitExpression, WhereExpression
from sql_to_ibis.sql.sql_objects import AliasRegistry, AmbiguousColumn
from sql_to_ibis.sql.sql_value_objects import (
    Aggregate,
    Column,
    CountStar,
    DerivedColumn,
    GroupByColumn,
    Literal,
    NestedCrossJoin,
    NestedJoin,
    NestedJoinBase,
    Subquery,
    Table,
    TableOrJoinbase,
    Value,
)

if TYPE_CHECKING:
    from lark.tree import Meta

TableWithColumn = Tuple[Table, AnyColumn]
TableWithColumnCollection = DefaultDict[str, List[TableWithColumn]]

GET_TABLE_REGEX: Pattern[str] = re.compile(
    r"^(?P<table>[a-z_]\w*)\.(?P<column>[a-z_]\w*)$", re.IGNORECASE
)
PANDAS_TYPE_PYTHON_TYPE_FUNCTION = {
    "object": str,
    "string": str,
    "int16": int,
    "int32": int,
    "int64": int,
    "float16": float,
    "float32": float,
    "float64": float,
    "bool": bool,
}

TYPE_TO_PANDAS_TYPE = {
    "varchar": "string",
    "integer": "int32",
    "int": "int32",
    "bigint": "int64",
    "float": "float64",
    "timestamp": "datetime64",
    "datetime64": "datetime64",
    "timedelta[ns]": "timedelta[ns]",
    "category": "category",
}

class TupleTree(Tree[Subquery]):
    def __init__(
        self,
        data: str,
        children: Tuple[Tree, Subquery],
        meta: Optional[Meta] = None,
    ) -> None:
        super().__init__(data, list(children), meta)

for TYPE in PANDAS_TYPE_PYTHON_TYPE_FUNCTION:
    TYPE_TO_PANDAS_TYPE[TYPE] = TYPE

@v_args(inline=True)
class SQLTransformer(TransformerBaseClass):
    """
    Transformer for the lark sql_to_ibis parser
    """

    def __init__(
        self,
        table_name_map: dict,
        table_map: dict,
        column_name_map: dict,
        column_to_table_name: dict,
    ) -> None:
        super().__init__(
            table_name_map,
            table_map,
            column_name_map,
            column_to_table_name,
            _temp_dataframes_dict={},
        )
        self._alias_registry = AliasRegistry()

    def add_column_to_column_to_table_name_map(self, column: str, table: str) -> None:
        """
        Adds a column to the _column_to_table_name map
        :param column:
        :param table:
        :return:
        """
        if self._column_to_table_name.get(column) is None:
            self._column_to_table_name[column] = table
            return
        table_name = self._column_to_table_name[column]
        if isinstance(table_name, AmbiguousColumn):
            table_name.add_table(table)
        else:
            original_table = table_name
            self._column_to_table_name[column] = AmbiguousColumn(
                {original_table, table}
            )

    def table(self, table_name: str, alias: str = "") -> Table:
        """
        Check for existence of table with same name
        If not exists raise TableExprDoesNotExist
        Otherwise return the name of the actual TableExpr
        :return:
        """
        table_name = table_name.lower()
        if table_name not in self._table_name_map:
            raise TableExprDoesNotExist(table_name)
        true_name = self._table_name_map[table_name]
        if isinstance(alias, Tree) and alias.data == "alias_string":
            alias = str(alias.children[0])
        table_value_from_map = self._table_map[true_name]
        assert isinstance(table_value_from_map, Table)
        table = Table(
            value=table_value_from_map.get_table_expr(),
            name=true_name,
            alias=alias,
        )
        if alias:
            self._alias_registry.add_to_registry(alias, table)
        return table

    def order_by_expression(self, rank_tree: Tree[Tree[str]]) -> OrderByInfo:
        """
        Returns the column name for the order sql_object
        :param rank_tree: Tree containing order info
        :return:
        """
        order_type = rank_tree.data
        ascending = order_type == "order_asc"
        order_by_column_tree = rank_tree.children[0]
        order_by_column_name = order_by_column_tree.children[0]
        assert isinstance(order_by_column_name, str)
        return OrderByInfo(order_by_column_name, ascending)

    def integer_(self, integer_token: Token) -> int:
        """
        Returns the integer value
        :param integer_token:
        :return:
        """
        integer_value = int(integer_token.value)
        return integer_value

    def limit_count(self, limit_count_value: int) -> LimitExpression:
        """
        Returns a limit token_or_tree
        :param limit_count_value:
        :return:
        """
        return LimitExpression(limit_count_value)

    def query_expr(
        self, query_info: QueryInfo, *args: List[Union[OrderByInfo, LimitExpression]]
    ) -> QueryInfo:
        """
        Handles the full query, including order and set operations such as union
        :param query_info: Map of all query information
        :param args: Additional arguments aside from query info
        :return: Query info
        """
        for token in args:
            if isinstance(token, OrderByInfo):
                query_info.add_order_by_info(token)
            if isinstance(token, LimitExpression):
                query_info.limit = token.limit
        return query_info

    def _handle_join_subqueries(self, join: NestedJoinBase) -> QueryInfo:
        info = QueryInfo(
            InternalTransformer(
                join.get_tables(),
                join.get_table_map(),
                self._column_name_map,
                self._column_to_table_name,
                self._table_name_map,
                self._alias_registry,
            ),
        )
        info.add_table(join)
        info.add_column(Column(name="*"))
        return info

    def subquery(
        self, query_object: Union[QueryInfo, NestedJoinBase], alias: Tree
    ) -> Subquery:
        """
        Handle subqueries amd return a subquery object
        :param query_object:
        :param alias:
        :return:
        """
        alias_name = str(alias.children[0])
        if isinstance(query_object, NestedJoinBase):
            query_info = self._handle_join_subqueries(query_object)
        else:
            query_info = query_object
        subquery_value = self._to_ibis_table(query_info)
        subquery = Subquery(name=alias_name, value=subquery_value)
        self._table_map[alias_name] = subquery
        self._column_name_map[alias_name] = {}
        for column in subquery.column_names:
            self.add_column_to_column_to_table_name_map(column.lower(), alias_name)
            self._column_name_map[alias_name][column.lower()] = column
        return subquery

    def column_name(self, *names: Optional[str]) -> Tree:
        full_name = ".".join([str(name) for name in names if name is not None])
        return Tree("column_name", [full_name])

    def join(self, join_expression: NestedJoin) -> NestedJoin:
        """
        Handle join tree
        :param join_expression:
        :return:
        """
        return join_expression

    def comparison_type(self, comparison: Tree) -> Tree:
        """
        Return the comparison expression
        :param comparison:
        :return:
        """
        return comparison

    def join_expression(self, *args: Union[NestedJoin, Table, str, Tree]) -> NestedJoin:
        # There will only ever be four args
        # args_with_join: Tuple[Table, Optional[str], Table, Tree] = args
        table1 = args[0]
        assert isinstance(table1, (Table, NestedJoin))
        join_type = args[1] if args[1] is not None else "inner"
        assert isinstance(join_type, str)
        table2 = args[2]
        assert isinstance(table2, Table)
        join_condition = args[3]
        assert isinstance(join_condition, Tree)
        if "outer" in join_type:
            match = re.match(r"(?P<type>.*)\souter", join_type)
            if match:
                join_type = match.group("type")
        if join_type in {"full", "cross"}:
            join_type = "outer"
        return NestedJoin(
            left_table=table1,
            right_table=table2,
            join_type=join_type,
            join_condition=join_condition,
        )

    def select(self, *select_expressions: Tree[NestedJoinBase]) -> QueryInfo:
        """
        Forms the final sequence of methods that will be executed
        :param select_expressions:
        :return:
        """
        tables: List[TableOrJoinbase] = []
        having_expr = None
        where_expr = None
        for select_expression in select_expressions:
            if isinstance(select_expression, Tree):
                if select_expression.data == "from_expression":
                    table_object = select_expression.children[0]
                    if isinstance(table_object, NestedJoinBase):
                        tables += [
                            table_object.right_table,
                            table_object.left_table,
                        ]
                    elif (
                        isinstance(table_object, Tree)
                        and table_object.data == "cross_join_expression"
                    ):
                        cross_join = table_object.children[0]
                        assert isinstance(cross_join, NestedCrossJoin)
                        tables += [
                            cross_join.right_table,
                            cross_join.left_table,
                        ]
                    elif isinstance(table_object, Table):
                        tables.append(table_object)
                    else:
                        raise Exception(f"Invalid type found: {type(table_object)}")
                elif select_expression.data == "having_expr":
                    having_expr = select_expression
                elif select_expression.data == "where_expr":
                    where_expr = select_expression

        internal_transformer = InternalTransformer(
            tables,
            self._table_map,
            self._column_name_map,
            self._column_to_table_name,
            self._table_name_map,
            self._alias_registry,
        )

        select_expressions_no_boolean_clauses: List[Union[str, Tree]] = []
        distinct = False
        for select_expression in select_expressions:
            if isinstance(select_expression, Tree) and select_expression.data not in (
                "having_expr",
                "where_expr",
            ):
                select_expressions_no_boolean_clauses.append(select_expression)
            if (
                isinstance(select_expression, Token)
                and select_expression.value.lower() == "distinct"
            ):
                distinct = True

        return QueryInfo(
            internal_transformer,
            select_expressions_no_boolean_clauses,
            having_expr=having_expr,
            where_expr=where_expr,
            distinct=distinct,
        )

    def cross_join(self, table1: Table, table2: Table) -> NestedCrossJoin:
        """
        Returns the crossjoin between two dataframes
        :param table1: TableExpr1
        :param table2: TableExpr2
        :return: Crossjoined dataframe
        """
        return NestedCrossJoin(
            left_table=table1,
            right_table=table2,
        )

    def from_item(self, item: Subquery) -> Subquery:
        return item

    def _handle_count_star(
        self, aggregate: Aggregate, relation: TableExpr
    ) -> Aggregate:
        if isinstance(aggregate.value, CountStar):
            aggregate.value = relation.count()
        return aggregate

    def _get_aggregate_ibis_columns(
        self, aggregates: Dict[str, Aggregate], relation: TableExpr
    ) -> List[AnyColumn]:
        aggregate_ibis_columns: List[AnyColumn] = []
        for aggregate_column_name in aggregates:
            aggregate_column = aggregates[aggregate_column_name]
            self._handle_count_star(aggregate_column, relation)
            column = aggregate_column.value.name(aggregate_column_name)
            aggregate_ibis_columns.append(column)
        return aggregate_ibis_columns

    def _handle_having_expressions(
        self,
        having_expr: Optional[Tree],
        internal_transformer: InternalTransformer,
        table: TableExpr,
        aggregates: Dict[str, Aggregate],
        group_column_names: List[str],
    ) -> Optional[AnyColumn]:
        having = None
        if having_expr:
            transform_result = internal_transformer.transform(having_expr.children[0])
            assert isinstance(transform_result, Value)
            having = transform_result.value
        if having is not None and not aggregates:
            for column in table.columns:
                if column not in group_column_names:
                    raise NeedsAggOrGroupQueryException(column)
        return having

    def handle_aggregation(
        self,
        aggregates: Dict[str, Aggregate],
        group_columns: List[GroupByColumn],
        table: TableExpr,
        having_expr: Optional[Tree],
        internal_transformer: InternalTransformer,
        selected_columns: List[Value],
    ) -> TableExpr:
        """
        Handles all aggregation operations when translating from dictionary info
        to dataframe
        """
        selected_column_names = {
            column.get_name().lower() for column in selected_columns
        }
        aggregate_ibis_columns = self._get_aggregate_ibis_columns(aggregates, table)
        having = self._handle_having_expressions(
            having_expr,
            internal_transformer,
            table,
            aggregates,
            [group_column.get_name() for group_column in group_columns],
        )

        if group_columns and not selected_column_names:
            for group_column in group_columns:
                group_column.set_ibis_name_to_name()

        if group_columns and having is not None and not aggregates:
            raise NotImplementedError(
                "Group by, having, without aggregation not yet implemented"
            )
        if group_columns and not aggregates:
            for column in [
                selected_column.get_name() for selected_column in selected_columns
            ]:
                if column not in group_columns:
                    raise NeedsAggOrGroupQueryException(column)
            table = table.distinct()
        elif aggregates and not group_columns:
            table = table.aggregate(aggregate_ibis_columns, having=having)
        elif aggregates and group_columns:
            table = table.group_by(
                [group_column.value for group_column in group_columns]
            )
            if having is not None:
                table = table.having(having)
            table = table.aggregate(aggregate_ibis_columns)

        non_selected_columns = []
        if group_columns and aggregates:
            for group_column in group_columns:
                if group_column.get_name().lower() not in selected_column_names:
                    non_selected_columns.append(group_column.group_by_name)
            if non_selected_columns:
                table = table.drop(non_selected_columns)

        return table

    def handle_filtering(
        self,
        ibis_table: TableExpr,
        where_expr: Optional[Tree],
        internal_transformer: InternalTransformer,
    ) -> TableExpr:
        """
        Returns frame with appropriately selected and named columns
        :param ibis_table: Ibis expression table to manipulate
        :param where_expr: Syntax tree containing where clause
        :param internal_transformer: Transformer to transform the where clauses
        :return: Filtered TableExpr
        """
        if where_expr is not None:
            where_expression = internal_transformer.transform(where_expr)
            assert isinstance(where_expression, WhereExpression)
            return ibis_table.filter(where_expression.value.get_value())
        return ibis_table

    def subquery_in(self, column: Tree, subquery: Subquery) -> TupleTree:
        return TupleTree("subquery_in", (column, subquery))

    def handle_selection(
        self, ibis_table: TableExpr, columns: List[Value]
    ) -> TableExpr:
        column_mutation = []
        for column in columns:
            if column.get_name() == "*":
                return ibis_table
            column_value = column.get_value().name(column.get_name())
            column_mutation.append(column_value)
        if column_mutation:
            return ibis_table.projection(column_mutation)
        return ibis_table

    @staticmethod
    def _columns_have_select_star(columns: List[Value]) -> bool:
        for column in columns:
            if column.get_name() == "*":
                return True
        return False

    def _get_all_columns_rename_duplicates(
        self, tables: List[Table]
    ) -> List[AnyColumn]:
        columns = {table: table.get_ibis_columns() for table in tables}

        def set_dict_column_name(table: Table, col_name: str) -> None:
            index = table.column_names.index(col_name)
            columns[table][index] = columns[table][index].name(
                f"{table.get_alias_else_name()}.{col_name}"
            )

        for i, table1 in enumerate(tables):
            for table2 in tables[i + 1 :]:
                table1_column_names = set(table1.column_names)
                table2_column_names = set(table2.column_names)
                intersect = table1_column_names.intersection(table2_column_names)
                for column_name in intersect:
                    for table in [table1, table2]:
                        set_dict_column_name(table, column_name)

        all_columns = []
        for table in columns:
            all_columns += columns[table]
        return all_columns

    @staticmethod
    def _get_overlapping_column_names(
        columns_by_name: TableWithColumnCollection,
    ) -> Set[AnyColumn]:
        overlapping = set()
        for column_name, column_list in columns_by_name.items():
            if len(column_list) > 1:
                for table, column in column_list:
                    overlapping.add(column.name(table.name + "." + column_name))
        return overlapping

    def handle_join(
        self,
        join: NestedJoinBase,
        columns: List[Value],
        internal_transformer: InternalTransformer,
    ) -> TableExpr:
        """
        Return the table expr resulting from the join
        :param join:
        :param columns: List of all column values
        :return:
        """

        def get_join_table(
            join: NestedJoin, left_table: TableExpr, right_table: TableExpr
        ) -> TableExpr:
            compiled_condition = internal_transformer.transform(join.join_condition)
            assert isinstance(compiled_condition, Value)
            return left_table.join(
                right_table,
                predicates=compiled_condition.get_value(),
                how=join.join_type,
            )

        def get_cross_table(
            join: CrossJoin, left_table: TableExpr, right_table: TableExpr
        ) -> TableExpr:
            return ibis.cross_join(left_table, right_table)

        def rename_or_add_columns(
            table: Table, columns: TableWithColumnCollection
        ) -> None:
            for column in table.column_names:
                columns[column].append(
                    (table, table.get_table_expr().get_column(column))
                )

        def resolve_table(
            table: TableOrJoinbase,
            columns: TableWithColumnCollection,
        ) -> TableExpr:
            if isinstance(table, NestedJoinBase):
                left = resolve_table(table.left_table, columns)
                right = resolve_table(table.right_table, columns)
                join_actions = {
                    NestedJoin: get_join_table,
                    NestedCrossJoin: get_cross_table,
                }
                return join_actions[type(table)](table, left, right)
            rename_or_add_columns(table, columns)
            return table.get_table_expr()

        result: TableExpr = None
        columns_by_name: TableWithColumnCollection = defaultdict(lambda: [])
        left_table = Table(resolve_table(join.left_table, columns_by_name), "left")
        right_table = Table(resolve_table(join.right_table, columns_by_name), "right")

        all_columns: List[Value] = []
        if self._columns_have_select_star(columns):
            all_columns = join.get_all_join_columns_handle_duplicates(
                left_table, right_table
            )

        left_ibis_table = left_table.get_table_expr()
        right_ibis_table = right_table.get_table_expr()
        if isinstance(join, NestedJoin):
            compiled_condition = internal_transformer.transform(join.join_condition)
            assert isinstance(compiled_condition, Value)
            result = left_ibis_table.join(
                right_ibis_table,
                predicates=compiled_condition.get_value(),
                how=join.join_type,
            )
        if isinstance(join, NestedCrossJoin):
            result = ibis.cross_join(left_ibis_table, right_ibis_table)

        def get_renamed_columns(
            column_collection: TableWithColumnCollection,
        ) -> List[AnyColumn]:
            renamed_columns: List[AnyColumn] = []
            for column_name in column_collection:
                if len(column_collection[column_name]) > 1:
                    for table, column in column_collection[column_name]:
                        renamed_columns.append(
                            column.name(f"{table.get_alias_else_name()}.{column_name}")
                        )
                else:
                    original_column = column_collection[column_name][0][1]
                    renamed_columns.append(original_column)
            return renamed_columns

        overlapping = self._get_overlapping_column_names(columns_by_name)
        if overlapping and not all_columns:
            renamed_columns = get_renamed_columns(columns_by_name)
            return result.projection(renamed_columns)

        if all_columns:
            return result[all_columns]
        return result

    def _set_casing_for_groupby_names(self, query_info: QueryInfo) -> None:
        lower_case_to_true_column_name = {
            column.get_name().lower(): column.get_name()
            for column in query_info.columns
        }
        for groupby_column in query_info.group_columns:
            lower_case_group_name = groupby_column.get_name().lower()
            if lower_case_group_name in lower_case_to_true_column_name:
                selection_statement_name = lower_case_to_true_column_name[
                    lower_case_group_name
                ]
                groupby_column.group_by_name = selection_statement_name
                groupby_column.value = groupby_column.value.name(
                    selection_statement_name
                )

    def get_table_value(
        self, table: Union[Table, NestedJoinBase, Subquery]
    ) -> Optional[Union[TableExpr, NestedJoinBase]]:
        if isinstance(table, Table):
            return table.get_table_expr()
        if isinstance(table, NestedJoinBase):
            return table
        return None

    def _get_relation(self, query_info: QueryInfo) -> TableExpr:
        tables = query_info.tables
        if not tables:
            raise Exception("No table specified")
        first_table = self.get_table_value(tables[0])
        if isinstance(first_table, NestedJoinBase):
            first_table = self.handle_join(
                join=first_table,
                columns=query_info.columns,
                internal_transformer=query_info.internal_transformer,
            )
        for table in tables[1:]:
            next_table = self.get_table_value(table)
            first_table = first_table.cross_join(next_table)
        if len(tables) > 1 and self._columns_have_select_star(query_info.columns):
            all_columns = self._get_all_columns_rename_duplicates(
                [table for table in tables if isinstance(table, Table)]
            )
            first_table = first_table[all_columns]
        return first_table

    def _to_ibis_table(self, query_info: QueryInfo) -> TableExpr:
        """
        Returns the dataframe resulting from the SQL query
        :return:
        """
        query_info.perform_transformation()

        relation = self._get_relation(query_info)

        self._set_casing_for_groupby_names(query_info)

        selected_group_columns = []
        group_column_names = {
            group_column.group_by_name for group_column in query_info.group_columns
        }
        if query_info.group_columns and query_info.aggregates:
            selection_column_names = [
                column.get_name() for column in query_info.columns
            ]
            remaining_columns = set(selection_column_names) - group_column_names
            if remaining_columns:
                raise InvalidQueryException(
                    f"Must have grouping for columns in {remaining_columns}"
                )
            selected_group_columns = [
                column
                for column in query_info.columns
                if column.get_name() in set(group_column_names)
            ]

            query_info.columns = [
                column
                for column in query_info.columns
                if column.get_name() in remaining_columns
            ]
        new_table = self.handle_filtering(
            relation,
            query_info.where_expr,
            query_info.internal_transformer,
        )
        new_table = self.handle_selection(new_table, query_info.columns)
        new_table = self.handle_aggregation(
            query_info.aggregates,
            query_info.group_columns,
            new_table,
            query_info.having_expr,
            query_info.internal_transformer,
            selected_group_columns,
        )

        if query_info.distinct:
            new_table = new_table.distinct()

        order_by = query_info.order_by
        if order_by:
            new_table = new_table.sort_by(order_by)

        if query_info.limit is not None:
            new_table = new_table.head(query_info.limit)

        return new_table

    def set_expr(self, query_info: QueryInfo) -> TableExpr:
        """
        Return different sql_object with set relational operations performed
        :param query_info:
        :return:
        """
        return self._to_ibis_table(query_info)

    def union_all(
        self,
        expr1: TableExpr,
        expr2: TableExpr,
    ) -> TableExpr:
        """
        Return union distinct of two TableExpr
        :param expr1: Left TableExpr
        :param expr2: Right TableExpr
        :return:
        """
        return expr1.union(expr2)

    def union_distinct(
        self,
        expr1: TableExpr,
        expr2: TableExpr,
    ) -> TableExpr:
        """
        Return union distinct of two TableExpr
        :param expr1: Left TableExpr
        :param expr2: Right TableExpr
        :return:
        """
        return expr1.union(expr2, distinct=True)

    def intersect_distinct(self, expr1: TableExpr, expr2: TableExpr) -> TableExpr:
        """
        Return distinct intersection of two TableExpr
        :param expr1: Left TableExpr
        :param expr2: Right TableExpr
        :return:
        """
        return expr1.intersect(expr2)

    def except_distinct(self, expr1: TableExpr, expr2: TableExpr) -> TableExpr:
        """
        Return distinct set difference of two TableExpr
        :param expr1: Left TableExpr
        :param expr2: Right TableExpr
        :return:
        """
        return expr1.difference(expr2).distinct()

    def except_all(self, expr1: TableExpr, expr2: TableExpr) -> TableExpr:
        """
        Return set difference of two TableExpr
        :param expr1: Left TableExpr
        :param expr2: Right TableExpr
        :return:
        """
        return expr1.difference(expr2)

    def final(self, table: TableExpr) -> TableExpr:
        """
        Returns the final dataframe
        :param table:
        :return:
        """
        DerivedColumn.reset_expression_count()
        Literal.reset_literal_count()
        return table


# import relevant libraries
import time
import random
import decimal
import requests
from bs4 import BeautifulSoup

# helper function which strips leading and ending spaces from strings
def list_strip(my_list):
    my_new_list = []
    for item in my_list:
        item = item.strip()
        my_new_list.append(item)
    return my_new_list

# helper function to give an error message for string parsing inconsistency, if needed
def string_parser(my_string,start,end,positioning):
    if positioning == 'Both':
        start_string_length = len(start)
        if my_string.find(start) != -1 and my_string.find(end) != -1:
            string_return = my_string[my_string.find(start) + start_string_length : my_string.find(end)]
            return string_return
        else:
            string_return = 'Could not parse!'
            return string_return
    if positioning == 'Back':
        if my_string.find(end) != -1:
            string_return = my_string[start : my_string.find(end)]
            return string_return
        else:
            string_return = 'Could not parse!'
            return string_return

# prepare csv file for writing
FDA_approved_drugs = open('FDA_approved_drugs.csv','w')
FDA_approved_drugs.write('Count' + ',Release Year' + ',Release Month' + ',Trade Name' + ',Technical Name' + ',Drug Manufacturer' + ',Treated Disease' + ',URL' + ',Top-Level Domain' + ',URL Type' + '\n')

# define time scope of program run
start_year_range = int(input('Please enter the starting year for which you would like to build this dataset. '))
end_year_range = int(input('Please enter the ending year for which you would like to build this dataset. ')) + 1
year_range = range(start_year_range, end_year_range)

# define relevant URLs for scraping
centerwatch_link = str('https://www.centerwatch.com/drug-information/fda-approved-drugs/year/')
google_query_link = str('https://www.google.com/search?q=')

# run program for each year in range
for year in year_range:
    year = str(year)
    centerwatch_webpage = requests.get(centerwatch_link + year)
    centerwatch_BeautifulSoup = BeautifulSoup(centerwatch_webpage.content, 'lxml')
    drugs_list = centerwatch_BeautifulSoup.find_all('p')
    # run program for each drug in the year the program is currently running
    for drug in drugs_list[3:-1]:
        # run time delay to not overload Google servers
        time_delay = float(decimal.Decimal(random.randrange(100,500))/100)
        time.sleep(time_delay)
        drug = drug.text
        drug = str(drug.replace(',',' '))
        release_year = str(year)
        release_month = string_parser(drug,'Approved',release_year,'Both')
        release_month = str(release_month.replace('of',''))
        trade_name = string_parser(drug,0,'(','Back')
        technical_name = string_parser(drug,'(',')','Both')
        drug_manufacturer = drug[drug.find(';') + 1 : drug.find(';',drug.find(';')+1)]
        treated_disease = string_parser(drug,'For ','Approved','Both')
        treated_disease = str(treated_disease.replace('the treatment of',''))
        drug_google_search_webpage = requests.get(google_query_link + trade_name)
        drugs_google_search_BeautifulSoup = BeautifulSoup(drug_google_search_webpage.content, 'lxml')
        ads_list = drugs_google_search_BeautifulSoup.find_all('cite', {'class': '_WGk'})
        ad_count = 0
        for ad in ads_list:
            ad_count += 1
            ad_url = ad.text
            top_level_domain = str(ad_url.replace('https://',''))
            top_level_domain = str(top_level_domain.replace('http://',''))
            top_level_domain = str(top_level_domain.replace('www.',''))
            top_level_domain = string_parser(top_level_domain,0,'/','Back')
            drug_features = [str(ad_count), release_year, release_month, trade_name, technical_name, drug_manufacturer, treated_disease, ad_url, top_level_domain, 'Google Ad']
            drug_features = list_strip(drug_features)
            drug_features_comma_separated = ','.join(drug_features)
            FDA_approved_drugs.write(drug_features_comma_separated + '\n')
            print(drug_features_comma_separated)
        links_list = drugs_google_search_BeautifulSoup.find_all('h3',attrs={'class': 'r'})
        link_count = 0
        for link in links_list:
            for a_link in link.find_all('a'):
                link_count += 1
                search_result = a_link.attrs['href']
                search_url = string_parser(search_result,'/url?q=','&sa=','Both')
                top_level_domain = str(search_url.replace('https://',''))
                top_level_domain = str(top_level_domain.replace('http://',''))
                top_level_domain = str(top_level_domain.replace('www.',''))
                top_level_domain = string_parser(top_level_domain,0,'/','Back')
                drug_features = [str(link_count), release_year, release_month, trade_name, technical_name, drug_manufacturer, treated_disease, search_url, top_level_domain, 'Google Search Result']
                drug_features = list_strip(drug_features)
                drug_features_comma_separated = ','.join(drug_features)
                FDA_approved_drugs.write(drug_features_comma_separated + '\n')
                print(drug_features_comma_separated)

FDA_approved_drugs.close()