import pygame, random, sys, os
from pygame.locals import*
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()
#set main directory and load sounds from 'sounds folder'
main_dir = os.path.dirname(os.path.abspath("__file__"))
bump1 = pygame.mixer.Sound(os.path.join(main_dir, "bump.wav"))
hit1 = pygame.mixer.Sound(os.path.join(main_dir, "bwubwub.wav"))
coin1 = pygame.mixer.Sound(os.path.join(main_dir, "beep.wav"))
pygame.mixer.music.load(os.path.join(main_dir, "music.wav"))
#set winsize and basic colours
WINSIZE = [640, 640]
white = (255, 255, 255)
black = (0, 0, 0)
#set paddlewidth since it doesn't change
PADDLEWIDTH = 15
Done = 0
pygame.init()
#load font and images
font = pygame.font.Font('Minecraft.ttf', 25)
titlefont = pygame.font.Font('Minecraft.ttf', 50)
coindwg = pygame.image.load(os.path.join(main_dir, 'coin.png'))
heart = pygame.image.load(os.path.join(main_dir, 'heart.png'))
#set ai offset, see ai move function in paddle class
aiOffset = 0

class paddle:
    def __init__(self, x, y, health, maxhealth, length, speed, bspeed):
        self.x = x
        self.y = y
        self.health = health
        self.length = length
        self.speed = speed
        self.bspeed = bspeed
        self.maxhealth = maxhealth

    def ai_move(self):
        #ignoring the ai offset this would check if the paddle center was at the ball center
        #and move it towards if it was not
        #with offset the hit is set to be between the top and bottom of the paddle
        #the purpose it to return the ball with some y velocity(see how y vel is set in ball collision)
        if self.y + (self.length / 2) < playball.y + (playball.size/2) - aiOffset:
            if self.y < WINSIZE[0] - self.length:
                self.y += self.speed#move towards at paddle speed
        if self.y + (self.length/ 2) > playball.y + (playball.size/2) - aiOffset:
            if self.y > 0:
                self.y -= self.speed

    def input(self):
        #same idea as ai move but with input from player
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit(); sys.exit()
        keystate = pygame.key.get_pressed()
        if keystate[K_UP]:
            if self.y > 0:
                self.y -= self.speed
        if keystate[K_DOWN]:
            if self.y < WINSIZE[0] - self.length:
                self.y += self.speed

    def render(self):
        pygame.draw.rect(screen, white, (self.x, self.y, PADDLEWIDTH, self.length), 0)

class ball:
    def __init__(self, x, y, dirX, dirY, size):
        self.x = x
        self.y = y
        self.dirX = dirX
        self.dirY = dirY
        self.size = size
    def move(self):
        #move ball along its current path set by dirX and dirY
        self.x += self.dirX
        self.y += self.dirY
    def col_check(self):
        global aiOffset
        global score
        #ball hits top screen will set y velocity to be opposite
        if self.y < 0:
            if self.dirY < 0:#this checks to make sure that ball hasn't been bounced a bit out of bounds and is in the process of bouncing back in
                self.dirY = self.dirY * -1
                play_sound("bounce")
        #same thing but for bottom of screen
        if self.y + self.size > WINSIZE[1]:
            if self.dirY > 0:
                self.dirY *= -1
                play_sound("bounce")
        if self.x + self.size > WINSIZE[0]:
            #if ball hits player2(ai)side remove 1 of their health units, play hit sound and restart
            player2.health -= 1
            play_sound("hit")
            score += 5
            if player2.health < 1:
                win()
            restart()
        if self.x < 0:
            #same thing but checks for no health left and runs the you died function
            player1.health -= 1
            play_sound("hit")
            if player1.health < 1:
                you_died()
            restart()
        if self.dirX < 0:
            #checks for collision with paddle and bounces the ball back but..
            #only if the ball is headed towards you
            #done to avoid bouncing the ball again when its already headed away
            if collision(self, player1):
                self.dirX = 3
                self.dirY = (((playball.y + self.size/2) - (player1.y + player1.length/2))/(player1.length/2))*5
                play_sound("bounce")
        if self.dirX > 0:
            #same dealio but also set aiOffset after a bounce and create a new coin
            if collision(self, player2):
                self.dirX = -3
                self.dirY = (((playball.y + self.size/2) - (player2.y + player2.length/2))/(player2.length/2))*5
                coin.new(player2.x, (player2.y + player2.length/2))
                aiOffset = random.randrange(0, 48, 1)-24
                play_sound("bounce")
    def render(self):
        pygame.draw.rect(screen, white, (self.x, self.y, self.size, self.size), 0)

class coin:
    def __init__(self, x, y, size, speed):
        self.x = x
        self.y = y
        self.size = size
        self.speed = speed
    def new(x, y):
        speed = (random.random()*4) + 1
        newcoin = coin(x, y, 20, speed)
        coinlist.append(newcoin)
    def move(self):
        self.x -= self.speed
    def col_check(self):
        #calls collision, if you get the coin with the paddle score increases by 10
        global score
        if collision(self, player1):
            play_sound("coin")
            score += 10
            return(1)
    def draw(self):
        screen.blit(coindwg, (self.x, self.y))

def collision(ball, player):
    #basic collision function takes 2 objects.. yeah
    if((ball.x + ball.size >= player.x and ball.x <= player.x + PADDLEWIDTH) and (ball.y + ball.size > player.y and ball.y <= player.y + player.length)):
        return(1) #collision
    else:
        return(0)

def main():
    global screen
    screen = pygame.display.set_mode(WINSIZE)
    pygame.display.set_caption('pong legacy')

    menu_title()
    initialize_game()
    while not Done:
        run_game()
def menu_title():
    pygame.mixer.music.play(-1)
    ren = font.render("Paddle Mans Evloutionary Grinding Adventure", True, white)
    screen.blit(ren, (30, 100))
    ren = font.render("PRESS SPACE", True, white)
    screen.blit(ren, (225, 500))
    pygame.display.update()
    key_wait()
    ren = font.render("AKA", True, white)
    screen.blit(ren, (290, 165))
    pygame.display.update()
    key_wait()
    ren = titlefont.render("Pong Legacy", True, white)
    screen.blit(ren, (150, 220))
    pygame.display.update()
    key_wait()
    pygame.mixer.music.fadeout(2500)

def run_game():
    handle_coins()
    handle_paddles()
    playball.move()
    playball.col_check()
    render_all()
    pygame.display.update()
    screen.fill(black)
    clock.tick(60)

def initialize_game():
    global clock
    clock = pygame.time.Clock()
    global player1
    global player2
    player1 = paddle(10, 320, 1, 1, 20, 2, 1)
    player2 = paddle(615, 320, 12, 12, 50, 5, 1)
    global playball
    playball = ball(320, 320, 1, 1, 10)
    global coinlist
    coinlist = []
    global score
    score = 0

    global upgradelist
    upgradelist = []
    speedup = upgrade(10, 10, speed_up, "Paddle Speed increase", 0)
    sizeup = upgrade(10, 10, size_up, "Paddle length increase", 0)
    healthup = upgrade(30, 30, health_up, "Max life plus 1", 0)
    upgradelist.append(sizeup)
    upgradelist.append(speedup)
    upgradelist.append(healthup)
    restart()

def restart():
    for object in coinlist:
        coinlist.remove(object)
    render_all()
    pygame.time.wait(1000)
    ren = font.render("3", True, white)
    screen.blit(ren, (320, 280))
    pygame.display.update()
    play_sound("bounce")
    pygame.time.wait(1000)
    pygame.draw.rect(screen, black, (320, 280, 30, 30), 0)
    ren = font.render("2", True, white)
    screen.blit(ren, (320, 280))
    pygame.display.update()
    play_sound("bounce")
    pygame.time.wait(1000)
    pygame.draw.rect(screen, black, (320, 280, 30, 30), 0)
    ren = font.render("1", True, white)
    screen.blit(ren, (320, 280))
    pygame.display.update()
    play_sound("bounce")
    pygame.time.wait(1000)
    play_sound("coin")
    pygame.draw.rect(screen, black, (320, 280, 30, 30), 0)

    player1.y = 320
    player2.y = 320
    start = random.randrange(1, 4, 1)
    if start == 1 or start == 2:
        playball.dirX = 3
    if start == 3:
        playball.dirX = -3
    playball.dirY = random.random()*2
    playball.x = 320
    playball.y = 320
def render_all():
    heartX = 0
    heartY = 0
    for heartX in range (player1.health):
        screen.blit(heart, (heartX * 35 + 10, heartY))
    for heartX in range (player2.health):
        HDX = heartX*35 + 35
        if heartX > 7:
            heartY = 40
            HDX -= 9 * 35
        screen.blit(heart, (WINSIZE[0] - HDX, heartY))
    for object in coinlist:
        object.draw()
    player1.render()
    player2.render()
    playball.render()
    text = (str(score))
    ren = font.render(text, True, white)
    screen.blit(ren, (310, 10))

def handle_coins():
    #move and check if a coin has collision or is off the map
    for object in coinlist:
        object.move()
        if object.x < 0 - object.size:
            coinlist.remove(object)
            del object
        elif object.x < 50:
            if object.col_check():
                coinlist.remove(object)
                del object

def play_sound(event):
    #play sound based on what event is given when called
    if event == "bounce":
        bump1.play()
    if event == "hit":
        hit1.play()
    if event == "coin":
        coin1.play()

def you_died():
    #render the you died text and send you to the store
    ren = font.render("You Died", True, white)
    screen.blit(ren, (300, 280))
    pygame.display.update()
    pygame.time.wait(1000)
    store()

class upgrade:
    #class for upgrade, pretty simple
    def __init__(self, cost, costInc, function, name, level):
        self.cost = cost
        self.costInc = costInc
        self.function = function
        self.name = name
        self.level = level

def store():
    pygame.mixer.music.set_volume(.1)
    pygame.mixer.music.play(-1)
    pygame.key.set_repeat(100, 100)
    pos = 0
    instore = 1
    while instore:
        render_store(pos)
        pygame.display.update()
        screen.fill(black)
        pos, instore = store_input(pos, instore)
        if pos > 2:
            pos = 0
        if pos < 0:
            pos = 2
        clock.tick(60)
    pygame.key.set_repeat()

def render_store(pos):
    x = 200
    y = 150
    #draw the upgrade options from the upgrade list and draw a box beside the option you are on
    pygame.draw.rect(screen, white, (x - 30, y + 30 * pos, 20, 20), 0)
    for object in upgradelist:
        text = ("lv" + str(object.level) + " " + object.name + " " + str(object.cost))
        ren = font.render(text, True, white)
        screen.blit(ren, (x, y))
        y += 30
    text = (str(score))
    ren = font.render(text, True, white)
    screen.blit(ren, (310, 10))
    ren = font.render("Press Space to Upgrade", True, white)
    screen.blit(ren, (200, 450))
    ren = font.render("Press Enter to Begin Anew", True, white)
    screen.blit(ren, (200, 510))

