# -*- coding: utf-8 -*-
#coding: utf-8
import sys
# sys.path.append("../..")
sys.path.append("../")
from itertools import cycle
from datetime import datetime, timedelta, date
#from dateutil import rrule
import configparser
import re
from unicodedata import normalize
import os
import zipfile
import urllib.request
import shutil
import inspect
import glob
import csv
import base64
import ftplib
import selenium
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
#from selenium.webdriver import ActionChains
from PIL import ImageDraw
from PIL import ImageFont
from PIL import Image
import cv2
import PIL
import unicodedata
import numpy as np
import pandas as pd
import img2pdf
import time
import logging
import logging.config
import os
from anticaptchaofficial.imagecaptcha import *
from anticaptchaofficial.recaptchav2proxyless import *
from anticaptchaofficial.turnstileproxyless import *
from threading import Thread
import openpyxl
import xlsxwriter
import pkg_resources
import pyautogui
import cv2
from pathlib import Path
import os
import imageio.v3 as imageio
from PIL import ImageFont
from PIL import Image
from colorama import Fore, Back, Style

pyautogui.MINIMUM_DURATION = 0.5
pyautogui.MINIMUM_SLEEP = 0.8
pyautogui.PAUSE = 0.7


if os.name == 'nt':
    import winreg
    import win32com.shell.shell as shell
    import win32api, win32con, win32event, win32process
    # from win32com.shell.shell import ShellExecuteEx
    from win32com.shell import shellcon
    # import pywin32.shell.shell as shell
    from colorama import just_fix_windows_console
    just_fix_windows_console()

logging.getLogger('PIL').setLevel(logging.WARNING)

from pyvirtualdisplay import Display
import Xlib.display
import xml.etree.ElementTree as ET


# Criar uma subclasse da classe Formatter



class CustomFormatter(logging.Formatter):
    # https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output
    grey = "\x1b[38;20m"
    green = "\x1b[1;32m"
    yellow = "\x1b[33;20m"
    red = "\x1b[31;20m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    # format = "  %(asctime)s | %(name)s | %(levelname)s | %(funcName)s | (%(filename)s:%(lineno)d) | %(message)s  "
    # format0 = " %(asctime)s.%(msecs)03d|%(name)s|%(filename)s|%(funcName)s|%(lineno)d|%(levelname)s|%(message)s|"
    format0 = "%(asctime)s.%(msecs)03d|%(name)s|%(process)d|%(filename)s|%(funcName)s|%(lineno)d|%(module)s|%(threadName)s|%(processName)s|%(levelname)s|%(message)s|"
    # format0 = "%(asctime)s.%(msecs)03d|%(name)s|(process)|%(filename)s|%(funcName)s|%(lineno)d|%(module)s|%(threadName)s|%(processName)s|%(levelname)s|%(message)s|"

    # format1 = "%(asctime)s.%(msecs)03d|%(name)s|%(filename)s|%(funcName)s|%(lineno)d"
    format1 = "%(asctime)s.%(msecs)03d|%(process)d|%(filename)s|%(funcName)s|%(lineno)d"
    format2 = "| %(levelname)s | %(message)s |"

    # file_format = "%(asctime)s.%(msecs)03d|%(name)s|%(filename)s|%(funcName)s|%(lineno)d|%(levelname)s|%(message)s"



    # XXXFORMATS = {logging.DEBUG: green + format + reset, logging.INFO: grey + format + reset, logging.WARNING: yellow + format + reset, logging.ERROR: red + format + reset, logging.CRITICAL: bold_red + format + reset}

    FORMATS_file = {logging.DEBUG: format0,
                    logging.INFO: format0,
                    logging.WARNING: format0,
                    logging.ERROR: format0,
                    logging.CRITICAL: format0}

    FORMATS = {logging.DEBUG: f'   {format1}' + Fore.MAGENTA + f'{format2} ' + Fore.RESET,
               logging.INFO: f'   {format1}' + Fore.CYAN + f'{format2} ' + Fore.RESET,
               logging.WARNING:'\n   ' + f'{format1}' + Fore.YELLOW + f'{format2} \n' + Fore.RESET,
               logging.ERROR: '\n   ' + Fore.RED + f'{format1}' + f'{format2}' + Fore.RESET + '\n',
               logging.CRITICAL: '\n   ' + Back.RED + f'{format1}' + f'{format2}' + Back.RESET + '\n' }

    # def __init__(self, fmt=None, datefmt=None, style="%", use_formatfile=False):
    def __init__(self, use_formatfile=False):
        # Chamar o método __init__ da classe pai
        # super().__init__(fmt, datefmt, style)
        # Atribuir o valor do parâmetro use_formatfile a um atributo da instância
        self.use_formatfile = use_formatfile

    def header(self):
        #header = "(asctime)|(name)|(filename)|(funcName)|(lineno)|(levelname)|(message)"
        header = self.format0.replace('%','')
        return header

    def format(self, record):
        if self.use_formatfile:
            # log_fmt = self.FORMATS_file.get(record.levelno)
            # formatter = logging.Formatter(log_fmt)
            # record.message = record.getMessage().replace("\\n", "")
            # # return formatter.format(record)
            # return formatter.format(record)

            # Obter a mensagem de log sem as quebras de linha
            # message = \


            # Criar um novo record com a mensagem modificada
            # new_record = record.copy()
            # new_record = record
            # new_record.message = message
            # Obter o formato desejado para o nível de log
            # print(f'\n\n\nrecord = {record}\n\n\n')
            # record = f"{record}".replace("b", "YYY")
            log_fmt = self.FORMATS_file.get(record.levelno)
            # record = f"{record}".replace("c", "ZZZ")
            formatter = logging.Formatter(log_fmt)
            # record.getMessage().replace("a", "XXX")
            # record = f"{record}".replace("a", "XXX")
            # Retornar a mensagem formatada usando o novo record
            # print(f'record2 = {record}\n\n\n')

            # print(f'record3 = {formatter.format(record)}\n\n\n')
            # time.sleep(10)
            rec_formt = formatter.format(record).replace("\n", "// ").replace("  ", " ").replace("  ", " ").replace("  ", " ").replace("  ", " ").replace("  ", " ").replace("  ", " ")
            # return formatter.format(record)
            return rec_formt

        else:
            log_fmt = self.FORMATS.get(record.levelno)
            formatter = logging.Formatter(log_fmt)
            return formatter.format(record)





