import argparse
import csv
import random
import subprocess

#classes
class Users:
	def __init__( self, name, is_playing ):
		self.name = name
		self.is_playing = is_playing

class Robots:
	def __init__( self, name, hp, attack, defense, owner ):
		self.name = name
		self.hp = hp
		self.attack = attack
		self.defense = defense
		self.owner = owner

class Games:
	def __init__( self, player1, robot1, player2, robot2, winner = None ):
		self.player1 = player1
		self.robot1 = robot1
		self.player2 = player2
		self.robot2 = robot2
		self.winner = winner

#loaders
def load_users( user_file, use_aws ):
	user_list = []
	if use_aws == "aws":
		subprocess.run([ "aws", "s3", "cp", "s3://robot-battle-files/users.txt", user_file ])
	with open( user_file, 'r' ) as f:
		readcsv = csv.reader( f, delimiter=',' )
		for row in readcsv:
			user_list.append( Users( row[0], int(row[1]) ))
	return user_list

def load_robots( robot_file, use_aws ):
	robot_list = []
	if use_aws == "aws":
		subprocess.run([ "aws", "s3", "cp", "s3://robot-battle-files/robots.txt", robot_file ])
	with open( robot_file, 'r' ) as f:
		readCSV = csv.reader( f, delimiter=',' )
		for row in readCSV:
			robot_list.append( Robots( row[0], int(row[1]), int(row[2]), int(row[3]), row[4] ))
	return robot_list

def load_games( game_file, use_aws ):
	game_list = []
	if use_aws == "aws":
		subprocess.run([ "aws", "s3", "cp", "s3://robot-battle-files/games.txt", game_file ])
	with open( game_file, 'r' ) as f:
		readCSV = csv.reader( f, delimiter=',' )
		for row in readCSV:
			game_list.append( Games( row[0], row[1], row[2], row[3], row[4] ))
	return game_list

#save to file
def save_users( user_list, out_file, use_aws ):
	output = []
	with open( out_file, 'w' ) as f:
		writer = csv.writer(f)
		for user in user_list:
			output.append( user.name )
			output.append( user.is_playing )
			writer.writerow( output )
			output.clear()
	if use_aws == "aws":
		subprocess.run([ "aws", "s3", "cp", out_file, "s3://robot-battle-files/users.txt" ])

def save_robots( robot_list, out_file, use_aws ):
	output = []
	with open( out_file, 'w' ) as f:
		writer = csv.writer(f)
		for robot in robot_list:
			output.append( robot.name )
			output.append( robot.hp )
			output.append( robot.attack )
			output.append( robot.defense )
			output.append( robot.owner )
			writer.writerow( output )
			output.clear()
	if use_aws == "aws":
		subprocess.run([ "aws", "s3", "cp", out_file, "s3://robot-battle-files/robots.txt" ])

def save_games( game_list, out_file, use_aws ):
	output = []
	with open( out_file, 'w' ) as f:
		writer = csv.writer(f)
		for game in game_list:
			output.append( game.player1 )
			output.append( game.robot1 )
			output.append( game.player2 )
			output.append( game.robot2 )
			output.append( game.winner )
			writer.writerow( output )
			output.clear()
	if use_aws == "aws":
		subprocess.run([ "aws", "s3", "cp", out_file, "s3://robot-battle-files/games.txt" ])

#commands
def show_commands():
	print( "List of commands" )
	print( "0 - exit" )
	print( "1 - play" )
	print( "2 - game history" )
	print( "3 - user history" )
	print( "4 - robot history" )
	print( "5 - user's robots" )

def game_history( game_list, fetch_type = None, name = None ):
	print( "Player1, Robot1, Player2, Robot2, Winner" )
	for game in game_list:
		to_display = False
		if ( fetch_type == "user" and ( game.player1 == name or game.player2 == name ))   \
		   or ( fetch_type == "robot" and ( game.robot1 == name or game.robot2 == name )) \
		   or ( fetch_type is None ):
			to_display = True

		if to_display:
			print( game.player1, end = ', ' )
			print( game.robot1, end = ', ' )
			print( game.player2, end = ', ' )
			print( game.robot2, end = ', ' )
			if game.winner:
				print( game.winner )
			else:
				print( "Game in Progress" )

def get_robots( robot_list, name ):
	print( "Name, HP, Attack, Defense" )
	for robot in robot_list:
		if robot.owner == name:
			print( robot.name, end = ', ' )
			print( robot.hp, end = ', ' )
			print( robot.attack, end = ', ' )
			print( robot.defense )

def play( user_list, robot_list, game_list ):

	available_user_num = []
	print( "Available users:" )
	for idx, user in enumerate( user_list ):
		if user.is_playing == 0:
			print( str( idx ) + " " + user.name )
			available_user_num.append( idx )

	p1_num = -1
	r1_num = -1
	r1_hp = -1

	is_valid_user = False
	while not is_valid_user:
		user_num = int( input( "Input User Num:" ))
		if user_num in available_user_num:
			p1_num = user_num
			user_list[p1_num].is_playing = 1
			available_user_num.remove( p1_num )
			is_valid_user = True

			p1_robot_nums = []
			print( user_list[user_num].name + "'s robots:" )
			for idx, robot in enumerate( robot_list ):
				if robot.owner == user_list[p1_num].name:
					print( str( idx ) + " " + robot.name )
					p1_robot_nums.append( idx )

			is_valid_robot = False
			while not is_valid_robot:
				robot_num = int( input( "Choose the robot num:" ))
				if robot_num in p1_robot_nums:
					r1_num = robot_num
					r1_hp = robot_list[robot_num].hp
					is_valid_robot = True
				else:
					print( "Robot Num Invalid" )

		else:
			print( "User Not Available" )

	p2_num = random.choice( available_user_num )
	user_list[p2_num].is_playing = 1

	p2_robot_nums = []
	for idx, robot in enumerate( robot_list ):
		if robot.owner == user_list[p2_num].name:
			p2_robot_nums.append( idx )

	r2_num = random.choice( p2_robot_nums )
	r2_hp = robot_list[r2_num].hp

	game_num = len( game_list )
	game_list.append( Games( user_list[p1_num].name, robot_list[r1_num].name, \
						user_list[p2_num].name, robot_list[r2_num].name,None ))

	print( "Starting Battle..." )
	print( "Player 1: " + user_list[p1_num].name )
	print( "Robot 1: " + robot_list[r1_num].name )
	print( "Player 2: " + user_list[p2_num].name )
	print( "Robot 2: " + robot_list[r2_num].name )

	player_turn = random.randint( 1, 2 )
	while r1_hp > 0 and r2_hp > 0:
		if player_turn == 1:
			print( "Player 1's turn" )
			r2_hp -= robot_list[r1_num].attack - robot_list[r2_num].defense
			print( "Damage: " + str( robot_list[r1_num].attack ) + "-" + str( robot_list[r2_num].defense ))
			player_turn = 2
		else:
			print( "Player 2's turn" )
			r1_hp -= robot_list[r2_num].attack - robot_list[r1_num].defense
			print( "Damage: " + str( robot_list[r2_num].attack ) + "-" + str( robot_list[r1_num].defense ))
			player_turn = 1

	if r1_hp > 0:
		print( "Player 1 wins" )
		game_list[game_num].winner = user_list[p1_num].name
	else:
		print( "Player 2 wins" )
		game_list[game_num].winner = user_list[p2_num].name

	user_list[p1_num].is_playing = 0
	user_list[p2_num].is_playing = 0

def main(args):
	print("Robot Battle!")

	print( "Loading Users..." )
	user_list = load_users( args.user_file, args.use_aws )

	print( "Loading Robots..." )
	robot_list = load_robots( args.robot_file, args.use_aws )

	print( "Loading Games History..." )
	game_list = load_games( args.game_file, args.use_aws )

	show_commands()
	command = input( "Input command: " )
	while command != "0":
		if command == "1":
			play( user_list, robot_list, game_list)
		elif command == "2":
			game_history( game_list )
		elif command == "3":
			name = input( "User Name: " )
			game_history( game_list, "user", name )
		elif command == "4":
			name = input ( "Robot Name: " )
			game_history( game_list, "robot", name )
		elif command == "5":
			name = input ( "User Name: " )
			get_robots( robot_list, name )

		print()
		show_commands()
		command = input( "Input command: " )

	print( "Saving Users..." )
	save_users( user_list, args.user_file, args.use_aws )

	print( "Saving Robots..." )
	save_robots( robot_list, args.robot_file, args.use_aws )

	print( "Saving Games..." )
	save_games( game_list, args.game_file, args.use_aws )

if __name__ == "__main__":
	parser = argparse.ArgumentParser( description = "Robot Battle" )
	parser.add_argument( '--user_file', dest = "user_file", required = True )
	parser.add_argument( '--robot_file', dest = "robot_file", required = True )
	parser.add_argument( '--game_file', dest = "game_file", required = True )
	parser.add_argument( '--run_on', dest = "use_aws", default = "cmd" )

	args = parser.parse_args()

	main(args)


import asyncio
import datetime
import io
import os
import re
import random
import signal
import sys

import discord
import pygame

from pgbot import commands, common, db, emotion, routine
from pgbot.utils import embed_utils, utils