def store_input(pos, instore):
    #cycle through upgrade options, pos is your cursor positin in the store
    global score
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); sys.exit()
        if event.type == KEYDOWN:
            if event.key == K_UP:
                pos -= 1
            if event.key == K_DOWN:
                pos += 1
            if event.key == K_SPACE:
                #space to buy and run the upgrade function if you can afford
                if score >= upgradelist[pos].cost:
                    score -= upgradelist[pos].cost
                    upgradelist[pos].cost += upgradelist[pos].costInc
                    upgradelist[pos].level += 1
                    upgradelist[pos].function()
                    play_sound("coin")
                else:
                    play_sound("bounce")
            #enter to go back and play again
            if event.key == K_RETURN:
                player1.health = player1.maxhealth
                player2.health = player2.maxhealth
                player1.y = 300
                player2.y = 300
                instore = 0
                score = 0
                pygame.mixer.music.fadeout(2500)
    return(pos, instore)

def speed_up():
    player1.speed += .5
def size_up():
    player1.length += 5
def health_up():
    player1.maxhealth += 1

def handle_paddles():
    player1.input()
    player2.ai_move()

def win():
    pygame.mixer.music.play(-1)
    ren = titlefont.render("You Defeated", True, white)
    screen.blit(ren, (150, 220))
    pygame.display.update()
    ren = font.render("Press Space to exit", True, white)
    screen.blit(ren, (200, 300))
    pygame.display.update()
    key_wait()
    pygame.quit(); sys.exit()

def key_wait():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                return

main()


from hitchbuildpg.server import PostgresServer
from commandlib import Command
from path import Path
import hitchbuild

class PostgresDatafiles(hitchbuild.HitchBuild):
    def __init__(self, build_path, postgresapp, databuild):
        self.buildpath = Path(build_path).abspath()
        self._databuild = databuild
        self.fingerprint_path = self.buildpath / "fingerprint.txt"
        self.postgresapp = self.dependency(postgresapp)

    @property
    def workingpath(self):
        return self.buildpath / "working"

    @property
    def snapshotpath(self):
        return self.buildpath / "snapshot"

    @property
    def postgres(self):
        return self.postgresapp.build.bin.postgres.with_trailing_args(
            "-D",
            self.workingpath,
            "--unix_socket_directories={0}".format(self.workingpath),
            "--log_destination=stderr",
        )

    @property
    def psql(self):
        return self.postgresapp.build.bin.psql("--host", self.workingpath)

    def server(self):
        return PostgresServer(self)

    def build(self):
        if self.incomplete() or self.postgresapp.rebuilt:
            self.postgresapp.build.ensure_built()
            self.buildpath.rmtree(ignore_errors=True)
            self.buildpath.mkdir()
            self.workingpath.mkdir()
            self.snapshotpath.mkdir()
            self.postgresapp.build.bin.initdb(self.workingpath).run()
            self._databuild.from_datafiles(self).build()
            self._rsync(self.workingpath, self.snapshotpath)
            self.refingerprint()
        else:
            self._rsync(self.snapshotpath, self.workingpath)

    def _rsync(self, from_path, to_path):
        Command("rsync")(
            "--del",
            "-av",
            # Trailing slash so contents are moved not whole dir
            from_path + "/",
            to_path,
        ).run()

    def clean(self):
        if self.buildpath.exists():
            self.buildpath.rmtree()


import json

from flask import jsonify, request, current_app
from flask.views import MethodView

from . import books
from .models import Publisher
from .serializers import pub_schema, pubs_schema
from books.app import db

# crud
##########
# Publishers
##########

# list
@books.route('/api/publishers', methods=['GET'])
def get_publishers():
    query = Publisher.query.all()
    result = pubs_schema.dump(query)
    return jsonify(result.data)
    # return query

@books.route('/api/publishers_uc', methods=['GET'])
def get_publishers_uc():
    query = Publisher.query.all()
    result = pubs_schema.dump(query)
    return jsonify(result.data)

# get
@books.route('/api/publishers/<int:id>', methods=['GET'])
def get_publisher(id):
    query = Publisher.query.filter_by(id=id).first()
    result = pub_schema.dump(query)
    return jsonify(result.data)
    # TODO add not found exception

# add
@books.route('/api/publishers/add', methods=['POST'])
def create_publisher(**kwargs):
    # TODO check for duplicate b4 adding to db
    name = request.json['name']
    # email = request.json['email']

    new_pub = Publisher(name, **kwargs)

    db.session.add(new_pub)
    db.session.commit()

    result = pub_schema.dump(new_pub).data
    return jsonify(result)

# update
@books.route('/api/publishers/edit/<int:id>', methods=['PUT'])
def update_publisher(id):
    pub = Publisher.query.filter_by(id=id).first()

    name = request.json['name']
    # email = request.json['email']

    pub.name = name

    db.session.commit()

    result = pub_schema.dump(pub).data
    return jsonify(result)

# delete
@books.route('/api/publishers/del/<int:id>', methods=('DELETE',))
# @jwt_required()
def delete_publisher(id):
    pub = Publisher.query.filter_by(id=id).first()
    db.session.delete(pub)
    db.session.commit()
    return 'Deleted', 200

##########
# Authors
##########

##########
# Books
##########


import logging
from abc import ABC, abstractmethod
import aioftp
import aiohttp
import aiosnmp
from jinja2 import Template
from scrapli.driver.core import AsyncIOSXEDriver

from ..remote_apis.snmp import DeviceSNMP
from .snmp import get_port_vlans, bytes_to_portlist, portlist_to_bytes
from ..db.models.ztp import Model, Entry
from .netbox import get_vlan, get_default_gateway
from .ftp import get_file_content, upload_file

class BaseInterface(ABC):
    @abstractmethod
    def __init__(self, ip: str, interface: str, management_vlan: int):
        raise NotImplementedError

    @abstractmethod
    async def switch_to_management(self):
        raise NotImplementedError

    @abstractmethod
    async def switch_back(self):
        raise NotImplementedError

    @abstractmethod
    async def get_untagged(self):
        raise NotImplementedError

class CiscoInterface(BaseInterface):
    def __init__(self, ip: str,
                 interface: str,
                 management_vlan: int,
                 terminal: AsyncIOSXEDriver):
        self.ip: str = ip
        self.interface: str = interface
        self.management_vlan = str(management_vlan)
        self.untagged: str | None = None
        self.terminal: AsyncIOSXEDriver | None = terminal

    async def get_untagged(self):
        resp = await self.terminal.send_command(
            f'show run int {self.interface}'
        )
        native = list(
            filter(lambda x: x.startswith('switchport trunk native vlan'),
                   map(str.strip, resp.result.split('\n'))))
        if native:
            self.untagged = native[0].split(' ')[-1]
            return self.untagged

    async def switch_to_management(self):
        commands = [
            f'interface {self.interface}',
            f'switchport trunk native vlan {self.management_vlan}'
        ]
        await self.terminal.send_configs(commands)

    async def switch_back(self):
        if self.untagged:
            commands = [
                f'interface {self.interface}',
                f'switchport trunk native vlan {self.untagged}'
            ]
        else:
            commands = [
                f'interface {self.interface}',
                f'no switchport trunk native vlan'
            ]
        await self.terminal.send_configs(commands)

    async def __aenter__(self):
        await self.terminal.open()

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.terminal.close()

class DlinkInterface(BaseInterface):
    def __init__(self, ip: str,
                 interface: str,
                 management_vlan: int,
                 snmp_factory: DeviceSNMP):
        self.ip: str = ip
        self.interface: str = interface
        self.management_vlan: str = str(management_vlan)
        self.untagged: list | None = None
        self.snmp_factory: DeviceSNMP = snmp_factory
        self.snmp: aiosnmp.Snmp | None = None

    async def switch_to_management(self):
        if self.untagged:
            for vlan in self.untagged:
                # Remove interface from "untagged ports"
                resp = await self.snmp.get(f'1.3.6.1.2.1.17.7.1.4.3.1.4.{vlan}')
                hexlen = len(resp[0].value) * 2
                plist = bytes_to_portlist(resp[0].value)
                plist.remove(int(self.interface))
                await self.snmp.set([(f'1.3.6.1.2.1.17.7.1.4.3.1.4.{vlan}',
                                      portlist_to_bytes(plist, hexlen))])
                # Remove interface from "all ports"
                resp = await self.snmp.get(f'1.3.6.1.2.1.17.7.1.4.3.1.2.{vlan}')
                hexlen = len(resp[0].value) * 2
                plist = bytes_to_portlist(resp[0].value)
                plist.remove(int(self.interface))
                await self.snmp.set([(f'1.3.6.1.2.1.17.7.1.4.3.1.2.{vlan}',
                                      portlist_to_bytes(plist, hexlen))])
        # Add interface to management "all ports"
        resp = await self.snmp.get(
            f'1.3.6.1.2.1.17.7.1.4.3.1.2.{self.management_vlan}')
        hexlen = len(resp[0].value) * 2
        plist = bytes_to_portlist(resp[0].value)
        plist = list(set(plist) | {int(self.interface)})
        await self.snmp.set(
            [(f'1.3.6.1.2.1.17.7.1.4.3.1.2.{self.management_vlan}',
              portlist_to_bytes(plist, hexlen))])
        # Add interface to management "untagged ports"
        resp = await self.snmp.get(
            f'1.3.6.1.2.1.17.7.1.4.3.1.4.{self.management_vlan}')
        hexlen = len(resp[0].value) * 2
        plist = bytes_to_portlist(resp[0].value)
        plist = list(set(plist) | {int(self.interface)})
        await self.snmp.set(
            [(f'1.3.6.1.2.1.17.7.1.4.3.1.4.{self.management_vlan}',
              portlist_to_bytes(plist, hexlen))])

    async def switch_back(self):
        # Remove interface from management "untagged ports"
        resp = await self.snmp.get(
                f'1.3.6.1.2.1.17.7.1.4.3.1.4.{self.management_vlan}')
        hexlen = len(resp[0].value) * 2
        plist = bytes_to_portlist(resp[0].value)
        plist.remove(int(self.interface))
        await self.snmp.set(
            [(f'1.3.6.1.2.1.17.7.1.4.3.1.4.{self.management_vlan}',
              portlist_to_bytes(plist, hexlen))])
        # Add interface to management "all ports" just to be sure
        resp = await self.snmp.get(
                f'1.3.6.1.2.1.17.7.1.4.3.1.2.{self.management_vlan}')
        hexlen = len(resp[0].value) * 2
        plist = bytes_to_portlist(resp[0].value)
        plist = list(set(plist) | {int(self.interface)})
        await self.snmp.set(
            [(f'1.3.6.1.2.1.17.7.1.4.3.1.2.{self.management_vlan}',
              portlist_to_bytes(plist, hexlen))])
        if self.untagged:
            for vlan in self.untagged:
                # Add interface to "all ports"
                resp = await self.snmp.get(
                    f'1.3.6.1.2.1.17.7.1.4.3.1.2.{vlan}')
                hexlen = len(resp[0].value) * 2
                plist = bytes_to_portlist(resp[0].value)
                plist = list(set(plist) | {int(self.interface)})
                await self.snmp.set([(f'1.3.6.1.2.1.17.7.1.4.3.1.2.{vlan}',
                                      portlist_to_bytes(plist, hexlen))])
                # Add interface to "untagged ports"
                resp = await self.snmp.get(
                    f'1.3.6.1.2.1.17.7.1.4.3.1.4.{vlan}')
                hexlen = len(resp[0].value) * 2
                plist = bytes_to_portlist(resp[0].value)
                plist = list(set(plist) | {int(self.interface)})
                await self.snmp.set([(f'1.3.6.1.2.1.17.7.1.4.3.1.4.{vlan}',
                                      portlist_to_bytes(plist, hexlen))])

    async def get_untagged(self):
        port_vlans = await get_port_vlans(self.ip, self.snmp_factory)
        untagged = port_vlans[self.interface]['untagged']
        if untagged:
            self.untagged = untagged
            return self.untagged

    async def __aenter__(self):
        self.snmp = self.snmp_factory(self.ip)
        await self.snmp.__aenter__()

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.snmp.__aexit__(exc_type, exc_val, exc_tb)
        self.snmp = None