class Utilities():
    # def __init__(self, nome_robo_exec, robo_pid_exec, test_config_dir_direto=True, logger=None):
    def __init__(self, nome_robo_exec=None, robo_pid_exec=0, arquivo_de_config='src/config.ini', logger=None, disp_pyautogui=None):

        if nome_robo_exec is not None:

            self.loop_screenshot = 0
            if disp_pyautogui is not None:
                # pyautogui._pyautogui_x11._display = disp_pyautogui
                self.pyautogui = disp_pyautogui
                self.screen_rgb = True
            else:
                self.pyautogui = pyautogui
                self.screen_rgb = False

            # print(self.pyautogui._pyautogui_x11._display)

            # disp = Display(visible=True, size=(1920, 1680), backend="xvfb", use_xauth=True)
            # disp.start()
            # self.pyautogui._pyautogui_x11._display = Xlib.display.Display(os.environ['DISPLAY'])

            # print('  \n\n\n')

            arquivo_de_config = os.path.abspath(arquivo_de_config)
            print(f'   arquivo_de_config = {arquivo_de_config}')

            self.dir_atual = os.path.dirname(os.path.abspath(__file__))

            print(f'   self.dir_atual utilities = {self.dir_atual}')

            self.lista_saindo_webdriver = []

            robo_nome1 = self.pega_nome_robo(nome_robo_exec)
            robo_dir = self.pegar_caminho_diretorio_do_arquivo(nome_robo_exec)
            robo_pid = robo_pid_exec
            # robo_codigo = '{}_{}'.format(robo_nome, robo_pid)
            robo_codigo = '{}'.format(robo_nome1)
            # robo_nome = '{} {}'.format(robo_nome1, robo_pid)
            robo_nome = '{}'.format(robo_nome1)

            if logger is None:
                # self.logger = self.criar_logger(robo_codigo=robo_codigo, robo_nome=robo_nome, arquivo_de_config=arquivo_de_config)
                self.logger = self.customlog(robo_codigo=robo_codigo, robo_nome=robo_nome)
            else:
                self.logger = logger

            try:
                self.path_font=config['utilidades']['path_font']
                self.path_tmp=config['utilidades']['path_tmp']
                self.font_ttf=config['utilidades']['font_ttf']
            except:
                self.path_font = f'{self.dir_atual}/'
                self.path_tmp = f'{self.dir_atual}/TMP/tmp'
                self.font_ttf = f'Roboto-Regular.ttf'

                # import pkg_resources
                # from PIL import ImageFont
                # font_path = pkg_resources.resource_filename('idftools', 'fonts/Roboto-Regular.ttf')
                # font = ImageFont.truetype(font_path, size=32)


            self.logger.debug(f'    INICIALIZADO utilities   ')

        print('  \n')
        time.sleep(1)
    
    def pega_pyautogui(self):
        return self.pyautogui
    def pega_logger_atual(self):
        return self.logger
    def customlog(self, robo_codigo='bot', robo_nome='bot', log_dir='TMP/log'):

        # print(f'\n\n\n\n\n\nrobo_codigo={robo_codigo}\n\n')
        # print(f'\n\n\n\n\n\nrobo_nome={robo_nome}\n\n')

        logger = logging.getLogger(robo_nome)
        self.criar_diretorio(log_dir)
        log_file = f"{log_dir}/{robo_codigo}.log"
        # file_handler = logging.FileHandler(log_file, errors='replace', encoding="utf-8")
        file_handler = logging.FileHandler(log_file, encoding="utf-8")

        # Cria um handler do tipo StreamHandler que envia as mensagens de log para o console
        console_handler = logging.StreamHandler()

        #https://tool.oschina.net/uploads/apidocs/Python/library/logging.html

        # Cria um formatter personalizado com o formato "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        # formatter = logging.Formatter(" %(asctime)s.%(msecs)03d|%(name)s|%(funcName)s|%(levelname)s|%(message)s ")
        # formatter = logging.Formatter(CustomFormatter)
        formatter = CustomFormatter()

        header = formatter.header()
        self.agregar_arquivo(log_file,f'_________\n{header}')

        formatterfile = CustomFormatter(use_formatfile=True)
        # file_format = "%(asctime)s.%(msecs)03d|%(name)s|(%(filename)s:%(funcName)s:%(lineno)d)|%(module)s|%(threadName)s|%(process)d|%(processName)s|%(levelname)s|%(message)s|"
        # file_format = "%(asctime)s.%(msecs)03d|%(name)s|%(filename)s|%(funcName)s|%(lineno)d|%(levelname)s|%(message)s"
        # file_formatter = logging.Formatter(file_format)

        # Atribui o formatter aos handlers
        # file_handler.setFormatter(file_formatter)
        console_handler.setFormatter(formatter)
        file_handler.setFormatter(formatterfile)


        # Atribui os handlers ao logger
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)

        # Define o nível de log para DEBUG tanto para o logger quanto para os handlers
        logger.setLevel(logging.DEBUG)
        file_handler.setLevel(logging.DEBUG)
        console_handler.setLevel(logging.DEBUG)
        # logger.setLevel(logging.CRITICAL)
        # file_handler.setLevel(logging.CRITICAL)
        # console_handler.setLevel(logging.CRITICAL)

        # logger.debug(f'    ----------------------   ')

        # Registra algumas mensagens de log com diferentes níveis
        # logger.debug("Esta é uma mensagem de debug")
        # logger.info("Esta é uma mensagem de info")
        # logger.warning("Esta é uma mensagem de warning")
        # logger.error("Esta é uma mensagem de error")
        # logger.critical("Esta é uma mensagem de critical")
        print('\n')

        return logger


    def criar_logger(self, robo_codigo, robo_nome, arquivo_de_config=None):
        try:
            config = self.get_config(arquivo_de_config=arquivo_de_config)
        except:
            pass

        # if logger is None:
        try:
            log_dir = os.path.abspath(config['log']['log_dir'])
        except:
            log_dir = 'TMP/log'
        finally:
            self.criar_diretorio(log_dir)

        try:
            log_config = os.path.abspath(config['log']['log_config'])
        except:
            log_config = 'TMP/log/logging.conf'
            self.logging_conf(log_config_file=log_config, dir=log_dir)

        logging.config.fileConfig(log_config)
        # logging.config()

        # self.logger = logging.getLogger('bot|{}'.format(robo_codigo))
        lloger = logging.getLogger('bot|{}'.format(robo_codigo))


        try:
            log_file = os.path.abspath(config['log']['log_file'])
        except:
            log_file = f'{log_dir}/robos.log'

        # self.duplica_log_robo(logging, self.logger, log_dir, robo_nome)
        self.duplica_log_robo(logging, lloger, log_dir, robo_nome)
        # else:
        #     self.logger = logger
        return lloger

    def logging_conf(self, log_config_file, dir):

        try:
            os.makedirs(dir)
            print(f"Directory {dir} Created OK")
        # except FileExistsError:
        except Exception as e:
            print(f"Directory {dir} already exists: {e}")

        texto = '''
[loggers]
# keys=root,bot
keys=bot

[handlers]
keys=consoleHandler,logFile
# keys=logFile

[formatters]
keys=simpleFormatter,logfileFormatter
# keys=logfileFormatter

[logger_root]
level=DEBUG
# handlers=consoleHandler
handlers=logFile

[logger_bot]
level=DEBUG
handlers=consoleHandler,logFile
# handlers=logFile
qualname=Bot
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_logFile]
class=handlers.RotatingFileHandler
level=DEBUG
args=("TMP/log/robos.log","a",0,0)
formatter=logfileFormatter

[formatter_simpleFormatter]
format=%(asctime)s.%(msecs)03d|%(name)s|(%(filename)s:%(funcName)s:%(lineno)d)|%(levelname)s| %(message)s
datefmt=%Y-%m-%d %H:%M:%S

[formatter_logfileFormatter]
format=%(asctime)s.%(msecs)03d|%(name)s|(%(filename)s:%(funcName)s:%(lineno)d)|%(levelname)s|%(message)s
datefmt=%Y-%m-%d %H:%M:%S
        '''

        # self.util.sobrescrever_arquivo(self.log_config, texto)
        with open(log_config_file, 'w') as file:
            file.write('{}'.format(texto))
            file.close()

    def create_robo_header(self, robo_codigo, robo_descricao):
        print('\n\n')

        self.logger.info('******************************************************************')
        # self.logger.info('*')
        self.logger.info('*   Robô:   ' + robo_codigo)
        self.logger.info('*  <|º_º|>  ' + robo_descricao )
        # self.logger.info('*')
        # self.logger.info('******************************************************************\n')
        print('\n\n')


    def create_robo_footer(self, status, inicio_execucao, fim_execucao):
        print('\n\n')
        # self.logger.info('******************************************************************')
        #self.logger.info('*')
        self.logger.info('* Status:  ' + status)
        self.logger.info('* Duração: ' + self.convert_seconds(fim_execucao - inicio_execucao))
        #self.logger.info('*')
        self.logger.info('******************************************************************\n\n')

    def test_if_exist(self, file):


        #path = Path(os.path.abspath("README.mdx"))
        path = Path(os.path.abspath(file))


        if path.exists():
            print(f'   path exist = {path}')
            return True
        else:
            print(f'   path NOT exist = {path}')
            return False

    def get_config(self, arquivo_de_config):

        # arquivo_de_config = os.path.abspath(arquivo_de_config)

        if self.test_if_exist(arquivo_de_config):

            config = configparser.ConfigParser()
            #print('config001')
            #print(f'config self.test_config_dir_direto={self.test_config_dir_direto}')

            path_arquivo_de_config = os.path.abspath(arquivo_de_config)
            try:
                config.read(path_arquivo_de_config, encoding='utf-8')
                print(f"   Configurações config utf8 carregadas com sucesso! {path_arquivo_de_config}")
            except:
                config.read(path_arquivo_de_config)
                print(f"   Configurações config SEM utf8: {path_arquivo_de_config}")

            if False:
                if self.test_config_dir_direto == False:
                    try:
                        #print('config004')
                        #config.read('/var/robo/ROBOS2/Adapter/config/config/config.ini', encoding='utf-8')
                        config.read('/var/robo/ROBOS2/Adapter/config/config/config.ini', encoding='utf-8')

                        print("Configurações config utf8 carregadas com sucesso! 2")
                    except:
                        #print('config005')
                        #config.read('/var/robo/ROBOS2/Adapter/config/config/config.ini')
                        config.read('/var/robo/ROBOS2/Adapter/config/config/config.ini')

                        print("Configurações sem utf config carregadas com sucesso! 3")

                else:
                    try:
                        #print('config002')
                        config.read('src/config/config.ini', encoding='utf-8')
                        # print("Configurações config utf8 carregadas com sucesso! 1")
                    except:
                        #print('config005')
                        config.read('src/config/config.ini')
                        print("Configurações sem utf config carregadas com sucesso! 3")
                        #print('config006')

            return config
        else:
            raise Exception(f'   Arquivo não existe: {arquivo_de_config}')


    def obter_data_atual_sem_horas(self, mask):
        now = date.today()
        current_date = datetime.strptime(str(now), mask).date()
        return current_date


    def obter_data_atual_com_horas(self, mask):
        now = date.today()
        current_date = datetime.strftime(now, mask)
        return current_date

    def data_hora_brasil(self):
        return time.strftime("%d/%m/%Y - %H:%M:%S")

    def data_hora_americano(self):
        return time.strftime("%Y/%m/%d - %H:%M:%S")



    ''' Método utilizado para determinar a data limite de retenção '''
    def obter_data_limite_retencao(self, data):

        # Formata data
        nova_data = datetime.strptime(data, "%d/%m/%Y")

        holidays = [datetime.strptime('07/09/2018', "%d/%m/%Y"),
                    datetime.strptime('12/10/2018', "%d/%m/%Y"),
                    datetime.strptime('02/11/2018', "%d/%m/%Y"),
                    datetime.strptime('15/11/2018', "%d/%m/%Y"),
                    datetime.strptime('25/12/2018', "%d/%m/%Y")]

        # Create a rule to recur every weekday starting today
        # r = rrule.rrule(rrule.DAILY,
        #                 byweekday=[rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR],
        #                 dtstart=nova_data)

        # Create a rruleset
        # rs = rrule.rruleset()

        # Attach our rrule to it
        # rs.rrule(r)
        rs=''

        # Add holidays as exclusion days
        for exdate in holidays:
            rs.exdate(exdate)

        nova_data = rs[5]

        return datetime.strftime(nova_data, "%d/%m/%Y")


    def convert_seconds(self, seconds):
        seconds = int(seconds)

        minutes, seconds = divmod(seconds, 60)
        hours, minutes = divmod(minutes, 60)
        periods = [('hours', hours), ('minutes', minutes), ('seconds', seconds)]
        time_string = ', '.join('{} {}'.format(value, name)
                                for name, value in periods
                                if value)
        return time_string


    def convert_millis(self, millis):
        millis = int(millis)
        seconds=(millis/1000)%60
        seconds = int(seconds)
        minutes=(millis/(1000*60))%60
        minutes = int(minutes)
        hours=(millis/(1000*60*60))%24

        return "%d:%d:%d" % (hours, minutes, seconds)


    def recupera_nome_mes(self, mes):
        meses = ["Unknown",
                 "JANEIRO",
                 "FEVEREIRO",
                 "MARÇO",
                 "APRIL",
                 "MAIO",
                 "JUNHO",
                 "JULHO",
                 "AGOSTO",
                 "SETEMBRO",
                 "OUTUBRO",
                 "NOVEMBRO",
                 "DEZEMBRO"]
        return meses[int(mes)]


    def retorna_primeiro_dia_semana(self, mask):
        day = time.strftime(mask)
        dt = datetime.strptime(day, mask)
        start = dt - timedelta(days=dt.weekday())
        return start.strftime(mask)


    def retorna_ultimo_dia_semana(self, mask):
        day = time.strftime(mask)
        dt = datetime.strptime(day, mask)
        start = dt - timedelta(days=dt.weekday())
        end = start + timedelta(days=4)
        return end.strftime(mask)


    def ler_arquivo_para_lista(self, filename, encoding=None):
        print(f'ler_arquivo_para_lista -> {filename}')
        if encoding is None:
            f = open(filename, errors='replace')
        else:
            # f = open(filename, encoding="iso-8859-1", errors='replace')
            f = open(filename, encoding=encoding, errors='replace')
        lines = f.read().splitlines()
        # print(f'lendo lines={lines}')

        f.close()
        time.sleep(0.2)
        return lines


    def ler_arquivo(self, filename, encoding=None):

        if encoding is None:
            f = open(filename, errors='replace')
        else:
            # f = open(filename, encoding="iso-8859-1", errors='replace')
            f = open(filename, encoding=encoding, errors='replace')

        lines = f.read()
        #lines = f.readlines()
        #print(f'lendo lines={lines}')
        f.close()
        # time.sleep(0.2)
        #f=open("guru99.txt", "r")
        #if f.mode == 'r':
	    #contents =f.read()
        #time.sleep(10)
        return lines



    def ler_primeira_linha_arquivo(self, filename, encoding=None):
        if encoding is None:
            f = open(filename, errors='replace')
        else:
            # f = open(filename, encoding="iso-8859-1", errors='replace')
            f = open(filename, encoding=encoding, errors='replace')
        lines = f.readlines()
        # print(f'lendo lines={lines[0]}')
        f.close()
        # time.sleep(0.2)
        # time.sleep(5)
        return lines[0].strip('\n')






    def salvar_arquivo(self, filename, valor):
        with open(filename, 'w+', errors='replace', encoding="utf-8") as f:
            f.seek(0)
            f.write(valor)
            f.close()
            # time.sleep(0.2)

    def sobrescrever_arquivo(self, filename, valor):
        #with open(filename, 'w') as f:
            #f.seek(0)
            #f.write(valor)
        with open(filename, 'w', errors='replace', encoding="utf-8") as file:
            file.write('{}'.format(valor))
            file.close()
            # time.sleep(0.2)


    def recriar_arquivo(self, filename):
        f = open(filename, "w+", errors='replace', encoding="utf-8")
        f.close()
        # time.sleep(0.2)


    def agregar_arquivo(self, filename, valor, encode="utf-8"):
        # print(f'\narq: {filename}:: "{valor}"\n')
        with open(filename, 'a', errors='replace', encoding=encode) as f:
            f.write(valor + '\n')
            f.close()
            # time.sleep(0.2)

    def agregar_arquivo_sem_quebra(self, filename, valor):
        with open(filename, 'a', errors='replace', encoding="utf-8") as f:
            f.write(valor)
            f.close()
            # time.sleep(0.2)



    def formatar_cpf(self, cpf):
        if len(cpf) < 11:
            cpf = cpf.zfill(11)
        cpf = '{}.{}.{}-{}'.format(cpf[:3], cpf[3:6], cpf[6:9], cpf[9:])
        return cpf

    def formatar_cnpj(self, cnpj):
        if len(cnpj) < 14:
            cnpj = cnpj.zfill(14)
        cnpj = '{}.{}.{}/{}-{}'.format(cnpj[:2], cnpj[2:5], cnpj[5:8], cnpj[8:12], cnpj[12:])
        #      19.596.170/0001-09
        #      :2 2:5 5:8 8:12 12:
        #     058.522.529-05
        #      :3 3:6 6:9 9:

        return cnpj


    def limpar_cpf(self, cpf):
        return str(cpf).replace('-','').replace('.','')


    def cpf11digits(self, cpf):
        return str('{:0>11}'.format(self.somente_numeros(cpf)))   #cpf com 11 digito e somente numeros (deve ser passado como string)


    def convert_data_excel_humano(self, excel_data):
        #excel_data = 43411 >> 07/11/2018
        data_transformada = datetime.fromordinal(datetime(1900, 1, 1).toordinal() + excel_data - 2).date() #tem que ser '-2' por causa de uma contagem maluca do excel
        data_formato_humano = str('{:0>2}/{:0>2}/{:0>4}'.format(data_transformada.day,data_transformada.month,data_transformada.year))
        #self.logger.info('{} > {} >> {}'.format(excel_data, data_transformada, data_formato_humano))
        return data_formato_humano


    def line_count(self, fname):
        num_lines = 0
        with open(fname, 'r', encoding="utf8") as f:
            for line in f:
                num_lines += 1
            f.close()
            # time.sleep(0.2)
        return (num_lines)


    def somente_numeros(self, numerossujos):
        # print(numerossujos)
        xxx = str(numerossujos).replace('\n','')
        #print(xxx)
        saida = re.sub('[^0-9]', '', xxx)
        # print(saida)
        return re.sub('[^0-9]', '', xxx)

    def remover_acentos(self, txt):
        # https://wiki.python.org.br/RemovedorDeAcentos
        return normalize('NFKD', txt).encode('ASCII', 'ignore').decode('ASCII')

    # def somente_letras_numeros(self, string):
    #     return re.sub('[^a-zA-Z0-9]+', '', string)

    def somente_letras_numeros_espaco_ponto(self, string):
        return re.sub('[^a-zA-Z0-9. ]+', '', string)

    def somente_letras_espaco(self, string):
        return re.sub('[^a-zA-Z ]+', '', string)

    def somente_letras(self, string):
        return re.sub('[^a-zA-Z]+', '', string)



    #enio 07/08/2019
    def removedor_acentuacao(self, string_com_acentos):
        #fonte da dica: https://pt.stackoverflow.com/questions/331297/como-remover-acentua%C3%A7%C3%B5es-com-express%C3%B5es-regulares-no-python
        #Um modo simples que usa o módulo unicodedata, incluído no python, pra decompor cada acento unicode em seu codepoint original + codepoint de combinação, depois filtrar os codepoints de combinação para ter uma string limpa:
        #import unicodedata
        #string_com_acentos = "Olá você está????"
        string_nova = ''.join(ch for ch in unicodedata.normalize('NFKD', string_com_acentos) if not unicodedata.combining(ch))
        #self.logger.info(string_nova)
        return f'{string_nova}'

    def convert_csv_to_xlsx(self, arquivo_csv, separador_csv, arquivo_destino_xlsx, nome_tab_xsls='Planilha', encode='utf-8'):

        nome_tab_xsls = nome_tab_xsls[0:28]

        arquivo_csv = os.path.abspath(arquivo_csv)
        # Importar o módulo pandas
        # import pandas as pd
        print(f"encode={encode}")
        # if xencode != 'utf-8':
        #     print(f"xencode={xencode}")
        #     time.sleep(5)
        #     with open(arquivo_csv, "rb") as f:
        #         print(f"open arquivo_csv={arquivo_csv}")
        #         time.sleep(5)
        #         data = f.read()
        #         print(f"leu dados data = f.read()")
        #         time.sleep(5)
        #         # data = data.decode("iso-8859-1")
        #         data = data.decode(xencode).encode()
        #         # data = data.decode('utf-8')
        #         print(f"encode data data = data.decode({xencode})")
        #         time.sleep(5)
        #         # data = data.encode(xencode)
        # else:
        #     data = arquivo_csv

        # Criar um DataFrame a partir do arquivo CSV com separador "|"
        # df = pd.read_csv(arquivo_csv, sep=f"{separador_csv}")
        print(f"fazendo df = pd.read_csv(data, sep={separador_csv})")
        # time.sleep(5)
        # df = pd.read_csv(data, sep=f"{separador_csv}", errors='replace')
        # df = pd.read_csv(arquivo_csv, sep=f"{separador_csv}", errors='replace', encoding=xencode)
        df = pd.read_csv(arquivo_csv, sep=f"{separador_csv}",  encoding=encode)

        # Salvar o DataFrame como um arquivo XLSX
        print(f"salvando df.to_excel({arquivo_destino_xlsx}))")
        # time.sleep(5)
        # df.to_excel("arquivo.xlsx")
        df.to_excel(f'{arquivo_destino_xlsx}', index=False, sheet_name=nome_tab_xsls)

        # Verificar se o arquivo XLSX foi criado corretamente
        print(f"Arquivo XLSX criado com sucesso! {arquivo_destino_xlsx}")
        # time.sleep(5)


    def convert_pasta_csv_para_xlsx(self, dir_arqs=None, separador_csv='|', encode='utf-8'):
        dir_arqs=os.path.abspath(dir_arqs)

        if self.test_if_exist(dir_arqs) is False:
            msg = f' ERRO: a pasta "{dir_arqs}" não existe! Por favor, verifique isso'
            self.logger.critical(msg)
            raise Exception(msg)

        self.logger.info(f'dir_arqs = {dir_arqs}')

        list_arqs = os.listdir(dir_arqs)

        for arq in list_arqs:
            arq_ext = arq.split('.')[-1]
            if arq_ext == 'csv':
                print('\n')
                self.logger.info(f'iniciando analise do arquivo {arq}')
                file = os.path.abspath(f'{dir_arqs}/{arq}')
                file_saida_xlsx = file + '.xlsx'
                # nome_tab_xsls=f'{arq}'.replace('-',' ').replace('_',' ').replace('.',' ')
                nome_tab_xsls=arq[0:28]
                self.logger.debug(f'saida excel: {nome_tab_xsls} {file_saida_xlsx}')
                # self.convert_csv_to_xlsx(arquivo_csv=file, separador_csv='|', arquivo_destino_xlsx=file_saida_xlsx, nome_tab_xsls=nome_tab_xsls)
                self.util.convert_csv_to_xlsx(arquivo_csv=file,
                                              separador_csv=separador_csv,
                                              arquivo_destino_xlsx=file_saida_xlsx,
                                              nome_tab_xsls=nome_tab_xsls,
                                              encode=encode)


    def auto_ajuste_largura_excel(self,arq_xls, titulo_tab):
        try:
            print(f'        arq_xls: {arq_xls}')
            print(f'     titulo_tab: {titulo_tab}')
            #writer = pd.ExcelWriter(f'{arq_xls}')
            #print(f'writer = {writer}')

            df = pd.read_excel(f'{arq_xls}', f'{titulo_tab}')
            print(f'df = {df}')

            #writer = df
            #df.to_excel(writer, sheet_name='my_analysis', index=False, na_rep='NaN')

            # Auto-adjust columns' width
            for column in df:
                print(f'column: {column}')
                column_width = max(df[column].astype(str).map(len).max(), len(column))
                print(f'column_width = {column_width}')
                col_idx = df.columns.get_loc(column)
                print(f'col_idx = {col_idx}')
                #df[f'{titulo_tab}'].set_column(col_idx, col_idx, column_width)
                #df[f'{titulo_tab}'] = df[f'{titulo_tab}'].set_column(col_idx, col_idx, column_width)
                #df[f'{titulo_tab}'].set_column(col_idx, col_idx, column_width)
                #df.set_column(col_idx, col_idx, column_width)
                #letra_coluna = self.numero_para_coluna_excel(col_idx)
                #print(f'letra_coluna = {letra_coluna}')
                #df.set_column(f'{letra_coluna}:{letra_coluna}', column_width)
                #cell_format = workbook.add_format({'bold': True})
                df.set_column(col_idx, col_idx , column_width)
                #df.set_column(col_idx, col_idx, column_width)
                print(f'fim df: {df}')
                #writer.save()
        except Exception as e:
            print(f'ERRO auto_ajuste_largura_excel: {e}')

        # def auto_ajuste_largura_excel(self, arq_xls, titulo_tab):
        #     try:
        #         print(f'        arq_xls: {arq_xls}')
        #         print(f'     titulo_tab: {titulo_tab}')
        #         writer = pd.ExcelWriter(f'{arq_xls}', engine='xlsxwriter')  # Crie um objeto ExcelWriter
        #         print(f'writer = {writer}')
        #
        #         df = pd.read_excel(f'{arq_xls}', f'{titulo_tab}')
        #         print(f'df = {df}')
        #
        #         df.to_excel(writer, sheet_name='my_analysis', index=False, na_rep='NaN')  # Escreva o DataFrame no ExcelWriter
        #
        #         worksheet = writer.sheets['my_analysis']  # Obtenha o objeto Worksheet do ExcelWriter
        #
        #         # Auto-adjust columns' width
        #         for column in df:
        #             print(f'column: {column}')
        #             column_width = max(df[column].astype(str).map(len).max(), len(column))
        #             print(f'column_width = {column_width}')
        #             col_idx = df.columns.get_loc(column)
        #             print(f'col_idx = {col_idx}')
        #             worksheet.set_column(col_idx, col_idx, column_width)  # Use o método set_column do Worksheet
        #             print(f'fim df: {df}')
        #
        #         writer.save()  # Salve o arquivo Excel atualizado
        #     except Exception as e:
        #         print(f'ERRO auto_ajuste_largura_excel: {e}')

    def numero_para_coluna_excel(self,dividendo):
        # dividendo L divisor=26
        # resto         quociente-1
        colunas = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
                   "U", "V", "W", "X", "Y", "Z"]
        if dividendo < 702:
            if dividendo > 25:
                resto = dividendo % 26
                quociente = dividendo / 26 - 1
                return '{}{}'.format(colunas[int(quociente)], colunas[int(resto)])
            else:
                return '{}'.format(colunas[dividendo])
        else:
            return 'null'


    def pegar_nome_arquivo(self,diretorio_e_arquivo):

        if False:
            # TODO: "C:\Users\USARIO\.aws\credentials.ini"
            file = diretorio_e_arquivo.strip(' ')
            if os.name == 'nt':                    #identifica o tipo de OS, para win=nt
                listfiledir=file.split('\\')       #windows
            else:
                listfiledir=file.split('/')        # linux
            filelen = len(listfiledir)         #pega a quantidade de diretorios que tem  no caminho do arquivo usando o separador /(unix) ou \(win)
            filename = listfiledir[filelen-1]
            return filename

        return os.path.basename(os.path.abspath(diretorio_e_arquivo))



    def pegar_caminho_diretorio_do_arquivo(self, diretorio_e_arquivo):

        if False:
            # TODO: "C:\Users\USARIO\.aws\"
            file = diretorio_e_arquivo.strip(' ')
            if os.name == 'nt':  # identifica o tipo de OS, para win=nt
                listfiledir = file.split('\\')  # windows
            else:
                listfiledir = file.split('/')  # linux
            filelen = len(
                listfiledir)  # pega a quantidade de diretorios que tem  no caminho do arquivo usando o separador /(unix) ou \(win)
            dirname = self.converte_lista_para_texto(listfiledir[0:filelen - 1], '/')
            return dirname

        return os.path.dirname(os.path.abspath(diretorio_e_arquivo))

    def pega_colunas_lista(self, lista, colunas=[]):
        pass


    def converte_lista_para_texto(self, lista, separador):
        # Converting integer list to string list
        list_string = [str(i) for i in lista]
        # Join list items using join()
        list_string = f"{separador}".join(list_string)
        return list_string

    # def converte_texto_para_lista(self, texto):
    #     lista = f"{texto.split()}"
    #     return lista

    def mover_arquivo(self, origem, destino, loop=7):
        try:
            while loop > 0:
                if self.test_if_exist(origem):
                    self.logger.info(f"movendo arquivo original {origem} para novo destino {destino} ")
                    new_path = shutil.move(origem, destino)
                    #print(new_path)
                    self.logger.info(f"movido arquivo com sucesso: {new_path} ")
                    loop = 0
                    return True
                else:
                    self.logger.info(f"ainda não achou o arquivo de origem {origem}")
                    time.sleep(2)
                    loop -= 1
            if loop <= 0:
                raise Exception(f'Erro ao mover arquivo')
        except Exception as e:
            self.logger.error(f"erro ao mover arquivo {e}")
            self.logger.error(f"ERRO ao mover arquivo original {origem} para novo destino {destino} - {e}")
            return False

    def copiar_arquivo(self,origem,destino):
        try:
            self.logger.info(f"COPIANDO arquivo original {origem} para novo destino {destino} ")
            new_path = shutil.copy2(origem, destino)
            #print(new_path)
            self.logger.info(f"COPIADO arquivo com sucesso: {new_path} ")
        except Exception as e:
            self.logger.error(f"erro ao copiar arquivo {e}")
            self.logger.error(f"ERRO ao copiar arquivo original {origem} para novo destino {destino} - {e}")




    def criar_diretorio(self,dirName):
        try:
            os.makedirs(dirName)
            #self.logger.info("Directory ", dirName, " Created ")
            #self.logger.info(f"Directory {dirName} Created OK")
            print(f"       Directory {dirName} Created OK")
        #except FileExistsError:
        except Exception as e:
            #print(f'')
            #print(f'{e}')
            #self.logger.warning("Directory ", dirName, " already exists")
            #self.logger.warning(f"Directory {dirName} already exists: {e}")
            # print(f"       Directory {dirName} already exists: {e}")
            pass


    def limpar_diretorio(self,path):
        #path = "diretorio"
        dir = os.listdir(path)
        os.listdir(path)
        #time.sleep(0.2)
        print('\n')
        for file in dir:
            self.logger.info('deletando {}/{}'.format(path,file))
            try:
                os.remove(path+'/'+file)
            except:
                self.logger.info('Deletando file {} ---> não encontrado'.format(file))
            #if file == "arquivo.txt":
            #    os.remove(file)
        print('\n')
        time.sleep(0.3)


    def zipar(self, nome_do_zip, arqs_para_zipar):
                               #arqs_para_zipar em forma de lista, parecido com o append do anexos do qlikview e mailler_gmail
        fantasy_zip = zipfile.ZipFile('{}'.format(nome_do_zip), 'w')
        files = arqs_para_zipar
        #files=anexos
        qtde_files = len(files)
        files_count = 0
        while files_count < qtde_files:
            file = files[files_count].strip(' ')

            if os.name == 'nt':                    #identifica o tipo de OS, para win=nt
                listfiledir=file.split('\\')       #windows
            else:
                listfiledir=file.split('/')
            filelen = len(listfiledir)         #pega a quantidade de diretorios que tem  no caminho do arquivo usando o separador /(unix) ou \(win)
            filename=listfiledir[filelen-1]
            self.logger.info('filename {}'.format(filename))
            fantasy_zip.write(file, filename, compress_type=zipfile.ZIP_DEFLATED)
            #fantasy_zip.write(file, compress_type=zipfile.ZIP_DEFLATED)
            files_count +=1
        fantasy_zip.close()
        time.sleep(0.2)


    def subir_ftp(self, host, username, password, arquivo, dir_destino):
        File2Send = arquivo
        filename=self.pegar_nome_arquivo(arquivo)
        #self.logger.info('File2Send {}'.format(File2Send))
        Output_Directory = dir_destino
        #self.logger.info('Output_Directory {}'.format(Output_Directory))
        tentativas=0
        max_tentativas=3
        print('\n')
        self.logger.info("start ftp")
        while tentativas < max_tentativas:
            try:
                # ftp = FTP(host)
                ftp = FTP(host, timeout=600)
                #ftp.connect(host='', port=0, timeout=None, source_address=None)
                ftp.login(username, password)
                #self.logger.info('ftp: {} {} {}'.format(ftp,username,password))
                file = open(File2Send, "rb")
                file.close()
                time.sleep(0.2)
                #self.logger.info('file {}'.format(file))
                ftp.cwd(Output_Directory)
                ftp.storbinary('STOR ' + filename, file)
                self.logger.info("STORing File now...")
                ftp.quit()
                file.close()
                time.sleep(0.2)
                self.logger.info("File transfered")
                tentativas = 100
                #status='Sucesso'
            except:
                self.logger.info("An error occured {}".format(tentativas))
                tentativas +=1
                #status = 'Erro'
                time.sleep(30)

            finally:
                if tentativas == 3:
                    self.logger.info('Tentativa {} falhou. Não foi possivel enviar :(  {} - {}'.format(tentativas, arquivo,host))
                    raise Exception("falha em subir arquivo {} para ftp {} - tentativa {}".format(arquivo,host,tentativas))
                #time.sleep(10)


    def criar_dir_ftp(self, host, username, password, arquivo, dir_destino):
        File2Send = arquivo
        filename=self.pegar_nome_arquivo(arquivo)
        #self.logger.info('File2Send {}'.format(File2Send))
        Output_Directory = dir_destino
        #self.logger.info('Output_Directory {}'.format(Output_Directory))
        tentativas=0
        max_tentativas=3
        print('\n')
        self.logger.info("start ftp")
        while tentativas < max_tentativas:
            try:
                ftp = FTP(host, timeout=600)
                ftp.login(username, password)
                file = open(File2Send, "rb")
                file.close()
                time.sleep(0.2)
                ftp.cwd(Output_Directory)
                ftp.storbinary('STOR ' + filename, file)
                self.logger.info("STORing File now...")
                ftp.quit()
                file.close()
                time.sleep(0.2)
                self.logger.info("File transfered")
                tentativas = 100
            except:
                self.logger.info("tentativas excedidas{}".format(tentativas))
                tentativas +=1
                time.sleep(30)
            finally:
                if tentativas == 3:
                    self.logger.info('Tentativa {} falhou. Não foi possivel enviar :(  {} - {}'.format(tentativas, arquivo,host))
                    raise Exception("falha em subir arquivo {} para ftp {} - tentativa {}".format(arquivo,host,tentativas))
                #time.sleep(10)



    def pega_nome_robo(self, nome_robo):

        nome_ext = os.path.basename(nome_robo)
        ext = os.path.basename(nome_robo).split('.')[-1]
        nome_final = nome_ext.replace(f'.{ext}','')

        return nome_final  # serve para pegar só o nome do executavel


    def xxxpega_nome_robo(self, nome_robo):

        if os.name == 'nt':                    #identifica o tipo de OS, para win=nt
            #listfiledir=file.split('\\')       #windows
            nome_1ist = nome_robo.split('\\')
        else:
            #listfiledir=file.split('/')        # linux
            nome_1ist = nome_robo.split('/')

        #nome_1ist = nome_robo.split('/')



        # print(f'pega_nome_robo nome_1ist:{nome_1ist}')
        nome_last=len(nome_1ist) - 1
        # print(f'pega_nome_robo nome_last:{nome_last}')
        nome_list2=nome_1ist[nome_last].split('.')
        # print(f'pega_nome_robo nome_list2:{nome_list2}')
        nome_last2=nome_list2[0]

        extensao = nome_1ist[-1].split('.')[-1]
        nome_final = f'{nome_1ist[-1]}'. replace(f'.{extensao}')

        # print(f'pega_nome_robo nome_last2:{nome_last2}')
        # time.sleep(1)
        # return '{}'.format(nome_last2)
        return '{}'.format(nome_final)

    def duplica_log_robo(self,logging,logger,log_dir,robo_nome):
        #self.logger.info('logging {} ,logger {} ,log_dir {} ,robo_nome {}'.format(logging,logger,log_dir,robo_nome))
        data_hoje_log = datetime.today().strftime("%Y%m%d")
        arq = '{}/{}_{}.log'.format(log_dir, robo_nome, data_hoje_log)
        self.agregar_arquivo(arq,'\n\n------------------------\n\n')
        fh = logging.FileHandler(arq)
        #fh = logging.StreamHandler()

        #fh.setLevel(logging.DEBUG)
        #ch = logging.StreamHandler()

        #ch.setLevel(logging.ERROR)
        # formatter = logging.Formatter('%(asctime)s|%(name)s|%(levelname)s|%(message)s ')

        handler = logging.StreamHandler()
        formatter = CustomFormatter()
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.setLevel(logging.DEBUG)
        handler.setLevel(logging.DEBUG)


        #ch.setFormatter(formatter)
        #ch.setFormatter(CustomFormatter())

        #fh.setFormatter(formatter)
        fh.setFormatter(CustomFormatter())
        #logger.addHandler(ch)
        logger.addHandler(fh)

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

    def escreve_imagem_old(self, arquivo_entrada, arquivo_saida,path_font,font_ttf, size, corR, corG, corB, top, left, texto):


        #exemplo de paramentros font:
        #font_ttf, size, corR, corG, corB, top, left = 'Roboto-Regular.ttf', 10, 218, 220, 224, 1, 25
        #exemplos robo stormtech_002
        # self.util.criar_diretorio(self.util.pegar_caminho_diretorio_do_arquivo(f'{arquivo_saida}'))

        self.criar_diretorio(self.pegar_caminho_diretorio_do_arquivo(f'{arquivo_saida}'))
        #self.logger.info(f'abrindo imagem para editar {arquivo_entrada}')
        # self.logger.info(f'abrindo imagem para editar {arquivo_entrada}')

        #font = ImageFont.truetype(f'{self.path_font}{font_ttf}', size)
        font = ImageFont.truetype(f'{path_font}{font_ttf}', size)
        img_arquivo_entrada = Image.open(f'{arquivo_entrada}')
        draw = ImageDraw.Draw(img_arquivo_entrada)
        draw.text((left, top), f'{texto}', (corR, corG, corB), font=font)
        # self.logger.info(f'salvando imagem {arquivo_saida}')
        self.logger.debug(f'salvando imagem {arquivo_saida}')
        img_arquivo_entrada.save(f'{arquivo_saida}')
        # self.logger.info('\n')
        print('\n')

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

    def escreve_imagemV2(self, texto, arquivo, size=17, top=1, left=22, corR=28, corG=28, corB=28, font_ttf='', path_font='', path_tmp='' ):
        arquivo=os.path.abspath(arquivo)
        if f'{path_tmp}' == '':
            path_tmp=os.path.abspath(self.path_tmp)
        if f'{path_font}' == '':
            path_font=os.path.abspath(self.path_font)
        if f'{font_ttf}' == '':
            font_ttf = self.font_ttf
        self.criar_diretorio(path_tmp)
        self.escreve_imagem(arquivo,path_tmp,path_font,font_ttf, size, corR, corG, corB, top, left, texto)

    # def desenha_circulo(self, arquivo=None, path_tmp="TMP", mouse_pos=None, corR=255, corG=0, corB=0, preenchido=False):
    # def desenha_circulo(self, arquivo=None, mouse_pos=None, corR=255, corG=0, corB=0, preenchido=None):
    def desenha_circulo(self, arquivo=None, mouse_pos=None, corR=255, corG=0, corB=255, outline=None):
        # path_tmp = os.path.abspath(path_tmp)
        arquivo = os.path.abspath(arquivo)
        img = PIL.Image.open(arquivo)
        draw = PIL.ImageDraw.Draw(img)
        # draw.ellipse((295, 495, 305, 505), (255, 0, 0), True)
        draw.ellipse(mouse_pos, (corR, corG, corB), outline)
        img.save(arquivo)

    def escreve_imagem(self,arquivo,path_tmp,path_font,font_ttf, size, corR, corG, corB, top, left, texto):
        # print(f'escreve = 1')
        # print(f'escreve_imagem: arquivo={arquivo}')
        # print(f'escreve_imagem: path_tmp={path_tmp}')
        # print(f'escreve_imagem: path_font={path_font}')
        # print(f'escreve_imagem: font_ttf={font_ttf}')
        # print(f'escreve_imagem: size={size}')
        # print(f'escreve_imagem: corR={corR}')
        # print(f'escreve_imagem: corG={corG}')
        # print(f'escreve_imagem: corB={corB}')
        # print(f'escreve_imagem: top={top}')
        # print(f'escreve_imagem: left={left}')
        # print(f'escreve_imagem: texto={texto}')
        nome_saida = self.pegar_nome_arquivo(arquivo)
        # print(f'escreve = 2')
        #exemplo de paramentros font:
        #font_ttf, size, corR, corG, corB, top, left = 'Roboto-Regular.ttf', 10, 218, 220, 224, 1, 25
        #exemplos robo stormtech_002
        # self.util.criar_diretorio(self.util.pegar_caminho_diretorio_do_arquivo(f'{arquivo_saida}'))

        # self.criar_diretorio(self.pegar_caminho_diretorio_do_arquivo(f'{arquivo}'))
        # print(f'escreve = 3')
        self.criar_diretorio(self.pegar_caminho_diretorio_do_arquivo(f'{path_tmp}'))
        self.criar_diretorio(f'{path_tmp}/temp/')
        # self.logger.info(f'abrindo imagem para editar {arquivo}')
        # print(f'escreve = 4')
        #font = ImageFont.truetype(f'{self.path_font}{font_ttf}', size)
        font_path = os.path.abspath(f'{path_font}/{font_ttf}')
        # print(f'escreve_imagem: font_path={font_path}')
        # print(f'escreve = 5')
        font = ImageFont.truetype(font_path, size)
        # print(f'escreve = 6')
        img_arquivo_entrada = Image.open(f'{arquivo}')
        # print(f'escreve = 7')
        draw = ImageDraw.Draw(img_arquivo_entrada)
        # print(f'escreve = 8')
        #PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left")

        # draw.text((left, top), f'{texto}', (corR, corG, corB), font=font, anchor=None, spacing=0, align="right")
        draw.text((left, top), f'{texto}', (corR, corG, corB), font=font)
        # print(f'escreve = 9 {path_tmp}/temp/{nome_saida}')
        # self.logger.info(f'salvando imagem {path_tmp}{nome_saida}_temp.png')
        # self.logger.info(f'salvando imagem {path_tmp}temp{nome_saida}')
        # img_arquivo_entrada.save(f'{path_tmp}{nome_saida}_temp.png')
        img_arquivo_entrada.save(f'{path_tmp}/temp/{nome_saida}')
        # print(f'escreve = 10')
        #self.logger.info(f'removendo imagem {arquivo}')
        #os.remove(f'{arquivo}')
        # self.logger.info(f'movendo imagem {path_tmp}{nome_saida}_temp.png >> {arquivo}')
        # self.logger.info(f'movendo imagem {path_tmp}temp{nome_saida} >> {arquivo}')
        #print('x1')
        # os.rename(f"{path_tmp}{nome_saida}_temp.png", f"{arquivo}")
        os.rename(f"{path_tmp}/temp/{nome_saida}", f"{arquivo}")
        # self.logger.debug(f'Salvando screenshot: {arquivo}')
        # print(f'escreve = 11')
        #print('x2')

        # print('\n')
        # self.logger.info('\n')

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


    def cortar_imagem(self, arquivo_entrada, arquivo_saida, esquerda, superior, direita, inferior):

        self.logger.info(f'arquivo_entrada {arquivo_entrada}, arquivo_saida {arquivo_saida}, esquerda {esquerda}, superior {superior}, direita {direita}, inferior {inferior}')

        # exemplos robo stormtech_002
        #self.util.criar_diretorio(self.util.pegar_caminho_diretorio_do_arquivo(f'{arquivo_saida}'))
        # self.logger.info('cortando 001')
        self.criar_diretorio(self.pegar_caminho_diretorio_do_arquivo(f'{arquivo_saida}'))
        # self.logger.info('cortando 002')
        #self.logger.info(f'recortando imagem {arquivo_entrada}')
        self.logger.info(f'recortando imagem {arquivo_entrada}')
        # self.logger.info('cortando 003')
        img1 = Image.open(f'{arquivo_entrada}')
        # self.logger.info('cortando 004')
        # esquerda, superior, direita, inferior = 300, 128, 1570, 625
        time.sleep(0.3)
        cropped_img = img1.crop((esquerda, superior, direita, inferior))
        # self.logger.info('cortando 005')
        # self.logger.info(f'salvando imagem cortada {arquivo_saida}')
        self.logger.info(f'salvando imagem cortada {arquivo_saida}')
        time.sleep(0.3)
        # self.logger.info('cortando 006')
        cropped_img.save(f'{arquivo_saida}')
        # self.logger.info('cortando 007')
        self.logger.info('\n')


        # self.logger.info(f'abrindo imagem para editar {arquivo_entrada}')
        # font = ImageFont.truetype(f'{self.path_font}{font_ttf}', size)
        # img_arquivo_entrada = Image.open(f'{arquivo_entrada}')
        # draw = ImageDraw.Draw(img_arquivo_entrada)
        # draw.text((left, top), f'{texto}', (corR, corG, corB), font=font)
        # self.logger.info(f'salvando imagem {arquivo_saida}')
        # img_arquivo_entrada.save(f'{arquivo_saida}')

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

    #tipo= xpath ou id
    def screenshot_elemento(self, tipo, elemento, driver, file_saida, dir_tmp, margem_cima=0,margem_dir=0,margem_baixo=0,margem_esq=0):

    #def screenshot_elemento(self, id, driver, file_saida, dir_tmp):

        dir_tmp=os.path.abspath(dir_tmp)


        nome_saida=self.pegar_nome_arquivo(file_saida)

        # exemplos robo stormtech_002
        # https://seleniumwithjavapython.wordpress.com/selenium-with-java/code-snippets/screenshot-of-a-specific-element/
        # http://www.software-testing-tutorials-automation.com/2015/01/how-to-capture-element-screenshot-using.html
        # https://stackoverflow.com/questions/15510882/selenium-get-coordinates-or-dimensions-of-element-with-python
        # https://tutorial.eyehunts.com/python/python-delete-file-remove-multiple-if-exists/

        self.logger.info(f'\ntipo: {tipo}')

        self.logger.info(f'file_saida: {file_saida}')
        self.logger.info(f'dir_tmp: {dir_tmp}')
        self.logger.info(f'elemento: {elemento}\n')


        if f'{tipo}' == 'id':
            self.logger.info(f'\nlocalizando {tipo} elemento: {elemento}')
            # imagem=self.driver.find_element_by_xpath("//*[@id='post-22532']/div[1]/div[2]/div/div/p[25]/a/img")
            #element = driver.find_element_by_xpath(f'{xpath}')
            element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, f"{elemento}")))
        elif f'{tipo}' == 'xpath':
            self.logger.info(f'\nlocalizando {tipo} elemento: {elemento}')
            # imagem=self.driver.find_element_by_xpath("//*[@id='post-22532']/div[1]/div[2]/div/div/p[25]/a/img")
            #element = driver.find_element_by_xpath(f'{elemento}')
            element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, f"{elemento}")))
            #element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, f"{id}")))


        size_elemento_a=element.size

        self.logger.info(f'\n\n\nsize_elemento_a {size_elemento_a}')
        #time.sleep(5)

        image_width = int(element.size['width']) + margem_dir
        image_height = int(element.size['height']) + margem_baixo
        location_x = int(element.location['x']) - margem_esq
        location_y = int(element.location['y']) - margem_cima

        self.logger.info(f'image_width {image_width}')
        self.logger.info(f'image_height {image_height}')
        self.logger.info(f'location_x {location_x}')
        self.logger.info(f'location_y {location_y}')

        #time.sleep(0.2)
        self.criar_diretorio(dir_tmp)
        driver.save_screenshot(f'{dir_tmp}{nome_saida}_temp.png')
        #time.sleep(1.0)
        self.cortar_imagem(f'{dir_tmp}{nome_saida}_temp.png', f'{file_saida}',location_x,location_y,location_x + image_width,location_y + image_height)
        self.logger.info(f'removendo file temp: {dir_tmp}{nome_saida}_temp.png')
        time.sleep(0.2)
        os.remove(f'{dir_tmp}{nome_saida}_temp.png')


    def saindo_driver(self,driver,logger):
        # self.thead_loop_screenshot = False
        # self.loop_screenshot += 1
        self.lista_saindo_webdriver.append(f'driver {driver}')

        print('\n\n')
        try:
            logger.info(f'Iniciando processo de finalização do webdriver "{driver}" de {self.lista_saindo_webdriver}')
            # print('Encerrado webdriver! ')

            try:
                # print('\n')
                logger.debug('saindo stop_client webdriver')
                driver.stop_client()
                # Thread(target=driver.close).start()
                logger.info('OK stop_client webdriver')
                # time.sleep(0.2)
            except Exception as e:
                logger.warning(f'saindo close webdriver {e}')
                # print('')


            try:
                # print('\n')
                logger.debug('saindo close webdriver')
                driver.close()
                # Thread(target=driver.close).start()
                logger.info('OK close webdriver')
                # time.sleep(0.2)
            except Exception as e:
                logger.warning(f'saindo close webdriver {e}')
                # print('')


            try:
                # print('\n')
                logger.debug('saindo quit webdriver')
                driver.quit()
                # Thread(target=driver.quit).start()
                logger.info('OK quit webdriver')
                # time.sleep(0.2)
            except Exception as e:
                logger.warning(f'saindo quit webdriver {e}')
                # print('')

            try:
                # print('\n')
                logger.debug('saindo exit webdriver')
                driver.exit()
                # Thread(target=driver.exit).start()
                logger.info('OK exit webdriver')
                # time.sleep(0.2)
            except Exception as e:
                logger.warning(f'saindo exit webdriver {e}')
                # print('')



            try:
                # print('\n')
                logger.debug('saindo stop webdriver')
                driver.stop()
                # Thread(target=driver.stop).start()
                logger.info('OK stop webdriver')
                # time.sleep(0.2)
            except Exception as e:
                logger.warning(f'saindo stop webdriver {e}')
                # print('')

            try:
                # print('\n')
                logger.debug('saindo dispose webdriver')
                driver.dispose()
                # Thread(target=driver.dispose).start()
                logger.info('OK dispose webdriver')
                # time.sleep(0.2)
            except Exception as e:
                logger.warning(f'saindo dispose webdriver {e}')
                # print('')

        except Exception as e:
            # print('\n')
            # logger.error(f'ERRO Encerrado webdriver! {e}')
            self.logger.error(f'ERRO ao finalizar webdriver! {e}')
            # print('\n')
            # time.sleep(0.2)
        # time.sleep(3)


        self.lista_saindo_webdriver.remove(f'driver {driver}')
        logger.info(f'Encerrado processo de finalização do webdriver "{driver}" de {self.lista_saindo_webdriver}')
        print('\n\n')

    ######################################
    ########### adicionar novos abaixo com marcação de data e criador:

    ######################################
    ### enio: 12/08/2019
    # def trocacor(self,int(r_in),int(g_in),int(b_in),img_in,int(r_out),int(g_out),int(b_out),img_out):

    def conta_lista_saindo_webdriver(self):
        qtde = int(len(self.lista_saindo_webdriver))
        print(f'qtde_webdriver = {qtde}')
        return qtde

    def zerar_conta_lista_saindo_webdriver(self):
        self.lista_saindo_webdriver = []

    def trocacor(self,r_in,g_in,b_in,img_in,r_out,g_out,b_out,img_out):
        r_in=int(r_in)
        g_in=int(g_in)
        b_in=int(b_in)
        r_out=int(r_out)
        g_out=int(g_out)
        b_out=int(b_out)
        self.logger.info(f'input {r_in},{g_in},{b_in},{img_in}')
        self.logger.info(f'ouput {r_out},{g_out},{b_out},{img_out}')
        # time.sleep(2.5)


        ## alterador de cor de imagem
        #parametros de rgb de entrada e saida
        #caminho completo do arquivo de entrata e saida
        #obs: o arquivo de saida pode ser o mesmo de entrada, assim será sobrescrito a imagem
        self.logger.info('im')
        im = cv2.imread(f'{img_in}')
        #rgb é invertido nessa função: bgr, por isso o [::-1] para inverter as listas, assim podemos passar os parametros rgb de forma normal
        ####                 B  G   R                     B   G  R
        #im[np.where((im == [255, 135, 0][::-1]).all(axis=2))] = [201, 41, 220][::-1]
        self.logger.info('im np')
        # im[np.where((im == [r_in, g_in, b_in][::-1]).all(axis=2))] = [r_out, g_out, b_out][::-1]
        im[np.where((im == [r_in, g_in, b_in][::-1]).all(axis=2))] = [r_out, g_out, b_out][::-1]
        #im[np.where((im == [f'{b_in}', f'{g_in}', f'{r_in}']).all(axis=2))] = [f'{b_out}', f'{g_out}', f'{r_out}']
        # im[np.where((im == [255, 135, 0]).all(axis=2))]        = [201, 41, 220]
        # im[np.where((im == [255, 135, 0][::-1]).all(axis=2))] = [201, 41, 220][::-1]
        self.logger.info('cv2')
        # cv2.imwrite(f'{img_out}_tmp', im)
        cv2.imwrite(f'{img_out}', im)
        # time.sleep(0.2)
        # self.logger.info('os rename')
        # os.rename(f'{img_out}_tmp', f'{img_out}')
        # self.logger.info('os remove')
        # time.sleep(0.2)
        # os.remove(f"{img_out}_tmp")

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