async def _init():
    """
    Startup call helper for pygame bot
    """
    if not common.TEST_MODE:
        # when we are not in test mode, we want stout/stderr to appear on a console
        # in a discord channel
        sys.stdout = sys.stderr = common.stdout = io.StringIO()

    print("The PygameCommunityBot is now online!")
    print("Server(s):")

    for server in common.bot.guilds:
        prim = ""

        if common.guild is None and (
            common.GENERIC or server.id == common.ServerConstants.SERVER_ID
        ):
            prim = "| Primary Guild"
            common.guild = server

        print(" -", server.name, "| Number of channels:", len(server.channels), prim)
        if common.GENERIC:
            continue

        for channel in server.channels:
            if channel.id == common.ServerConstants.DB_CHANNEL_ID:
                common.db_channel = channel
                await db.init()
            elif channel.id == common.ServerConstants.LOG_CHANNEL_ID:
                common.log_channel = channel
            elif channel.id == common.ServerConstants.ARRIVALS_CHANNEL_ID:
                common.arrivals_channel = channel
            elif channel.id == common.ServerConstants.GUIDE_CHANNEL_ID:
                common.guide_channel = channel
            elif channel.id == common.ServerConstants.ROLES_CHANNEL_ID:
                common.roles_channel = channel
            elif channel.id == common.ServerConstants.ENTRIES_DISCUSSION_CHANNEL_ID:
                common.entries_discussion_channel = channel
            elif channel.id == common.ServerConstants.CONSOLE_CHANNEL_ID:
                common.console_channel = channel
            elif channel.id == common.ServerConstants.RULES_CHANNEL_ID:
                common.rules_channel = channel
            for key, value in common.ServerConstants.ENTRY_CHANNEL_IDS.items():
                if channel.id == value:
                    common.entry_channels[key] = channel

async def init():
    """
    Startup call helper for pygame bot
    """
    try:
        await _init()
    except Exception:
        # error happened in the first init sequence. report error to stdout/stderr
        # note that the chances of this happening are pretty slim, but you never know
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__
        raise

    routine.handle_console.start()
    routine.routine.start()

    if common.guild is None:
        raise RuntimeWarning(
            "Primary guild was not set. Some features of bot would not run as usual."
            " People running commands via DMs might face some problems"
        )

def format_entries_message(msg: discord.Message, entry_type: str):
    """
    Formats an entries message to be reposted in discussion channel
    """
    if entry_type != "":
        title = f"New {entry_type.lower()} in #{common.ZERO_SPACE}{common.entry_channels[entry_type].name}"
    else:
        title = ""

    attachments = ""
    if msg.attachments:
        for i, attachment in enumerate(msg.attachments):
            attachments += f" • [Link {i + 1}]({attachment.url})\n"
    else:
        attachments = "No attachments"

    desc = msg.content if msg.content else "No description provided."

    fields = [
        ["**Posted by**", msg.author.mention, True],
        ["**Original msg.**", f"[View]({msg.jump_url})", True],
        ["**Attachments**", attachments, True],
        ["**Description**", desc, True],
    ]
    return title, fields

def entry_message_validity_check(
    message: discord.Message, min_chars=32, max_chars=float("inf")
):
    """Checks if a message posted in a showcase channel for projects has the right format.

    Returns:
        bool: True/False
    """
    url_regex_pattern = r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])"
    # https://stackoverflow.com/a/6041965/14826938

    search_obj = re.search(
        url_regex_pattern, (message.content if message.content else "")
    )
    link_in_msg = bool(search_obj)
    first_link_str = search_obj.group() if link_in_msg else ""

    if (
        message.content
        and (link_in_msg and len(message.content) > len(first_link_str))
        and min_chars < len(message.content) < max_chars
    ):
        return True

    elif (message.content or message.reference) and message.attachments:
        return True

    return False

async def delete_bad_entry_and_warning(
    entry_msg: discord.Message, warn_msg: discord.Message, delay: float = 0.0
):
    """A function to pardon a bad entry message with a grace period. If this coroutine is not cancelled during the
    grace period specified in `delay` in seconds, it will delete both `entry_msg` and `warn_msg`, if possible.

    Args:
        entry_msg (discord.Message): [description]
        warn_msg (discord.Message): [description]
        delay (float, optional): [description]. Defaults to 0..
    """
    try:
        await asyncio.sleep(delay)  # allow cancelling during delay
    except asyncio.CancelledError:
        return

    try:
        await warn_msg.edit("Sorry, but your showcase entry will now be deleted.")
        await asyncio.sleep(0)
    except asyncio.CancelledError:
        pass

    finally:
        await entry_msg.delete()
        await warn_msg.delete()

async def member_join(member: discord.Member):
    """
    This function handles the greet message when a new member joins
    """
    if common.TEST_MODE or member.bot or common.GENERIC:
        # Do not greet people in test mode, or if a bot joins
        return

    greet = random.choice(common.BOT_WELCOME_MSG["greet"])
    check = random.choice(common.BOT_WELCOME_MSG["check"])

    grab = random.choice(common.BOT_WELCOME_MSG["grab"])
    end = random.choice(common.BOT_WELCOME_MSG["end"])

    # This function is called right when a member joins, even before the member
    # finishes the join screening. So we wait for that to happen and then send
    # the message. Wait for a maximum of six hours.
    for _ in range(10800):
        await asyncio.sleep(2)

        if not member.pending:
            # Don't use embed here, because pings would not work
            await common.arrivals_channel.send(
                f"{greet} {member.mention}! {check} "
                + f"{common.guide_channel.mention}{grab} "
                + f"{common.roles_channel.mention}{end}"
            )
            # new member joined, yaayyy, snek is happi
            await emotion.update("happy", 20)
            return

async def clean_db_member(member: discord.Member):
    """
    This function silently removes users from database messages
    """
    for table_name in ("stream", "reminders", "clock"):
        async with db.DiscordDB(table_name) as db_obj:
            data = db_obj.get({})
            if member.id in data:
                data.pop(member)
                db_obj.write(data)

async def message_delete(msg: discord.Message):
    """
    This function is called for every message deleted by user.
    """
    if msg.id in common.cmd_logs.keys():
        del common.cmd_logs[msg.id]

    elif msg.author.id == common.bot.user.id:
        for log in common.cmd_logs.keys():
            if common.cmd_logs[log].id is not None:
                if common.cmd_logs[log].id == msg.id:
                    del common.cmd_logs[log]
                    return

    if common.GENERIC or common.TEST_MODE:
        return

    if msg.channel in common.entry_channels.values():
        if (
            msg.channel.id == common.ServerConstants.ENTRY_CHANNEL_IDS["showcase"]
            and msg.id in common.entry_message_deletion_dict
        ):  # for case where user deletes their bad entry by themselves
            deletion_data_list = common.entry_message_deletion_dict[msg.id]
            deletion_task = deletion_data_list[0]
            if not deletion_task.done():
                deletion_task.cancel()
                try:
                    warn_msg = await msg.channel.fetch_message(
                        deletion_data_list[1]
                    )  # warning and entry message were already deleted
                    await warn_msg.delete()
                except discord.NotFound:
                    pass

            del common.entry_message_deletion_dict[msg.id]

        async for message in common.entries_discussion_channel.history(
            around=msg.created_at, limit=5
        ):
            try:
                link = message.embeds[0].fields[1].value
                if not isinstance(link, str):
                    continue

                if int(link.split("/")[6][:-1]) == msg.id:
                    await message.delete()
                    break

            except (IndexError, AttributeError):
                pass

async def message_edit(old: discord.Message, new: discord.Message):
    """
    This function is called for every message edited by user.
    """
    if new.content.startswith(common.PREFIX):
        try:
            if new.id in common.cmd_logs.keys():
                await commands.handle(new, common.cmd_logs[new.id])
        except discord.HTTPException:
            pass

    if common.GENERIC or common.TEST_MODE:
        return

    if new.channel in common.entry_channels.values():
        embed_repost_edited = False
        if new.channel.id == common.ServerConstants.ENTRY_CHANNEL_IDS["showcase"]:
            if not entry_message_validity_check(new):
                if new.id in common.entry_message_deletion_dict:
                    deletion_data_list = common.entry_message_deletion_dict[new.id]
                    deletion_task = deletion_data_list[0]
                    if deletion_task.done():
                        del common.entry_message_deletion_dict[new.id]
                    else:
                        try:
                            deletion_task.cancel()  # try to cancel deletion after noticing edit by sender
                            warn_msg = await new.channel.fetch_message(
                                deletion_data_list[1]
                            )
                            deletion_datetime = (
                                datetime.datetime.utcnow()
                                + datetime.timedelta(minutes=2)
                            )
                            await warn_msg.edit(
                                content=(
                                    "I noticed your edit, but: Your entry message must contain an attachment or a (Discord recognized) link to be valid."
                                    " If it doesn't contain any characters but an attachment, it must be a reply to another entry you created."
                                    f" If no attachments are present, it must contain at least 32 characters (including any links, but not links alone)."
                                    f" If you meant to comment on another entry, please delete your message and go to {common.entries_discussion_channel.mention}."
                                    " If no changes are made, your entry message will be"
                                    f" deleted {utils.format_datetime(deletion_datetime, tformat='R')}."
                                )
                            )
                            common.entry_message_deletion_dict[new.id] = [
                                asyncio.create_task(
                                    delete_bad_entry_and_warning(
                                        new, warn_msg, delay=120
                                    )
                                ),
                                warn_msg.id,
                            ]
                        except discord.NotFound:  # cancelling didn't work, warning and entry message were already deleted
                            del common.entry_message_deletion_dict[new.id]

                else:  # an edit led to an invalid entry message from a valid one
                    deletion_datetime = datetime.datetime.utcnow() + datetime.timedelta(
                        minutes=2
                    )
                    warn_msg = await new.reply(
                        "Your entry message must contain an attachment or a (Discord recognized) link to be valid."
                        " If it doesn't contain any characters but an attachment, it must be a reply to another entry you created."
                        f" If no attachments are present, it must contain at least 32 characters (including any links, but not links alone)."
                        f" If you meant to comment on another entry, please delete your message and go to {common.entries_discussion_channel.mention}."
                        " If no changes are made, your entry message will be"
                        f" deleted {utils.format_datetime(deletion_datetime, tformat='R')}."
                    )

                    common.entry_message_deletion_dict[new.id] = [
                        asyncio.create_task(
                            delete_bad_entry_and_warning(new, warn_msg, delay=120)
                        ),
                        warn_msg.id,
                    ]
                return

            elif (
                entry_message_validity_check(new)
                and new.id in common.entry_message_deletion_dict
            ):  # an invalid entry was corrected
                deletion_data_list = common.entry_message_deletion_dict[new.id]
                deletion_task = deletion_data_list[0]
                if not deletion_task.done():  # too late to do anything
                    try:
                        deletion_task.cancel()  # try to cancel deletion after noticing valid edit by sender
                        warn_msg = await new.channel.fetch_message(
                            deletion_data_list[1]
                        )
                        await warn_msg.delete()
                    except discord.NotFound:  # cancelling didn't work, warning and entry message were already deleted
                        pass
                del common.entry_message_deletion_dict[new.id]

        async for message in common.entries_discussion_channel.history(
            around=old.created_at, limit=5
        ):
            try:
                embed = message.embeds[0]
                link = embed.fields[1].value
                if not isinstance(link, str):
                    continue

                if int(link.split("/")[6][:-1]) == new.id:
                    _, fields = format_entries_message(new, "")
                    await embed_utils.edit(message, embed=embed, fields=fields)
                    embed_repost_edited = True
                    break

            except (IndexError, AttributeError):
                pass

        if not embed_repost_edited:
            if (datetime.datetime.utcnow() - old.created_at) < datetime.timedelta(
                minutes=5
            ):  # for new, recently corrected entry messages
                entry_type = "showcase"
                color = 0xFF8800

                title, fields = format_entries_message(new, entry_type)
                await embed_utils.send(
                    common.entries_discussion_channel,
                    title=title,
                    color=color,
                    fields=fields,
                )