def make_config_from_template(template: str, **kwargs):
    template = Template(template)
    config = template.render(**kwargs)
    return config

async def gather_initial_configuration_parameters(
        entry: Entry, model: Model, netbox: aiohttp.ClientSession):
    vlan_id, vlan_name = await get_vlan(entry.ip_address.exploded, netbox)
    default_gateway = await get_default_gateway(entry.ip_address.exploded,
                                                netbox)
    return {
        'configuration_prefix': model.configuration_prefix,
        'portcount': model.portcount,
        'ip_address': entry.ip_address.exploded,
        'management_vlan_id': vlan_id,
        'management_vlan_name': vlan_name,
        'subnet_mask': default_gateway.netmask.exploded,
        'gateway': default_gateway.ip.exploded,
    }

async def generate_initial_config(entry: Entry, model: Model,
                                  base_folder: str,
                                  template_filename: str,
                                  config_filename: str,
                                  netbox: aiohttp.ClientSession,
                                  ftp: aioftp.Client):
    config_filename = base_folder + config_filename
    template_filename = base_folder + template_filename
    template = await get_file_content(template_filename, ftp)
    params = await gather_initial_configuration_parameters(entry, model, netbox)
    config = make_config_from_template(template, **params)
    await upload_file(config_filename, config, ftp)

async def gather_full_configuration_parameters(
        entry: Entry, model: Model, netbox: aiohttp.ClientSession):
    vlan_id, vlan_name = await get_vlan(entry.ip_address.exploded, netbox)
    return {
        'portcount': model.portcount,
        'management_vlan_tag': int(vlan_id),
        'port_settings': entry.modified_port_settings,
        'vlan_settings': entry.modified_vlan_settings,
        'ip_address': entry.ip_address.exploded
    }

async def generate_full_config(entry: Entry, model: Model,
                               base_folder: str,
                               template_filename: str,
                               config_filename: str,
                               netbox: aiohttp.ClientSession,
                               ftp: aioftp.Client):
    config_filename = base_folder + config_filename
    template_filename = base_folder + template_filename
    template = await get_file_content(template_filename, ftp)
    params = await gather_full_configuration_parameters(entry, model, netbox)
    config = make_config_from_template(template, **params)
    await upload_file(config_filename, config, ftp)


import os
import readline

# CAT

def cat(file):
    try:
        with open(file, "r") as f:
            read = f.read()
            print(read)
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")

    except Exception as e:
        print(e)

# LS

def ls(path="."):
    try:
        files = os.listdir(path)
        for file in files:
            print("-", file)
    except FileNotFoundError:
        print(f"ERROR: Directory '{path}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for directory '{path}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# CD

def cd(path):
    try:
        os.chdir(path)
    except FileNotFoundError or WindowsError:
        print(f"ERROR: Directory '{path}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for directory '{path}'.")
    except Exception as e:
        print(f"ERROR: {e}")

def complete_cd(text, state):
    dirs = [d for d in os.listdir('.') if os.path.isdir(d)]
    matches = [d for d in dirs if d.startswith(text)]
    if state < len(matches):
        return matches[state]
    else:
        return None

readline.set_completer_delims(' \t\n')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete_cd)

# CLEAR

def clear():
    os.system("cls")

# ECHO

def echo(text):
    print(text)

# MKDIR