######################################
    ### enio: 28/08/2019
    def num_para_mes(self,mes_num):
        num=int(mes_num)
        if num == 1:
            mes='janeiro'
        elif num == 2:
            mes='fevereiro'
        elif num == 3:
            mes='março'
        elif num == 4:
            mes='abril'
        elif num == 5:
            mes='maio'
        elif num == 6:
            mes='junho'
        elif num == 7:
            mes='julho'
        elif num == 8:
            mes='agosto'
        elif num == 9:
            mes='setembro'
        elif num == 10:
            mes='outubro'
        elif num == 11:
            mes='novembro'
        elif num == 12:
            mes='dezembro'
        else:
            mes=''
        return f'{mes}'
    ######################################

    ######################################
    ### enio: 28/08/2019
    def dimensoes_de_font_de_texto(self,caminho_da_font,tamanho,string):
        # font = ImageFont.truetype('times.ttf', 12)
        # size = font.getsize('Hello world')
        # print(size)
        font = ImageFont.truetype(f'{caminho_da_font}', int(tamanho))
        size = font.getsize(f'{string}')
        #       (x,y)
        self.logger.info(f'x={size[0]}    y={size[1]}')
        return size
    ######################################

    ######################################
    ### enio: 28/08/2019
    def convert_img_para_pdf(self,imagem,pdf,remove_imagem):
        #                                     remove_imagem >> 0 remove imagem,   1 = não remove
        # Python3 program to convert image to pfd
        # using img2pdf library

        # importing necessary libraries
        print('# storing image path')
        img_path = f"{imagem}"

        print('# storing pdf path ')
        pdf_path = f"{pdf}"

        print('# opening image ')
        image = Image.open(img_path)

        print('# converting into chunks using img2pdf')
        pdf_bytes = img2pdf.convert(image.filename)

        print('# opening or creating pdf file ')
        file = open(pdf_path, "wb")

        print('# writing pdf files with chunks')
        file.write(pdf_bytes)

        print('# closing image file ')
        image.close()
        time.sleep(0.2)

        print('# closing pdf file ')
        file.close()
        time.sleep(0.2)

        # output
        print("Successfully made pdf file")
        if remove_imagem == 0:
            print("removendo imagem")
            time.sleep(0.3)
            os.remove(f'{img_path}')
        else:
            print("não remove imagem")

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