async def raw_reaction_add(payload: discord.RawReactionActionEvent):
    """
    Helper to handle a raw reaction added on discord
    """

    # Try to fetch channel without API call first
    channel = common.bot.get_channel(payload.channel_id)
    if channel is None:
        try:
            channel = await common.bot.fetch_channel(payload.channel_id)
        except discord.HTTPException:
            return

    if not isinstance(channel, discord.TextChannel):
        return

    try:
        msg: discord.Message = await channel.fetch_message(payload.message_id)
    except discord.HTTPException:
        return

    if not msg.embeds or common.UNIQUE_POLL_MSG not in str(msg.embeds[0].footer.text):
        return

    for reaction in msg.reactions:
        async for user in reaction.users():
            if user.id == payload.user_id and not utils.is_emoji_equal(
                payload.emoji, reaction.emoji
            ):
                await reaction.remove(user)

async def handle_message(msg: discord.Message):
    """
    Handle a message posted by user
    """
    if msg.type == discord.MessageType.premium_guild_subscription:
        await emotion.server_boost(msg)

    if msg.content.startswith(common.PREFIX):
        ret = await commands.handle(msg)
        if ret is not None:
            common.cmd_logs[msg.id] = ret

        if len(common.cmd_logs) > 100:
            del common.cmd_logs[list(common.cmd_logs.keys())[0]]

        await emotion.update("bored", -10)

    elif not common.TEST_MODE:
        await emotion.check_bonk(msg)

        # Check for these specific messages, do not try to generalise, because we do not
        # want the bot spamming the bydariogamer quote
        # no_mentions = discord.AllowedMentions.none()
        # if unidecode.unidecode(msg.content.lower()) in common.DEAD_CHAT_TRIGGERS:
        #     # ded chat makes snek sad
        #     await msg.channel.send(
        #         "good." if await emotion.get("anger") >= 60 else common.BYDARIO_QUOTE,
        #         allowed_mentions=no_mentions,
        #     )
        #     await emotion.update("happy", -8)

        if common.GENERIC:
            return

        if msg.channel in common.entry_channels.values():

            if msg.channel.id == common.ServerConstants.ENTRY_CHANNEL_IDS["showcase"]:
                if not entry_message_validity_check(msg):
                    deletion_datetime = datetime.datetime.utcnow() + datetime.timedelta(
                        minutes=2
                    )
                    warn_msg = await msg.reply(
                        "Your entry message must contain an attachment or a (Discord recognized) link to be valid."
                        " If it doesn't contain any characters but an attachment, it must be a reply to another entry you created."
                        f" If no attachments are present, it must contain at least 32 characters (including any links, but not links alone)."
                        f" If you meant to comment on another entry, please delete your message and go to {common.entries_discussion_channel.mention}."
                        " If no changes are made, your entry message will be"
                        f" deleted {utils.format_datetime(deletion_datetime, tformat='R')}."
                    )
                    common.entry_message_deletion_dict[msg.id] = [
                        asyncio.create_task(
                            delete_bad_entry_and_warning(msg, warn_msg, delay=120)
                        ),
                        warn_msg.id,
                    ]
                    return

                entry_type = "showcase"
                color = 0xFF8800
            else:
                entry_type = "resource"
                color = 0x0000AA

            title, fields = format_entries_message(msg, entry_type)
            await embed_utils.send(
                common.entries_discussion_channel,
                title=title,
                color=color,
                fields=fields,
            )
        elif (
            random.random() < await emotion.get("happy") / 200
            or msg.author.id == 683852333293109269
        ):
            await emotion.dad_joke(msg)

def cleanup(*_):
    """
    Call cleanup functions
    """
    common.bot.loop.run_until_complete(db.quit())
    common.bot.loop.run_until_complete(common.bot.close())
    common.bot.loop.close()

def run():
    """
    Does what discord.Client.run does, except, handles custom cleanup functions
    and pygame init
    """

    os.environ["SDL_VIDEODRIVER"] = "dummy"
    pygame.init()  # pylint: disable=no-member
    common.window = pygame.display.set_mode((1, 1))

    # use signal.signal to setup SIGTERM signal handler, runs after event loop
    # closes
    signal.signal(signal.SIGTERM, cleanup)

    try:
        common.bot.loop.run_until_complete(common.bot.start(common.TOKEN))

    except KeyboardInterrupt:
        # Silence keyboard interrupt traceback (it contains no useful info)
        pass

    finally:
        cleanup()


import os, time
import pika

time.sleep(10)

########### CONNEXIÓN A RABBIT MQ #######################

HOST = os.environ['RABBITMQ_HOST']
print("rabbit:"+HOST)

connection = pika.BlockingConnection(
    pika.ConnectionParameters(host=HOST))
channel = connection.channel()

#El consumidor utiliza el exchange 'cartero'
channel.exchange_declare(exchange='cartero', exchange_type='topic', durable=True)

#Se crea un cola temporaria exclusiva para este consumidor (búzon de correos)
result = channel.queue_declare(queue="traductor", exclusive=True, durable=True)
queue_name = result.method.queue

#La cola se asigna a un 'exchange'
channel.queue_bind(exchange='cartero', queue=queue_name, routing_key="traductor")

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

########## ESPERA Y HACE ALGO CUANDO RECIBE UN MENSAJE ####

print(' [*] Waiting for messages. To exit press CTRL+C')

#----------------------------------
import requests

def Traduccion(source, target, text):
	parametros = {'sl': source, 'tl': target, 'q': text}
	cabeceras = {"Charset":"UTF-8","User-Agent":"AndroidTranslate/5.3.0.RC02.130475354-53000263 5.1 phone TRANSLATE_OPM5_TEST_1"}
	url = "https://translate.google.com/translate_a/single?client=at&dt=t&dt=ld&dt=qca&dt=rm&dt=bd&dj=1&hl=es-ES&ie=UTF-8&oe=UTF-8&inputm=2&otf=2&iid=1dd3b944-fa62-4b55-b330-74909a99969e"
	response = requests.post(url, data=parametros, headers=cabeceras)
	if response.status_code == 200:
		for x in response.json()['sentences']:
			return x['trans']
	else:
		return "Ocurrió un error"

#---------------------------------

def callback(ch, method, properties, body):
	print(body.decode("UTF-8"))
	arguments = body.decode("UTF-8").split(" ")

	idioma = arguments[1]
	frase = " ".join(arguments[2:])
	result = Traduccion("es", idioma, frase)

	########## PUBLICA EL RESULTADO COMO EVENTO EN RABBITMQ ##########
	print("send a new message to rabbitmq: "+result)
	channel.basic_publish(exchange='cartero',routing_key="discord_writer",body=result)

channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

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


from django import template
import math

register = template.Library()

@register.simple_tag
def products_discount_calculate(Price, Discount):
    if Discount is None or Discount is 0:
        return Price
    Descount_pric = Price
    Descount_pric = Price - (Price * Discount / 100)
    # pro= Products.objects.all()
    # pro.Discount_Price=Descount_pric
    # pro.save()
    return math.floor(Descount_pric)

@register.simple_tag
def progress_bar_quantity(Availability, Total_quantity):
    progress_Bar = Availability
    progress_Bar = Availability * (100/Total_quantity)
    return progress_Bar


import numpy as np
import math
import matplotlib.pyplot as plt
import ratlibfunc
import torch

#%%
# %%
# get_ipython().run_line_magic('matplotlib', 'inline') #
# % matplotlib

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

#%%

cantidad_estados = 6
cantidad_acciones = 5
act = ''  # acción tomada
Rme = 0  # acumulado de rwrd
Dme = 0  # acumulado de tiempo.
#%%
V = torch.empty((cantidad_estados, 2))
# Valor de los estados V(estadp, tiempo)
# filas corresponden a estados
# columna 0 tiempo mas reciente
Q = torch.empty((cantidad_estados, cantidad_acciones))
#Costo C(a(t),s(t-1),a(t-1))
Casa = torch.empty((cantidad_acciones, cantidad_estados, cantidad_acciones))
#Probabilidades para alpha
#P(a(t)/s(t-1),a(t-1))
Pasa_reg = torch.empty((300, cantidad_acciones, cantidad_estados))
# Pas_reg: P(a(t)/s(t)) .... no confundir con Psa_reg
Pas_reg = torch.empty((300, cantidad_acciones, cantidad_estados))

#%%
print(V)
print(Casa)

#%%