def mkdir(folder):
    try:
        os.mkdir(folder)
    except FileExistsError:
        print(f"ERROR: Directory '{folder}' already exists.")
    except FileNotFoundError:
        print(f"ERROR: Directory '{folder}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for directory '{folder}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# RMDIR

def rmdir(folder):
    try:
        os.rmdir(folder)
    except FileNotFoundError:
        print(f"ERROR: Directory '{folder}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for directory '{folder}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# RM

def rm(file):
    try:
        os.remove(file)
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{file}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# RENAME

def rename(old, new):
    try:
        os.rename(old, new)
    except FileNotFoundError:
        print(f"ERROR: File '{old}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{old}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# HELP

def help():
    print("Commands:")
    print("  cat <file> - print file contents")
    print("  cd <path> - change directory")
    print("  clear - clear screen")
    print("  echo <text> - print text")
    print("  exit - exit shell")
    print("  help - print this help")
    print("  ls - list directory contents")
    print("  mkdir <folder> - create directory")
    print("  rename <old> <new> - rename file")
    print("  rm <file> - remove file")
    print("  rmdir <folder> - remove directory")
    print("  set <var> <value> - set environment variable")
    print("  unset <var> - unset environment variable")
    print("  whoami - print current user")

# SET

def set(var, value):
    try:
        os.environ[var] = value
    except Exception as e:
        print(f"ERROR: {e}")

# UNSET

def unset(var):
    try:
        del os.environ[var]
    except Exception as e:
        print(f"ERROR: {e}")

# WHOAMI

def whoami():
    print(os.getenv("USERNAME"))

# PATH

def path():
    print(os.getenv("PATH"))

# PWD

def pwd():
    print(os.getcwd())

# CP

def cp(old, new):
    try:
        with open(old, "r") as f:
            read = f.read()
            with open(new, "w") as f2:
                f2.write(read)
    except FileNotFoundError:
        print(f"ERROR: File '{old}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{old}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# MV

def mv(old, new):
    try:
        os.rename(old, new)
    except FileNotFoundError:
        print(f"ERROR: File '{old}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{old}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# DATE

def date():
    import datetime
    print(datetime.datetime.now())

# GREP

def grep(file, pattern):
    try:
        with open(file, "r") as f:
            lines = f.readlines()
            for line in lines:
                if pattern in line:
                    print(line)
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{file}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# FIND

def find(path, pattern):
    try:
        files = os.listdir(path)
        for file in files:
            if pattern in file:
                print(file)
    except FileNotFoundError:
        print(f"ERROR: Directory '{path}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for directory '{path}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# TREE

def tree(path="."):
    try:
        files = os.listdir(path)
        for file in files:
            print(file)
            if os.path.isdir(file):
                tree(file)
    except FileNotFoundError:
        print(f"ERROR: Directory '{path}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for directory '{path}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# CHMOD

def chmod(file, mode):
    try:
        os.chmod(file, mode)
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{file}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# CHOWN

def chown(file, user):
    try:
        os.chown(file, user)
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{file}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# HISTORY

def history():
    try:
        with open("history.txt", "r") as f:
            lines = f.readlines()
            for line in lines:
                print(line)
    except FileNotFoundError:
        print(f"ERROR: File 'history.txt' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file 'history.txt'.")
    except Exception as e:
        print(f"ERROR: {e}")

# ZIP

def zip(file, zip):
    try:
        import zipfile
        with zipfile.ZipFile(zip, "w") as zip_file:
            zip_file.write(file)
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{file}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# UNZIP

def unzip(zip, path):
    try:
        import zipfile
        with zipfile.ZipFile(zip, "r") as zip_file:
            zip_file.extractall(path)
    except FileNotFoundError:
        print(f"ERROR: File '{zip}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{zip}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# PYTHON

def python(file):
    try:
        import subprocess
        subprocess.run(["python", file])
    except FileNotFoundError:
        print(f"ERROR: File '{file}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for file '{file}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# PING

def ping(host):
    try:
        import subprocess
        subprocess.run(["ping", host])
    except FileNotFoundError:
        print(f"ERROR: Host '{host}' not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for host '{host}'.")
    except Exception as e:
        print(f"ERROR: {e}")

# IFCONFIG

def ifconfig():
    try:
        import subprocess
        subprocess.run(["ipconfig"])
    except FileNotFoundError:
        print(f"ERROR: ipconfig not found.")
    except PermissionError:
        print(f"ERROR: Permission denied for ipconfig.")
    except Exception as e:
        print(f"ERROR: {e}")


import pandas as pd
import random
from sklearn import metrics
# --------------
from transformers import BertTokenizer
from transformers.configuration_albert import AlbertConfig
from transformers.modeling_albert import AlbertForMaskedLM
from transformers.modeling_albert import load_tf_weights_in_albert
# --------------
from rdkit import Chem
from torch_geometric.data import DataLoader
from torch.nn.utils.rnn import pad_sequence
from torch_geometric.utils import to_dense_batch
import torch
from typing import Sequence, Tuple, List, Union

from ligand_graph_features import *
from data_tool_box_DRD import load_json

def sample_minibatch_maml(args,train_maml_pfam,train_maml):
    batch_data_maml = []
    batch_pfam_maml = np.random.choice(train_maml_pfam,args['task_num'])

    for task_pfam in batch_pfam_maml:
    # for task_pfam in train_maml_pfam:
        per_task ={}
        task_data = train_maml[task_pfam]
        for s in ['spt','qry']:
            df = pd.DataFrame()
            df= df.append(  task_data['pos'].sample(args['k_' +s], replace=True))
            df= df.append(  task_data['neg'].sample(args['k_'+s], replace=True))
            per_task[s]= df
        batch_data_maml.append(per_task)
    return batch_data_maml

def sample_minibatch_classic(args,train_classic):
    batch_pos = train_classic['pos'].sample(args['classic_batch_size'])
    batch_neg = train_classic['neg'].sample(args['classic_batch_size'])

    batch_classic = pd.concat([batch_pos, batch_neg])
    return batch_classic
def sample_minibatch_test(args,train_classic):
    batch_pos = train_classic['pos'].sample(args['test_batch_size_pos'])
    batch_neg = train_classic['neg'].sample(args['test_batch_size_neg'])

    batch_classic = pd.concat([batch_pos, batch_neg])
    return batch_classic
# =======================================================
#             model related
# =======================================================
def load_DISAE(cwd):
    all_config = load_json( 'DTI_config.json')
    albertconfig = AlbertConfig.from_pretrained(cwd+ all_config['DISAE']['albertconfig'])
    m = AlbertForMaskedLM(config=albertconfig)
    m = load_tf_weights_in_albert(m, albertconfig, cwd+all_config['DISAE']['albert_pretrained_checkpoint'])
    prot_descriptor = m.albert
    prot_tokenizer = BertTokenizer.from_pretrained(cwd+all_config['DISAE']['albertvocab'])
    return  prot_descriptor,prot_tokenizer
# =======================================================
#             maml
# =======================================================
def get_repr_DTI_MAML(batch_data_pertask,mode,prot_tokenizer,chem_dict,protein_dict,args):
    batch_chem_graphs_spt,batch_protein_tokenized_spt = get_repr_DTI(batch_data_pertask[mode],
                                                                    prot_tokenizer,
                                                                    chem_dict,protein_dict,
                                                                    'DISAE')
    batch_chem_graphs_spt = batch_chem_graphs_spt.to(args['device'])
    batch_protein_tokenized_spt = batch_protein_tokenized_spt.to(args['device'])

    y_spt = torch.LongTensor(list(batch_data_pertask[mode]['Activity'].values)).to(args['device'])
    return batch_chem_graphs_spt,batch_protein_tokenized_spt,y_spt

def core_batch_prediction_MAML(batch_data_pertask, args, tokenizer,mode,loss_fn,
                               chem_dict, protein_dict, model,detach=False):

    batch_chem_graphs_spt, batch_protein_tokenized_spt, y_spt = get_repr_DTI_MAML(batch_data_pertask,
                                                              mode,
                                                              tokenizer, chem_dict, protein_dict,
                                                              args)

    batch_logits = model(batch_protein_tokenized_spt, batch_chem_graphs_spt)
    loss = loss_fn(batch_logits,y_spt)
    if detach == True:
        batch_logits = batch_logits.detach().cpu()
        y_spt = y_spt.detach().cpu()

    return batch_logits, y_spt,loss
def get_repr_DTI_0shot(batch_data,tokenizer,chem_dict,protein_dict,args):
    #  . . . .  chemicals  . . . .
    chem_smiles = chem_dict[batch_data['InChIKey'].values.tolist()].values.tolist()
    chem_graph_list = []
    for smiles in chem_smiles:
        mol = Chem.MolFromSmiles(smiles)
        graph = mol_to_graph_data_obj_simple(mol)
        chem_graph_list.append(graph)
    chem_graphs_loader = DataLoader(chem_graph_list, batch_size=batch_data.shape[0],
                                    shuffle=False)
    for batch in chem_graphs_loader:
        chem_graphs = batch
    #  . . . .  proteins  . . . .
    # if prot_descriptor_choice =='DISAE':
    uniprot_list = batch_data['uniprot+pfam'].values.tolist()
    protein_tokenized = torch.tensor([tokenizer.encode(protein_dict[uni]) for uni in uniprot_list  ])
    #  . . . .  to return  . . . .
    chem_graphs = chem_graphs.to(args['device'])
    protein_tokenized = protein_tokenized.to(args['device'])
    label = torch.LongTensor(list(batch_data['Activity'].values)).to(args['device'])
    return chem_graphs, protein_tokenized,label

def core_batch_prediction_0shot(batch_data_pertask, args, tokenizer,loss_fn,
                               chem_dict, protein_dict, model,detach=True):
    batch_chem_graphs_spt, batch_protein_tokenized_spt, y_spt = get_repr_DTI_0shot(batch_data_pertask,
                                                              tokenizer, chem_dict, protein_dict,
                                                              args)

    batch_logits = model(batch_protein_tokenized_spt, batch_chem_graphs_spt)
    loss = loss_fn(batch_logits,y_spt)
    if detach == True:
        batch_logits = batch_logits.detach().cpu()
        y_spt = y_spt.detach().cpu()
    return batch_logits, y_spt,loss

def get_repr_DTI(batch_data,tokenizer,chem_dict,protein_dict,prot_descriptor_choice):
    #  . . . .  chemicals  . . . .
    chem_smiles = chem_dict[batch_data['InChIKey'].values.tolist()].values.tolist()
    chem_graph_list = []
    for smiles in chem_smiles:
        mol = Chem.MolFromSmiles(smiles)
        graph = mol_to_graph_data_obj_simple(mol)
        chem_graph_list.append(graph)
    chem_graphs_loader = DataLoader(chem_graph_list, batch_size=batch_data.shape[0],
                                    shuffle=False)
    for batch in chem_graphs_loader:
        chem_graphs = batch
    #  . . . .  proteins  . . . .
    if prot_descriptor_choice =='DISAE':
        uniprot_list = batch_data['uniprot+pfam'].values.tolist()
        protein_tokenized = torch.tensor([tokenizer.encode(protein_dict[uni]) for uni in uniprot_list  ])

    return chem_graphs, protein_tokenized

#------------------
#  read data
#------------------

def load_training_data(exp_path,debug_ratio):
    def load_data(exp_path,file,debug_ratio):
        dataset = pd.read_csv(exp_path +file)
        cut = int(dataset.shape[0] * debug_ratio)
        print(file[:-3] + ' size:', cut)
        return dataset.iloc[:cut,:]

    train = load_data(exp_path,'train.csv',debug_ratio)
    dev   = load_data(exp_path,'dev.csv',debug_ratio)
    test  = load_data(exp_path,'test.csv',debug_ratio)

    return train, dev, test

#------------------
#  evaluate
#------------------

def evaluate_binary_predictions(label, predprobs):
    probs = np.array(predprobs)
    predclass = np.argmax(probs, axis=1)
    # --------------------------by label---
    bothF1 = metrics.f1_score(label, predclass, average=None)
    bothprecision = metrics.precision_score(label, predclass, average=None)
    bothrecall = metrics.recall_score(label, predclass, average=None)
    class0 = [bothF1[0], bothprecision[0], bothrecall[0]]
    class1 = [bothF1[1], bothprecision[1], bothrecall[1]]
    # -------------------------overall---
    f1 = metrics.f1_score(label, predclass, average='weighted')
    fpr, tpr, thresholds = metrics.roc_curve(label, probs[:, 1], pos_label=1)
    auc = metrics.auc(fpr, tpr)
    prec, reca, thresholds = metrics.precision_recall_curve(label, probs[:, 1], pos_label=1)
    aupr = metrics.auc(reca, prec)

    overall = [f1,auc,aupr]
    return overall,class0,class1


import json
import logging
import os
import time

from datalad.interface.base import Interface
from datalad.interface.base import build_doc
from datalad.interface.results import get_status_dict
from datalad.support.exceptions import (
    CapturedException,
    InvalidGitRepositoryError,
    MissingExternalDependency,
)

__docformat__ = "restructuredtext"

lgr = logging.getLogger("datalad.local.check_dates")

def _git_repos(paths):
    for path in paths:
        for root, dirs, _ in os.walk(path):
            if any(d == ".git" for d in dirs):
                yield root

def _parse_date(date):
    if date.startswith("@"):  # unix timestamp
        timestamp = int(date[1:])
    else:
        try:
            import dateutil.parser
        except ImportError:
            raise MissingExternalDependency(
                "python-dateutil",
                msg="This package is required to parse non-timestamp dates")

        from calendar import timegm
        # Note: datetime.timestamp isn't available in Python 2.
        try:
            timestamp = timegm(dateutil.parser.parse(date).utctimetuple())
        except TypeError as exc:
            # Make older dateutil versions return a consistent error for
            # invalid dates.
            raise ValueError(exc)
    return timestamp

@build_doc
class CheckDates(Interface):
    """Find repository dates that are more recent than a reference date.

    The main purpose of this tool is to find "leaked" real dates in
    repositories that are configured to use fake dates. It checks dates from
    three sources: (1) commit timestamps (author and committer dates), (2)
    timestamps within files of the "git-annex" branch, and (3) the timestamps
    of annotated tags.
    """
    from datalad.interface.base import eval_results
    import datalad.support.ansi_colors as ac
    from datalad.support.constraints import EnsureChoice, EnsureNone, EnsureStr
    from datalad.support.param import Parameter

    result_renderer = "tailored"

    @staticmethod
    def custom_result_renderer(res, **kwargs):
        """Like 'json_pp', but skip non-error results without flagged objects.
        """
        # FIXME: I think the proper way to do this is to use 'result_filter',
        # but I couldn't seem to get eval_results to detect the filter when I
        # used
        #
        #      result_renderer = "json_pp"
        #      result_filter = lambda x: ...
        #
        # Also, I want to keep the "message" key for errors.
        from datalad.ui import ui
        to_render = {}
        if res["status"] == "error":
            to_render = dict(res.items())
        elif "report" in res and res["report"]["objects"]:
            to_render = {k: v for k, v in res.items()
                         if k not in ["status", "message", "logger"]}
        if to_render:
            ui.message(json.dumps(to_render, sort_keys=True, indent=2,
                                  default=str)
                       )

    _params_ = dict(
        paths=Parameter(
            args=("paths",),
            metavar="PATH",
            nargs="*",
            doc="""Root directory in which to search for Git repositories. The
            current working directory will be used by default.""",
            constraints=EnsureStr() | EnsureNone()),
        reference_date=Parameter(
            args=("-D", "--reference-date"),
            metavar="DATE",
            doc="""Compare dates to this date. If dateutil is installed, this
            value can be any format that its parser recognizes. Otherwise, it
            should be a unix timestamp that starts with a "@". The default
            value corresponds to 01 Jan, 2018 00:00:00 -0000.""",
            constraints=EnsureStr()),
        revs=Parameter(
            args=("--rev",),
            dest="revs",
            action="append",
            metavar="REVISION",
            doc="""Search timestamps from commits that are reachable from [PY:
            these revisions PY][CMD: REVISION CMD]. Any revision specification
            supported by :command:`git log`, including flags like --all and
            --tags, can be used.[CMD:  This option can be given multiple times.
            CMD]"""),
        annex=Parameter(
            args=("--annex",),
            doc="""Mode for "git-annex" branch search. If 'all', all blobs
            within the branch are searched. 'tree' limits the search to blobs
            that are referenced by the tree at the tip of the branch. 'none'
            disables search of "git-annex" blobs.""",
            constraints=EnsureChoice("all", "tree", "none")),
        no_tags=Parameter(
            args=("--no-tags",),
            action="store_true",
            doc="""Don't check the dates of annotated tags."""),
        older=Parameter(
            args=("--older",),
            action="store_true",
            doc="""Find dates which are older than the reference date rather
            than newer."""),
    )

    @staticmethod
    @eval_results
    def __call__(paths,
                 *,
                 reference_date="@1514764800",
                 revs=None,
                 annex="all",
                 no_tags=False,
                 older=False):
        from datalad.support.repodates import check_dates

        which = "older" if older else "newer"

        try:
            ref_ts = _parse_date(reference_date)
        except ValueError as exc:
            lgr.error("Could not parse '%s' as a date", reference_date)
            ce = CapturedException(exc)
            yield get_status_dict("check_dates",
                                  status="error",
                                  message=str(ce),
                                  exception=ce)
            return

        lgr.info("Searching for dates %s than %s",
                 which,
                 time.strftime("%d %b %Y %H:%M:%S +0000", time.gmtime(ref_ts)))

        for repo in _git_repos(paths or ["."]):
            fullpath = os.path.abspath(repo)
            lgr.debug("Checking %s", fullpath)

            try:
                report = check_dates(repo,
                                     ref_ts,
                                     which=which,
                                     revs=revs or ["--all"],
                                     annex={"all": True,
                                            "none": False,
                                            "tree": "tree"}[annex],
                                     tags=not no_tags)
            except InvalidGitRepositoryError as exc:
                lgr.warning("Skipping invalid Git repo: %s", repo)
                continue

            yield get_status_dict(
                "check_dates",
                status="ok",
                path=fullpath,
                message=("Found {} dates" if report["objects"]
                         else "No {} dates found").format(which),
                report=report)


import time
from concurrent.futures import thread

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select

import json
from PIL import Image

from selenium.webdriver.support.wait import WebDriverWait

def get_properties(element):
    return {
        prop: element.value_of_css_property(prop)
        for prop in ['background-color', 'color', 'text-decoration']
    }

def reverse_properties(driver, element, before_properties):
    for prop in ['background-color', 'color', 'text-decoration']:
        #element.set_attribute(prop, before_properties[prop])
        driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2]);", element, prop, before_properties[prop])

def main():
    chrome_options = Options()
    #chrome_options.add_argument("--headless")
    chrome_options.add_argument("--window-size=1920,1080")

    driver = webdriver.Chrome(options=chrome_options)

    driver.get("https://www.arbeitsagentur.de/jobsuche/suche?angebotsart=1&id=16521-U4BTJIK9BEVF2ZYL-S")
    modal = WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.XPATH, '/html/body/bahf-cookie-disclaimer-dpl3')))
    WebDriverWait(modal.shadow_root, 120).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, 'button'))).click()

    WebDriverWait(driver, 60).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="listen-layout-button"]'))).click()
    WebDriverWait(driver, 60).until(
        EC.visibility_of_element_located((By.XPATH, '// *[ @ id = "Sortierung-dropdown-button"]'))).click()
    WebDriverWait(driver, 60).until(
        EC.visibility_of_element_located((By.XPATH, '// *[ @ id = "Sortierung-dropdown-item-2"]'))).click()

    #driver.implicitly_wait(120)
    #time.sleep(30)
    #modal.shadow_root.find_element(By.CSS_SELECTOR, 'button').click()
    #modal_container.get_attribute
    #modal.find_element(By.XPATH, '//*[@id="bahf-cookie-disclaimer-modal"]/div/div/div[3]/button[1]').click()
    list = WebDriverWait(driver, 60).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="ergebnisliste-liste-1"]')))
    #EC.visibility_of()
    #list = driver.find_element(By.XPATH, '//*[@id="ergebnisliste-liste-1"]')
    i = 0
    while (i <= 21):
        element = driver.find_element(By.XPATH, f'//*[@id="ergebnisliste-item-{i}"]')
        before_props = get_properties(element)

        element.send_keys(Keys.SPACE)
        reverse_properties(driver,element, before_props)
        #driver.execute_script("arguments[0].scrollIntoView(true);", element)
        #ActionChains(driver).move_to_element(element).perform()
        WebDriverWait(driver, 60).until(
            EC.visibility_of(element))
        time.sleep(30)
        #list = driver.find_element(By.XPATH, '//*[@id="ergebnisliste-liste-1"]')
        #time.sleep(30)
        list.screenshot(f"../output1/1000{i}.png")
        i +=4
        print(i)

    # i/3 * 4
    j = 0
    html_and_json_list = []
    while j <= 20:
        result_html = ""
        result_json = {}
        #k = int((j/3) *4)
        tid = 0
        for l in range(j+1,j+5):
            element = driver.find_element(By.XPATH, f'//*[@id="ergebnisliste-item-{l}"]')
            #result_html += element.get_attribute("innerHTML")
            entry_start = len(result_json.keys())
            result_json[f"{entry_start}"]={"rect": element.rect, "font": element.value_of_css_property("color"),
             "color": element.value_of_css_property("font")}

            child_list = element.find_elements(By.CSS_SELECTOR, "*")
            for child in child_list:
                driver.execute_script("arguments[0].setAttribute('tid',arguments[1])",child, tid)
                tid += 1
            for entry in range(len(child_list)):
                #driver.execute_script(f"child_list[entry].set_attribute('tid', '{tid}')")
                #tid += 1
                result_json[f"{entry_start + entry +1}"] = {"rect": child_list[entry].rect, "font": child_list[entry].value_of_css_property("color"),
                                    "color": child_list[entry].value_of_css_property("font")}

            result_html += element.get_attribute("innerHTML")
        with open(f"../output1/1000{j}.html", "w") as html, open(f"../output1/1000{j}.json", "w") as json_f:
            html.write(result_html)
            json.dump(result_json, json_f)
        j += 4
    # json_dict = {}
    # el_nr = 0
    # for page in range(1,len(pages)-1):
    #     driver.get(f"https://www.jobbörse.de/jobsuche/ajax/?page={page}sort=date&type=local")
    #     for element in driver.find_elements(By.CSS_SELECTOR, "*"):
    #         json_dict[f"{el_nr}"] = {"rect": element.rect, "font": element.value_of_css_property("color"),
    #                                  "color": element.value_of_css_property("font")}
    #         el_nr += 1
    #
    #     print(el_nr)
    #     driver.find_element(By.ID, "article").screenshot(f"../output1/1000{page}.png")
    with open(f"../output1/10001.html", "w") as html:
        html.write(driver.page_source)
        #json.dump(json_dict, json_f)