######################################
    ### enio: 09/04/2020
    def sort_arquivo_e_sobrescreve(self,fname):
        self.logger.info('\n')
        try:
            self.logger.info(f'sort_arquivo_e_sobrescreve1 abrindo: {fname}')
            list_fname=[]
            temp_fname=f'{fname}.01.tmp'

            self.logger.info(f'sort_arquivo_e_sobrescreve2 lendo: {fname}')
            with open(fname, 'r', encoding="utf8") as f:
                for line in f:
                    list_fname.append(f'{line}')
                f.close()
                time.sleep(0.2)

            self.logger.info(f'sort_arquivo_e_sobrescreve3 ordenado: {fname}')
            comp_list=len(list_fname)
            count_comp_list = 0
            list_fname.sort()

            self.logger.info(f'sort_arquivo_e_sobrescreve4 escrevendo temp: {fname}')
            while count_comp_list < comp_list:
                self.agregar_arquivo(temp_fname,list_fname[count_comp_list])
                count_comp_list += 1

            self.logger.info(f'sort_arquivo_e_sobrescreve5 sobrescrevendo original: {fname}')
            leitura_temp_fname=self.ler_arquivo(temp_fname)
            self.sobrescrever_arquivo(fname,leitura_temp_fname)

            self.logger.info(f'sort_arquivo_e_sobrescreve6 apangando temp: {fname}')
            os.remove(temp_fname)
        except Exception as e:
            self.logger.info(f'sort_arquivo_e_sobrescreve7 {fname} ERRO: {e}' )
        self.logger.info('\n')
    ######################################

    def uniq_arquivo_sobrescreve(self,fname):
        self.logger.info('\n')

        #mylist = ["d", "b", "a", "c", "c", "a", "a", "c", "c", "d", "d"]
        #mylist = list(dict.fromkeys(mylist))
        #print(mylist)

        try:
            self.logger.info(f'uniq_arquivo_sobrescreve1 abrindo: {fname}')
            list_fname=[]
            temp_fname=f'{fname}.02.tmp'

            self.logger.info(f'uniq_arquivo_sobrescreve2 lendo: {fname}')
            with open(fname, 'r', encoding="utf8") as f:
                for line in f:
                    print(f'line={line}')
                    list_fname.append(f'{line}')
                    print(f'list_fname={list_fname}')
                    print('--\n--')
                    #time.sleep(5)
                f.close()
                time.sleep(0.2)


            self.logger.info(f'uniq_arquivo_sobrescreve3 remove duplos e mantem ordem da primeira aparição: {fname}')
            list_fname = list(dict.fromkeys(list_fname))

            #

            #comp_list=len(list_fname_temp)
            comp_list=len(list_fname)
            count_comp_list = 0
            #list_fname.sort()

            self.logger.info(f'uniq_arquivo_sobrescreve4 escrevendo temp: {fname} - comp_list{comp_list} - count_comp_list{count_comp_list}')
            while count_comp_list < comp_list:
                # self.agregar_arquivo(temp_fname,list_fname[count_comp_list])
                self.agregar_arquivo_sem_quebra(temp_fname,list_fname[count_comp_list])
                count_comp_list += 1

            self.logger.info(f'uniq_arquivo_sobrescreve6 sobrescrevendo original: {fname} - comp_list{comp_list} - count_comp_list{count_comp_list}')
            leitura_temp_fname=self.ler_arquivo(temp_fname)
            self.sobrescrever_arquivo(fname,leitura_temp_fname)

            self.logger.info(f'uniq_arquivo_sobrescreve7 apangando temp: {fname}')
            os.remove(temp_fname)
        except Exception as e:
            self.logger.info(f'uniq_arquivo_sobrescreve8 {fname} ERRO: {e}' )
        self.logger.info('\n\n\n')
        #time.sleep(10)
    ######################################


    def convert_image_to_base64(self, imagem):
        try:
            self.logger.debug(f'abrindo imagem {imagem} para extrair o base64')
            with open(imagem, "rb") as img_file:
                my_string = base64.b64encode(img_file.read())
                img_file.close()
                time.sleep(0.2)
            #print(my_string)
            #print(my_string.decode('utf-8'))
            return '{}'.format(my_string.decode('utf-8'))
        except Exception as e:
            self.logger.error(f'ERRO abrir imagem {imagem} para extrair o base64: {e}')
            return ''



    def recaptcha_V2(self, url_website=None, key_id_website=None, chave=None, type='recaptchaV2Proxyless'):
        #                                                                     type='turnstileProxyless'
        retorno= 'ERROR: vazio'
        try:
            if chave is None:
                config = self.get_config()
                chave = config['anticapcha']['chave']
            # print(f'chave={chave}')
            print(f'url_website={url_website}')
            print(f'key_id_website={key_id_website}')
            # time.sleep(3)
            retorno = ''

            if type == 'recaptchaV2Proxyless':
                solver = recaptchaV2Proxyless()
            if type == 'turnstileProxyless':
                solver = turnstileProxyless()

            solver.set_verbose(1)
            solver.set_key(f'{chave}')
            solver.set_website_url(f'{url_website}')
            solver.set_website_key(f'{key_id_website}')

            # Optionally specify page action
            solver.set_action("login")

            solver.set_soft_id(0)
            g_response = solver.solve_and_return_solution()

            if g_response != 0:
                # self.logger.info(f'g-response: g_response={g_response}')
                # time.sleep(3)
                retorno = g_response
                return g_response
            else:
                self.logger.error(f'task finished with error solver.error_code={solver.error_code}')
                retorno = f'ERROR: {solver.error_code}'

        except Exception as e:
            msg=f'ERRO ao extrair texto do recaptcha url_website="{url_website}" - key_id_website="{key_id_website} - erro:{e}'
            self.logger.error(f'{msg}')
            retorno = f'ERROR: desconhecido: {msg}'.replace('\n',' ').strip('\n').strip(' ')
        return retorno


    ############################################
    def extrai_texto_captcha(self, caminho_da_imagem):

        #fonte: https://anti-captcha.com/apidoc/image

        try:
            config = self.get_config()
            retorno = ''
            solver = imagecaptcha()
            solver.set_verbose(1)
            #solver.set_key("b2f7941460fdb717dd4477e4e24804bb")

            chave=config['anticapcha']['chave']

            solver.set_key(f'{chave}')

            self.logger.info(f'indianos começando a digitar o captcha')
            captcha_text = solver.solve_and_return_solution(caminho_da_imagem)
            if captcha_text != 0:
                self.logger.info(f'recebendo resultado captcha_text={captcha_text}')
                retorno=captcha_text
            else:
                self.logger.error(f'{self.usuario}: {self.complemento_url}:ERRO no resultado captcha_text={captcha_text} - solver.error_code={solver.error_code}')  # print  # "task finished with error " + solver.error_code
                retorno = f'{captcha_text};{caminho_da_imagem};ERROR:{solver.error_code}'.replace('\n',' ').strip('\n').strip(' ')
        except Exception as e:
            self.logger.error(f'ERRO ao extrair texto do captcha da imagem {caminho_da_imagem} - erro:{e}')
            retorno = f'ERRO desconhecido {caminho_da_imagem}: {e}'.replace('\n',' ').strip('\n').strip(' ')

        return retorno







    ############################################
    def remove_linhas_duplicadas_de_uma_coluna(self, dataframe, titulo_da_coluna):
        print('')

        try:
            df1 = dataframe

            qtde_linhas_df1_a = len(df1.index)
            # usuario = f''
            # df1 = pd.read_excel(f'/home/enio/Downloads/1.xls', 'Propostas')
            self.logger.debug(f': remove duplos da coluna {titulo_da_coluna}: INI')
            # qtde_linhas_df1_a = len(df1.index)
            self.logger.debug(f': remove duplos da coluna {titulo_da_coluna}: qtde_linhas_df1_a={qtde_linhas_df1_a} ')

            if qtde_linhas_df1_a > 0:
                dict_df1_index_prop = {}
                qtde_linhas_df_index_prop = qtde_linhas_df1_a
                while qtde_linhas_df_index_prop > 0:
                    self.logger.debug(f': qtde_linhas_df_index_prop={qtde_linhas_df_index_prop} ')  # fez até aqui
                    index_da_vez = qtde_linhas_df_index_prop - 1
                    self.logger.debug(f': index_da_vez={index_da_vez} ')
                    #proposta_do_index_da_vez = df1["PROPOSTA"][index_da_vez]
                    proposta_do_index_da_vez = df1[f"{titulo_da_coluna}"][index_da_vez]
                    self.logger.debug(f': proposta_do_index_da_vez={proposta_do_index_da_vez} ')
                    dict_df1_index_prop.update({f'{index_da_vez}': f'{proposta_do_index_da_vez}'})
                    self.logger.debug(f': remove duplos da coluna {titulo_da_coluna} {dict_df1_index_prop}')
                    qtde_linhas_df_index_prop -= 1

                qtde_linhas_df_dict_index = qtde_linhas_df1_a
                while qtde_linhas_df_dict_index > 0:
                    self.logger.debug(f': INI qtde_linhas_df_dict_index={qtde_linhas_df_dict_index} ')
                    indice_dict_da_vez = qtde_linhas_df_dict_index - 1
                    prop_dict_da_vez = dict_df1_index_prop[f'{indice_dict_da_vez}']
                    self.logger.debug(f': remove duplos da coluna {titulo_da_coluna}: proposta da posicao {indice_dict_da_vez} >>> {prop_dict_da_vez}')

                    counter_loop_dict = qtde_linhas_df1_a
                    while counter_loop_dict > 0:
                        self.logger.debug(f': counter_loop_dict={counter_loop_dict} ')
                        # indice_counter_loop_dict = counter_loop_dict - 1
                        indice_counter_loop_dict = counter_loop_dict - 1
                        try:
                            self.logger.debug(f': remove duplos da coluna {titulo_da_coluna}: tentando comparar {prop_dict_da_vez} == {df1[f"{titulo_da_coluna}"][indice_counter_loop_dict]}')
                            if f'{prop_dict_da_vez}' == f'{df1[f"{titulo_da_coluna}"][indice_counter_loop_dict]}':
                                # if indice_counter_loop_dict != indice_dict_da_vez:
                                self.logger.debug(f': propostas sao iguais {prop_dict_da_vez} == {df1[f"{titulo_da_coluna}"][indice_counter_loop_dict]}')
                                if indice_counter_loop_dict != indice_dict_da_vez:
                                    # if f'{prop_dict_da_vez}' == f'{df1["PROPOSTA"][indice_counter_loop_dict]}':
                                    self.logger.debug(f': remove duplos da colun {titulo_da_coluna}: INICIO sucesso  INDICES diferentes: remove: indiceREFdavez:({indice_dict_da_vez}){prop_dict_da_vez} >> indiceremovido:({indice_counter_loop_dict}){prop_dict_da_vez}')
                                    # df1.drop(indice_counter_loop_dict)
                                    df1.drop(indice_dict_da_vez)
                                    self.logger.debug(f': {df1}')
                                    # df1 = df1.drop(indice_counter_loop_dict)
                                    df1 = df1.drop(indice_dict_da_vez)
                                    self.logger.debug(f': FIM sucesso  INDICES diferentes: remove: indiceREFdavez:({indice_dict_da_vez}){prop_dict_da_vez} >> indiceremovido:({indice_counter_loop_dict}){prop_dict_da_vez}\n\n')
                                else:
                                    self.logger.debug(f': remove duplos da colun {titulo_da_coluna}: INDICES iguais: entao Não REmove remove')
                        except Exception as e:
                            self.logger.debug(f': Deu erro: não existe o indice no dataframe: {e}\n')
                        counter_loop_dict -= 1

                    self.logger.debug(f': remove duplos da coluna {titulo_da_coluna}: FIM qtde_linhas_df_dict_index={qtde_linhas_df_dict_index}\n\n--------\n\n ')
                    qtde_linhas_df_dict_index -= 1

            self.logger.debug(f': remove duplos da coluna {titulo_da_coluna}: FIM')

            # df1.to_excel(f'/home/enio/Downloads/2B.xls', index=False, sheet_name='Propostas')
        except Exception as e:
            self.logger.error(f': ERRO remove duplos PROPOSTA: {e}')

        return df1

    # def remove_repetidos_lista(lista):
    #     l = []
    #     for i in lista:
    #         if i not in l:
    #             l.append(i)
    #     l.sort()
    #     return l

    def encerra_Thread_screenshot_driver(self):
        self.loop_screenshot += 1
        self.thead_loop_screenshot = False
    def Thread_screenshot_driver(self, driver=None, dir='screenshot', nome='screenshot', intervalo=1, is_html=True):
        dir = os.path.abspath(dir)
        self.thead_loop_screenshot = True
        self.loop_screenshot = 0
        self.logger.debug(f'iniciando Thread_screenshot_driver')
        self.criar_diretorio(dir)
        while self.loop_screenshot == 0:
            if self.thead_loop_screenshot:
                if is_html:
                    # Thread(target=self.screenshot_driver, args=[driver, dir, nome, is_html]).start()
                    self.screenshot_driver(driver, dir, nome, is_html)

                else:
                    # Thread(target=self.screenshot_driver, args=[None, dir, nome, is_html]).start()
                    self.screenshot_driver(None, dir, nome, is_html)
                time.sleep(intervalo)
                # try:
                #     testa_html = driver.find_elements_by_xpath('//html')
                # except Exception as e:
                #     loop=1
                #     self.logger.error(f'Erro no Thread_screenshot_driver: {e}')
                #     time.sleep(10)
            else:
                break
        # break


    def screenshot_driver(self, driver=None, dir='screenshot', nome='screenshot', is_html=True):
        dir=os.path.abspath(dir)
        try:
            data_print1 = datetime.today().strftime("%Y/%m/%d %H:%M:%S.%f")
            data_print = f'{data_print1}'.replace('/','').replace(' ','').replace(':','')
            # self.logger.info(f'screenshot {dir}/{nome}_{data_print}.png')
            arquivo = f'{dir}/{nome}_{data_print}.png'
            if is_html:
                driver.save_screenshot(arquivo)
            else:
                mouse_pos = self.posicoes_circulo_mouse()
                # self.logger.info(f'mouse_pos = {mouse_pos}')
                self.pyautogui.screenshot(f'{dir}/{nome}_{data_print}.png')
                self.desenha_circulo(arquivo=arquivo, mouse_pos=mouse_pos)
            # print(f'salvou em {dir}/{nome}_{data_print}.png')
            self.escreve_imagemV2(f'{nome} {data_print1}',f'{dir}/{nome}_{data_print}.png')

        except Exception as e:
            self.logger.error(f'Erro no screenshot: {dir}/{nome}_{data_print}.png {e}')
            self.loop_screenshot += 1
            if 'closed' in str(e) or 'refused' in str(e) or 'exceeded' in str(e) :
                self.thead_loop_screenshot = False
                self.loop_screenshot += 1
                raise Exception(e)

    def somente_emails(self,string_emails_baguncados):

        self.logger.debug(f'lendo emails: {string_emails_baguncados}')
        match = re.findall(r'[\w\.-]+@[\w\.-]+', string_emails_baguncados)
        emails_organizados=self.converte_lista_para_texto(match,',').strip(',')
        return emails_organizados

    def XXXXwait_for_downloads(self, dir_downloadx=None, timemax=300):
        dir_download=os.path.abspath(f'{dir_downloadx}')
        self.logger.debug("Waiting for downloads", end="")
        while any([ filename.endswith(".crdownload") for filename in os.listdir(f'{dir_download}')] ):
            time.sleep(1)
            self.logger.debug(".", end="")
        self.logger.info("done!")

    def wait_for_downloads(self, dir_downloadx=None, timemax=300, delay = 0.3):
        time.sleep(delay)
        download_path=os.path.abspath(f'{dir_downloadx}')
        print(f'download_path={download_path}')
        print(f'dir_downloadx={dir_downloadx}')

        max_delay = timemax
        interval_delay = 1
        total_delay = 0
        file = ''
        done = False
        while not done and total_delay < max_delay:
            files = [f for f in os.listdir(download_path) if f.endswith('.crdownload')]
            file_full = [f for f in os.listdir(download_path) if not f.endswith('.crdownload')]

            if files:
                print(f'        downloading... {files}')

            if file_full:
                print(f'         downloaded!!! "{file_full}"')
            #if not files and len(file) > 1:
            if not files and len(file_full) >= 1:
                done = True
                max_delay=0
            if file_full:
                file = file_full[0]

            time.sleep(interval_delay)
            total_delay += interval_delay
            print(f'                loop: {total_delay}')

        if done:
            self.logger.info(f'File(s) "{file}" downloaded in {total_delay} seconds')
        if not done:
            self.logger.error("File(s) couldn't be downloaded")
        final = os.path.abspath(download_path + '/' + file).replace(".crdownload", "")
        return final




    def cnpj_matriz(self, cnpj_input_raw):
        print('')
        cnpj = '00000000000000'
        try:
            # cnpj_input='19596170001423'
            cnpj_input = self.cnpj14digits(cnpj_input_raw)

            print(f'Analisando CPNJ filial: {cnpj_input}')
            raiz = cnpj_input[0:8]
            print(f'      raiz:   {raiz}')
            matriz = f'{raiz}0001'
            print(f'      matriz: {matriz}')

            cnpj_raw = matriz

            # print(cnpj_raw)

            dig1 = int(cnpj_raw[11])
            dig2 = int(cnpj_raw[10])
            dig3 = int(cnpj_raw[9])
            dig4 = int(cnpj_raw[8])
            dig5 = int(cnpj_raw[7])
            dig6 = int(cnpj_raw[6])
            dig7 = int(cnpj_raw[5])
            dig8 = int(cnpj_raw[4])
            dig9 = int(cnpj_raw[3])
            dig10 = int(cnpj_raw[2])
            dig11 = int(cnpj_raw[1])
            dig12 = int(cnpj_raw[0])
            # print(f'dig12: {dig12}')
            cgcPriDig = (
                                    dig1 * 9 + dig2 * 8 + dig3 * 7 + dig4 * 6 + dig5 * 5 + dig6 * 4 + dig7 * 3 + dig8 * 2 + dig9 * 9 + dig10 * 8 + dig11 * 7 + dig12 * 6) % 11
            # print(cgcPriDig)

            if cgcPriDig == 10:
                cgcPriDig = 0

            cgcSegDig = (
                                    cgcPriDig * 9 + dig1 * 8 + dig2 * 7 + dig3 * 6 + dig4 * 5 + dig5 * 4 + dig6 * 3 + dig7 * 2 + dig8 * 9 + dig9 * 8 + dig10 * 7 + dig11 * 6 + dig12 * 5) % 11
            # print(cgcSegDig)

            if cgcSegDig == 10:
                cgcSegDig = 0

            cgcDV = cgcPriDig * 10 + cgcSegDig
            # print(cgcDV)

            if cgcDV == 0:
                cgcDV = "00"
                # print(f'if 1: {cgcDV}')

            if cgcDV > 0 and cgcDV < 10:
                cgcDV = "0" + str(cgcDV)
                # print(f'if 2: {cgcDV}')

            print(f'      digito verificador:  {cgcDV}')

            cnpj = f'{matriz}{cgcDV}'
            print(f' CNPJ correto: {cnpj}')

        except Exception as e:
            err = f'{e}'.replace('\n', ' ')
            print(f'Erro ao calcular CNPJ da matriz: {err}. ')

        print('')
        return self.cnpj14digits(cnpj)



    def cnpj_validate(self, cnpj: str) -> bool:

        # numbers = str(numbers_x)

        LENGTH_CNPJ = 14
        if len(cnpj) != LENGTH_CNPJ:
            # print(f' {cnpj} não1 é CNPJ\n\n')
            return False

        if cnpj in (c * LENGTH_CNPJ for c in "1234567890"):
            # print(f' {cnpj} não2 é CNPJ\n\n')
            return False

        cnpj_r = cnpj[::-1]
        for i in range(2, 0, -1):
            cnpj_enum = zip(cycle(range(2, 10)), cnpj_r[i:])
            dv = sum(map(lambda x: int(x[1]) * x[0], cnpj_enum)) * 10 % 11
            if cnpj_r[i - 1:i] != str(dv % 10):
                # print(f' {cnpj} não3 é CNPJ\n\n')
                return False
        print(f' {cnpj}  é CNPJ!!!!!!!!!\n\n')
        return True


    # def cpf_validate(self, numbers_x):
    def cpf_validate(self, numbers: str) -> bool:

        # numbers = str(numbers_x)


        print('\n\nvalidando teste se é CPF')
        numbers = int(self.somente_numeros(numbers))
        #print(f'numbersA: "{numbers}"')
        numbers = self.cpf11digits(self.somente_numeros(numbers))
        #print(f'numbersB: "{numbers}"')

        print(f'    verificando e validando se {numbers} é um cpf')

        #  Obtém os números do CPF e ignora outros caracteres
        cpf = [int(char) for char in numbers if char.isdigit()]

        #  Verifica se o CPF tem 11 dígitos
        if len(cpf) != 11:
            # print(f' {numbers} não1 é CPF\n\n')
            return False

        #  Verifica se o CPF tem todos os números iguais, ex: 111.111.111-11
        #  Esses CPFs são considerados inválidos mas passam na validação dos dígitos
        #  Antigo código para referência: if all(cpf[i] == cpf[i+1] for i in range (0, len(cpf)-1))
        if cpf == cpf[::-1]:
            # print(f' {numbers} não2 é CPF\n\n')
            return False

        #  Valida os dois dígitos verificadores
        for i in range(9, 11):
            value = sum((cpf[num] * ((i + 1) - num) for num in range(0, i)))
            digit = ((value * 10) % 11) % 10
            if digit != cpf[i]:
                # print(f' {numbers} não3 é CPF\n\n')
                return False

        print(f' {numbers} É CPF !!!!!\n\n')
        return True

    def cnpj14digits(self, cnpj):
        return str('{:0>14}'.format(self.somente_numeros(cnpj)))


    def testa_windows(self):
        if os.name == 'nt':                    #identifica o tipo de OS, para win=nt
            print(f'TRUE: OS is Windows: {os.name}')
            return True
        else:
            print(f'FALSE: OS is not Windows: {os.name}')
            return False


    def existe_xpath(self, driver, xpath, tempo=2, loop=9):
        url_atual = driver.current_url
        self.logger.debug(f'url_atual: {url_atual}')
        retorno = False
        counter = 1
        while loop > 0:
            try:
                # print('\n\n')
                self.logger.info(f'  ({loop})Procurando xpath "{xpath}"')
                elemento = WebDriverWait(driver, tempo).until(EC.presence_of_element_located((By.XPATH, xpath)))
                self.logger.info(f'  ({loop})Achou xpath "{xpath}"')
                retorno = True
                loop = 0
            except:
                time.sleep(counter)
            counter += 1
            loop -= 1
        if not retorno:
            url_atual = driver.current_url
            msg = f'    ({loop})Não achou o xpath {xpath} para clicar na pagina ({url_atual}) do navegador; '
            self.logger.error(msg)
            # raise Exception(msg)
        print('\n\n')
        return retorno

    def clicar_xpath(self, driver, xpath, tempo=2, loop=5):
        url_atual = driver.current_url
        self.logger.debug(f'url_atual: {url_atual}')
        retorno = False
        counter = 1
        while loop > 0:
            try:
                # print('\n\n')
                self.logger.info(f'  ({loop})Procurando xpath "{xpath}"')
                elemento = WebDriverWait(driver, tempo).until(EC.presence_of_element_located((By.XPATH, xpath)))
                self.logger.info(f'  ({loop})Achou xpath "{xpath}"')
                elemento.click()
                self.logger.info(f'  ({loop})Clicou xpath "{xpath}"')
                retorno = True
                loop = 0
            except:
                time.sleep(counter)
            counter += 1
            loop -= 1
        if not retorno:
            url_atual = driver.current_url
            msg = f'    ({loop})Não achou o xpath {xpath} para clicar na pagina ({url_atual}) do navegador; '
            self.logger.error(msg)
            # raise Exception(msg)
        print('\n\n')
        return retorno


    def clicar_e_limpar_xpath(self, driver, xpath, tempo=1, loop=3):
        url_atual = driver.current_url
        self.logger.debug(f'url_atual: {url_atual}')
        retorno = False
        counter = 1
        while loop > 0:
            try:
                # print('\n\n')
                self.logger.info(f'  Procurando xpath "{xpath}"')
                elemento = WebDriverWait(driver, tempo).until(EC.presence_of_element_located((By.XPATH, xpath)))
                self.logger.info(f'  Achou xpath "{xpath}"')
                elemento.click()
                self.logger.info(f'  Clicou xpath "{xpath}"')
                elemento.clear()
                self.logger.info(f'  limpou xpath "{xpath}"')
                retorno = True
                loop = 0
            except:
                time.sleep(counter)
            counter += 1
            loop -= 1
        if not retorno:
            url_atual = driver.current_url
            msg = f'Não achou o xpath {xpath} para clicar e limpar na pagina ({url_atual}) do navegador; '
            self.logger.error(msg)
            raise Exception(msg)
        print('\n\n')
        return retorno

    def digitar_input_xpath(self, driver, xpath, texto, tempo=1, loop=3):
        url_atual = driver.current_url
        self.logger.debug(f'url_atual: {url_atual}')
        retorno = False
        counter = 1
        while loop > 0:
            try:
                # print('\n\n')
                self.logger.info(f' digitar: Procurando xpath "{xpath}"')
                elemento = WebDriverWait(driver, tempo).until(EC.presence_of_element_located((By.XPATH, xpath)))
                self.logger.info(f' digitar: Achou xpath "{xpath}"')
                elemento.click()
                self.logger.info(f' digitar: Clicou xpath "{xpath}"')

                elemento.send_keys(texto)
                self.logger.info(f' digitar: Digitou "{texto}" no xpath "{xpath}"')

                retorno = True
                loop = 0
            except:
                time.sleep(counter)
            counter += 1
            loop -= 1
        if not retorno:
            url_atual = driver.current_url
            msg = f'Não achou o xpath {xpath} para digitar o texto {texto} na pagina ({url_atual}) do navegador; '
            self.logger.error(msg)
            # raise Exception(msg)
        print('\n\n')
        return retorno

    def captura_texto_xpath(self, driver, xpath, tempo=1, loop=3):
        url_atual = driver.current_url
        self.logger.debug(f'url_atual: {url_atual}')
        retorno = ''
        bol_retorno = False
        counter = 1

        while loop > 0:

            try:

                # print('\n\n')
                self.logger.info(f' txt Procurando xpath "{xpath}"')
                elemento = WebDriverWait(driver, tempo).until(EC.presence_of_element_located((By.XPATH, xpath)))
                self.logger.info(f' txt Achou xpath "{xpath}"')
                retorno = elemento.text
                self.logger.info(f' txt Capturou texto "{retorno}"')
                loop = 0
                bol_retorno = True
            except:
                time.sleep(counter)
            counter += 1
            loop -= 1
        if not bol_retorno:
            url_atual = driver.current_url
            msg = f'Não achou o xpath {xpath} para capturar o texto na pagina ({url_atual}) do navegador; '
            self.logger.error(msg)
            #raise Exception(msg)
            retorno = False
        print('\n\n')
        return retorno





    def esperar_comparar_url(self, driver, url_esperada, counter=5):
        retorno = False
        url_atual = driver.current_url
        self.logger.debug(f'url_atual: {url_atual}')
        if url_atual == url_esperada:
            self.logger.info(f'({counter})Carregou de PRIMEIRA a url esperada ({url_esperada}) = url atual ({url_atual})\n')
            counter = 0
            retorno = True
        while url_atual != url_esperada and counter > 0:
            time.sleep(2)
            url_atual = driver.current_url
            self.logger.warning(f'({counter})Ainda não carregou a url esperada ({url_esperada}), url atual ({url_atual})')
            counter -= 1
            if url_atual == url_esperada:
                self.logger.info(f'({counter})Carregou a url esperada ({url_esperada}) = url atual ({url_atual})')
                counter = 0
                retorno = True
        return retorno

    def printa(self, tipo='', msg=''):
        msg = msg.replace('\n', '// ').replace('  ', ' ').replace('  ', ' ').replace('  ', ' ').replace('  ', ' ').replace('  ', ' ').replace('  ', ' ')
        agora = datetime.datetime.today().strftime("%Y/%m/%d %H:%M:%S")
        if tipo == 'error':
            print(Fore.RED + f'{agora} | {tipo} | {msg}' + Style.RESET_ALL)
        elif tipo == 'debug':
            print(Fore.MAGENTA + f'{agora} | {tipo} | {msg}' + Style.RESET_ALL)
        elif tipo == 'warning':
            print(Fore.YELLOW + f'{agora} | {tipo} | {msg}' + Style.RESET_ALL)
        elif tipo == 'info':
            print(Fore.CYAN + f'{agora} | {tipo} | {msg}' + Style.RESET_ALL)
        elif tipo == 'critical':
            print(Back.RED + f'{agora} | {tipo} | {msg}' + Style.RESET_ALL)
        else:
            print(f'{agora} | {tipo} | {msg}')

    # def pyautogui_clicar_img(self, img, grayscale=True, confidence=0.7, x=0, y=0, largura=800, altura=800):
    #     pos_img2 = self.pyautogui.locateCenterOnScreen(img, grayscale=True, confidence=0.7, region=(0, 0, 800, 800))
    #     pos_img2 = self.pyautogui.locateCenterOnScreen(img, grayscale=grayscale, confidence=confidence, region=(x, y, largura, altura))

    def posicoes_circulo_mouse(self, raio=4):
        x, y = pyautogui.position()
        esq = int(x) - raio
        dir = int(x) + raio
        cima = int(y) - raio
        baixo = int(y) + raio
        return (esq, cima, dir, baixo)

    def localizar_img_pyautogui(self, imagem, grayscale=True, confidence=0.7, x=0, y=0, largura=800, altura=800, sleep=2, loop=2):
        retorno = False

    def move_quadradinho_pyautogui(self, x, y, largura, altura, duration=0.4):
        # print(self.pyautogui._pyautogui_x11._display)
        # time.sleep(0.7)

        # self.logger.debug(f'mapeando esq-sup: x={x} y={y}')
        # self.pyautogui.moveTo(x, y, duration=duration)
        #
        # self.logger.debug(f'mapeando dir-sup: x={largura} y={y}')
        # self.pyautogui.moveTo(largura, y, duration=duration)
        #
        # self.logger.debug(f'mapeando dir-inf: x={largura} y={altura}')
        # self.pyautogui.moveTo(largura, altura, duration=duration)
        #
        # self.logger.debug(f'mapeando esq-inf: x={x} y={altura}')
        # self.pyautogui.moveTo(x, altura, duration=duration)

        half_x = int((x + largura)/2)
        half_y = int((y + altura)/2)
        # self.logger.debug(f'{self.pyautogui._pyautogui_x11._display} mapeando centro:  x={half_x} y={half_y}')
        self.logger.debug(f'mapeando centro:  x={half_x} y={half_y}')
        try:
            pyautogui = self.pyautogui
            pyautogui.moveTo(half_x, half_y, duration=duration)
        except Exception as e:
            self.logger.error('erro: pyautogui.moveT ' + str(e))
            raise Exception('erro: pyautogui.moveT' + str(e))

        # time.sleep(0.7)

    def clica_img_pyautogui(self, imagem='', click=True, grayscale=True, confidence=0.9, x=50, y=50, largura=1000, altura=1000, loop=5, duration=0.7):

        sleep_loop = 0.5

        # print(self.pyautogui._pyautogui_x11._display)
        # time.sleep(0.7)
        retorno = False
        print('\n')

        img = str(os.path.abspath(imagem))
        nome_img = self.pegar_nome_arquivo(img)
        if self.screen_rgb:
            imgrgb = img + '.rgp.png'
            img_1 = str(os.path.abspath(imagem))
            # from PIL import Image
            img_2 = Image.open(img_1)
            img = img_2.convert("RGB")
            # img.save(imgrgb)
            # img = imgrgb





        # self.move_quadradinho_pyautogui(x, y, largura, altura)

        # self.logger.debug(f'{self.pyautogui._pyautogui_x11._display} ini pyautogui procurando imagem: {img}')
        # self.logger.debug(f'img001 {img}')
        #pos_img = self.pyautogui.locateCenterOnScreen(img, grayscale=True, confidence=0.7, region=(0,0, 800, 800))
        pos_img = None
        # self.logger.debug(f'img002 {img} pos_img={pos_img}')
        counter = loop
        # self.logger.debug(f'img003 {img}  pos_img={pos_img}')
        while counter > 0:
            # self.logger.debug(f'img004 {img} x={x}, y={y}, lar={largura}, alt={altura}  pos_img={pos_img}')

            # time.sleep(1)
            # pos_img1 = None

            if click:
                pos_img1 = self.pyautogui.locateCenterOnScreen(image=img, grayscale=grayscale, confidence=confidence, region=(x, y, largura, altura))
            else:
                pos_img1 = self.pyautogui.locateOnScreen(image=img,  grayscale=grayscale, confidence=confidence, region=(x, y, largura, altura))

            # self.logger.debug(f'  "pyautogui.locateCenterOnScreen(image={img}, grayscale={grayscale}, confidence={confidence}, region=({x}, {y}, {largura}, {altura})"  -   img005 {img} {pos_img1}')
            # time.sleep(1)
            time.sleep(sleep_loop)
            pos_img = pos_img1

            # self.logger.debug(f'img006 {img} {pos_img}')
            count_pos = 2

            while pos_img is None:
                # pos_img = None
                # self.logger.debug(f'img007')
                # print(f'whileB1 counter{counter}/count_pos{count_pos} pos_img={pos_img}')
                # time.sleep(1)
                time.sleep(sleep_loop)
                count_pos -= 1

                if click:
                    # pos_img2 = None
                    pos_img2 = self.pyautogui.locateCenterOnScreen(image=img, grayscale=grayscale, confidence=confidence, region=(x, y, largura, altura))
                    # self.logger.debug(f'img008 {pos_img2} image={img},')
                else:
                    # pos_img2 = None
                    pos_img2 = self.pyautogui.locateOnScreen(image=img, grayscale=grayscale, confidence=confidence, region=(x, y, largura, altura))
                    # self.logger.debug(f'img009 {pos_img2} image={img},')

                # self.logger.debug(f'  "pyautogui.locateCenterOnScreen(image={img}, grayscale={grayscale}, confidence={confidence}, region=({x}, {y}, {largura}, {altura})"  -   img010 {img} {pos_img2}')
                # time.sleep(1)
                time.sleep(sleep_loop)
                pos_img = pos_img2
                # self.logger.debug(f'img010 {pos_img} image={img}')
                if count_pos <= 0:
                    break

            if pos_img is not None:
                self.logger.debug(f'({counter}) if pyautogui localizado img {nome_img}  na posição: pos_img={pos_img}')

                if click:
                    # self.logger.debug(f'({counter}) if vai tentar clicar na img "{nome_img}" pos_img={pos_img}')
                    center_x = pos_img[0]
                    center_y = pos_img[1]
                    self.pyautogui.moveTo(x=center_x, y=center_y, duration=duration)
                    # time.sleep(0.5)
                    time.sleep(sleep_loop)
                    self.pyautogui.click(pos_img)
                    # time.sleep(0.5)
                    time.sleep(sleep_loop)
                    retorno = True
                    self.logger.debug(f'({counter}) if pyautogui clicou na img "{nome_img}" pos_img={pos_img}')
                else:
                    r_x = pos_img.left
                    r_y = pos_img.top
                    r_larg = pos_img.width
                    r_alt = pos_img.height
                    # self.logger.debug(f"X:{r_x}")
                    # self.logger.debug(f"Y:{r_y}")
                    # self.logger.debug(f"Largura:{r_larg}")
                    # self.logger.debug(f"Altura:{r_alt}")
                    # self.move_quadradinho_pyautogui(r_x, r_y, r_larg + r_x, r_alt + r_y)
                    retorno = {'x': r_x, 'y': r_y, 'largura': r_larg, 'altura': r_alt}
                    self.logger.debug(f'({counter}) if pyautogui localizou img "{nome_img}" pos_img={pos_img}: {retorno}')
                counter = 0
                break
            else:
                retorno = False

            counter -= 1
            sleep_loop += 0.5
            # if retorno:
            #     self.logger.info(f'({counter})  achou e clicou na img: {img} ')
            # else:
            #     self.logger.info(f'({counter})  ainda não achou e clicou na img: {img}. Vai tentar novamente ')
            #     time.sleep(sleep)

        # self.logger.debug(f'({counter}) fim pyautogui retorno:{retorno} - img "{nome_img}" - pos_img={pos_img}')
        print('\n')
        print('\n')

        if retorno:
            self.logger.info(f' ---->OK: retorno:{retorno} - img "{nome_img}" - pos_img={pos_img}')
        else:
            self.logger.error(f' ---->ERRO: retorno:{retorno} - img "{nome_img}" - pos_img={pos_img}')
            # input(f"Deu erro na função clica_img_pyautogui, não achou {nome_img}, faça manualmente e tecle ENTER...")
            # pass

        print('\n')
        print('\n')
        # time.sleep(0.5)
        return retorno

    def converter_img_video(self, dir='dir', prefix='prefix', dir_output='dir_output', name_output='name_output'):
        dir_output = os.path.abspath(dir_output)
        dir = os.path.abspath(dir)
        output = f"{dir_output}/{name_output}.mp4"
        # List of image filenames
        image_filenames = sorted([file for file in os.listdir(dir) if file.startswith(prefix)])

        # Create an empty list to hold the images
        images = []

        # Load the images and add them to the list
        for filename in image_filenames:
            images.append(imageio.imread(os.path.join(dir, filename)))

        # Create the output video file
        imageio.imwrite(output, images, fps=2)

    def converter_img_video1(self, dir='dir', prefix='prefix', dir_output='dir_output', name_output='name_output'):
        # output=f"{dir_output}/{name_output}.mp4"

        dir_output = os.path.abspath(dir_output)
        dir = os.path.abspath(dir)
        self.logger.debug(f'dir_output={dir_output}')
        self.logger.debug(f'dir={dir}')
        # output=f"{dir_output}/{name_output}.webm"
        # output=f"{dir_output}/{name_output}.avi"
        output = f"{dir_output}/{name_output}.mp4"
        self.logger.debug(f'output={output}')
        #sudo apt-get install x264
        #sudo apt-get install x265

        # codec = cv2.VideoWriter_fourcc(*"X265")
        # codec = cv2.VideoWriter_fourcc(*"X264")
        # codec = cv2.VideoWriter_fourcc(*"vp80")
        # codec = cv2.VideoWriter_fourcc(*"mp4v")
        codec = cv2.VideoWriter_fourcc(*"DIVX")
        # codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.logger.debug(f'codec={codec}')
        # codec = cv2.VideoWriter_fourcc(*"MJPG")
        # video = cv2.VideoWriter("video.avi", cv2.VideoWriter_fourcc(*"MJPG"), 30, (800, 600))
        framesize = (800, 600)
        video = cv2.VideoWriter(output, codec, 30, framesize)
        self.logger.debug(f'video={video}')

        self.criar_diretorio(dir_output)
        # images = os.listdir("images")
        images = os.listdir(dir)
        images.sort()
        filtrado = []
        for img in images:
            if img.startswith(prefix):
                # self.logger.debug(f'adicionando na lista: {dir}/{img}')
                filtrado.append(os.path.abspath(f'{dir}/{img}'))

        for printt in filtrado:
            imgg = cv2.imread(printt)
            # img = cv2.resize(img, (800, 600))
            video.write(imgg)
            self.logger.info(f'Adicionando img no video: {printt}: {video}')
            # time.sleep(0.5)

        video.release()
        return output

          #   <video width="320" height="240" preload="none" loop controls autoplay muted id="vid">
          # <source src="file.webm" type="video/webm; codecs='vp8.0, vorbis'">
          #   </video>

    def debug(self, mensagem):
        # Obtém o número da linha atual usando a função inspect.currentframe()
        linha = inspect.currentframe().f_back.f_lineno
        # Imprime a mensagem e o número da linha separados por um espaço
        print(f'{mensagem}\n     ::::::::::[{linha}]\n')

    def comand_windows(self, command, params='', ADMIN=False):

        # self.comand_windows(command='arp', params=' -d', ADMIN=True)
        # self.comand_windows(command='TASKKILL', params=' /F /IM chrome.exe /T', ADMIN=True)
        # self.comand_windows(command='TASKKILL', params=' /F /IM chrome.exe /T', ADMIN=False)

        if os.name == 'nt':
            if ADMIN:
                print(f'run AS ADMIN: \n              {command} {params}')
            else:
                print(f'run AS user: \n              {command} {params}')

            if params == '':
                if ADMIN:
                    result = shell.ShellExecuteEx(lpVerb='runas', nShow=win32con.SW_SHOWNORMAL, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpFile=f'{command}')
                else:
                    result = shell.ShellExecuteEx(lpVerb='', nShow=win32con.SW_SHOWNORMAL, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpFile=f'{command}')

            else:
                params_x = ' '.join([params])
                if ADMIN:
                    result = shell.ShellExecuteEx(lpVerb='runas', nShow=win32con.SW_SHOWNORMAL, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpFile=f'{command}', lpParameters=params_x)
                else:
                    result = shell.ShellExecuteEx(lpVerb='', nShow=win32con.SW_SHOWNORMAL, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpFile=f'{command}', lpParameters=params_x)

            procHandle = result['hProcess']
            print(f'result: {procHandle}')
            obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
            print(f'obj: {obj}')
            rc = win32process.GetExitCodeProcess(procHandle)
            print(f'rc: {rc}')
        else:
            msg_erro = f'OS não é Windows::: {os.name}'
            raise Exception(msg_erro)

    def extrair_tag_xml(self, arquivo_xml, tag):
        try:
            print(f'extrair_tag_xml 1: tag={tag}')
            tree = ET.parse(arquivo_xml)  # abre o arquivo XML e retorna um objeto ElementTree
            print(f'extrair_tag_xml 2: tree={tree}')
            root = tree.getroot()  # obtém o elemento raiz do XML
            print(f'extrair_tag_xml 3: root={root}')
            # infadic = root.find('{http://www.portalfiscal.inf.br/nfe}NFe/{http://www.portalfiscal.inf.br/nfe}infNFe/{http://www.portalfiscal.inf.br/nfe}' + tag)  # encontra o elemento que contém a tag "infAdic"
            infadic = root.findtext('{http://www.portalfiscal.inf.br/nfe}NFe/{http://www.portalfiscal.inf.br/nfe}infNFe/{http://www.portalfiscal.inf.br/nfe}' + tag)  # encontra o elemento que contém a tag "infAdic"
            infadic = root.findtext('*/*/' + tag)  # encontra o elemento que contém a tag "infAdic"
            print(f'extrair_tag_xml 4: infadic={infadic}')
            # texto = infadic.text  # obtém o texto do elemento
            texto = infadic  # obtém o texto do elemento
            print(f'extrair_tag_xml 5')
            print(f'texto={texto}')  # imprime o texto
            print(f'extrair_tag_xml 6')
            # return str(texto)
            print('\n\n')
            return texto
        except Exception as e:
            print(f'ERROR extrair_tag_xml: {e}')
            self.debug(f'ERROR extrair_tag_xml: {e}')
            print('\n\n')
            return None