class CtxAsoc:
    """
    Esta corteza es la que de alguna manera aprende a resolver el problema.
    Vamos a plantear esta corteza por medio de un algorítmo de reinforcemente learning
    @author: ivan
    """

    def __init__(self):
        """
        Constructor.
        """

        # parametros
        self.cantidad_estados = 6
        self.cantidad_acciones = 5
        self.act = ''  # acción tomada
        self.Rme = 0  # acumulado de rwrd
        self.Dme = 0  # acumulado de tiempo.


import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder

def predict_qty(Fulfilment, fulfilled_by, ship_service_level, Courier_Status, Category):
    # Load the data from the CSV file
    dataamazon = pd.read_csv("C:/Users/nassim/Desktop/pi/material-dashboard-django/apps/quantityprediction/Amazon Sale Report.csv", delimiter=";")

    # Encode the categorical variables
    le = LabelEncoder()
    dataamazon['Fulfilment'] = le.fit_transform(dataamazon['Fulfilment'])
    dataamazon['fulfilled-by'] = le.fit_transform(dataamazon['fulfilled-by'])
    dataamazon['ship-service-level'] = le.fit_transform(dataamazon['ship-service-level'])
    dataamazon['Courier Status'] = le.fit_transform(dataamazon['Courier Status'])
    dataamazon['Category'] = le.fit_transform(dataamazon['Category'])

    # Split the data into features (X) and target variable (y)
    X = dataamazon[['Fulfilment', 'ship-service-level', 'Category', 'Qty', 'Amount', 'fulfilled-by']]
    y = dataamazon["Qty"].values

    # Split the data into training and testing sets
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

    # Create and fit the Random Forest Classifier model
    rf = RandomForestClassifier(n_estimators=100, random_state=0)
    rf.fit(X_train, y_train)

    # Create a new data point using the provided parameters
    new_data = pd.DataFrame([[Fulfilment, ship_service_level, Category, 0, 0, fulfilled_by]],
                            columns=['Fulfilment', 'ship-service-level', 'Category', 'Qty', 'Amount', 'fulfilled-by'])

    # Predict the Qty for the new data point
    qty_pred = rf.predict(new_data)

    return qty_pred.tolist()


import contextlib
from datetime import datetime
from functools import partial
import os
import parser

__author__ = 'jay'

def get_page_by_href(href, cursor, path=None, tracker=None, project_name=None):
    if '#' in href:
        href = href[:href.index('#')]
    if '?' in href:
        href = href[:href.index('?')]
    href = os.path.join(os.path.dirname(path), href)
    href = os.path.normpath(href)
    cursor.execute("select * from resources "
                   "join pages on resources.id=pages.resource_id "
                   "where resources.res_type='page' && path=%s && project=%s",
                   (href, project_name))
    target = cursor.fetchone()
    page = {}
    if target:
        page.update(target)
        if target['postfix']:
            page['title'] = add_postfix_to_title(
                page['title'], page['postfix'])
    if tracker is not None:
        tracker.add(path)
    return page

def add_postfix_to_title(page_title, postfix):
    if isinstance(page_title, unicode):
        page_title = page_title.encode('utf-8')
    if isinstance(postfix, unicode):
        postfix = postfix.encode('utf-8')
    return str(page_title) + ' - ' + str(postfix)

def remove_postfix(page_title, postfix):
    if isinstance(postfix, unicode):
        postfix = postfix.encode()
    return page_title[:page_title.rindex(' - ' + str(postfix))]

def update_page(page_handle, attach_handle, page, source_path, wc, logger):
    with open(source_path, 'r') as reader:
        soup = parser.ConfluencePageInflater(reader, page_handle,
                                             attach_handle)
    page_title = soup.title
    postfix_added = False
    if page['oid'] and page['postfix'] and page['title'] == page_title:
        page_title = add_postfix_to_title(page_title, page['postfix'])
        postfix_added = True
    is_home_page = soup.is_home_page
    keep_trying = True
    while keep_trying:
        if not page['oid']:
            message = "trying create page with title %s" % page_title
        else:
            message = "trying to modify page [%s:%s] with title %s" % (
                page['oid'], page['version'], page_title)
        logger.info(message)
        try:
            page_info = wc.register_page(
                page_title, soup.cleaned_src, pid=page['oid'],
                parent_id=page['parent_id'], mark_home=is_home_page,
                version=page['version'])
            page['oid'] = page_info[0]
            page['version'] = page_info[1]
            break
        except wc.OperationError:
            # maybe version not right, let's try again
            try:
                page_info = wc.get_page(pid=page['oid'], title=page_title)
            except wc.OperationError:
                page_info = None
            if page_info:
                if page['oid']:
                    if page_info[1] != page['version']:
                        logger.error(
                            "failed.\n"
                            "updated page version [%s] and try again"
                            % page_info[1])
                        page['version'] = page_info[1]
                    else:
                        logger.error("fatal problem, will panic.")
                        raise
                elif not postfix_added and page['postfix']:
                    logger.warning(
                        'page with title %s exists, will try create new page'
                        ' with postfix %s' % (page_title,
                                              str(page['postfix'])))
                    page_title = add_postfix_to_title(page_title,
                                                      page['postfix'])
                    postfix_added = True
                else:
                    logger.error('page with title %s exists!!!' % page_title)
                    raise
            else:
                if page['oid']:
                    logger.warning("It seems that remote page has been"
                                   " deleted, lets create a new one...")
                    page['oid'], page['version'] = None, None
                else:
                    logger.error("fatal problem, let's panic.")
                    raise
    if postfix_added:
        page['title'] = remove_postfix(page_title, page['postfix'])
    else:
        page['title'] = page_title
        page['postfix'] = None
    return page