if __name__ == "__main__" :
    main()


import snscrape.modules.twitter as sntwitter
import pandas as pd
import text2emotion as te
import threading

def get_tweets(user):
    print("User: " + user)
    query = f"(from:{user}) since:2018-7-1 until:2022-11-30"

    # Creating list to append tweet data to
    tweets_list = []
    print("grabbing tweets")
    i = 0
    for tweet in sntwitter.TwitterSearchScraper(query).get_items():
        # print(tweet.json())
        tweets_list.append(
            [tweet.date, tweet.id, tweet.content, tweet.retweetCount, tweet.replyCount, tweet.likeCount, tweet.user.username])
        i += 1
        print(user, i)

    # Creating a dataframe from the tweets list above
    tweets_df = pd.DataFrame(tweets_list, columns=[
        'Datetime', 'Tweet Id', 'Text', 'Retweets', 'Replies', 'Likes', 'Username'])
    print(
        f"tweets grabbed, saving to csv, please wait, 'tweets{user}.csv' will be created")
    # export to csv
    tweets_df.to_csv(f'tweets{user}.csv', index=False)

list_of_users = ["GiuseppeConteIT"]
for user in list_of_users:
    t = threading.Thread(target=get_tweets, args=(user,))
    t.start()


# MOVE INTO src/video_embedder DIR TO TEST

from vid_embed import VideoEmbedder
import cv2
import numpy as np

if __name__ == '__main__':
    cap = cv2.VideoCapture(0)                               # camera
    model = cv2.imread('model.png')                         # model image
    carousel = [cv2.VideoCapture('video.mp4'), cv2.VideoCapture('video2.mp4')]                # overlay video(s)

    vidEmbedder = VideoEmbedder(cap, model, carousel)
    vidEmbedder.run()

    cap.release()
    cv2.destroyAllWindows()


import logging
import pandas as pd
import json
import pickle
import sklearn

LOGGER = logging.getLogger(__name__)

def execute(data: dict):
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logger.info('event parameter: {}'.format(data))
    # print("Received event: " + json.dumps(event, indent=2))
    body = data
    print("Received body:  " + str(body))
    try:
        return score(body)
    except Exception as e:
        logger.error(e)
        print(json.dumps({'error': str(e)}))
        return {
            'statusCode': 500,
            'body': json.dumps({'error': str(e)})
        }

def score(data):
    LOGGER.info("Hello World")
    feat_dict = data
    pid = feat_dict["applicant_id"]
    feat_dict.pop("applicant_id", None)
    feat_dict["credit_score"] = feat_dict["credit_score"] / 1000
    feat_dict["age"] = feat_dict["age"] / 85
    mod = pd.DataFrame([feat_dict])
    model = pickle.load(open("src/data_applicant_scoring/tenant_score_ml_model", 'rb'))
    ans = model.predict(mod)[0]
    print("tenant scoring: ")
    if ans > 1:
        fans = 100
    elif ans < 0:
        fans = 20
    elif 0.85 > ans:
        fans = ans * 100 + 7
    else:
        fans = ans * 100
    if fans < 25:
        level = 1
    elif fans < 45:
        level = 2
    elif fans < 60:
        level = 3
    elif fans < 80:
        level = 4
    else:
        level = 5
    return {
        'statusCode': 200,
        'body': json.dumps(dict({'applicant_id': str(pid), 'level': level, 'score': fans}))}

# score({"applicant_id": "BC5T2A09-ELE2-4HG0-AE74-4E2DF78A3E1D",
#         "credit_score": 678,
#         "eviction": 2,
#         "criminal": 1,
#         "age": 28})


"""Parser for virtual-machine sub-command."""
import argparse

from emulation_system.commands import EmulationSystemCommand

from .abstract_parser import AbstractParser
from .parser_utils import get_formatter

class EmulationSystemParser(AbstractParser):
    """Parser for virtual-machine sub-command."""

    @classmethod
    def get_parser(cls, parser: argparse.ArgumentParser) -> None:
        """Build parser for "emulation-system" command."""
        subparser = parser.add_parser(  # type: ignore
            "emulation-system",
            aliases=["em-sys"],
            formatter_class=get_formatter(),
            help="Create docker-compose files",
        )

        subparser.set_defaults(func=EmulationSystemCommand.from_cli_input)

        subparser.add_argument(
            "input_path",
            action="store",
            metavar="<input_path>",
            type=argparse.FileType("r"),
            help='Input path to read file from. Specify "-" to read from stdin.',
        )

        subparser.add_argument(
            "output_path",
            action="store",
            metavar="<output_path>",
            type=argparse.FileType("w"),
            help='Output path write compose file to. Specify "-" to write to stdout.',
        )

        subparser.add_argument(
            "--remote-only", action="store_true", help="Allow only remote source-types"
        )

        subparser.add_argument(
            "--dev", action="store_true", help="Create dev compose file"
        )


# The Nature of Code
# Daniel Shiffman
# http://natureofcode.com

import pygame
import random as rnd

width = 640
height = 360
window = pygame.display.set_mode((width,height))

def setup():
    global width
    global height

def draw():
    global width
    global height
    global window

    # Get a gaussian random number w/ mean of 0 and standard deviation of 1.0
    xloc = rnd.gauss(0, 1.0)

    sd = 60.0                       # Define a standard deviation
    mean = width/2                  # Define a mean value (middle of the screen along the x-axis)
    xloc = (xloc * sd) + mean       # Scale the gaussian random number by standard deviation and mean

    pygame.draw.ellipse(window, pygame.Color('white'), (xloc, height/2, 16, 16))  # Draw an ellipse at our "normal" random position
    pygame.display.update()
    pygame.time.delay(10)

setup()
while(True):
  draw()


from tkinter import EXCEPTION
from django.shortcuts import render
import requests
from .models import City
from .forms import CityForm
import os

apiId = os.environ['weatherapp_api_key']