class Filter(object):
    """filter tags and revert

    A filter should work like follow:

    clean up pages:
    process content -> clean up unneeded tags and attributes -> format -> pass to result

    add or modified pages:
                                           not found
    process content -> search attachment  ---------->  create full page      ->      update database
                           |found                                                            |
                           -> create blank page -> upload attachment -> format link -> modified full page

    add attachment: see above

    modified attachment:
    search references -> update all reference -> update database

    delete content:
           page
    delete ----> delete page -----> update database
             | attachment                      |
             -> search references -> delete all reference

    move content: dismiss
    """

    def __init__(self, project_id, db, logger,
                 write_client=None, form=None, **kwargs):
        """

        :param project_id:
        :param db: a database connection. You should be aware that db should
         produce a cursor type that return result as dict.
        :param logger:
        :param write_client:
        :param form:
        :return:
        """
        if not write_client:
            raise ValueError('write_client should not be None!')
        self.project_name = project_id
        self.wc = write_client
        self.db = db
        self.need_update = set()
        self.form = form
        self.logger = logger

    def _put_page(self, cursor, working_dir, page, tracker=None):
        """put a page with client

        :param cursor:
        :param working_dir: path of source, where can actually read source.
            if None, just create a blank page.
        :param page: page info
        :param tracker: a tracker record the page that unable to be update
         complete this time.
        :return:
        """
        source_path = os.path.join(working_dir, page['path'])

        need_update = True
        if not page['oid']:
            need_update = set()

            def do_nothing(*args):
                need_update.add(args[0])

            page = update_page(
                partial(get_page_by_href, path=page['path'], tracker=tracker,
                        cursor=cursor, project_name=self.project_name),
                do_nothing, page, source_path, self.wc, self.logger)
            cursor.execute('insert into resources values(%s, %s, %s, %s)',
                           (None, 'page', page['path'], self.project_name))
            assert cursor.lastrowid > 0
            cursor.execute("insert into pages values(%s, %s, %s, %s, %s,"
                           " %s, %s)",
                           (page['oid'], page['parent_id'], cursor.lastrowid,
                            page['title'], page['postfix'], page['version'],
                            datetime.utcnow()))
        if need_update:
            oid = page['oid']
            page = update_page(
                partial(get_page_by_href, path=page['path'], tracker=tracker,
                        cursor=cursor, project_name=self.project_name),
                partial(self.get_attach_by_href, page_id=page['oid'],
                        base_path=page['path'], cursor=cursor,
                        working_dir=working_dir),
                page, source_path, self.wc, self.logger)
            cursor.execute("update pages set oid=%s, version=%s, title=%s "
                           "where oid=%s", (page['oid'], page['version'],
                                            page['title'], oid))
        return page['oid'], page['version']

    def apply(self, base_dir, outline, changes):
        """push logs to remote

        :param base_dir:
        :param outline: outline of menu, should follow the format {
            phisical_path: list_of_menu_inheritance,
            ...
        }
        :param changes: should follow the format [{
            'operation': operation,  # 'add', 'modified', 'delete' or 'move'
            'content_type': content_type,  # 'page' or 'attachment'
            'path': path,  # path to documentation
            'second_path': second_path,  #  if operation is 'move', this field
            means target_path, else this field is optional.
        }, ...]
        """
        for change_type in ('delete', 'add', 'modified'):
            target_changes = tuple(
                (c['content_type'], c['path'], c['second_path'])
                for c in changes if c['operation'] == change_type)
            if not target_changes:
                continue
            try:
                handle = getattr(self, 'apply_' + change_type)
            except AttributeError:
                continue  # well, fall silently
            try:
                self.db.begin()
                handle(base_dir, outline, target_changes)
                self.db.commit()
            except:
                self.db.rollback()
                raise

    # noinspection PyUnusedLocal
    def apply_delete(self, base_dir, outline, changes):
        """apply delete action on changes

        :param base_dir:
        :param outline:
        :param changes: should follow the format
         [(content_type, path, second_path), ...].
        :return:
        """
        self.logger.info('applying delete changes...')
        attach_changes = tuple(c for c in changes if c[0] == 'attachment')
        page_changes = tuple(c for c in changes if c[0] == 'page')
        with contextlib.closing(self.db.cursor()) as c:
            for change in attach_changes:
                c.execute("select * from resources "
                          "right join attachments"
                          " on resources.id=attachments.resource_id "
                          "where res_type=%s && path=%s && project=%s",
                          ('attachment', change[1], self.project_name))
                attachments = c.fetchall()
                if not attachments:
                    continue  # maybe something we don't care
                for attach in attachments:
                    self.logger.info('deleting attachment: [%s] %s' % (
                        attach['parent_id'], attach['resource_name']))
                    self.wc.delete_attachment(attach['parent_id'],
                                              attach['resource_name'])
                c.execute("delete from attachments where resources_id=%s",
                          (attach['resource_id'],))
                c.execute("delete from resources where id=%s",
                          (attach['resource_id'],))
            for change in page_changes:
                c.execute("select * from resources "
                          "join pages on resources.id=pages.resource_id "
                          "where res_type=%s && path=%s && project",
                          ('page', change[1], self.project_name))
                res = c.fetchone()
                if not res:
                    continue
                self.logger.info(
                    'deleting page: [%s] %s' % (res['oid'], res['title']))
                self.wc.delete_page(res['oid'])
                c.execute("delete from pages where oid=%s", (res['oid'],))
                c.execute("delete from resources where id=%s",
                          (res['resource_id'],))
        self.logger.info('success applying delete.')

    def get_attach_by_href(self, href, title, cursor, base_path=None,
                           page_id=None, working_dir=None):
        """return attachment referenced by href.

         If not exits, will try to create one.
        :param href:
        :param title:
        :param base_path:
        :param page_id:
        :param cursor:
        :param working_dir:
        :return:
        """
        if not page_id:
            return None
        if '#' in href:
            href = href[:href.index('#')]
        if '?' in href:
            href = href[:href.index('?')]
        href = os.path.join(os.path.dirname(base_path), href)
        href = os.path.normpath(href)

        def get_attach():
            cursor.execute("select * from resources "
                           "join attachments "
                           "on resources.id=attachments.resource_id "
                           "where resources.res_type='attachment' "
                           "&& path = %s && parent_id=%s && project=%s",
                           (href, page_id, self.project_name))
            return cursor.fetchone()

        attach = get_attach()
        if attach:
            return attach
        source_path = os.path.join(working_dir, href)
        file_name = href.replace('/', '_')
        try:
            with open(source_path, 'r') as img_reader:
                aid = \
                    self.wc.register_attachment(img_reader,
                                                page_id, file_name)[0]
                self.logger.info(
                    'added a new attachment to page %s: [%s] %s' % (
                        page_id, aid, file_name))
        except IOError:
            return None
        # noinspection PyBroadException
        try:
            cursor.execute("insert into resources values(%s, %s, %s, %s)",
                           (None, 'attachment', href, self.project_name))
            assert cursor.lastrowid > 0
            res_id = cursor.lastrowid
        except:
            # I guess it's because of Duplicate entry. I don't catch
            #  IntegrityError here because no database type is assumed
            cursor.execute("select id from resources "
                           "where path=%s && project=%s",
                           (href, self.project_name))
            res_id = cursor.fetchone()
            if not res_id:
                raise Exception('insert new resources %s fail' % href)
            res_id = res_id['id']

        cursor.execute("insert into attachments values(%s, %s, %s, %s, %s)",
                       (aid, page_id, res_id, file_name, datetime.now()))
        attach = get_attach()
        return attach

    def apply_add(self, base_dir, outline, changes):
        """apply put action on changes

        put action will create a new page if referenced one does not exist.
        only support single parent mode currently.
        :param base_dir:
        :param outline:
        :param changes:
        :return:
        """
        self.logger.info('applying add changes...')
        page_changes = list(c[1] for c in changes if c[0] == 'page')
        tracker = set()  # used to track page not updated completely

        def loop_back(path, c, level, force_update=False):
            c.execute("select * from resources "
                      "join pages on pages.resource_id=resources.id "
                      "where res_type=%s && path=%s && project=%s",
                      ('page', path, self.project_name))
            cur_page = cursor.fetchone()
            if not cur_page:
                dependency = outline.get(path, None)
                if dependency:
                    parent_page = loop_back(dependency[-1], c, level + 1)
                    cur_page = dict(
                        oid=None,
                        version=None,
                        parent_id=parent_page['oid'],
                        path=path,
                        postfix=path,
                    )
                else:
                    cur_page = dict(
                        oid=None,
                        version=None,
                        parent_id=None,
                        path=path,
                        postfix=path,
                    )
                self.logger.info("Can't find registered page with path %s,"
                                 " will create one." % path)
                cur_page['oid'], cur_page['version'] = self._put_page(
                    c, base_dir, cur_page, tracker)
                page_changes.remove(path)
            elif force_update:
                cur_page['oid'], cur_page['version'] = self._put_page(
                    c, base_dir, cur_page, tracker)
                page_changes.remove(path)
            return cur_page

        with contextlib.closing(self.db.cursor()) as cursor:
            clean_outline = set()
            for outline_path in outline:
                if '#' in outline_path:
                    outline_path = outline_path[:outline_path.index('#')]
                if '?' in outline_path:
                    outline_path = outline_path[:outline_path.index('?')]
                clean_outline.add(outline_path)
            # after this loop, all new pages should be created.
            while page_changes:
                page_path = page_changes[-1]
                # we only care about page listed in outline
                if page_path in clean_outline:
                    loop_back(page_path, cursor, 1)
                if page_changes and page_path == page_changes[-1]:
                    page_changes.pop()
            page_changes, tracker = list(tracker), set()
            # after this loop, all possible missing link should be filled.
            while page_changes:
                page_path = page_changes[-1]
                loop_back(page_path, cursor, 1, force_update=True)
        self.logger.info('success applying add.')

    def apply_modified(self, base_dir, outline, changes):
        self.logger.info('applying modified changes...')
        page_changes = tuple(c[1] for c in changes if c[0] == 'page')
        with contextlib.closing(self.db.cursor()) as cursor:
            sql = "select path from resources" \
                  " where project=%s && res_type=%s && path in (%s"
            if len(page_changes) == 1:
                sql += ')'
            else:
                # question: does a sql statement has a length limit?
                sql = ','.join((sql,) +
                               ('%s',) * (len(page_changes) - 2) + ('%s)',))
            args = (self.project_name, 'page') + page_changes
            cursor.execute(sql, args)
            in_path = set(c['path'] for c in cursor.fetchall())
            add_path = set(page_changes) - in_path
            if add_path:
                # in case we start merging from middle
                self.apply_add(base_dir, outline, add_path)
            for page_path in page_changes:
                cursor.execute("select * from resources "
                               "join pages on pages.resource_id=resources.id "
                               "where res_type=%s && path=%s && project=%s",
                               ('page', page_path, self.project_name))
                cur_page = cursor.fetchone()
                assert cur_page is not None
                self._put_page(cursor, base_dir, cur_page, None)
        self.logger.info('success applying modified.')


import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

plt.style.use("seaborn-v0_8-white")
plt.rc('axes', grid=False, facecolor="white")
plt.rcParams.update({'font.size': 14})

df_train = pd.read_csv("response_train.csv")
df_test = pd.read_csv("response_test.csv")

fig, axes = plt.subplots(1, 2, figsize=(10, 8))

g1 = sns.scatterplot(ax=axes[0], data=df_train, x="Real values", y="Predictions")
axes[0].set_title("Training process")

# Draw a line of x=y
x0 = min(df_train['Real values'])
x1 = max(df_train['Real values'])

y0 = min(df_train['Predictions'])
y1 = max(df_train['Predictions'])

lims = [min(x0, y0), max(x1, y1)]
g1.plot(lims, lims, '-r')

g2 = sns.scatterplot(ax=axes[1], data=df_test, x="Real values", y="Predictions")
axes[1].set_title("Testing process")

x0 = min(df_test['Real values'])
x1 = max(df_test['Real values'])

y0 = min(df_test['Predictions'])
y1 = max(df_test['Predictions'])

lims = [min(x0, y0), max(x1, y1)]
g2.plot(lims, lims, '-r')

plt.savefig("plot_real_testing.png", dpi=300)


# -*- coding: utf-8 -*-
import json

import pandas as pd
import scrapy

from mro.items import UniversalItem

out = pd.read_csv("mro/spiders/csv_data/Rexnord/Rexnord_images.csv", sep=',')
catalog = [str(item).strip() for item in list(out.catalog_number)]
ids = list(out.id)
catalog_ids = dict(zip(catalog, ids))

class RexnordImagesCrawl(scrapy.Spider):
    name = "rexnord_docs"

    def start_requests(self):
        for row in catalog:
            url = 'https://www.rexnord.com/Product/' + row
            yield scrapy.Request(url=url,
                                 callback=self.parse_item,
                                 dont_filter=True,
                                 meta={'row': row}
                                 )

    def create_item(self, row, name, url):
        item = UniversalItem()
        item['ids'] = catalog_ids[row]
        item['catalog_number'] = row
        item['name'] = name
        item['url'] = url
        return item

    def custom_extractor(self, response, expression):
        data = response.xpath(expression).extract_first()
        return data if data else ''

    def parse_item(self, response):
        row = response.meta['row']
        productId = response.xpath('//*').re(r'"productId":"(.+)","productName')[0]
        return scrapy.Request(
            url='https://www.rexnord.com/api/v1/products/' + productId + '?expand=documents,specifications,styledproducts,htmlcontent,attributes,crosssells,relatedproductbrand,excludeconfiguredproduct',
            callback=self.parse_img,
            meta={'row': row}
        )

    def parse_img(self, response):
        row = response.meta['row']
        data = response.body_as_unicode()
        data = json.loads(data)
        '''
        img = data['product']['largeImagePath']
        img = img if 'NotFound' not in img else ''
        upc = data['product']['upcCode']
        material = data['product']['modelNumber']
        brand = data['product']['manufacturerItem']
        shortDescription = data['product']['shortDescription']
        htmlContent = data['product']['htmlContent']

        descr = self.construct_table(upc, material, brand, model, shortDescription, htmlContent)
        '''
        list_docs = data['product']['documents']
        if list_docs != []:
            for item in list_docs:
                url = 'https://www.rexnord.com/api/v2/getProductDocuments?documentNumbers=' + item['filePath']
                yield scrapy.Request(url=url,
                                     callback=self.extract_docs,
                                     dont_filter=True,
                                     meta={'row': row}
                                     )

    def extract_docs(self, response):
        row = response.meta['row']
        data = response.body_as_unicode()
        data = json.loads(data)
        list_docs = data['productDocuments']
        for item in list_docs:
            yield self.create_item(row, item['name'], response.urljoin(item['filePath']))