def index(request):
    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=%s' %apiId
    cities = City.objects.all()

    error_msg = ''
    message = ''
    message_class = ''
    if request.method == 'POST':
        form = CityForm(request.POST)
        # form.save()

        if form.is_valid():
            new_city = form.cleaned_data['name']
            existing_city_count = City.objects.filter(name=new_city).count()

            if existing_city_count == 0:
                r = requests.get(url.format(new_city)).json()

                if r['cod'] == 200:
                    form.save()
                else:
                    error_msg = 'City doesnot exists.'
            else:
                error_msg = 'City already exists in the database!'

        if error_msg:
            message = error_msg
            message_class = 'is-danger'
        else:
            message = 'City added successfully'
            message_class= 'is-success'

    # if request.method == 'POST':
    #     form = CityForm(request.POST)
    #     form.save() #It will both validate and save the new city name to the databaseat hte same time

    form = CityForm()

    weather_list = []
    try:
        for city in cities:

            r = requests.get(url.format(city)).json()

            weather = {
                'city': city.name, # city or city.name, city print the object that contains city name, and city.name directly print the city name.
                'temperature': r['main']['temp'],
                'pressure': r['main']['pressure'],
                'humidity': r['main']['humidity'],
                'wind': r['wind']['speed'],
                'description': r['weather'][0]['description'], # 0 means first item in the list 'weather'
                'icon': r['weather'][0]['icon']
            }

            weather_list.append(weather)
    except KeyError:
        pass
    except EXCEPTION as e:
        pass
    print(weather_list)

    context = {'weather_list': weather_list,
                'form': form,
                 'message': message,
                 'message_class':message_class}
    return render(request, 'design/index.html', context)


import os
import shutil
from typing import List

from ini import Server, SERVERS

def sync_macros(servers: List[Server]):
    local_path = "C:\\Users\\Gregg\\AppData\\Local\\VeryVanilla\\Emu\\Release\\Macros"

    for file in os.listdir(local_path):
        if file.endswith(".mac") or file.endswith(".inc"):
            full_local = os.path.join(local_path, file)
            for server in servers:
                remote_path = os.path.join(server.mq_path, "Macros")
                full_other = os.path.join(remote_path, file)

                should_upload = False

                if os.path.exists(full_other):
                    local_mtime = os.path.getmtime(full_local)
                    remote_mtime = os.path.getmtime(full_other)

                    if remote_mtime <= local_mtime:
                        should_upload = True
                else:
                    should_upload = True

                if should_upload:
                    print(f"Uploading {full_other}")
                    shutil.copy(full_local, full_other)

if __name__ == '__main__':
    sync_macros(SERVERS[1:])


import random
from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)

# Define the grid size
GRID_SIZE = 20