#!/usr/bin/env python3

# Main process for smartcam devices

# NOTE:
# This process is managed through systemd and is configured to auto-restart
# in the event of a graceful exit or failure. Please be careful when including
# code before the update procedures to avoid potentially breaking the
# ability to upgrade/repair these modules remotely.

import hashlib
import importlib
import logging
import multiprocessing as mp
import os
import py_compile
import shutil
import socket
import subprocess
import sys
import time

import git

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

CAM_ID = socket.gethostname()
GIT_REPO = git.Repo('.')

logger = logging.getLogger()
logger.addHandler(logging.StreamHandler(sys.stdout))
if str(GIT_REPO.active_branch) is 'release':
    logger.setLevel(logging.INFO)
else: # master/devrelease/others
    logger.setLevel(logging.DEBUG)

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

def check_pyfile_integrity(filename):
    try:
        py_compile.compile(filename, doraise=True)
    except Exception as e:
        logger.error(e)
        return(False)
    return(True)

def get_file_checksum(filename):
    if not os.path.isfile(filename):
        logger.error("File %s not found" % filename)
        return(None)

    with open(filename, 'rb') as fileh:
        content = fileh.read()
        md5hash = hashlib.md5(content).hexdigest()
    return(md5hash)

def git_fetch_from_remote():
    try:
        fetch = GIT_REPO.remotes.origin.fetch()[0]
        if fetch.old_commit is not None:
            logger.info("Fetching %.7s .. %.7s" % (fetch.old_commit, fetch.commit))
            return(True)
    except Exception as e:
        logger.error(str(e))
    return(False)

def git_merge_changes():
    merge_res = str(subprocess.check_output(['git', 'merge']))
    logger.debug(merge_res)
    #if "Already up to date" in merge_res:
    #    this-shouldnt-happen-so-cleanup
    return

def check_main_update():
    upgrade_md5 = get_file_checksum('smartcam.py')
    running_md5 = get_file_checksum('smartcam')

    if not running_md5: return(False) # development

    if upgrade_md5 != running_md5:
        logger.info('Update to main process detected')
        if check_pyfile_integrity('smartcam.py'):
            logger.debug('Main process update passed integrity tests')
            shutil.copyfile('smartcam.py', 'smartcam')
            return(True)
        else:
            logger.error('Main process update failed integrity tests')
    return(False)

def check_worker_update(initial_md5):
    current_md5 = get_file_checksum('worker.py')
    if initial_md5 != current_md5:
        logger.info('Update to worker process detected')
        return(True)
    return(False)

def check_model_update(initial_md5):
    current_md5 = get_file_checksum('tiny_yolo.h5')
    if initial_md5 != current_md5:
        logger.info('Update to model detected')
        return(True)
    return(False)

def terminate_worker():
    if worker_p.is_alive():
        logger.info('Terminating existing worker')
        worker_p.terminate()
        time.sleep(10)
    return

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

# initial worker process data:
try:
    worker_md5 = get_file_checksum('worker.py')
    model_md5 = get_file_checksum('tiny_yolo.h5')

    import worker
    worker_p = mp.Process(target=worker.run, args=(CAM_ID,))
except Exception as e:
    worker_p = None
    logger.error(e)

###

while True:
    # check for updates:
    if git_fetch_from_remote():
        git_merge_changes()

        if check_main_update():
            logger.info('Main update installed, restarting daemon')
            terminate_worker()
            sys.exit(0)

        if check_worker_update(worker_md5):
            terminate_worker()
            try:
                importlib.reload(worker)
            except Exception as e:
                logger.error(e)

        # NOTE: reading large binary file causes memory spike
        if check_model_update(model_md5):
            terminate_worker()

    # monitor worker health:
    try:
        if worker_p.is_alive():
            logger.debug('Worker process seems alive and well')
        else:
            if worker_p.exitcode: # crashed
                logger.warning('Worker process seems to have crashed')
                worker_p = mp.Process(target=worker.run, args=(CAM_ID,))
            worker_p.start()
    except Exception as e:
        logger.error("Unable to initialise worker (%s)" % e)
    finally:
        time.sleep(600)


import torch
from Data_set import MyDataset
from torch import nn, save, load
from torch.optim import Adam
import os
import numpy as np

class PAD(nn.Module):
    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, (3,3)),
            nn.ReLU(),
            nn.Conv2d(32, 64, (3,3)),
            nn.ReLU(),
            nn.Conv2d(64, 64, (3,3)),
            nn.ReLU(),
            nn.Flatten(),
            nn.Linear(64*(28-6)*(28-6), 10)
        )

    def forward(self, x):
        return self.model(x)

data=[]
labels=[]

real_path = r"D:\db\training_tesnsors\real_tensors"
real_list = os.listdir(real_path)
count=0
for real in real_list:
    tensor = torch.load("D:/db/training_tesnsors/real_tensors/"+real)
    data.append(tensor)
    labels.append(0)
    count+=1
    if count ==4:
        break

hand_path = r"D:\db\training_tesnsors\hand_tensors"
hand_list = os.listdir(hand_path)
for hand in hand_list:
    tensor = torch.load("D:/db/training_tesnsors/hand_tensors/"+hand)
    data.append(tensor)
    labels.append(1)
    count+=1
    if count ==8:
        break

fixed_path = r"D:\db\training_tesnsors\fixed_tensors"
fixed_list = os.listdir(fixed_path)
for fixed in fixed_list:
    tensor = torch.load("D:/db/training_tesnsors/fixed_tensors/"+fixed)
    data.append(tensor)
    labels.append(2)
    count+=1
    if count ==12:
        break

labels_tensor = torch.from_numpy(np.array(labels)).long()
print('data has been loaded')
dataset = MyDataset(data, labels_tensor)

# Create a data loader to iterate over the dataset
dataloader = torch.utils.data.DataLoader(dataset, batch_size=200, shuffle=True)
# Instance of the neural network, loss, optimizer
clf = PAD().to('cpu')
opt = Adam(clf.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()

# Training flow
if __name__ == "__main__":
    for epoch in range(2): # train for 10 epochs
        for batch in dataset:
            print("running")
            X,y = batch
            X, y = X.to('cpu'), y.to('cpu')
            yhat = clf(X)
            loss = loss_fn(yhat, y)

            # Apply backprop
            opt.zero_grad()
            loss.backward()
            opt.step()

        print(f"Epoch:{epoch} loss is {loss.item()}")


#sam deploy --no-confirm-changeset
import json
import os
import logging
import boto3
import io
#import request
from botocore.exceptions import ClientError  # type: ignore[import]
import base64
import copy

from common import cors_headers

logger = logging.getLogger('scambiupload')
logger.setLevel(logging.INFO)

SCAMBIFOLDER = os.environ.get('SCAMBIFOLDER')
SCAMBIIMAGES = os.environ.get('SCAMBIIMAGES')
SCAMBICONFIG = os.environ.get('SCAMBICONFIG')
CONFIG_FILE = os.environ.get('CONFIG_FILE')
SAMPLE_CONFIG_FILE = os.environ.get('SAMPLE_CONFIG_FILE')
SIM_LAMBDA = os.environ.get('SIM_LAMBDA')
RAW_IMAGE = os.environ.get('RAW_IMAGE')
OVERLAY_IMAGE = os.environ.get('OVERLAY_IMAGE')
SCAMBIWEB = os.environ.get('SCAMBIWEB')
_EVENT_QUEUE_URL = os.environ.get('EVENT_QUEUE_URL')
EVENTS_TABLE = os.environ.get('EVENTS_TABLE')

s3_client = boto3.resource('s3')
sqs_client = boto3.client('sqs')
# sqs_url = sqs_client.get_queue_url(
#         QueueName="positions",
#     )
dynamodb = boto3.resource('dynamodb')
db_table_client = dynamodb.Table(EVENTS_TABLE)
lambda_client = boto3.client('lambda')

def purge_queue(_sqs_client, queue_url):
    """
    Deletes the messages in a specified queue
    """
    try:
        response = _sqs_client.purge_queue(QueueUrl=queue_url)
    except ClientError:
        logger.exception(f'Could not purge the queue - {queue_url}.')
        raise
    else:
        return response

def send_message(queue, message_body, message_attributes=None):
    """
    Send a message to an Amazon SQS queue.

    :param queue: The queue that receives the message.
    :param message_body: The body text of the message.
    :param message_attributes: Custom attributes of the message. These are key-value
                               pairs that can be whatever you want.
    :return: The response from SQS that contains the assigned message ID.
    """
    if not message_attributes:
        message_attributes = {}

    try:
        response = queue.send_message(
            MessageBody=message_body,
            MessageAttributes=message_attributes
        )
    except ClientError as error:
        logger.exception("Send message failed: %s", message_body)
        raise error
    else:
        return response

class Boto3S3Client():

    def __init__(self, aws_region):
        self._aws_region = aws_region
        self._s3_client = boto3.client(  # type: ignore[attr-defined]
            's3',
            region_name=aws_region
            )

    def write_img(
        self,
        img,
        bucket_name: str,
        folder_name: str,
        object_name: str
    ):
        if folder_name is None:
            self._s3_client.put_object(
                Bucket=bucket_name,
                Key=f"{object_name}",
                Body=bytes(img))
        else:
            self._s3_client.put_object(
                Bucket=bucket_name,
                Key=f"{folder_name}/{object_name}",
                Body=img)

    def delete(
            self,
            bucket_name: str,
            folder_name: str,
            object_name: str
    ):

        if folder_name is None:
            self._s3_client.delete_object(
                Bucket=bucket_name,
                Key=object_name)

        else:
            self._s3_client.delete_object(
                Bucket=bucket_name,
                Key=f"{folder_name}/{object_name}"
                )

    def write(
            self,
            input_bytes: bytes,
            *,
            bucket_name: str,
            folder_name: str,
            object_name: str
    ):

        bytes_io = io.BytesIO(input_bytes)
        if folder_name is None:
            self._s3_client.upload_fileobj(
                bytes_io,
                bucket_name,
                object_name
                )
        else:
            self._s3_client.upload_fileobj(
                bytes_io,
                bucket_name,
                f"{folder_name}/{object_name}"
                )

    def read(
            self,
            *,
            bucket_name: str,
            folder_name: str,
            object_name: str,
    ):

        obj = io.BytesIO()
        try:
            self._s3_client.download_fileobj(
                bucket_name,
                f"{folder_name}/{object_name}",
                obj)
        except ClientError as error:
            # logging here
            raise error

        return obj.getvalue()

    def list_files(
            self,
            *,
            bucket_name: str,
            folder_name: str,
    ):

        #  paginator required to avoid item limitation returned
        #  by list_objects_v2
        paginator = self._s3_client.get_paginator('list_objects_v2')
        pages = paginator.paginate(
            Bucket=bucket_name,
            Prefix=folder_name)

        files = []
        for page in pages:
            files += [i["Key"] for i in page["Contents"]]

        out_filenames = []
        prefix = folder_name + "/"
        for file in files:
            #  ensure file has folder prefix
            if file[0:len(prefix)] == prefix:
                out_filenames.append(file.replace(prefix, ""))

        return out_filenames
s3_custom = Boto3S3Client("us-east-1")

def decode_image_from_str(encoded_image: str):
    """decodes image from string. Expects base64 encoding

    Args:
        encoded_image: str representing image

    Returns:
        np.array image"""
    jpg_original = base64.b64decode(encoded_image)
    return jpg_original

def str_to_bytes(string_: str):
    return str.encode(string_)

def bytes_to_str(bytes_: bytes):
    return bytes_.decode()

def lambda_handler(event, context):

    order = json.loads(event['body'])
    # this is the dynamic reference in template.yaml
    authentication_code = order['authentication']

    if authentication_code not in ["farts", "teehee"]:
        print("bad log in")
        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({'message': 'stranger danger'})
        }

    action = order['action']

    if action == "image_raw":

        image_bytes = str_to_bytes(order['payload'])
        img_jpg = decode_image_from_str(order['payload'])
        s3_custom.write(
            input_bytes=image_bytes,
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBIIMAGES,
            object_name=RAW_IMAGE)

        s3_custom.write_img(
            img=img_jpg,
            bucket_name=SCAMBIWEB,
            folder_name=None,
            object_name=RAW_IMAGE)

        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'uploaded image to ',
                'bucketfiles': ""})
        }

    if action == "image_overlay":

        image_bytes = str_to_bytes(order['payload'])

        img_jpg = decode_image_from_str(order['payload'])

        s3_custom.write(
            input_bytes=image_bytes,
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBIIMAGES,
            object_name=OVERLAY_IMAGE)

        s3_custom.write_img(
            img=img_jpg,
            bucket_name=SCAMBIWEB,
            folder_name=None,
            object_name=OVERLAY_IMAGE)

        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'uploaded image to ',
                'bucketfiles': ""})
        }

    if action == "getimage_raw":

        obj = s3_custom.read(
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBIIMAGES,
            object_name=RAW_IMAGE)

        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'got raw image ok',
                'image': bytes_to_str(obj)})
        }

    if action == "getimage_overlay":

        obj = s3_custom.read(
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBIIMAGES,
            object_name=OVERLAY_IMAGE)

        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'got overlay image ok',
                'image': bytes_to_str(obj)})
        }

    if action in ["check_event"]:
        # check for event and then remove all
        scan = db_table_client.scan()# do not use on big tables!!

        print("getting event", scan['Items'])
        output = copy.deepcopy(scan['Items'])
        with db_table_client.batch_writer() as batch:
            for each in scan['Items']:
                batch.delete_item(Key=each)
        return{
            'statusCode': 200,
            'headers': cors_headers,
            'body': json.dumps(output)
        }
    if action in [
                    "reset",
                    "update_image",
                    "update_image_all"]:

        if action in ["update_image", "update_image_all"]:
            s3_custom.delete(
                bucket_name=SCAMBIWEB,
                folder_name=None,
                object_name=OVERLAY_IMAGE)
            s3_custom.delete(
                bucket_name=SCAMBIWEB,
                folder_name=None,
                object_name=RAW_IMAGE)
        # we onlyt want one action at a time

        scan = db_table_client.scan()# do not use on big tables!!
        print(scan['Items'])
        with db_table_client.batch_writer() as batch:
            for each in scan['Items']:
                batch.delete_item(Key=each)

        response = db_table_client.put_item(
            Item={
                'event': action
            }
        )

        status_code = response['ResponseMetadata']['HTTPStatusCode']
        print("WRITE TO DB", status_code)

        # need this for CORS
        return{
            'statusCode': 200,
            'headers': cors_headers,
            'body': json.dumps({
                'message': f'{action} ok'})
        }
    #sam deploy --no-confirm-changeset
    if action == "send_sample_config":
        click_data = (order['data'])
        messages_bytes = str_to_bytes(json.dumps(click_data))
        print(messages_bytes)

        # now load existing one
        curr_config_bytes = s3_custom.read(
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBICONFIG,
            object_name=SAMPLE_CONFIG_FILE)

        curr_config_str = bytes_to_str(curr_config_bytes)
        curr_config_json = json.loads(curr_config_str)
        print("curr_config_json", curr_config_json)
        print("type curr_config_json", type(curr_config_json))
        print("click_data", click_data)
        print("type click_data", type(click_data))

        # check both have same keys
        if not set(curr_config_json.keys()) == set(click_data.keys()):
            return {
                'statusCode': 201,
                'headers': cors_headers,
                'body': json.dumps({
                    'message': "ERROR - CONFIG KEYS DO NOT MATCH - RELOAD DEFAULTS"})
            }
        s3_custom.write(
            input_bytes=messages_bytes,
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBICONFIG,
            object_name=SAMPLE_CONFIG_FILE)

        # need this for CORS
        return {
            'statusCode': 200,
            'headers': cors_headers,
            'body': json.dumps({
                'message': "send_sample_config ok"})
        }

    if action == "get_region_sim":
        print("get region sim")
        lambda_payload = json.dumps({}).encode('utf-8') # doesnt matter for now - but add action in here later
        print("get region sim created json")
        response = lambda_client.invoke(FunctionName=SIM_LAMBDA,
                     InvocationType='RequestResponse',
                     Payload=lambda_payload)
        print("region sim response", response)
        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'request_config ok',
                'config': bytes_to_str(config_bytes)})
        }

    if action == "request_config":

        config_bytes = s3_custom.read(
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBICONFIG,
            object_name=CONFIG_FILE)
        print("req", config_bytes)
        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'request_config ok',
                'config': bytes_to_str(config_bytes)})
        }
    if action == "request_sample_config":

        config_bytes = s3_custom.read(
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBICONFIG,
            object_name=SAMPLE_CONFIG_FILE)
        print("req", config_bytes)
        return{
            'statusCode': 201,
            'headers': cors_headers,
            'body': json.dumps({
                'message': 'request_sample_config ok',
                'config': bytes_to_str(config_bytes)})
        }
    if action == "sendposfinish":
        # should be a list of dictionaries
        click_data = (order['data'])

        # for getting objects off a queue
        # clickpositions = []
        # for get_msg in range(0, 200):
        #     # Receive message from SQS queue
        #     response = sqs_client.receive_message(
        #         QueueUrl=_EVENT_QUEUE_URL,
        #         AttributeNames=[
        #             'SentTimestamp'
        #         ],
        #         MaxNumberOfMessages=1,
        #         MessageAttributeNames=[
        #             'All'
        #         ],
        #         VisibilityTimeout=0,
        #         WaitTimeSeconds=0
        #     )

        #     # obviously much better way of doing this
        #     if 'Messages' not in response:
        #         break

        #     message = response['Messages'][0]
        #     clickpositions.append(message['Body'])
        #     receipt_handle = message['ReceiptHandle']

        #     # Delete received message from queue
        #     sqs_client.delete_message(
        #         QueueUrl=_EVENT_QUEUE_URL,
        #         ReceiptHandle=receipt_handle
        #     )

        messages_bytes = str_to_bytes(json.dumps(click_data))
        print(messages_bytes)
        s3_custom.write(
            input_bytes=messages_bytes,
            bucket_name=SCAMBIFOLDER,
            folder_name=SCAMBICONFIG,
            object_name=CONFIG_FILE)

        # need this for CORS
        return {
            'statusCode': 200,
            'headers': cors_headers,
            'body': json.dumps({
                'message': "processed OK"})
        }

    return{
        'statusCode': 201,
        'headers': cors_headers,
        'body': json.dumps({
            'message': 'unmatched action'})
    }


"""Iperf benchmark
    https://github.com/esnet/iperf
"""
from __future__ import division

import json

from cached_property import cached_property

from hpcbench.api import Benchmark, Metrics, MetricsExtractor
from hpcbench.toolbox.process import find_executable, physical_cpus

class IPERFExtractor(MetricsExtractor):
    """Parse JSON file written by stream to extract interested metrics
    """

    @property
    def metrics(self):
        return dict(
            bandwidth_receiver=Metrics.MegaBytesPerSecond,
            bandwidth_sender=Metrics.MegaBytesPerSecond,
            max_bandwidth=Metrics.MegaBytesPerSecond,
            retransmits=Metrics.Cardinal,
        )

    def extract_metrics(self, metas):
        bits_in_mb = float(8 * 1024 * 1024)
        with open(self.stdout) as istr:
            data = json.load(istr)
        if not data['intervals']:
            raise Exception('Missing "intervals" in JSON: ')
        max_bits_per_second = max(
            [interval['sum']['bits_per_second'] for interval in data['intervals']]
        )
        sent = data['end']['sum_sent']
        received = data['end']['sum_received']
        return dict(
            max_bandwidth=max_bits_per_second / bits_in_mb,
            bandwidth_receiver=received['bits_per_second'] / bits_in_mb,
            bandwidth_sender=sent['bits_per_second'] / bits_in_mb,
            retransmits=sent['retransmits'],
        )