# Initialize the Snake game
class SnakeGame:
    def __init__(self):
        self.snake = [(GRID_SIZE // 2, GRID_SIZE // 2)]
        self.food = self.generate_food()
        self.direction = (0, 1)
        self.game_over = False

    def generate_food(self):
        while True:
            food = (random.randint(0, GRID_SIZE - 1), random.randint(0, GRID_SIZE - 1))
            if food not in self.snake:
                return food

    def move(self):
        if self.game_over:
            return

        head = self.snake[0]
        new_head = (head[0] + self.direction[0], head[1] + self.direction[1])

        if (
            new_head in self.snake
            or new_head[0] < 0
            or new_head[0] >= GRID_SIZE
            or new_head[1] < 0
            or new_head[1] >= GRID_SIZE
        ):
            self.game_over = True
            return

        self.snake.insert(0, new_head)

        if self.snake[0] == self.food:
            self.food = self.generate_food()
        else:
            self.snake.pop()

# Create a new Snake game instance
game = SnakeGame()

@app.route("/")
def index():
    return render_template("index.html", GRID_SIZE=GRID_SIZE)

@app.route("/move/<direction>")
def move(direction):
    if direction == "up":
        game.direction = (-1, 0)
    elif direction == "down":
        game.direction = (1, 0)
    elif direction == "left":
        game.direction = (0, -1)
    elif direction == "right":
        game.direction = (0, 1)
    game.move()
    return redirect(url_for("index"))

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)


# Defines some classes to be used to store the results of parsing
import networkx as nx
import tempfile as tf
import copy
from collections import defaultdict
import hashlib

from loguru import logger

import claripy

import semantics.mappings as maps
from semantics.typing import Typing, BitVec, Bool

class InstructionSemantics:
    def __init__(self, name, opcode, operands):
        self.name = name
        self.opcode = opcode
        self.operands = operands
        self.sems = dict()
        self.alias = []

        self.instruction_metadata = dict()

    def is_pseudo(self):
        return False

    def set_metadata(self, key, value):
        self.instruction_metadata[key] = value

    def get_metadata(self, key, default=None):
        return self.instruction_metadata.get(key, default)

    def define_semantics_for(self, key, sem):
        self.sems[key] = sem
        sem._instsem = self

    def undef_semantics_for(self, key):
        del self.sems[key]

    def add_alias(self, opcode, operands):
        self.alias.append({"opcode": opcode, "operands": operands})

    @property
    def is_float(self):
        return self.instruction_metadata.get("float", False)

    @property
    def is_vec(self):
        return self.instruction_metadata.get("vec", False)

    def is_flag_reg(self, reg):
        # XXX: This is a fragile heuristic and must be generalized if/when we
        # generalize to other/more architectures, perhaps by looking at the
        # flag register information for the specific architecture
        # This works fine for x86 as no real register other than the flags
        # ends with an "F"
        if reg[-1] == "F":
            return True
        return False

    # Iterators
    # Some methods to iterate over the outputs defined in the semantics

    def iter_semantics(self):
        return iter(self.sems)

    def iter_output_regs(self):
        for k, sem in self.sems.items():
            if not self.is_flag_reg(k):
                yield k, sem

    def iter_outputs(self):
        for k, sem in self.sems.items():
            yield k, sem

    def iter_output_flags(self):
        for k, sem in self.sems.items():
            if self.is_flag_reg(k):
                yield k, sem

    def clone_with_keys(self, keys, rename='auto'):
        new_inst = copy.deepcopy(self)
        remove_keys = [
            k for k in new_inst.sems.keys() if k not in keys
        ]

        for rk in remove_keys:
            del new_inst.sems[rk]

        new_name = None
        if rename == 'auto':
            new_name = f"pseudo-{new_inst.name}-{'-'.join(keys)}"
        elif rename:
            new_name = rename

        if new_name:
            new_inst.name = new_name

        return new_inst

    def generate_hash(self):
        hash = []
        for k, sem in sorted(self.sems.items(), key=lambda x: x[0]):
            hash.append(f"{k}={sem.hash()}")
        hash = ";".join(hash)
        m = hashlib.md5()
        m.update(hash.encode("utf-8"))
        return m.hexdigest()

    def split_at_nidx(self, reg, nidx):
        # Split an instruction I, into I' and I'_r (the residual) at node
        # index specified by nidx.
        new_inst = copy.deepcopy(self)
        new_inst_r = copy.deepcopy(self)

        # Delete all keys other than "reg" in both the new split up
        # instructions
        for k in list(new_inst.sems):
            if k != reg:
                del new_inst.sems[k]
                del new_inst_r.sems[k]

        sem = self.sems[reg]

        #logger.debug(f"Splitting {self.name} at: {nidx}")
        #fname = f"./{self.name}_{reg}.svg"
        #logger.debug(f"Writing sem: {fname}!")
        #sem.semantics_to_svg(fname)

        new_sem, new_sem_r = sem.split_at_nidx(nidx)

        new_sem._instsem = new_inst
        new_sem_r._instsem = new_inst_r

        new_inst.sems[reg] = new_sem
        new_inst_r.sems[reg] = new_sem_r

        # XXX: This may accidently remove flag and harware registers!
        # XXX: Add output register "reg" into the operand list if it does not
        # already exist
        new_inst_r._fixup_metadata(reg, nidx, False)
        new_inst._fixup_metadata(reg, nidx, True)

        # Set metadata for parent instruction
        new_inst_r.set_metadata("parent", self.name)
        new_inst.set_metadata("parent", self.name)

        return new_inst, new_inst_r

    def _fixup_metadata(self, reg, nidx, split):
        hardware = ["RAX", "RDX", "RCX"]
        if nidx is not None:
            if split:
                self.name = f"{self.name}-{reg}-s{nidx}"
            else:
                self.name = f"{self.name}-{reg}-sr{nidx}"

            if not self.name.startswith("pseudo"):
                self.name = f"pseudo-{self.name}"

        sem = self.sems[reg]
        used = sem._used_regs_in_sem()

        self.operands = []

        add_reg = -1
        ridx = 0

        for name in used:
            if name in hardware:
                continue

            self.operands.append(Operand(name, 64, "R64"))
            if name == reg:
                add_reg = ridx
            ridx += 1

        # Ensure that the output operand is a part of the arguments and that
        # its the last operand.
        if reg in hardware:
            pass
        elif add_reg < 0:
            self.operands.append(Operand(reg, 64, "R64"))
        elif add_reg != len(self.operands) - 1:
            tmp = self.operands[-1]
            self.operands[-1] = self.operands[add_reg]
            self.operands[add_reg] = tmp

        Typing.infer(sem)

    # Debug/pretty-printing methods

    def __str__(self):
        return f"{self.name}: {self.opcode} {[str(x) for x in self.operands]}"

    def semantics_to_dot(self, prefix="./"):
        for key, sem in self.sems.items():
            fname = f"{prefix}{self.name}_{key}.dot"
            sem.semantics_to_dot(fname)

    def semantics_to_svg(self, prefix="./"):
        for key, sem in self.sems.items():
            fname = f"{prefix}{self.name}_{key}.svg"
            sem.semantics_to_svg(fname)

    def pretty_print_semantics(self):
        for k, sem in self.sems.items():
            print("[*] Semantics for:", k)
            sem.pretty_print_semantics()

    def pretty_print_prefix(self):
        for k, sem in self.sems.items():
            print("[*] Prefix Semantics for:", k)
            sem.pretty_print_prefix()

    def __deepcopy__(self, memo):
        res = type(self)(self.name, self.opcode, self.operands)
        res.sems = copy.deepcopy(self.sems, memo)

        # Fix up the ptr in sems
        for _r, sem in res.sems.items():
            sem._instsem = res

        res.alias = copy.deepcopy(self.alias, memo)
        res.instruction_metadata = copy.deepcopy(self.instruction_metadata)

        return res

class Operand:
    def __init__(self, name, sz, opty):
        self.name = name
        self.opty = opty
        self.sz = sz

    def __str__(self):
        return f"{self.name}:{self.opty}{self.sz}"

class ConstOperand:
    def __init__(self, value):
        self.value = value

    @property
    def name(self):
        return str(self)

    @property
    def sz(self):
        return "??"

    def __str__(self):
        return f"${self.value}"

class Semantics:
    def __init__(self, lhs):
        self.lhs = lhs
        self.sem = nx.DiGraph()
        self.mapping = dict()
        self.nidx = -1
        # NOTE: Readonly, do not mutate this object!
        self._instsem = None

    @property
    def instsem(self):
        return self._instsem

    @property
    def name(self):
        return self._instsem.name

    def hash(self):
        # TODO: cache this later on. Needs support for marking clean/dirty
        hash = self._pseudo_hash_subtree(self.root)
        hash = ";".join(hash)
        m = hashlib.md5()
        m.update(hash.encode("utf-8"))
        return m.hexdigest()

    def _pseudo_hash_subtree(self, node):
        hash = []
        nd = self.node_data(node, key="data")
        hash.append(f"{nd}")
        for succ in self.successors_ordered(node):
            h = self._pseudo_hash_subtree(succ)
            hash.extend(h)
        return hash

    def lookup_mapping(self, idx):
        return self.mapping[idx]["data"]

    # Basic graph manipulation API for semantics.
    def add_node(self, **data):
        idx = self._get_next_idx()
        self.sem.add_node(idx, **data)
        self.mapping[idx] = data
        return idx

    # Introduce ways to create some basic nodes
    # These functions just call into existing semantics/graph manipulation
    # functions and makes it easier to introduce new nodes into the graph

    def add_zext(self, opn, sz):
        tyn = self.add_node(data=sz)
        zn = self.add_node(data="zextMInt")
        self.add_edge(zn, opn, idx=0)
        self.add_edge(zn, tyn, idx=1)

        return zn

    def add_cvt_bool_to_bv(self, opn):
        cvtn = self.add_node(data="bool2bv")
        self.add_edge(cvtn, opn, idx=0)

        return cvtn

    def add_cvt_bv_to_bool(self, opn):
        cvtn = self.add_node(data="bv2bool")
        self.add_edge(cvtn, opn, idx=0)

        return cvtn

    def add_extract(self, bv, b, e):
        basen = self.add_node(data=b)
        endn = self.add_node(data=e)
        extractn = self.add_node(data="extractMInt")

        self.add_edge(extractn, bv, idx=0)
        self.add_edge(extractn, basen, idx=1)
        self.add_edge(extractn, endn, idx=2)

        return extractn

    def remove_node(self, nidx):
        self.sem.remove_node(nidx)

    def add_edge(self, source, target, idx=None):
        self.sem.add_edge(source, target, idx=idx)

    def add_operand_type_checked(self, source, target, idx):
        # TODO: Actually add code that fixes types
        self.sem.add_edge(source, target, idx=idx)

    def remove_edge(self, source, target):
        self.sem.remove_edge(source, target)

    def add_node_data(self, nidx, key, data):
        self.sem.nodes[nidx][key] = data

    def replace_node_data(self, nidx, data, key):
        self.sem.nodes[nidx][key] = data
        if key == "data":
            self.mapping[nidx][key] = data

    def redirect_incoming(self, fr, to):
        to_remove = []
        for pred in self.sem.predecessors(fr):
            idx = self.edge_data(pred, fr)["idx"]
            self.add_edge(pred, to, idx=idx)
            # To remove the other edge
            to_remove.append(pred)

        for p in to_remove:
            self.remove_edge(p, fr)

    def redirect_outgoing(self, fr, to):
        to_remove = []
        for succ in self.sem.successors(fr):
            idx = self.edge_data(fr, succ)["idx"]
            self.add_edge(to, succ, idx=idx)
            to_remove.append(succ)

        for s in to_remove:
            self.remove_edge(fr, succ)

    def edge_data(self, s, d, key=None):
        res = self.sem.get_edge_data(s, d)
        if key:
            res = res[key]
        return res

    def node_data(self, nidx, key=None, default=None):
        res = self.sem.nodes[nidx]
        if key:
            res = res.get(key, default)
        return res

    def successors(self, nidx):
        return self.sem.successors(nidx)

    def successors_ordered(self, nidx):
        unordered = list(self.successors(nidx))
        succs = [None] * len(unordered)
        for s in unordered:
            eidx = self.edge_data(nidx, s, "idx")
            succs[eidx] = s
        return succs

    def predecessors(self, nidx):
        return self.sem.predecessors(nidx)

    def iter_preorder(self):
        return nx.dfs_preorder_nodes(self.sem)

    def iter_postorder(self):
        return nx.dfs_postorder_nodes(self.sem)

    def subgraph(self, nodes):
        return self.sem.subgraph(nodes)

    def clone_subtree(self, nidx):
        subtree = nx.dfs_tree(self.sem, nidx).copy()
        copy_map = dict()

        for node in subtree.nodes():
            data = self.node_data(node, key='data')
            new_idx = self.add_node(data=data)
            copy_map[node] = new_idx

        for node in subtree.nodes():
            for idx, succ in enumerate(self.successors_ordered(node)):
                self.add_edge(
                    copy_map[node],
                    copy_map[succ],
                    idx=idx)

        return copy_map[nidx]

    def split_at_nidx(self, nidx):
        sem = self
        semg = self.sem

        new_sem_r = copy.deepcopy(self)
        new_sem = copy.deepcopy(self)

        new_sem_rg = copy.deepcopy(semg)
        new_semg = copy.deepcopy(semg)

        new_sem_r.sem = new_sem_rg
        new_sem.sem = new_semg

        new_semg.remove_nodes_from(
            [n for n in new_semg.nodes if n not in nx.dfs_tree(semg, nidx)]
        )

        # At the split point, the operands to the parent node need to be
        # "fixed"/repaired for the instruction to make sense.
        parents = list(sem.predecessors(nidx))

        # Create the residual graph: graph - new_semg
        newsem_root = new_sem_r.root
        for parent in parents:
            new_sem_rg.remove_edge(parent, nidx)
        new_sem_rg.remove_nodes_from(
            [
                n
                for n in new_sem_rg.nodes
                if n not in nx.dfs_tree(new_sem_rg, newsem_root)
            ]
        )

        # All operands used in the AST + the output register itself
        used = new_sem_r._used_regs_in_sem()

        logger.debug(f"Used: {used} Split@{nidx}")
        logger.debug(f"CURRENT ROOT: {self.root}")

        # Fixup types in both the split and the split residual instructions
        # Get the original type information for the node we're splitting at
        orig_ty = sem.node_data(nidx, key=Typing.KEY)

        # Replace the removed subtree in the residual with an available
        # abstract register
        available = [f"R{i}" for i in range(1, 8)]
        selected = None
        for regr in available:
            if regr not in used:
                used.append(regr)
                selected = new_sem_r.add_node(data=regr)
                new_sem_r.add_node_data(selected, "no_split", True)
                break

        assert selected is not None, "No available abstract register?"

        # In the split instruction, the destination register is a 64-bit
        # abstract register. Insert extract/zero-extend to extend the result
        # of the node to a 64-bit register.
        if not isinstance(orig_ty, BitVec):
            if not isinstance(orig_ty, Bool):
                logger.error(
                    f"What's this type? {orig_ty}, {sem.node_data(nidx, key='data')}"
                )
                assert False

            # Ok, the result of the tree is Bool, convert to bitvec and expand
            # to 64 bits.
            cvt = new_sem.add_cvt_bool_to_bv(new_sem.root)
            zext = new_sem.add_zext(cvt, 64)

            new_sem.add_node_data(cvt, "no_split", True)
            new_sem.add_node_data(zext, "no_split", True)

            # new_sem_r: Fix residual, convert from bv -> bool
            cvtr = new_sem_r.add_cvt_bv_to_bool(selected)
            for parent in parents:
                edge_idx = sem.edge_data(parent, nidx, key="idx")
                new_sem_r.add_edge(parent, cvtr, idx=edge_idx)
            new_sem_r.add_node_data(cvtr, "no_split", True)
        elif orig_ty.sz < 64:
            zext = new_sem.add_zext(new_sem.root, 64)
            new_sem.add_node_data(zext, "no_split", True)

            # new_sem_r: Add an extract
            extr = new_sem_r.add_extract(selected, 0, orig_ty.sz - 1)
            for parent in parents:
                edge_idx = sem.edge_data(parent, nidx, key="idx")
                new_sem_r.add_edge(parent, extr, idx=edge_idx)
                logger.info(f"Edge: {parent} {extr} {edge_idx}")
            new_sem_r.add_node_data(extr, "no_split", True)
        elif orig_ty.sz > 64:
            # Extract the lower 64 bits
            extr = new_sem.add_extract(new_sem.root, 0, 63)
            new_sem.add_node_data(extr, "no_split", True)

            # new_sem_r: zero-extend the selected register
            zextn = new_sem_r.add_zext(selected, orig_ty.sz)
            for parent in parents:
                edge_idx = sem.edge_data(parent, nidx, key="idx")
                new_sem_r.add_edge(parent, zextn, idx=edge_idx)
                logger.info(f"Edge: {parent} {zextn} {edge_idx}")

            new_sem_r.add_node_data(zextn, "no_split", True)
        else:
            for parent in parents:
                edge_idx = sem.edge_data(parent, nidx, key="idx")
                new_sem_r.add_edge(parent, selected, idx=edge_idx)

        return new_sem, new_sem_r

    def _used_regs_in_sem(self):
        wl = [self.root]
        used = []
        while wl:
            current = wl.pop(0)
            nd = self.node_data(current, "data")
            if isinstance(nd, str) and nd.startswith("R") and nd not in used:
                used.append(nd)
            wl.extend(self.successors(current))
        return used

    @property
    def root(self):
        roots = []
        for node in self.sem.nodes():
            if not list(self.predecessors(node)):
                roots.append(node)

        if len(roots) != 1:
            logger.error(f"Multiple/No root found: {len(roots)} {self.name}: {self.lhs}")
            self.semantics_to_svg()
            assert False

        return roots[0]

    # Conversion to external representation/solver stuff
    def to_claripy(self):
        # Ensure type information is present
        # TODO: This is not optimized.
        Typing.infer(self)
        return self.to_claripy_subtree(self.root)

    def to_claripy_subtree(self, root):
        current = self.node_data(root, "data")
        # If its just an integer constant, return it.
        if isinstance(current, int):
            return current

        succs_vars = []
        succs = list(self.successors_ordered(root))
        for succ in succs:
            stree = self.to_claripy_subtree(succ)
            succs_vars.append((stree, self.edge_data(root, succ, "idx")))

        succs_vars = [x[0] for x in sorted(succs_vars, key=lambda x: x[1])]
        if current == "extractMInt":
            sz = self.node_data(succs[0], Typing.KEY).sz
            succs_vars[1] = sz - succs_vars[1] - 1
            succs_vars[2] = sz - succs_vars[2]

        cp_func = maps.to_claripy(current)

        if not cp_func:
            assert not succs_vars, f"{current} is not a terminal!"
            sz = self.node_data(root, Typing.KEY).sz
            # Other arguments to claripy.BVS
            succs_vars = [current, sz, None, None, None, False, True]
            cp_func = claripy.BVS

        return cp_func(*succs_vars)

    # Pretty-print/display methods
    def semantics_to_dot(self, fname=None):
        from networkx.drawing.nx_pydot import write_dot

        if not fname:
            fname = tf.NamedTemporaryFile(suffix=".dot", delete=False).name
        write_dot(self.sem, fname)

    def semantics_to_svg(self, fname=None):
        from matplotlib import pyplot as plt
        from networkx.drawing.nx_agraph import graphviz_layout

        if not fname:
            fname = tf.NamedTemporaryFile(suffix=".svg", delete=False).name

        plt.figure(3, figsize=(12, 9))
        pos = graphviz_layout(self.sem, prog="dot", root=0, args="-Gsize=12,9")
        nx.draw(self.sem, pos, node_color="w", node_shape="s")
        node_labels = nx.get_node_attributes(self.sem, "data")
        for k, v in node_labels.items():
            node_labels[k] = f"%{k}: {v}"
        nx.draw_networkx_labels(self.sem, pos, labels=node_labels, font_size=10)
        plt.savefig(fname)
        plt.show()
        plt.close()

    def pretty_print_semantics(self):
        for node in nx.dfs_preorder_nodes(self.sem):
            print(node, self.sem.nodes[node])

    def pretty_print_prefix(self):
        roots = []
        # Find root
        for n in self.sem.nodes():
            if len(list(self.predecessors(n))) == 0:
                roots.append(n)

        for node in roots:
            res = self.pretty_print_recursive(node, 0)

        print("Prefix:", res)
        return res

    def pretty_print_recursive(self, nidx, depth):
        results = []
        succ_count = len(list(self.successors(nidx)))
        for idx, node in enumerate(self.successors(nidx)):
            results.append(self.pretty_print_recursive(node, depth + 1))
            if idx < succ_count - 1:
                results.append(",")

        ndata = self.node_data(nidx)["data"]
        if succ_count > 0:
            results.insert(0, str(ndata))
            results.insert(0, " (")
            results.append(")")
        else:
            results.insert(0, " " + str(ndata))

        rr = "".join(results)

        if depth > 0:
            rr = "\n" + "    " * depth + rr

        return rr

    def to_pseudo_asm(self, nidx):
        pass

    def _get_next_idx(self):
        self.nidx += 1
        return self.nidx

    def __deepcopy__(self, memo):
        res = Semantics(self.lhs)
        res.mapping = copy.deepcopy(self.mapping, memo)
        res.nidx = self.nidx
        res.sem = copy.deepcopy(self.sem, memo)
        res._instsem = self._instsem
        return res

{'write_dot': 'networkx.drawing.nx_pydot.write_dot', 'plt': 'matplotlib.pyplot', 'graphviz_layout': 'networkx.drawing.nx_agraph.graphviz_layout'}


import math
import torch
from torch import nn as nn
import torch.nn.functional as F

MAX_VAL = 1e4

class PointWiseFeedForward(nn.Module):
    def __init__(self, hidden_units, dropout_rate):

        super(PointWiseFeedForward, self).__init__()

        self.conv1 = nn.Conv1d(hidden_units, hidden_units, kernel_size=1)
        self.dropout1 = nn.Dropout(p=dropout_rate)
        self.relu = nn.ReLU()
        self.conv2 = nn.Conv1d(hidden_units, hidden_units, kernel_size=1)
        self.dropout2 = nn.Dropout(p=dropout_rate)

    def forward(self, inputs):
        outputs = self.dropout2(self.conv2(self.relu(self.dropout1(self.conv1(inputs.transpose(-1, -2))))))
        outputs = outputs.transpose(-1, -2) # as Conv1D requires (N, C, Length)
        outputs += inputs
        return outputs

class Attention(nn.Module):
    def __init__(self, max_len, head_size, args=None):
        super().__init__()

        self.args = args

        self.global_num = args.trm_num_heads
        self.local_num = args.local_num_heads

        if self.local_num > 0:

            self.abs_pos_emb_key = nn.Embedding(max_len, head_size * self.local_num)
            self.abs_pos_emb_query = nn.Embedding(max_len, head_size * self.local_num)
            self.rel_pos_emb = nn.Embedding(2 * max_len - 1, head_size * self.local_num)
            position_ids_l = torch.arange(max_len, dtype=torch.long).view(-1, 1)
            position_ids_r = torch.arange(max_len, dtype=torch.long).view(1, -1)
            self.distance = position_ids_r - position_ids_l + max_len - 1
            self.mlps = nn.ModuleList([nn.Linear(head_size, 1) for _ in range(self.local_num)])
            self.sigmoid = nn.Sigmoid()

    "Compute 'Scaled Dot Product Attention"
    def forward(self, query, key, value, mask=None, dropout=None):
        b, h, l, d_k = query.size()
        query_g, key_g, value_g = query[:, :self.global_num, ...], key[:, :self.global_num, ...], value[:, :self.global_num, ...]

        scores_g = torch.matmul(query_g, key_g.transpose(-2, -1)) / math.sqrt(query_g.size(-1))
        scores_g = scores_g.masked_fill(mask, -MAX_VAL)
        p_attn_g = dropout(F.softmax(scores_g, dim=-1))
        value_g = torch.matmul(p_attn_g, value_g)

        if self.local_num > 0:
            query_l, key_l, value_l = query[:, self.global_num:, ...], key[:, self.global_num:, ...], value[:, self.global_num:, ...]
            index = self.distance.to(query_l.device)[-1]
            query_l = query_l + self.abs_pos_emb_query(index).unsqueeze(0).unsqueeze(0).view(1,-1,l,d_k)
            key_l = key_l + self.abs_pos_emb_key(index).unsqueeze(0).unsqueeze(0).view(1,-1,l,d_k)

            scores_l = torch.matmul(query_l, key_l.transpose(-2, -1)) / math.sqrt(query_l.size(-1))

            rel_pos_embedding = self.rel_pos_emb(self.distance.to(scores_l.device)).view(l, -1, self.local_num, d_k).permute(2,0,1,3).unsqueeze(0)
            inputs = rel_pos_embedding.repeat(b,1,1,1,1) + value_l.unsqueeze(dim=-2) + value_l.unsqueeze(dim=-3)# + self.user_proj(users).view(b, l, -1, d_k).permute(0,2,1,3).unsqueeze(-2)

            reweight = torch.cat([self.mlps[i](inputs[:, i, ...]).squeeze(-1).unsqueeze(1) for i in range(self.local_num)], dim=1)
            scores_l = scores_l + reweight

            scores_l = scores_l.masked_fill(mask, -MAX_VAL)
            p_attn_l = dropout(F.softmax(scores_l, dim=-1))
            value_l = torch.matmul(p_attn_l, value_l)

            if self.global_num > 0:
                return torch.cat([value_g, value_l], dim=1), (p_attn_g, p_attn_l)
            else:
                return value_l, p_attn_l

        else:

            return value_g, p_attn_g

class MultiHeadedAttention(nn.Module):
    "Take in model size and number of heads."
    def __init__(self, args=None):
        super().__init__()

        hidden_dim = args.trm_hidden_dim
        num_heads = args.trm_num_heads + args.local_num_heads
        assert hidden_dim % num_heads == 0
        dropout = args.trm_att_dropout
        max_len = args.trm_max_len

        self.head_size = hidden_dim // num_heads
        self.num_heads = num_heads

        self.linear_layers = nn.ModuleList([nn.Linear(hidden_dim, hidden_dim) for _ in range(3)])
        self.output_linear = nn.Linear(hidden_dim, hidden_dim)
        self.attention = Attention(max_len=max_len, head_size=self.head_size, args=args)

        self.dropout = nn.Dropout(p=dropout)

    def forward(self, query, key, value, mask=None):

        '''
        query, key, value: [batch, seq_len, hidden]
        '''

        batch_size = query.size(0)

        # 1) Do all the linear projections in batch from d_model => h x d_k
        query, key, value = [l(x).view(batch_size, -1, self.num_heads, self.head_size).transpose(1, 2)
                             for l, x in zip(self.linear_layers, (query, key, value))]

        # 2) Apply attention on all the projected vectors in batch.
        x, attn = self.attention(query, key, value, mask=mask, dropout=self.dropout)

        # 3) "Concat" using a view and apply a final linear.
        x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.num_heads * self.head_size)

        return self.output_linear(x)