class Iperf(Benchmark):
    """Provides TCP benchmark.
    """

    name = 'iperf'

    DEFAULT_DEVICE = 'network'
    DEFAULT_EXECUTABLE = 'iperf3'
    DEFAULT_SERVER = "localhost"

    def __init__(self):
        # locate `stream_c` executable
        super(Iperf, self).__init__(
            attributes=dict(
                executable=Iperf.DEFAULT_EXECUTABLE,
                server=Iperf.DEFAULT_SERVER,
                options=["-P", str(physical_cpus())],
                mpirun=[],
            )
        )

    @cached_property
    def executable(self):
        """Get absolute path to iperf executable
        """
        return self.attributes['executable']

    @property
    def server(self):
        """Specifies the Iperf server to connect to"""
        return self.attributes['server']

    @property
    def mpirun(self):
        """List of mpirun options (prepended to the command)
        "mpirun" is added if attribute is not empty and
        do not start by mpirun
        """
        return [str(e) for e in self.attributes['mpirun']]

    @property
    def options(self):
        """List of additional arguments appended
        to the command line"""
        return [str(e) for e in self.attributes['options']]

    def execution_matrix(self, context):
        del context  # unused
        yield dict(
            category=Iperf.DEFAULT_DEVICE,
            command=self.mpirun
            + [
                find_executable(self.executable, required=False),
                '-c',
                self.server,
                '-J',
            ]
            + self.options,
        )

    @cached_property
    def metrics_extractors(self):
        return IPERFExtractor()


from django.shortcuts import render
from django.views import View
from django import http

# Create your views here.
class IndexView(View):
    # 首页广告
    def get(self, request):
        return render(request, 'index.html')

class VersionView(View):
    # 返回版本号
    def get(self, request):
        """
        :return: JSON
        """
        # 实现业务逻辑，使用username查询对应记录的条数。
        version = "CI_COMMIT_TAG"
        # 响应结果
        return http.JsonResponse({'version': version})


import cv2
import pytesseract
import logging
import sys
import os
import threading
from flask import Flask,request,make_response
from flask_api import status
from werkzeug.utils import secure_filename
import requests
from datetime import datetime
import time

class Recognition:

    def __init__(self, static_files_detection, logging_path, verbosity, host, port, mutex) -> None:
        self.__static_files_detection = static_files_detection
        self.__verbosity=verbosity
        self.__flaskServer = None
        self.__detection= None
        self.__mutex= mutex
        self.__setup_logging(verbosity, logging_path)

    def __setup_logging(self, verbosity, path):
        format = "%(asctime)s %(filename)s:%(lineno)d %(levelname)s - %(message)s" #formato del messaggio
        filename = path
        datefmt = "%d/%m/%Y %H:%M:%S"
        level = logging.INFO
        if (self.__verbosity):
            level = logging.DEBUG

        ''' definisco un oggetto console handler tramite la classe logging.Streamhandler
         setto il livello del log, ed utilizzo metodo setformatter per definire il formato dei messaggi da stampare
          nel file di log   '''

        logging.basicConfig(filename=filename, filemode='a', format=format, level=level, datefmt=datefmt)

    def setup(self):
        if not os.path.exists(self.__static_files_detection):
            os.makedirs(self.__static_files_detection)

        if not os.path.exists("/opt/app/static-files/detected.txt"):
            current_directory = os.getcwd()
            directory = "/opt/app/static-files"
            os.chdir(directory)
            f = open("detected.txt", "w")
            f.close()
            os.chdir(current_directory)

        self.__detection = threading.Thread(
            target = self.__detection_job,
            args = ()
        )

    def  __detection_job(self):
        while True:
                if not self.__detected_folder_is_empty() and self.__oldest():
                    oldest_frame_path = self.__oldest()
                    frame =  self.__get_frame(oldest_frame_path)
                    frame_name = os.path.basename(oldest_frame_path)
                    text = pytesseract.image_to_string(frame, lang="eng")
                    text = text.replace(" ", "")
                    text = text.replace("\n", "")
                    if len(text)>1:
                        current_directory=os.getcwd()
                        os.chdir("/opt/app/static-files")
                        file = open("detected.txt","a")
                        file.write(text+"-"+frame_name+"-"+datetime.now().strftime("%d/%m/%Y %H:%M:%S")+"\n \n ")
                        file.close()
                        os.chdir(current_directory)
                        os.chdir(self.__static_files_detection)
                        os.rename(frame_name,"detected_"+frame_name)
                        os.chdir(current_directory)
                    else:
                        current_directory=os.getcwd()
                        os.chdir(self.__static_files_detection)
                        os.rename(frame_name,"not_detected_"+frame_name)
                        os.chdir(current_directory)
                time.sleep(0.4)

    def __detected_folder_is_empty(self):
        path = self.__static_files_detection
        return True if not len(os.listdir(path)) else False

    def __oldest(self):
        path = self.__static_files_detection
        files = os.listdir(path)
        paths = [os.path.join(path, basename) for basename in files if "crop_" in basename and "detected_" not in basename]
        if paths:
            return min(paths, key=os.path.getctime)
        else:
            return 0

    def __get_frame(self, filename):
        """ Read image from file using opencv.

            Args:
                filename(str): relative or absolute path of the image

            Returns:
                (numpy.ndarray) frame read from file
        """
        return cv2.imread(filename)

    def start(self):
        self.__detection.start()


# Process ls
#
# Unix based systems provide a command ls to list the contents of a directory.
# By default it displays the contents in multiple columns
# (the number of columns is determined by the width of the console window).
# The space between entries in adjacent columns is at least two spaces.
# An example of the output is as follows.
#
# acpid.pid     console-kit-daemon.pid  lock        pm-utils      sdp                      upstart-socket-bridge.pid
# acpid.socket  crond.pid               mdm.pid     postgresql    sendsigs.omit.d          upstart-udev-bridge.pid
# apache2       crond.reboot            mdm_socket  pppconfig     shm                      user
# apache2.pid   cups                    motd        resolvconf    udev                     utmp
# avahi-daemon  dbus                    mount       rsyslogd.pid  udisks                   wicd
# console       dhclient.pid            network     samba         udisks2                  wpa_supplicant
# ConsoleKit    initramfs               plymouth    screen        upstart-file-bridge.pid
# Write a function process_ls that takes a string, containing the output from a call to ls.
# The function returns a list containing the contents of the directory.
# The order of the contents should be the same as specified in the string,
# i.e. the contents in the first column from top to bottom, then the second column, etc.
#
# Note that the contents of the directory will not contain two adjacent spaces, and will not start or end with a space.
#
# Example
from pprint import pprint
import itertools as it
import re

def process_ls(string):
    lis =[]
    for ls in string.splitlines():
        lis.append(re.split('  +', ls))

    l = list(it.zip_longest(*lis))

    flattened = [val for sublist in l for val in sublist]
    a = [x for x in flattened if x is not None]

    a = list(filter(None, a))
    a = [x.strip('# ') for x in a]

    return a
    # fsasadsadsadasdasdsadsadasdassd
    #return sorted(a, key=lambda x: x.lower(), reverse=False)


from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

def find_by_text(driver, tag, text):
    """ --Encontrar o elemento com o texto 'text'--

    Argumentos:
     - driver: Instância do browser (Chrome)
     - text: Conteúdo que estará na tag
     - tag: Onde o texto será buscado
    """
    elements = driver.find_elements(By.TAG_NAME, tag) # Retorna lista, pois há vários elementos com a mesma TAG_NAME
    for e in elements:
        if e.text == text:
            return e

def find_by_href(driver, link):
    """ --Encontrar o elemento 'a' com o link 'link'

    Argumentos:
     - driver: Instância do browser (Chrome)
     - link: Link que será procurado dentro de todas as tag 'a'
    """
    elements = driver.find_elements(By.TAG_NAME, 'a')
    for e in elements:
        if link in e.get_attribute('href'):
            return e

driver.get('http://selenium.dunossauro.live/aula_04_a.html')

element_ddg = find_by_text(driver, 'li', 'DuckDuckGo')
print(element_ddg)
print(element_ddg.text)
print(element_ddg.get_attribute('href'))

element_ddg = find_by_href(driver, 'ddg.gg')
print(element_ddg)
print(element_ddg.text)
print(element_ddg.get_attribute('href'))


import os
import sqlite3
import glob

fileList = ['information.docx','Hello.txt','myImages.png', \
            'myMovie.mpg','World.txt','data.pdf','myPhoto.jpg']

conn = sqlite3.connect('pythonDrillDB.db')

with conn: # CREATE DB
    cur = conn.cursor()
    cur.execute("CREATE TABLE IF NOT EXISTS tbl_Files(\
        ID INTEGER PRIMARY KEY AUTOINCREMENT, \
        col_FileName TEXT \
        )")
    conn.commit()
cur.close()
conn.close()

for fileName in fileList: # VERIFY .TXT AND INSERT INTO TABLE
    if fileName.endswith(".txt"):
        x = fileName
        conn = sqlite3.connect('pythonDrillDB.db')
        with conn:
            cur = conn.cursor()
            cur.execute("INSERT INTO tbl_Files(col_FileName) VALUES (?)", \
                        (x,))
            conn.commit()
        conn.close()

conn = sqlite3.connect('pythonDrillDB.db')

with conn:
    cur = conn.cursor()
    cur.execute("SELECT col_FileName FROM tbl_Files")
    varFiles = cur.fetchall()
    for item in varFiles:
        msg = "File Names: {}".format(item)
        print(msg)