class LocalSelfAttention(nn.Module):
    def __init__(self, args):
        super().__init__()
        self.attention_layernorms = nn.ModuleList()
        self.attention_layers = nn.ModuleList()
        self.forward_layernorms = nn.ModuleList()
        self.forward_layers = nn.ModuleList()

        self.last_layernorm = nn.LayerNorm(args.trm_hidden_dim, eps=1e-8)

        for _ in range(args.trm_num_blocks):
            new_attn_layernorm = nn.LayerNorm(args.trm_hidden_dim, eps=1e-8)
            self.attention_layernorms.append(new_attn_layernorm)

            new_attn_layer =  MultiHeadedAttention(args)
            self.attention_layers.append(new_attn_layer)

            new_fwd_layernorm = nn.LayerNorm(args.trm_hidden_dim, eps=1e-8)
            self.forward_layernorms.append(new_fwd_layernorm)

            new_fwd_layer = PointWiseFeedForward(args.trm_hidden_dim, args.trm_dropout)
            self.forward_layers.append(new_fwd_layer)

    def forward(self, seqs, timeline_mask):

        seqs *= ~timeline_mask.unsqueeze(-1) # broadcast in last dim

        tl = seqs.shape[1] # time dim len for enforce causality
        attention_mask = ~torch.tril(torch.ones((tl, tl), dtype=torch.bool, device=seqs.device))

        attn_output_weights = []

        for i in range(len(self.attention_layers)):

            Q = self.attention_layernorms[i](seqs)
            mha_outputs = self.attention_layers[i](Q, seqs, seqs, mask=attention_mask)

            seqs = Q + mha_outputs

            seqs = self.forward_layernorms[i](seqs)
            seqs = self.forward_layers[i](seqs)
            seqs *=  ~timeline_mask.unsqueeze(-1)

        log_feats = self.last_layernorm(seqs) # (U, T, C) -> (U, -1, C)

        return log_feats, attn_output_weights


# pytest -v --driver Chrome --driver-path C:\\chromedrivers\\chromedriver.exe\\chromedriver tests/test_selenium_simple.py

from selenium import webdriver

import time
from selenium.webdriver.common.by import By
import pytest

def test_search_example():
    """ Search some phrase in google and make a screenshot of the page. """
    selenium = webdriver.Chrome('C:/chromedrivers/chromedriver.exe')
    # Open google search page:
    selenium.get('https://google.com')

    time.sleep(5)  # just for demo purposes, do NOT repeat it on real projects!

    # Find the field for search text input:
    search_input = selenium.find_element(by=By.NAME, value='q')

    # Enter the text for search:
    search_input.clear()
    search_input.send_keys('first test')

    time.sleep(5)  # just for demo purposes, do NOT repeat it on real projects!

    # Click Search:
    search_button = selenium.find_element(by=By.NAME, value='btnK')
    search_button.submit()
    # search_button.click()

    time.sleep(10)  # just for demo purposes, do NOT repeat it on real projects!

    # Make the screenshot of browser window:
    selenium.save_screenshot('result.png')
    selenium.close()
    selenium.quit()