mirror of
https://gitlab.os-k.eu/neox/CNIRevelator.git
synced 2023-08-25 14:03:10 +02:00
commit
3e2990dd82
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.pyc
|
||||
*.log
|
||||
*.spec
|
||||
|
||||
build/*
|
||||
dist/*
|
@ -1,50 +0,0 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
*** Projet CNI_Revelator ***
|
||||
|
||||
GNU GPL * 07/2018
|
||||
|
||||
Adrien Bourmault
|
||||
|
||||
VARIABLES
|
||||
|
||||
********************************************************************************
|
||||
"""
|
||||
CST_REV = "8"
|
||||
CST_VERTITLE = "2.2"
|
||||
CST_TAB_VER = ["2","2","1"]
|
||||
CST_VER = "{0}.{1}.{2}".format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2])
|
||||
CST_TYPE = "Final Release"
|
||||
CST_NAME = "CNIRevelator"
|
||||
CST_TITLE = CST_NAME + " " + CST_VER + " - GNU/GPL Licensing 2018"
|
||||
CST_MARK = CST_NAME + " " + CST_TYPE + " " + CST_VER + " - by NeoX, GNU/GPL Licensing 2018"
|
||||
CST_SUM_VER = int(CST_TAB_VER[0])*100 + int(CST_TAB_VER[1])*10 + int(CST_TAB_VER[2])
|
||||
CST_LINK = <lien vers serveur>
|
||||
CST_COLOR = "#003380"
|
||||
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import AES
|
||||
from tkinter import *
|
||||
from tkinter.messagebox import *
|
||||
from tkinter import filedialog
|
||||
from tkinter import ttk as ttk
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import sys
|
||||
import urllib.request as urllib2
|
||||
import urllib.error as URLExcept
|
||||
import random
|
||||
from pypac import PACSession
|
||||
from requests.auth import HTTPProxyAuth
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from PIL import Image, ImageFont, ImageDraw, ImageTk, ImageEnhance, ImageFilter
|
||||
import math
|
||||
import warnings
|
||||
import string
|
||||
|
||||
CST_FOLDER = os.getenv('APPDATA') + "/CNIRevelator/"
|
131
CNI_Revelator.py
131
CNI_Revelator.py
@ -1,131 +0,0 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
*** Projet CNI_Revelator ***
|
||||
|
||||
GNU GPL * 07/2018
|
||||
|
||||
Adrien Bourmault
|
||||
|
||||
main
|
||||
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
###IMPORTS GLOBAUX
|
||||
from CNI_GLOBALVAR import *
|
||||
|
||||
|
||||
##LOGGING
|
||||
|
||||
try:
|
||||
os.remove("error.log")
|
||||
os.remove("conf.ig")
|
||||
except:
|
||||
pass
|
||||
import logging
|
||||
|
||||
CST_NIVEAU_LOG = logging.ERROR
|
||||
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
# création de l'objet logger qui va nous servir à écrire dans les logs
|
||||
logger = logging.getLogger()
|
||||
# on met le niveau du logger à DEBUG, comme ça il écrit tout
|
||||
logger.setLevel(CST_NIVEAU_LOG)
|
||||
|
||||
# création d'un formateur qui va ajouter le temps, le niveau
|
||||
# de chaque message quand on écrira un message dans le log
|
||||
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')
|
||||
# création d'un handler qui va rediriger une écriture du log vers
|
||||
# un fichier en mode 'append', avec 1 backup et une taille max de 1Mo
|
||||
file_handler = RotatingFileHandler('error.log', 'a', 1000000, 1)
|
||||
# on lui met le niveau sur DEBUG, on lui dit qu'il doit utiliser le formateur
|
||||
# créé précédement et on ajoute ce handler au logger
|
||||
file_handler.setLevel(CST_NIVEAU_LOG)
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
##History
|
||||
|
||||
|
||||
|
||||
### IMPORTS LOCAUX
|
||||
from CNI_classes import *
|
||||
from CNI_Update import *
|
||||
|
||||
### FONCTION PRINCIPALE
|
||||
|
||||
|
||||
def main(logger):
|
||||
|
||||
logger.info("main() : " +"**** Creating App_main() ****")
|
||||
main_w = App_main(logger)
|
||||
|
||||
main_w.montext("* " + CST_NAME + " " + CST_VER + " " + CST_TYPE + " Revision " + CST_REV + " *\n")
|
||||
|
||||
import CNI_pytesseract as pytesseract
|
||||
|
||||
try:
|
||||
|
||||
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||
tesser_version = pytesseract.get_tesseract_version()
|
||||
|
||||
except Exception as e:
|
||||
logger.error("main() : " +"**** ERROR WITH TESSERACT MODULE " + str(e) + " ****")
|
||||
else:
|
||||
text = "Tesseract version " + str(tesser_version) +" Licensed Apache 2004 successfully initiated\n"
|
||||
main_w.montext(text)
|
||||
|
||||
|
||||
main_w.montext("\n\nEntrez la première ligne de MRZ svp \n")
|
||||
|
||||
|
||||
|
||||
logger.info("main() : " +"**** Launching App_main() ****")
|
||||
main_w.mainloop()
|
||||
logger.info("main() : " +"**** Ending App_main() ****")
|
||||
|
||||
|
||||
|
||||
##Launcher
|
||||
|
||||
logger.info("launcher : " + CST_NAME +" "+ CST_VER)
|
||||
logger.info("launcher : " +"*****Hello World*****")
|
||||
|
||||
logger.info("launcher : " +"*****Launching SoftUpdate()*****")
|
||||
try:
|
||||
Answer = SoftUpdate(logger)
|
||||
except Exception as e:
|
||||
logger.info("launcher : " +"*****FATAL ERROR*****" + str(e))
|
||||
os.abort()
|
||||
logger.info("launcher : " +"*****Ending SoftUpdate()*****")
|
||||
|
||||
|
||||
try:
|
||||
if Answer == True:
|
||||
logger.info("launcher : " +"*****Launching main()*****")
|
||||
State = main(logger)
|
||||
except Exception as e:
|
||||
logger.info("launcher : " +"*****FATAL ERROR*****" + str(e))
|
||||
os.abort()
|
||||
|
||||
logger.info("launcher : " +"*****Ending main()*****")
|
||||
logger.info("launcher : " +"*****Goodbye!*****")
|
||||
|
||||
handlers = logger.handlers[:]
|
||||
for handler in handlers:
|
||||
handler.close()
|
||||
logger.removeHandler(handler)
|
||||
|
||||
|
||||
if os.path.getsize("error.log") == 0:
|
||||
try:
|
||||
os.remove("error.log")
|
||||
except:
|
||||
raise(OSError)
|
||||
os.abort()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
568
CNI_Update.py
568
CNI_Update.py
@ -1,568 +0,0 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
*** Projet CNI_Revelator ***
|
||||
|
||||
GNU GPL * 07/2018
|
||||
|
||||
Adrien Bourmault
|
||||
|
||||
UPDATE
|
||||
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
|
||||
###IMPORTS GLOBAUX
|
||||
from CNI_GLOBALVAR import *
|
||||
|
||||
|
||||
###IMPORTS LOCAUX
|
||||
from CNI_classes import *
|
||||
|
||||
|
||||
###BEGIN
|
||||
|
||||
def SoftUpdate(logger): #Fonction de mise à jour de l'application
|
||||
|
||||
import zipfile
|
||||
# zip_ref = zipfile.ZipFile(path_to_zip_file, 'r')
|
||||
# zip_ref.extractall(directory_to_extract_to)
|
||||
# zip_ref.close()
|
||||
|
||||
logger.info("SoftUpdate() : " +"Verifying local data dir...")
|
||||
if not os.path.exists(CST_FOLDER):
|
||||
os.makedirs(CST_FOLDER)
|
||||
logger.info("SoftUpdate() : " +"Data dir created !")
|
||||
|
||||
logger.info("SoftUpdate() : " +"Looking for older version in dir...")
|
||||
list = os.listdir('.')
|
||||
|
||||
for file in list:
|
||||
|
||||
if file.startswith("CNIRevelator_"):
|
||||
|
||||
temp = ["0","0","0"]
|
||||
ver = file[13:].split(".")
|
||||
|
||||
|
||||
for i in range(len(ver)):
|
||||
|
||||
|
||||
|
||||
if ver[i] != "exe" :
|
||||
try:
|
||||
temp[i] = ver[i]
|
||||
except:
|
||||
None
|
||||
|
||||
|
||||
ver = temp.copy()
|
||||
|
||||
try :
|
||||
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
|
||||
|
||||
if sum_ver < CST_SUM_VER:
|
||||
|
||||
os.remove(file)
|
||||
logger.info("SoftUpdate() : " + "Removed old version : " + str(file) )
|
||||
|
||||
except:
|
||||
|
||||
logger.error("SoftUpdate() : " +"Failing to remove old version : " + str(file) )
|
||||
|
||||
None
|
||||
|
||||
|
||||
def updating(): #Fonction de lancement du thread de maj
|
||||
|
||||
def updator(): #Fonction d'exécution de la maj
|
||||
|
||||
logger.info("[updator() thread] : " + "Welcome !")
|
||||
global ret
|
||||
ret = 11
|
||||
canvas.itemconfigure(message, text="Recherche de mises-à-jour...")
|
||||
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||
p.start()
|
||||
upwin.update()
|
||||
logger.info("[updator() thread] : " + "Looking for updates...")
|
||||
|
||||
try:#Essai par proxy
|
||||
|
||||
def download(url, filename):
|
||||
|
||||
try: # if identifiants disponibles dans un fichier
|
||||
|
||||
logger.info("[download() thread] : " + "Trying getting credentials in the config file")
|
||||
|
||||
with open(CST_FOLDER +'conf.ig', "rb") as config:
|
||||
|
||||
AESObj = AESCipher("<clé>")
|
||||
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
|
||||
logger.info("[download() thread] : " + "Got credentials !")
|
||||
|
||||
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||
logger.info("[download() thread] : " + "Authenticated to proxy successfully")
|
||||
|
||||
|
||||
except IOError as e: #Else : on vérifie les identifiants de proxy
|
||||
|
||||
logger.error("[download() thread] : " + "False or absent credentials in the config file : " + str(e))
|
||||
|
||||
NoConnect = True
|
||||
|
||||
while NoConnect:
|
||||
|
||||
class LoginDialog(Toplevel):
|
||||
global login
|
||||
global key
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.title("Connexion")
|
||||
# ---------------------------------------
|
||||
Label(self, text="IPN : ").pack()
|
||||
self.entry_login = Entry(self)
|
||||
self.entry_login.insert(0, "")
|
||||
self.entry_login.pack()
|
||||
# ---------------------------------------
|
||||
Label(self, text="Mot de passe : ").pack()
|
||||
self.entry_pass = Entry(self, show='*')
|
||||
self.entry_pass.insert(0, "")
|
||||
self.entry_pass.pack()
|
||||
# ---------------------------------------
|
||||
Button(self, text="Connexion", command=self.connecti).pack()
|
||||
self.resizable(width=False, height=False)
|
||||
#taille souhaite de la fenetre
|
||||
w = 150
|
||||
h = 110
|
||||
#pour centrer la fenetre
|
||||
#taille de l'ecran
|
||||
self.update()
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
|
||||
if getattr( sys, 'frozen', False ) :
|
||||
self.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
|
||||
else:
|
||||
self.iconbitmap("id-card.ico")
|
||||
|
||||
upwin.update()
|
||||
#calcul la position de la fenetre
|
||||
x = (ws/2) - (w/2)
|
||||
y = (hs/2) - (h/2)
|
||||
#applique la taille et la position
|
||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
|
||||
def connecti(self):
|
||||
global login
|
||||
global key
|
||||
login = self.entry_login.get().strip()
|
||||
key = self.entry_pass.get().strip()
|
||||
self.destroy()
|
||||
|
||||
session = PACSession()
|
||||
|
||||
if session.get_pac() == None:
|
||||
IPN = ""
|
||||
IPN_PASS = ""
|
||||
NoConnect = False
|
||||
|
||||
break
|
||||
|
||||
canvas.itemconfigure(message, text="En attente de connexion au serveur proxy...")
|
||||
global login
|
||||
global key
|
||||
login = ""
|
||||
key = ""
|
||||
|
||||
result = LoginDialog(upwin)
|
||||
result.transient(upwin)
|
||||
result.grab_set()
|
||||
upwin.wait_window(result)
|
||||
|
||||
IPN = login
|
||||
IPN_PASS = key
|
||||
|
||||
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||
Ans = session.get("http://www.google.com")
|
||||
|
||||
if str(Ans) == "<Response [407]>":
|
||||
canvas.itemconfigure(message, text="Identifiants erronés, accès refusé")
|
||||
logger.info("[download() thread] : " + "407 Error")
|
||||
time.sleep(1)
|
||||
elif str(Ans) == "<Response [200]>":
|
||||
logger.info("[download() thread] : " + "Connection ok !")
|
||||
NoConnect = False
|
||||
else:
|
||||
raise(IOError())
|
||||
|
||||
AESObj = AESCipher("<clé>")
|
||||
|
||||
with open(CST_FOLDER +"conf.ig","wb+") as f:
|
||||
logger.info("[download() thread] : " + "Saving credentials in encrypted config file")
|
||||
f.write(AESObj.encrypt(IPN + "||" + IPN_PASS))
|
||||
|
||||
|
||||
|
||||
if IPN == "i005316":
|
||||
canvas.itemconfigure(message, text="Bienvenue Thierry !")
|
||||
elif IPN == "i020251":
|
||||
canvas.itemconfigure(message, text="Bienvenue Samia !")
|
||||
elif IPN == "i018410":
|
||||
canvas.itemconfigure(message, text="Bienvenue Adrien !")
|
||||
elif IPN == "i003067":
|
||||
canvas.itemconfigure(message, text="Bienvenue Remy !")
|
||||
elif IPN == "i018422":
|
||||
canvas.itemconfigure(message, text="Bienvenue Eloise !")
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
|
||||
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
|
||||
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
|
||||
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
|
||||
auth = urllib2.ProxyBasicAuthHandler()
|
||||
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
|
||||
urllib2.install_opener(server)
|
||||
logger.info("[download() thread] : " + "Proxy connection initiated successfully")
|
||||
|
||||
except:
|
||||
|
||||
logger.info("[download() thread] : " + "Proxy connection not initiated")
|
||||
|
||||
try:
|
||||
urllib2.urlretrieve(url, filename)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error("[download() thread] : " + "HTTP ERROR " )
|
||||
return e
|
||||
|
||||
##updator()
|
||||
logger.info("[updator() thread] : " + "Prepare downloading the version recap file...")
|
||||
tempfile = CST_FOLDER + 'temp' + str(random.randint(11111,99999)) + ".cniu"
|
||||
isOk = download(CST_LINK+"Version.txt", tempfile)
|
||||
|
||||
if not isOk:
|
||||
raise(isOk)
|
||||
|
||||
urllib2.urlcleanup()
|
||||
logger.info("[updator() thread] : " + "Opening version recap file...")
|
||||
file_ver = open(tempfile, "r")
|
||||
logger.info("[updator() thread] : " + "Reading version recap file...")
|
||||
version = file_ver.read()
|
||||
logger.info("[updator() thread] : " + "Closing version recap file...")
|
||||
repert = version.split("|")
|
||||
file_ver.close()
|
||||
logger.info("[updator() thread] : " + "Deleting version recap file...")
|
||||
os.remove(tempfile)
|
||||
logger.info("[updator() thread] : " + "Parsing informations about version...")
|
||||
final_f = "CNI_file"
|
||||
final_ver = ['0','0','0']
|
||||
|
||||
for file in repert:
|
||||
|
||||
if str.startswith(file,CST_NAME): #On prend les fichiers d'interêt du répertoire
|
||||
|
||||
ver = file.replace(CST_NAME+"_","").split(".") #On ne garde que le numéro de version
|
||||
|
||||
temp = ["0","0","0"]
|
||||
|
||||
for i in range(len(ver)):
|
||||
|
||||
temp[i] = ver[i]
|
||||
|
||||
ver = temp.copy()
|
||||
|
||||
sum_fver = int(final_ver[0])*100 + int(final_ver[1])*10 + int(final_ver[2])
|
||||
|
||||
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
|
||||
|
||||
|
||||
if sum_ver > sum_fver:
|
||||
|
||||
final_ver = ver.copy()
|
||||
final_f = file
|
||||
|
||||
sum_ver = int(final_ver[0])*100 + int(final_ver[1])*10 + int(final_ver[2])
|
||||
|
||||
if final_f != "CNI_file" and (sum_ver > CST_SUM_VER):
|
||||
#On a une maj
|
||||
logger.info("[updator() thread] : " + "New version of CNIRevelator found !")
|
||||
canvas.itemconfigure(message, text="Mise à jour disponible ! Préparation du téléchargement...")
|
||||
logger.info("[updator() thread] : " + "Preparing download")
|
||||
|
||||
with open(CST_FOLDER +'conf.ig', "rb") as config:
|
||||
|
||||
logger.info("[updator() thread] : " + "Reading credentials for proxy in config file...")
|
||||
|
||||
AESObj = AESCipher("<clé>")
|
||||
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
|
||||
|
||||
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||
|
||||
try:
|
||||
|
||||
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
|
||||
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
|
||||
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
|
||||
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
|
||||
auth = urllib2.ProxyBasicAuthHandler()
|
||||
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
|
||||
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !")
|
||||
|
||||
|
||||
except:
|
||||
|
||||
canvas.itemconfigure(message, text="Téléchargement en connexion directe...")
|
||||
server= urllib2.build_opener()
|
||||
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
|
||||
|
||||
logger.info("[updator() thread] : " + "Launching download of " + final_f)
|
||||
Statut = Download( CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger )
|
||||
|
||||
if Statut.success:
|
||||
|
||||
try:
|
||||
os.rename(final_f, final_f + ".exe")
|
||||
except IOError:
|
||||
logger.error("[updator() thread] : " + "Unable to rename the file ! Wait 3 sec and retry")
|
||||
time.sleep(3)
|
||||
try:
|
||||
os.rename(final_f, final_f + ".exe")
|
||||
except IOError:
|
||||
logger.critical("[updator() thread] : " + "Unable to rename the file !")
|
||||
|
||||
else:
|
||||
canvas.itemconfigure(message, text="Téléchargement terminé ! Préparation du lancement...")
|
||||
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully")
|
||||
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||
p.start()
|
||||
time.sleep(1)
|
||||
logger.info("[updator() thread] : " + "Launching " + final_f)
|
||||
try:
|
||||
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True)
|
||||
except:
|
||||
logger.error("[updator() thread] : " + "Unable to start the new version ! Wait 3 sec and retry")
|
||||
time.sleep(3)
|
||||
try:
|
||||
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True)
|
||||
except Exception as e:
|
||||
logger.critical("[updator() thread] : " + "Unable to start the new version ! Stopping : " + str(e))
|
||||
showerror("Erreur d'appel de procédure distante", "Le lancement du nouveau programme a échoué, vous devez le lancer manuellement une fois cette fenêtre fermée")
|
||||
ret = 12
|
||||
else:
|
||||
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP. Préparation du lancement...")
|
||||
logger.error("[updator() thread] : " + "Update has failed with HTTP error")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
|
||||
else:
|
||||
canvas.itemconfigure(message, text="Logiciel déjà à jour. Préparation du lancement...")
|
||||
logger.info("[updator() thread] : " + "CNIRevelator is up to date !")
|
||||
time.sleep(1)
|
||||
ret = 11
|
||||
|
||||
|
||||
#Tesseract
|
||||
if os.path.exists(CST_FOLDER + "Tesseract-OCR4\\tesseract.exe"):
|
||||
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||
|
||||
|
||||
else:
|
||||
|
||||
final_f = "tesseract_4"
|
||||
|
||||
#On a une maj
|
||||
logger.info("[updator() thread] : " + "Downloading tesseract 4 !")
|
||||
canvas.itemconfigure(message, text="Mise à jour du module OCR ! Préparation du téléchargement...")
|
||||
logger.info("[updator() thread] : " + "Preparing download")
|
||||
|
||||
with open(CST_FOLDER +'conf.ig', "rb") as config:
|
||||
|
||||
logger.info("[updator() thread] : " + "Reading credentials for proxy in config file...")
|
||||
|
||||
AESObj = AESCipher("<clé>")
|
||||
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
|
||||
|
||||
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||
|
||||
try:
|
||||
|
||||
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
|
||||
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
|
||||
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
|
||||
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
|
||||
auth = urllib2.ProxyBasicAuthHandler()
|
||||
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
|
||||
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !")
|
||||
|
||||
|
||||
except:
|
||||
|
||||
canvas.itemconfigure(message, text="Téléchargement en connexion directe...")
|
||||
server= urllib2.build_opener()
|
||||
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
|
||||
|
||||
logger.info("[updator() thread] : " + "Launching download of " + final_f)
|
||||
|
||||
Statut = Download( CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger )
|
||||
|
||||
if Statut.success:
|
||||
|
||||
canvas.itemconfigure(message, text="Téléchargement terminé ! Installation...")
|
||||
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully")
|
||||
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||
p.start()
|
||||
|
||||
try:
|
||||
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
|
||||
zip_ref.extractall(CST_FOLDER)
|
||||
zip_ref.close()
|
||||
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||
canvas.itemconfigure(message, text="Installation terminée !")
|
||||
|
||||
|
||||
|
||||
except:
|
||||
logger.error("[updator() thread] : " + "Unable to install the module. Wait and retry")
|
||||
time.sleep(3)
|
||||
try:
|
||||
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
|
||||
zip_ref.extractall(CST_FOLDER)
|
||||
zip_ref.close()
|
||||
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||
canvas.itemconfigure(message, text="Installation terminée !")
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.critical("[updator() thread] : " + "Unable to install the module ! Stopping : " + str(e))
|
||||
showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.")
|
||||
ret = 11
|
||||
|
||||
else:
|
||||
|
||||
logger.critical("[updator() thread] : " + "Unable to download the module ! ")
|
||||
showerror("Erreur de téléchargement", "L'installation du module OCR a échoué, merci d'indiquer le chemin d'accès au fichier tesseract_4 dans la fenêtre suivante")
|
||||
|
||||
path = filedialog.askopenfilename(title = "Indiquez le chemin d'accès à tesseract_4...",filetypes = (("Tesseract_4","*.cni4"), ("Tesseract_4","*.cni4")))
|
||||
|
||||
if path != "":
|
||||
|
||||
try:
|
||||
canvas.itemconfigure(message, text="Installation...")
|
||||
zip_ref = zipfile.ZipFile(path, 'r')
|
||||
zip_ref.extractall(CST_FOLDER)
|
||||
zip_ref.close()
|
||||
logger.error("[updator() thread] : " + "Manual installation successed")
|
||||
canvas.itemconfigure(message, text="Installation terminée !")
|
||||
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
logger.error("[updator() thread] : " + "Manual installation has failed" + str(e) )
|
||||
showerror("Erreur de lecture", "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner")
|
||||
|
||||
|
||||
else:
|
||||
|
||||
showerror("Opération annulée", "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner")
|
||||
|
||||
|
||||
except URLExcept.HTTPError as e:
|
||||
|
||||
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP " + str(e.code) + " . Préparation du lancement...")
|
||||
logger.error("[updator() thread] : " + "Update has failed with HTTP error" + str(e.code) )
|
||||
|
||||
if int(e.code) == 407:
|
||||
showerror("Erreur 407", "Attention : le système de mise à jour automatique a fait face à une erreur 407, signifiant que la connexion au serveur proxy a été refusée. Vos identifiants vous seront redemandés au prochain démarrage. La mise à jour a échoué.")
|
||||
logger.info("[updator() thread] : " + "Credential error. Deleting the config file...")
|
||||
os.remove(CST_FOLDER +"conf.ig")
|
||||
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||
p.start()
|
||||
time.sleep(3)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
canvas.itemconfigure(message, text="Echec de la mise à jour. Préparation du lancement...")
|
||||
logger.error("[updator() thread] : " + "Error from the updating system : " + str(e))
|
||||
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||
p.start()
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
p.stop()
|
||||
upwin.destroy()
|
||||
root.destroy()
|
||||
return ret
|
||||
|
||||
##updating()
|
||||
global ret
|
||||
logger.info("updating() : " + "Launching updator() thread...")
|
||||
threading.Thread(target=updator, daemon=True).start()
|
||||
logger.info("updating() [Thread] : " + "Ending updator() thread")
|
||||
return None
|
||||
|
||||
##SoftUpdate()
|
||||
|
||||
global ret
|
||||
ret = 11
|
||||
global upwin
|
||||
root = Tk()
|
||||
root.attributes('-alpha', 0.0) #For icon
|
||||
#root.lower()
|
||||
root.iconify()
|
||||
|
||||
upwin = Toplevel(root)
|
||||
upwin.overrideredirect(1)
|
||||
upwin.configure(bg = CST_COLOR)
|
||||
|
||||
upwin.resizable(width=False, height=False)
|
||||
#taille souhaite de la fenetre
|
||||
w = 600
|
||||
h = 300
|
||||
#pour centrer la fenetre
|
||||
#taille de l'ecran
|
||||
upwin.update()
|
||||
canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0)
|
||||
pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR)
|
||||
p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate')
|
||||
upwin.update()
|
||||
ws = upwin.winfo_screenwidth()
|
||||
hs = upwin.winfo_screenheight()
|
||||
canvas.create_text(w/2, h/3, text=CST_NAME + " " + CST_VERTITLE, font="Calibri 30 bold", fill="white")
|
||||
message = canvas.create_text(w/2, h/1.150, text=" ", font="Calibri 9", fill="white")
|
||||
upwin.update()
|
||||
#calcul la position de la fenetre
|
||||
x = (ws/2) - (w/2)
|
||||
y = (hs/2) - (h/2)
|
||||
#applique la taille et la position
|
||||
upwin.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
canvas.grid()
|
||||
pbar.grid()
|
||||
p.grid()
|
||||
upwin.after(2000, updating)
|
||||
|
||||
if getattr( sys, 'frozen', False ) :
|
||||
root.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
|
||||
else:
|
||||
root.iconbitmap("id-card.ico")
|
||||
|
||||
# On démarre la boucle Tkinter qui s'interrompt quand on ferme la fenêtre
|
||||
logger.info("SoftUpdate() : " + "Entering upwin mainloop()")
|
||||
upwin.protocol('WM_DELETE_WINDOW', lambda: root.destroy())
|
||||
upwin.mainloop()
|
||||
logger.info("SoftUpdate() : " + "Exiting upwin mainloop()")
|
||||
|
||||
if ret == 11:
|
||||
logger.info("SoftUpdate() : " + "OK to start to main() normally !")
|
||||
return True
|
||||
else:
|
||||
logger.info("SoftUpdate() : " + "Program will stop !")
|
||||
return False
|
1551
CNI_classes.py
1551
CNI_classes.py
File diff suppressed because one or more lines are too long
@ -1,413 +0,0 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
*** Projet CNI_Revelator ***
|
||||
|
||||
GNU GPL * 07/2018
|
||||
|
||||
Adrien Bourmault
|
||||
|
||||
Pytesseract modification to comply with Pyinstaller
|
||||
|
||||
*****
|
||||
"""
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
Python-tesseract. For more information: https://github.com/madmaze/pytesseract
|
||||
'''
|
||||
|
||||
try:
|
||||
import Image
|
||||
except ImportError:
|
||||
from PIL import Image
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shlex
|
||||
import string
|
||||
from glob import iglob
|
||||
from pkgutil import find_loader
|
||||
from distutils.version import LooseVersion
|
||||
from os.path import realpath, normpath, normcase
|
||||
|
||||
numpy_installed = find_loader('numpy') is not None
|
||||
if numpy_installed:
|
||||
from numpy import ndarray
|
||||
|
||||
# CHANGE THIS IF TESSERACT IS NOT IN YOUR PATH, OR IS NAMED DIFFERENTLY
|
||||
tesseract_cmd = 'tesseract'
|
||||
RGB_MODE = 'RGB'
|
||||
OSD_KEYS = {
|
||||
'Page number': ('page_num', int),
|
||||
'Orientation in degrees': ('orientation', int),
|
||||
'Rotate': ('rotate', int),
|
||||
'Orientation confidence': ('orientation_conf', float),
|
||||
'Script': ('script', str),
|
||||
'Script confidence': ('script_conf', float)
|
||||
}
|
||||
|
||||
|
||||
class Output:
|
||||
STRING = "string"
|
||||
BYTES = "bytes"
|
||||
DICT = "dict"
|
||||
|
||||
|
||||
class TesseractError(RuntimeError):
|
||||
def __init__(self, status, message):
|
||||
self.status = status
|
||||
self.message = message
|
||||
self.args = (status, message)
|
||||
|
||||
|
||||
class TesseractNotFoundError(EnvironmentError):
|
||||
def __init__(self):
|
||||
super(TesseractNotFoundError, self).__init__(
|
||||
tesseract_cmd + " is not installed or it's not in your path"
|
||||
)
|
||||
|
||||
|
||||
class TSVNotSupported(EnvironmentError):
|
||||
def __init__(self):
|
||||
super(TSVNotSupported, self).__init__(
|
||||
'TSV output not supported. Tesseract >= 3.05 required'
|
||||
)
|
||||
|
||||
|
||||
def run_once(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
if wrapper._result is wrapper:
|
||||
wrapper._result = func(*args, **kwargs)
|
||||
return wrapper._result
|
||||
|
||||
wrapper._result = wrapper
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_errors(error_string):
|
||||
return u' '.join(
|
||||
line for line in error_string.decode('utf-8').splitlines()
|
||||
).strip()
|
||||
|
||||
|
||||
def cleanup(temp_name):
|
||||
''' Tries to remove files by filename wildcard path. '''
|
||||
for filename in iglob(temp_name + '*' if temp_name else temp_name):
|
||||
try:
|
||||
os.remove(filename)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def prepare(image):
|
||||
if isinstance(image, Image.Image):
|
||||
return image
|
||||
|
||||
if numpy_installed and isinstance(image, ndarray):
|
||||
return Image.fromarray(image)
|
||||
|
||||
raise TypeError('Unsupported image object')
|
||||
|
||||
|
||||
def save_image(image):
|
||||
temp_name = tempfile.mktemp(prefix='tess_')
|
||||
if isinstance(image, str):
|
||||
return temp_name, realpath(normpath(normcase(image)))
|
||||
|
||||
image = prepare(image)
|
||||
img_extension = image.format
|
||||
if image.format not in {'JPEG', 'PNG', 'TIFF', 'BMP', 'GIF'}:
|
||||
img_extension = 'PNG'
|
||||
|
||||
if not image.mode.startswith(RGB_MODE):
|
||||
image = image.convert(RGB_MODE)
|
||||
|
||||
if 'A' in image.getbands():
|
||||
# discard and replace the alpha channel with white background
|
||||
background = Image.new(RGB_MODE, image.size, (255, 255, 255))
|
||||
background.paste(image, (0, 0), image)
|
||||
image = background
|
||||
|
||||
input_file_name = temp_name + os.extsep + img_extension
|
||||
image.save(input_file_name, format=img_extension, **image.info)
|
||||
return temp_name, input_file_name
|
||||
|
||||
|
||||
def subprocess_args(include_stdout=True):
|
||||
# The following is true only on Windows.
|
||||
if hasattr(subprocess, 'STARTUPINFO'):
|
||||
# On Windows, subprocess calls will pop up a command window by default
|
||||
# when run from Pyinstaller with the ``--noconsole`` option. Avoid this
|
||||
# distraction.
|
||||
si = subprocess.STARTUPINFO()
|
||||
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
# Windows doesn't search the path by default. Pass it an environment so
|
||||
# it will.
|
||||
env = os.environ
|
||||
else:
|
||||
si = None
|
||||
env = None
|
||||
|
||||
# ``subprocess.check_output`` doesn't allow specifying ``stdout``::
|
||||
#
|
||||
# Traceback (most recent call last):
|
||||
# File "test_subprocess.py", line 58, in <module>
|
||||
# **subprocess_args(stdout=None))
|
||||
# File "C:\Python27\lib\subprocess.py", line 567, in check_output
|
||||
# raise ValueError('stdout argument not allowed, it will be overridden.')
|
||||
# ValueError: stdout argument not allowed, it will be overridden.
|
||||
#
|
||||
# So, add it only if it's needed.
|
||||
if include_stdout:
|
||||
ret = {'stdout': subprocess.PIPE}
|
||||
else:
|
||||
ret = {}
|
||||
|
||||
# On Windows, running this from the binary produced by Pyinstaller
|
||||
# with the ``--noconsole`` option requires redirecting everything
|
||||
# (stdin, stdout, stderr) to avoid an OSError exception
|
||||
# "[Error 6] the handle is invalid."
|
||||
ret.update({'stdin': subprocess.PIPE,
|
||||
'stderr': subprocess.PIPE,
|
||||
'startupinfo': si,
|
||||
'env': env
|
||||
})
|
||||
return ret
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
def run_tesseract(input_filename,
|
||||
output_filename_base,
|
||||
extension,
|
||||
lang,
|
||||
config='',
|
||||
nice=0):
|
||||
cmd_args = []
|
||||
|
||||
if not sys.platform.startswith('win32') and nice != 0:
|
||||
cmd_args += ('nice', '-n', str(nice))
|
||||
|
||||
cmd_args += (tesseract_cmd, input_filename, output_filename_base)
|
||||
|
||||
if lang is not None:
|
||||
cmd_args += ('-l', lang)
|
||||
|
||||
cmd_args += shlex.split(config)
|
||||
|
||||
if extension not in ('box', 'osd', 'tsv'):
|
||||
cmd_args.append(extension)
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(cmd_args, **subprocess_args())
|
||||
except OSError:
|
||||
raise TesseractNotFoundError()
|
||||
|
||||
status_code, error_string = proc.wait(), proc.stderr.read()
|
||||
proc.stderr.close()
|
||||
|
||||
if status_code:
|
||||
raise TesseractError(status_code, get_errors(error_string))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def run_and_get_output(image,
|
||||
extension,
|
||||
lang=None,
|
||||
config='',
|
||||
nice=0,
|
||||
return_bytes=False):
|
||||
|
||||
temp_name, input_filename = '', ''
|
||||
try:
|
||||
temp_name, input_filename = save_image(image)
|
||||
kwargs = {
|
||||
'input_filename': input_filename,
|
||||
'output_filename_base': temp_name + '_out',
|
||||
'extension': extension,
|
||||
'lang': lang,
|
||||
'config': config,
|
||||
'nice': nice
|
||||
}
|
||||
|
||||
run_tesseract(**kwargs)
|
||||
filename = kwargs['output_filename_base'] + os.extsep + extension
|
||||
with open(filename, 'rb') as output_file:
|
||||
if return_bytes:
|
||||
return output_file.read()
|
||||
return output_file.read().decode('utf-8').strip()
|
||||
finally:
|
||||
cleanup(temp_name)
|
||||
|
||||
|
||||
def file_to_dict(tsv, cell_delimiter, str_col_idx):
|
||||
result = {}
|
||||
rows = [row.split(cell_delimiter) for row in tsv.split('\n')]
|
||||
if not rows:
|
||||
return result
|
||||
|
||||
header = rows.pop(0)
|
||||
if len(rows[-1]) < len(header):
|
||||
# Fixes bug that occurs when last text string in TSV is null, and
|
||||
# last row is missing a final cell in TSV file
|
||||
rows[-1].append('')
|
||||
|
||||
if str_col_idx < 0:
|
||||
str_col_idx += len(header)
|
||||
|
||||
for i, head in enumerate(header):
|
||||
result[head] = [
|
||||
int(row[i]) if i != str_col_idx else row[i] for row in rows
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def is_valid(val, _type):
|
||||
if _type is int:
|
||||
return val.isdigit()
|
||||
|
||||
if _type is float:
|
||||
try:
|
||||
float(val)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def osd_to_dict(osd):
|
||||
return {
|
||||
OSD_KEYS[kv[0]][0]: OSD_KEYS[kv[0]][1](kv[1]) for kv in (
|
||||
line.split(': ') for line in osd.split('\n')
|
||||
) if len(kv) == 2 and is_valid(kv[1], OSD_KEYS[kv[0]][1])
|
||||
}
|
||||
|
||||
|
||||
@run_once
|
||||
def get_tesseract_version():
|
||||
'''
|
||||
Returns LooseVersion object of the Tesseract version
|
||||
'''
|
||||
try:
|
||||
return LooseVersion(
|
||||
subprocess.check_output([tesseract_cmd, '--version'],
|
||||
**subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:])
|
||||
)
|
||||
except OSError:
|
||||
raise TesseractNotFoundError()
|
||||
|
||||
|
||||
def image_to_string(image,
|
||||
lang=None,
|
||||
config='',
|
||||
nice=0,
|
||||
boxes=False,
|
||||
output_type=Output.STRING):
|
||||
'''
|
||||
Returns the result of a Tesseract OCR run on the provided image to string
|
||||
'''
|
||||
if boxes:
|
||||
# Added for backwards compatibility
|
||||
print('\nWarning: Argument \'boxes\' is deprecated and will be removed'
|
||||
' in future versions. Use function image_to_boxes instead.\n')
|
||||
return image_to_boxes(image, lang, config, nice, output_type)
|
||||
|
||||
args = [image, 'txt', lang, config, nice]
|
||||
|
||||
if output_type == Output.DICT:
|
||||
return {'text': run_and_get_output(*args)}
|
||||
elif output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_boxes(image,
|
||||
lang=None,
|
||||
config='',
|
||||
nice=0,
|
||||
output_type=Output.STRING):
|
||||
'''
|
||||
Returns string containing recognized characters and their box boundaries
|
||||
'''
|
||||
config += ' batch.nochop makebox'
|
||||
args = [image, 'box', lang, config, nice]
|
||||
|
||||
if output_type == Output.DICT:
|
||||
box_header = 'char left bottom right top page\n'
|
||||
return file_to_dict(box_header + run_and_get_output(*args), ' ', 0)
|
||||
elif output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_data(image,
|
||||
lang=None,
|
||||
config='',
|
||||
nice=0,
|
||||
output_type=Output.STRING):
|
||||
'''
|
||||
Returns string containing box boundaries, confidences,
|
||||
and other information. Requires Tesseract 3.05+
|
||||
'''
|
||||
|
||||
if get_tesseract_version() < '3.05':
|
||||
raise TSVNotSupported()
|
||||
|
||||
config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip()
|
||||
args = [image, 'tsv', lang, config, nice]
|
||||
|
||||
if output_type == Output.DICT:
|
||||
return file_to_dict(run_and_get_output(*args), '\t', -1)
|
||||
elif output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_osd(image,
|
||||
lang='osd',
|
||||
config='',
|
||||
nice=0,
|
||||
output_type=Output.STRING):
|
||||
'''
|
||||
Returns string containing the orientation and script detection (OSD)
|
||||
'''
|
||||
config = '{}-psm 0 {}'.format(
|
||||
'' if get_tesseract_version() < '3.05' else '-',
|
||||
config.strip()
|
||||
).strip()
|
||||
args = [image, 'osd', lang, config, nice]
|
||||
|
||||
if output_type == Output.DICT:
|
||||
return osd_to_dict(run_and_get_output(*args))
|
||||
elif output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 2:
|
||||
filename, lang = sys.argv[1], None
|
||||
elif len(sys.argv) == 4 and sys.argv[1] == '-l':
|
||||
filename, lang = sys.argv[3], sys.argv[2]
|
||||
else:
|
||||
sys.stderr.write('Usage: python pytesseract.py [-l lang] input_file\n')
|
||||
exit(2)
|
||||
|
||||
try:
|
||||
print(image_to_string(Image.open(filename), lang=lang))
|
||||
except IOError:
|
||||
sys.stderr.write('ERROR: Could not open file "%s"\n' % filename)
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
8
README.MD
Normal file
8
README.MD
Normal file
@ -0,0 +1,8 @@
|
||||
# CNIRevelator
|
||||
### Analyzer for MRZ Codes on identity cards, passports and others for Windows.
|
||||
|
||||
This project is released under GNU General Public License v3 and has dependancies modules released under free licenses including :
|
||||
|
||||
Tesseract 4.0 beta, under Apache license 2004
|
||||
|
||||
Python Anaconda 3, under Berkeley Software Distribution license 2018-2019
|
12
README.md
12
README.md
@ -1,12 +0,0 @@
|
||||
# CNIRevelator
|
||||
Analyzer for MRZ Codes on identity cards, passports and others
|
||||
|
||||
This project is released under GNU General Public License v3 and contains modules released under free licenses including :
|
||||
|
||||
Tesseract 4.0 beta, under Apache license 2004
|
||||
|
||||
Python Miniconda3, under Berkeley Software Distribution license 2017
|
||||
|
||||
|
||||
It as been developped by NeoX_ in 2018, August.
|
||||
|
2
VERSIONS.LST
Normal file
2
VERSIONS.LST
Normal file
@ -0,0 +1,2 @@
|
||||
# ver|url|checksum, and | as separator, one version per ||
|
||||
0.0.0|https://www.os-k.eu|1234||0.0.1|https://www.os-k.eu|1234||0.0.2|https://www.os-k.eu|1234
|
BIN
id-card.ico
Normal file
BIN
id-card.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
18
make.bat
Normal file
18
make.bat
Normal file
@ -0,0 +1,18 @@
|
||||
@echo off
|
||||
title Compilation de CNIRLauncher
|
||||
|
||||
call pyinstaller -w -D --exclude-module PyQt5 --bootloader-ignore-signals --add-data "C:\users\adrie\Anaconda3\Lib\site-packages\tld\res\effective_tld_names.dat.txt";"tld\res" --add-data "id-card.ico";"id-card.ico" -i "id-card.ico" -n CNILauncher src\launcher\CNIRLauncher.py
|
||||
|
||||
title Compilation de CNIRevelator
|
||||
|
||||
call pyinstaller -w -D --exclude-module PyQt5 --bootloader-ignore-signals --add-data "C:\users\adrie\Anaconda3\Lib\site-packages\tld\res\effective_tld_names.dat.txt";"tld\res" --add-data "id-card.ico";"id-card.ico" -i "id-card.ico" -n CNIRevelator src\analyzer\CNI_Revelator.py
|
||||
|
||||
copy LICENSE dist\CNIRevelator\
|
||||
copy id-card.ico dist\CNIRevelator\
|
||||
|
||||
robocopy dist\CNILauncher dist\CNIRevelator /E /MOVE
|
||||
|
||||
rmdir dist\CNILauncher /Q /S
|
||||
|
||||
pause
|
||||
|
59
src/analyzer/CNI_GLOBALVAR.py
Normal file
59
src/analyzer/CNI_GLOBALVAR.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher & updater *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
class changelog:
|
||||
|
||||
def __init__(self):
|
||||
self.isOn = False
|
||||
self.text = "Mise-à-jour de sécurité avec corrections suivantes :\n- ajout de la signature numérique de l'exécutable\n- ajout d'une clé de cryptage plus performante pour le stockage des identifiants\n- passage à la méthode de cryptage AES256\n\nAjout/correction des fonctionnalités suivantes :\n- somme de contrôle des téléchargements pour une meilleure fiabilité\n- amélioration de présentation du log en cas d'erreur\n- correction d'un bug affectant l'analyse des MRZ après la correction manuelle\n\nEt un petit bonjour à tout le monde! ;)"
|
||||
|
||||
|
||||
CST_REV = '0'
|
||||
CST_VERTITLE = '2.2'
|
||||
CST_TAB_VER = ['2', '2', '5']
|
||||
CST_VER = '{0}.{1}.{2}'.format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2])
|
||||
CST_TYPE = 'Final Release'
|
||||
CST_NAME = 'CNIRevelator'
|
||||
CST_TITLE = CST_NAME + ' ' + CST_VER + ' - GNU/GPL Licensing 2018'
|
||||
CST_MARK = CST_NAME + ' ' + CST_TYPE + ' ' + CST_VER + ' - by NeoX, GNU/GPL Licensing 2018'
|
||||
CST_SUM_VER = int(CST_TAB_VER[0]) * 100 + int(CST_TAB_VER[1]) * 10 + int(CST_TAB_VER[2])
|
||||
CST_LINK = 'http://neoxgroup.eu/ftpaccess/Applicatifs/CNIRevelator/'
|
||||
CST_COLOR = '#003380'
|
||||
CST_TesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8'
|
||||
|
||||
|
||||
import base64, hashlib
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import AES
|
||||
from tkinter import *
|
||||
from tkinter.messagebox import *
|
||||
from tkinter import filedialog
|
||||
from tkinter import ttk
|
||||
import os, time, threading, sys, urllib.request as urllib2, urllib.error as URLExcept, random
|
||||
from datetime import datetime
|
||||
|
||||
CST_FOLDER = os.getenv('APPDATA') + '/CNIRevelator/'
|
||||
CST_CRYPTOKEY = '82Xh!efX3#@P~2eG'
|
||||
CST_CHANGELOG = changelog()
|
120
src/analyzer/CNI_Revelator.py
Normal file
120
src/analyzer/CNI_Revelator.py
Normal file
@ -0,0 +1,120 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher & updater *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from CNI_GLOBALVAR import *
|
||||
try:
|
||||
os.remove('error.log')
|
||||
os.remove('conf.ig')
|
||||
except:
|
||||
print("pass log deletion")
|
||||
pass
|
||||
|
||||
if not os.path.exists(CST_FOLDER):
|
||||
try:
|
||||
os.makedirs(CST_FOLDER)
|
||||
except IOError:
|
||||
print("pass IO ERROR")
|
||||
pass
|
||||
|
||||
print("debug")
|
||||
import logging
|
||||
from logging import FileHandler
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')
|
||||
error_handler = FileHandler((CST_FOLDER + '\\error.log'), mode='w', encoding='utf-8', delay=True)
|
||||
info_handler = FileHandler((CST_FOLDER + '\\cnirevelator.log'), mode='w', encoding='utf-8')
|
||||
error_handler.setLevel(logging.ERROR)
|
||||
logger.addHandler(error_handler)
|
||||
info_handler.setLevel(logging.DEBUG)
|
||||
info_handler.setFormatter(formatter)
|
||||
logger.addHandler(info_handler)
|
||||
from CNI_classes import *
|
||||
from CNI_Update import *
|
||||
|
||||
def main(logger):
|
||||
logger.error('')
|
||||
logger.info('main() : **** Creating App_main() ****')
|
||||
main_w = App_main(logger)
|
||||
main_w.montext('* ' + CST_NAME + ' ' + CST_VER + ' ' + CST_TYPE + ' Revision ' + CST_REV + ' *\n')
|
||||
import CNI_pytesseract as pytesseract
|
||||
try:
|
||||
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
|
||||
os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
|
||||
tesser_version = pytesseract.get_tesseract_version()
|
||||
except Exception as e:
|
||||
logger.error('main() : **** ERROR WITH TESSERACT MODULE ' + str(e) + ' ****')
|
||||
else:
|
||||
text = 'Tesseract version ' + str(tesser_version) + ' Licensed Apache 2004 successfully initiated\n'
|
||||
main_w.montext(text)
|
||||
main_w.montext('\n\nEntrez la première ligne de MRZ svp \n')
|
||||
if CST_CHANGELOG.isOn:
|
||||
showinfo('Changelog : résumé de mise à jour', ('Version du logiciel : ' + CST_VER + ' ' + CST_TYPE + ' Revision ' + CST_REV + '\n\n' + CST_CHANGELOG.text), parent=main_w)
|
||||
logger.info('main() : **** Launching App_main() ****')
|
||||
main_w.mainloop()
|
||||
logger.info('main() : **** Ending App_main() ****')
|
||||
|
||||
|
||||
logger.info('launcher : ' + CST_NAME + ' ' + CST_VER)
|
||||
logger.info('launcher : *****Hello World*****')
|
||||
logger.info('launcher : *****Launching SoftUpdate()*****')
|
||||
try:
|
||||
Answer = SoftUpdate(logger)
|
||||
except Exception as e:
|
||||
logger.info('launcher : *****FATAL ERROR*****' + str(e))
|
||||
os.abort()
|
||||
|
||||
logger.info('launcher : *****Ending SoftUpdate()*****')
|
||||
try:
|
||||
if Answer == True:
|
||||
logger.info('launcher : *****Launching main()*****')
|
||||
State = main(logger)
|
||||
except Exception as e:
|
||||
logger.info('launcher : *****FATAL ERROR*****' + str(e))
|
||||
os.abort()
|
||||
|
||||
logger.info('launcher : *****Ending main()*****')
|
||||
logger.info('launcher : *****Goodbye!*****')
|
||||
handlers = logger.handlers[:]
|
||||
for handler in handlers:
|
||||
handler.close()
|
||||
logger.removeHandler(handler)
|
||||
|
||||
try:
|
||||
with open(CST_FOLDER + '\\error.log') as (echo):
|
||||
try:
|
||||
os.remove('error.log')
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
from shutil import copyfile
|
||||
temptwo = str(echo.read())
|
||||
if len(temptwo) != 1:
|
||||
copyfile(CST_FOLDER + '\\cnirevelator.log', 'error.log')
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
print("exit")
|
||||
sys.exit(0)
|
506
src/analyzer/CNI_Update.py
Normal file
506
src/analyzer/CNI_Update.py
Normal file
@ -0,0 +1,506 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher & updater *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from CNI_GLOBALVAR import *
|
||||
from CNI_classes import *
|
||||
import hashlib
|
||||
from pypac import PACSession
|
||||
from requests.auth import HTTPProxyAuth
|
||||
import subprocess
|
||||
|
||||
def SoftUpdate(logger):
|
||||
global ret
|
||||
global upwin
|
||||
import zipfile
|
||||
for f in os.listdir(CST_FOLDER):
|
||||
if f[-4:] == '.tif':
|
||||
try:
|
||||
os.remove(CST_FOLDER + '\\' + f)
|
||||
except PermissionError as e:
|
||||
logger.info('SoftUpdate() : Failing to purge : ' + str(e))
|
||||
|
||||
logger.info('SoftUpdate() : Looking for older version in dir...')
|
||||
list = os.listdir('.')
|
||||
for file in list:
|
||||
if file.startswith('CNIRevelator_'):
|
||||
temp = ['0', '0', '0']
|
||||
ver = file[13:].split('.')
|
||||
for i in range(len(ver)):
|
||||
if ver[i] != 'exe':
|
||||
try:
|
||||
temp[i] = ver[i]
|
||||
except:
|
||||
pass
|
||||
|
||||
ver = temp.copy()
|
||||
try:
|
||||
sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2])
|
||||
if sum_ver < CST_SUM_VER:
|
||||
if file[-3:] == 'exe':
|
||||
os.remove(file)
|
||||
logger.info('SoftUpdate() : Removed old version : ' + str(file))
|
||||
CST_CHANGELOG.isOn = True
|
||||
except Exception as e:
|
||||
logger.error('SoftUpdate() : Failing to remove old version ' + str(file) + ' : ' + str(e))
|
||||
|
||||
def updating():
|
||||
|
||||
def updator():
|
||||
global ret
|
||||
logger.info('[updator() thread] : Welcome !')
|
||||
ret = 11
|
||||
canvas.itemconfigure(message, text='Recherche de mises-à-jour...')
|
||||
p.configure(mode='indeterminate', value=0, maximum=20)
|
||||
p.start()
|
||||
upwin.update()
|
||||
logger.info('[updator() thread] : Looking for updates...')
|
||||
try:
|
||||
|
||||
def download(url, filename):
|
||||
global key
|
||||
global login
|
||||
try:
|
||||
logger.info('[download() thread] : Trying getting credentials in the config file')
|
||||
with open(CST_FOLDER + 'conf.ig', 'rb') as (config):
|
||||
AESObj = AESCipher(CST_CRYPTOKEY)
|
||||
try:
|
||||
tempone = AESObj.decrypt(config.read())
|
||||
if tempone != '||':
|
||||
if tempone.find('||') != -1:
|
||||
IPN, IPN_PASS = tempone.split('||')[0:2]
|
||||
else:
|
||||
raise ValueError('Cryptokey is bad !')
|
||||
else:
|
||||
IPN = ''
|
||||
IPN_PASS = ''
|
||||
except Exception as e:
|
||||
raise IOError(str(e))
|
||||
else:
|
||||
logger.info('[download() thread] : Got credentials !')
|
||||
session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS)))
|
||||
logger.info('[download() thread] : Authenticated to proxy successfully')
|
||||
except IOError as e:
|
||||
logger.error('[download() thread] : False or absent credentials in the config file : ' + str(e))
|
||||
NoConnect = True
|
||||
while NoConnect:
|
||||
|
||||
class LoginDialog(Toplevel):
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.title('Connexion')
|
||||
Label(self, text='IPN : ').pack()
|
||||
self.entry_login = Entry(self)
|
||||
self.entry_login.insert(0, '')
|
||||
self.entry_login.pack()
|
||||
Label(self, text='Mot de passe : ').pack()
|
||||
self.entry_pass = Entry(self, show='*')
|
||||
self.entry_pass.insert(0, '')
|
||||
self.entry_pass.pack()
|
||||
Button(self, text='Connexion', command=(self.connecti)).pack()
|
||||
self.resizable(width=False, height=False)
|
||||
w = 150
|
||||
h = 110
|
||||
self.update()
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
if getattr(sys, 'frozen', False):
|
||||
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
||||
else:
|
||||
self.iconbitmap('id-card.ico')
|
||||
upwin.update()
|
||||
x = ws / 2 - w / 2
|
||||
y = hs / 2 - h / 2
|
||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
|
||||
def connecti(self):
|
||||
global key
|
||||
global login
|
||||
login = self.entry_login.get().strip()
|
||||
key = self.entry_pass.get().strip()
|
||||
self.destroy()
|
||||
|
||||
session = PACSession()
|
||||
if session.get_pac() == None:
|
||||
IPN = ''
|
||||
IPN_PASS = ''
|
||||
NoConnect = False
|
||||
break
|
||||
canvas.itemconfigure(message, text='En attente de connexion au serveur proxy...')
|
||||
login = ''
|
||||
key = ''
|
||||
result = LoginDialog(upwin)
|
||||
result.transient(upwin)
|
||||
result.grab_set()
|
||||
upwin.wait_window(result)
|
||||
IPN = login
|
||||
IPN_PASS = key
|
||||
session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS)))
|
||||
Ans = session.get('http://www.google.com')
|
||||
if str(Ans) == '<Response [407]>':
|
||||
canvas.itemconfigure(message, text='Identifiants erronés, accès refusé')
|
||||
logger.info('[download() thread] : 407 Error')
|
||||
time.sleep(1)
|
||||
else:
|
||||
if str(Ans) == '<Response [200]>':
|
||||
logger.info('[download() thread] : Connection ok !')
|
||||
NoConnect = False
|
||||
else:
|
||||
raise IOError()
|
||||
|
||||
AESObj = AESCipher(CST_CRYPTOKEY)
|
||||
with open(CST_FOLDER + 'conf.ig', 'wb+') as (f):
|
||||
logger.info('[download() thread] : Saving credentials in encrypted config file')
|
||||
f.write(AESObj.encrypt(IPN + '||' + IPN_PASS))
|
||||
|
||||
if IPN == 'i005316':
|
||||
canvas.itemconfigure(message, text='Bienvenue Thierry !')
|
||||
else:
|
||||
if IPN == 'i020251':
|
||||
canvas.itemconfigure(message, text='Bienvenue Samia !')
|
||||
else:
|
||||
if IPN == 'i018410':
|
||||
canvas.itemconfigure(message, text='Bienvenue Adrien !')
|
||||
else:
|
||||
if IPN == 'i003067':
|
||||
canvas.itemconfigure(message, text='Bienvenue Remy !')
|
||||
else:
|
||||
if IPN == 'i018422':
|
||||
canvas.itemconfigure(message, text='Bienvenue Eloise !')
|
||||
time.sleep(1)
|
||||
try:
|
||||
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu')
|
||||
PROXY_USABLE = Prox_us[6:-1].split(';')[0]
|
||||
proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE
|
||||
ph = urllib2.ProxyHandler({'http': proxy_server_url})
|
||||
auth = urllib2.ProxyBasicAuthHandler()
|
||||
server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler)
|
||||
urllib2.install_opener(server)
|
||||
logger.info('[download() thread] : Proxy connection initiated successfully')
|
||||
except:
|
||||
logger.info('[download() thread] : Proxy connection not initiated')
|
||||
|
||||
try:
|
||||
urllib2.urlretrieve(url, filename)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error('[download() thread] : HTTP ERROR ')
|
||||
return e
|
||||
|
||||
logger.info('[updator() thread] : Prepare downloading the version recap file...')
|
||||
tempfile = CST_FOLDER + 'temp' + str(random.randint(11111, 99999)) + '.cniu'
|
||||
isOk = download(CST_LINK + 'cnir.ver', tempfile)
|
||||
if not isOk:
|
||||
raise isOk
|
||||
urllib2.urlcleanup()
|
||||
logger.info('[updator() thread] : Opening version recap file...')
|
||||
file_ver = open(tempfile, 'r')
|
||||
logger.info('[updator() thread] : Reading version recap file...')
|
||||
version = file_ver.read()
|
||||
logger.info('[updator() thread] : Closing version recap file...')
|
||||
repert = version.split('|')
|
||||
file_ver.close()
|
||||
logger.info('[updator() thread] : Deleting version recap file...')
|
||||
os.remove(tempfile)
|
||||
logger.info('[updator() thread] : Parsing informations about version...')
|
||||
final_f = 'CNI_file'
|
||||
final_ver = ['0', '0', '0']
|
||||
final_hash = ''
|
||||
for sentence in repert:
|
||||
try:
|
||||
file, hashref = sentence.split(':')
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
if str.startswith(file, CST_NAME):
|
||||
ver = file.replace(CST_NAME + '_', '').split('.')
|
||||
temp = [
|
||||
'0', '0', '0']
|
||||
for i in range(len(ver)):
|
||||
temp[i] = ver[i]
|
||||
|
||||
ver = temp.copy()
|
||||
sum_fver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2])
|
||||
sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2])
|
||||
if sum_ver > sum_fver:
|
||||
final_ver = ver.copy()
|
||||
final_f = file
|
||||
final_hash = hashref
|
||||
|
||||
sum_ver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2])
|
||||
if final_f != 'CNI_file':
|
||||
if sum_ver > CST_SUM_VER:
|
||||
logger.info('[updator() thread] : New version of CNIRevelator found !')
|
||||
canvas.itemconfigure(message, text='Mise à jour disponible ! Préparation du téléchargement...')
|
||||
logger.info('[updator() thread] : Preparing download')
|
||||
with open(CST_FOLDER + 'conf.ig', 'rb') as (config):
|
||||
logger.info('[updator() thread] : Reading credentials for proxy in config file...')
|
||||
AESObj = AESCipher(CST_CRYPTOKEY)
|
||||
IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2]
|
||||
session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS)))
|
||||
try:
|
||||
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu')
|
||||
PROXY_USABLE = Prox_us[6:-1].split(';')[0]
|
||||
proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE
|
||||
ph = urllib2.ProxyHandler({'http': proxy_server_url})
|
||||
auth = urllib2.ProxyBasicAuthHandler()
|
||||
server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler)
|
||||
logger.info('[updator() thread] : Connection to the proxy initiated successfully !')
|
||||
except:
|
||||
canvas.itemconfigure(message, text='Téléchargement en connexion directe...')
|
||||
server = urllib2.build_opener()
|
||||
logger.info('[updator() thread] : Direct connection initiated successfully')
|
||||
|
||||
logger.info('[updator() thread] : Launching download of ' + final_f)
|
||||
Statut = Download(CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger)
|
||||
BLOCKSIZE = 65536
|
||||
hasher = hashlib.sha1()
|
||||
try:
|
||||
with open(final_f, 'rb') as (afile):
|
||||
buf = afile.read(BLOCKSIZE)
|
||||
while len(buf) > 0:
|
||||
hasher.update(buf)
|
||||
buf = afile.read(BLOCKSIZE)
|
||||
|
||||
hashcod = hasher.hexdigest()
|
||||
if hashcod != final_hash:
|
||||
Statut.success = False
|
||||
logger.error('[updator() thread] : Hashcode Error :' + final_f)
|
||||
try:
|
||||
os.remove(final_f)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
else:
|
||||
logger.info('[updator() thread] : Hashcode pass :' + final_f)
|
||||
except FileNotFoundError as e:
|
||||
logger.error('[updator() thread] : File not found ' + final_f)
|
||||
|
||||
if Statut.success:
|
||||
try:
|
||||
os.rename(final_f, final_f + '.exe')
|
||||
except IOError:
|
||||
logger.error('[updator() thread] : Unable to rename the file ! Wait 3 sec and retry')
|
||||
time.sleep(3)
|
||||
try:
|
||||
os.rename(final_f, final_f + '.exe')
|
||||
except IOError:
|
||||
logger.critical('[updator() thread] : Unable to rename the file !')
|
||||
|
||||
else:
|
||||
canvas.itemconfigure(message, text='Téléchargement terminé ! Préparation du lancement...')
|
||||
logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully')
|
||||
p.configure(mode='indeterminate', value=0, maximum=20)
|
||||
p.start()
|
||||
time.sleep(1)
|
||||
logger.info('[updator() thread] : Launching ' + final_f)
|
||||
try:
|
||||
proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True)
|
||||
except:
|
||||
logger.error('[updator() thread] : Unable to start the new version ! Wait 3 sec and retry')
|
||||
time.sleep(3)
|
||||
try:
|
||||
proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True)
|
||||
except Exception as e:
|
||||
logger.critical('[updator() thread] : Unable to start the new version ! Stopping : ' + str(e))
|
||||
showerror("Erreur d'appel de procédure distante", 'Le lancement du nouveau programme a échoué, vous devez le lancer manuellement une fois cette fenêtre fermée', parent=upwin)
|
||||
|
||||
ret = 12
|
||||
else:
|
||||
canvas.itemconfigure(message, text='Echec de la mise à jour : Erreur HTTP. Préparation du lancement...')
|
||||
logger.error('[updator() thread] : Update has failed with HTTP error')
|
||||
time.sleep(1)
|
||||
canvas.itemconfigure(message, text='Logiciel déjà à jour. Préparation du lancement...')
|
||||
logger.info('[updator() thread] : CNIRevelator is up to date !')
|
||||
time.sleep(1)
|
||||
ret = 11
|
||||
if os.path.exists(CST_FOLDER + 'Tesseract-OCR4\\tesseract.exe'):
|
||||
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
|
||||
os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
|
||||
else:
|
||||
final_f = 'tesseract_4'
|
||||
logger.info('[updator() thread] : Downloading tesseract 4 !')
|
||||
canvas.itemconfigure(message, text='Mise à jour du module OCR ! Préparation du téléchargement...')
|
||||
logger.info('[updator() thread] : Preparing download')
|
||||
with open(CST_FOLDER + 'conf.ig', 'rb') as (config):
|
||||
logger.info('[updator() thread] : Reading credentials for proxy in config file...')
|
||||
AESObj = AESCipher(CST_CRYPTOKEY)
|
||||
IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2]
|
||||
session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS)))
|
||||
try:
|
||||
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu')
|
||||
PROXY_USABLE = Prox_us[6:-1].split(';')[0]
|
||||
proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE
|
||||
ph = urllib2.ProxyHandler({'http': proxy_server_url})
|
||||
auth = urllib2.ProxyBasicAuthHandler()
|
||||
server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler)
|
||||
logger.info('[updator() thread] : Connection to the proxy initiated successfully !')
|
||||
except:
|
||||
canvas.itemconfigure(message, text='Téléchargement en connexion directe...')
|
||||
server = urllib2.build_opener()
|
||||
logger.info('[updator() thread] : Direct connection initiated successfully')
|
||||
|
||||
logger.info('[updator() thread] : Launching download of ' + final_f)
|
||||
Statut = Download(CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger)
|
||||
hashcod = ''
|
||||
logger.info('[updator() thread] : Verifying hashcode of ' + final_f)
|
||||
BLOCKSIZE = 65536
|
||||
hasher = hashlib.sha1()
|
||||
try:
|
||||
with open(CST_FOLDER + final_f, 'rb') as (afile):
|
||||
buf = afile.read(BLOCKSIZE)
|
||||
while len(buf) > 0:
|
||||
hasher.update(buf)
|
||||
buf = afile.read(BLOCKSIZE)
|
||||
|
||||
hashcod = hasher.hexdigest()
|
||||
if hashcod != CST_TesserHash:
|
||||
Statut.success = False
|
||||
logger.error('[updator() thread] : Hashcode Error : ' + final_f)
|
||||
try:
|
||||
os.remove(CST_FOLDER + final_f)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
else:
|
||||
logger.info('[updator() thread] : Hashcode pass : ' + final_f)
|
||||
except FileNotFoundError as e:
|
||||
logger.error('[updator() thread] : File not found ' + final_f)
|
||||
|
||||
if Statut.success:
|
||||
canvas.itemconfigure(message, text='Téléchargement terminé ! Installation...')
|
||||
logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully')
|
||||
p.configure(mode='indeterminate', value=0, maximum=20)
|
||||
p.start()
|
||||
try:
|
||||
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
|
||||
zip_ref.extractall(CST_FOLDER)
|
||||
zip_ref.close()
|
||||
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
|
||||
os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
|
||||
canvas.itemconfigure(message, text='Installation terminée !')
|
||||
except:
|
||||
logger.error('[updator() thread] : Unable to install the module. Wait and retry')
|
||||
time.sleep(3)
|
||||
try:
|
||||
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
|
||||
zip_ref.extractall(CST_FOLDER)
|
||||
zip_ref.close()
|
||||
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
|
||||
os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
|
||||
canvas.itemconfigure(message, text='Installation terminée !')
|
||||
except Exception as e:
|
||||
logger.critical('[updator() thread] : Unable to install the module ! Stopping : ' + str(e))
|
||||
showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.")
|
||||
|
||||
ret = 11
|
||||
else:
|
||||
logger.critical('[updator() thread] : Unable to download the module ! ')
|
||||
showerror('Erreur de téléchargement', "L'installation du module OCR a échoué, merci d'indiquer le chemin d'accès au fichier tesseract_4 dans la fenêtre suivante")
|
||||
path = filedialog.askopenfilename(title="Indiquez le chemin d'accès à tesseract_4...", filetypes=(('Tesseract_4', '*.cni4'),
|
||||
('Tesseract_4', '*.cni4')))
|
||||
if path != '':
|
||||
try:
|
||||
canvas.itemconfigure(message, text='Installation...')
|
||||
zip_ref = zipfile.ZipFile(path, 'r')
|
||||
zip_ref.extractall(CST_FOLDER)
|
||||
zip_ref.close()
|
||||
logger.error('[updator() thread] : Manual installation successed')
|
||||
canvas.itemconfigure(message, text='Installation terminée !')
|
||||
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
|
||||
os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
|
||||
except Exception as e:
|
||||
logger.error('[updator() thread] : Manual installation has failed' + str(e))
|
||||
showerror('Erreur de lecture', "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner")
|
||||
|
||||
else:
|
||||
showerror('Opération annulée', "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner")
|
||||
except URLExcept.HTTPError as e:
|
||||
canvas.itemconfigure(message, text=('Echec de la mise à jour : Erreur HTTP ' + str(e.code) + ' . Préparation du lancement...'))
|
||||
logger.error('[updator() thread] : Update has failed with HTTP error' + str(e.code))
|
||||
if int(e.code) == 407:
|
||||
showerror('Erreur 407', 'Attention : le système de mise à jour automatique a fait face à une erreur 407, signifiant que la connexion au serveur proxy a été refusée. Vos identifiants vous seront redemandés au prochain démarrage. La mise à jour a échoué.')
|
||||
logger.info('[updator() thread] : Credential error. Deleting the config file...')
|
||||
os.remove(CST_FOLDER + 'conf.ig')
|
||||
p.configure(mode='indeterminate', value=0, maximum=20)
|
||||
p.start()
|
||||
time.sleep(3)
|
||||
except Exception as e:
|
||||
canvas.itemconfigure(message, text='Echec de la mise à jour. Préparation du lancement...')
|
||||
logger.error('[updator() thread] : Error from the updating system : ' + str(e))
|
||||
p.configure(mode='indeterminate', value=0, maximum=20)
|
||||
p.start()
|
||||
time.sleep(2)
|
||||
|
||||
p.stop()
|
||||
upwin.destroy()
|
||||
root.destroy()
|
||||
return ret
|
||||
|
||||
logger.info('updating() : Launching updator() thread...')
|
||||
threading.Thread(target=updator, daemon=True).start()
|
||||
logger.info('updating() [Thread] : Ending updator() thread')
|
||||
|
||||
ret = 11
|
||||
root = Tk()
|
||||
root.attributes('-alpha', 0.0)
|
||||
root.iconify()
|
||||
upwin = Toplevel(root)
|
||||
upwin.overrideredirect(1)
|
||||
upwin.configure(bg=CST_COLOR)
|
||||
upwin.resizable(width=False, height=False)
|
||||
w = 600
|
||||
h = 300
|
||||
upwin.update()
|
||||
canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0)
|
||||
pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR)
|
||||
p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate')
|
||||
upwin.update()
|
||||
ws = upwin.winfo_screenwidth()
|
||||
hs = upwin.winfo_screenheight()
|
||||
canvas.create_text((w / 2), (h / 3), text=(CST_NAME + ' ' + CST_VERTITLE), font='Calibri 30 bold', fill='white')
|
||||
message = canvas.create_text((w / 2), (h / 1.15), text=' ', font='Calibri 9', fill='white')
|
||||
upwin.update()
|
||||
x = ws / 2 - w / 2
|
||||
y = hs / 2 - h / 2
|
||||
upwin.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
canvas.grid()
|
||||
pbar.grid()
|
||||
p.grid()
|
||||
upwin.after(2000, updating)
|
||||
if getattr(sys, 'frozen', False):
|
||||
root.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
||||
else:
|
||||
root.iconbitmap('id-card.ico')
|
||||
logger.info('SoftUpdate() : Entering upwin mainloop()')
|
||||
upwin.protocol('WM_DELETE_WINDOW', lambda : root.destroy())
|
||||
upwin.mainloop()
|
||||
logger.info('SoftUpdate() : Exiting upwin mainloop()')
|
||||
if ret == 11:
|
||||
logger.info('SoftUpdate() : OK to start to main() normally !')
|
||||
return True
|
||||
else:
|
||||
logger.info('SoftUpdate() : Program will stop !')
|
||||
return False
|
1332
src/analyzer/CNI_classes.py
Normal file
1332
src/analyzer/CNI_classes.py
Normal file
File diff suppressed because it is too large
Load Diff
326
src/analyzer/CNI_pytesseract.py
Normal file
326
src/analyzer/CNI_pytesseract.py
Normal file
@ -0,0 +1,326 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Pytesseract modification to comply with Pyinstaller *
|
||||
* *
|
||||
* Copyright © 2017-2018 Matthias A. Lee (madmaze) *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
|
||||
try:
|
||||
import Image
|
||||
except ImportError:
|
||||
from PIL import Image
|
||||
|
||||
import os, sys, subprocess, tempfile, shlex, string
|
||||
from glob import iglob
|
||||
from pkgutil import find_loader
|
||||
from distutils.version import LooseVersion
|
||||
from os.path import realpath, normpath, normcase
|
||||
numpy_installed = find_loader('numpy') is not None
|
||||
if numpy_installed:
|
||||
from numpy import ndarray
|
||||
tesseract_cmd = 'tesseract'
|
||||
RGB_MODE = 'RGB'
|
||||
OSD_KEYS = {'Page number':(
|
||||
'page_num', int),
|
||||
'Orientation in degrees':(
|
||||
'orientation', int),
|
||||
'Rotate':(
|
||||
'rotate', int),
|
||||
'Orientation confidence':(
|
||||
'orientation_conf', float),
|
||||
'Script':(
|
||||
'script', str),
|
||||
'Script confidence':(
|
||||
'script_conf', float)}
|
||||
|
||||
class Output:
|
||||
STRING = 'string'
|
||||
BYTES = 'bytes'
|
||||
DICT = 'dict'
|
||||
|
||||
|
||||
class TesseractError(RuntimeError):
|
||||
|
||||
def __init__(self, status, message):
|
||||
self.status = status
|
||||
self.message = message
|
||||
self.args = (status, message)
|
||||
|
||||
|
||||
class TesseractNotFoundError(EnvironmentError):
|
||||
|
||||
def __init__(self):
|
||||
super(TesseractNotFoundError, self).__init__(tesseract_cmd + " is not installed or it's not in your path")
|
||||
|
||||
|
||||
class TSVNotSupported(EnvironmentError):
|
||||
|
||||
def __init__(self):
|
||||
super(TSVNotSupported, self).__init__('TSV output not supported. Tesseract >= 3.05 required')
|
||||
|
||||
|
||||
def run_once(func):
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
if wrapper._result is wrapper:
|
||||
wrapper._result = func(*args, **kwargs)
|
||||
return wrapper._result
|
||||
|
||||
wrapper._result = wrapper
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_errors(error_string):
|
||||
return ' '.join(line for line in error_string.decode('utf-8').splitlines()).strip()
|
||||
|
||||
|
||||
def cleanup(temp_name):
|
||||
""" Tries to remove files by filename wildcard path. """
|
||||
for filename in iglob(temp_name + '*' if temp_name else temp_name):
|
||||
try:
|
||||
os.remove(filename)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def prepare(image):
|
||||
if isinstance(image, Image.Image):
|
||||
return image
|
||||
if numpy_installed:
|
||||
if isinstance(image, ndarray):
|
||||
pass
|
||||
return Image.fromarray(image)
|
||||
raise TypeError('Unsupported image object')
|
||||
|
||||
|
||||
def save_image(image):
|
||||
temp_name = tempfile.mktemp(prefix='tess_')
|
||||
if isinstance(image, str):
|
||||
return (temp_name, realpath(normpath(normcase(image))))
|
||||
else:
|
||||
image = prepare(image)
|
||||
img_extension = image.format
|
||||
if image.format not in frozenset({'BMP', 'JPEG', 'GIF', 'TIFF', 'PNG'}):
|
||||
img_extension = 'PNG'
|
||||
if not image.mode.startswith(RGB_MODE):
|
||||
image = image.convert(RGB_MODE)
|
||||
if 'A' in image.getbands():
|
||||
background = Image.new(RGB_MODE, image.size, (255, 255, 255))
|
||||
background.paste(image, (0, 0), image)
|
||||
image = background
|
||||
input_file_name = temp_name + os.extsep + img_extension
|
||||
(image.save)(input_file_name, format=img_extension, **image.info)
|
||||
return (
|
||||
temp_name, input_file_name)
|
||||
|
||||
|
||||
def subprocess_args(include_stdout=True):
|
||||
if hasattr(subprocess, 'STARTUPINFO'):
|
||||
si = subprocess.STARTUPINFO()
|
||||
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
env = os.environ
|
||||
else:
|
||||
si = None
|
||||
env = None
|
||||
if include_stdout:
|
||||
ret = {'stdout': subprocess.PIPE}
|
||||
else:
|
||||
ret = {}
|
||||
ret.update({'stdin':subprocess.PIPE, 'stderr':subprocess.PIPE,
|
||||
'startupinfo':si,
|
||||
'env':env})
|
||||
return ret
|
||||
|
||||
|
||||
def run_tesseract(input_filename, output_filename_base, extension, lang, config='', nice=0):
|
||||
cmd_args = []
|
||||
if not sys.platform.startswith('win32'):
|
||||
if nice != 0:
|
||||
cmd_args += ('nice', '-n', str(nice))
|
||||
cmd_args += (tesseract_cmd, input_filename, output_filename_base)
|
||||
if lang is not None:
|
||||
cmd_args += ('-l', lang)
|
||||
cmd_args += shlex.split(config)
|
||||
if extension not in ('box', 'osd', 'tsv'):
|
||||
cmd_args.append(extension)
|
||||
try:
|
||||
proc = (subprocess.Popen)(cmd_args, **subprocess_args())
|
||||
except OSError:
|
||||
raise TesseractNotFoundError()
|
||||
|
||||
status_code, error_string = proc.wait(), proc.stderr.read()
|
||||
proc.stderr.close()
|
||||
if status_code:
|
||||
raise TesseractError(status_code, get_errors(error_string))
|
||||
return True
|
||||
|
||||
|
||||
def run_and_get_output(image, extension, lang=None, config='', nice=0, return_bytes=False):
|
||||
temp_name, input_filename = ('', '')
|
||||
try:
|
||||
temp_name, input_filename = save_image(image)
|
||||
kwargs = {'input_filename':input_filename,
|
||||
'output_filename_base':temp_name + '_out',
|
||||
'extension':extension,
|
||||
'lang':lang,
|
||||
'config':config,
|
||||
'nice':nice}
|
||||
run_tesseract(**kwargs)
|
||||
filename = kwargs['output_filename_base'] + os.extsep + extension
|
||||
with open(filename, 'rb') as (output_file):
|
||||
if return_bytes:
|
||||
return output_file.read()
|
||||
return output_file.read().decode('utf-8').strip()
|
||||
finally:
|
||||
cleanup(temp_name)
|
||||
|
||||
|
||||
def file_to_dict(tsv, cell_delimiter, str_col_idx):
|
||||
result = {}
|
||||
rows = [row.split(cell_delimiter) for row in tsv.split('\n')]
|
||||
if not rows:
|
||||
return result
|
||||
else:
|
||||
header = rows.pop(0)
|
||||
if len(rows[(-1)]) < len(header):
|
||||
rows[(-1)].append('')
|
||||
if str_col_idx < 0:
|
||||
str_col_idx += len(header)
|
||||
for i, head in enumerate(header):
|
||||
result[head] = [int(row[i]) if i != str_col_idx else row[i] for row in rows]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def is_valid(val, _type):
|
||||
if _type is int:
|
||||
return val.isdigit()
|
||||
else:
|
||||
if _type is float:
|
||||
pass
|
||||
try:
|
||||
float(val)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def osd_to_dict(osd):
|
||||
return {OSD_KEYS[kv[0]][0]:OSD_KEYS[kv[0]][1](kv[1]) for kv in (line.split(': ') for line in osd.split('\n')) if len(kv) == 2 if is_valid(kv[1], OSD_KEYS[kv[0]][1])}
|
||||
|
||||
|
||||
@run_once
|
||||
def get_tesseract_version():
|
||||
"""
|
||||
Returns LooseVersion object of the Tesseract version
|
||||
"""
|
||||
try:
|
||||
return LooseVersion((subprocess.check_output)([tesseract_cmd, '--version'], **subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:]))
|
||||
except OSError:
|
||||
raise TesseractNotFoundError()
|
||||
|
||||
|
||||
def image_to_string(image, lang=None, config='', nice=0, boxes=False, output_type=Output.STRING):
|
||||
"""
|
||||
Returns the result of a Tesseract OCR run on the provided image to string
|
||||
"""
|
||||
if boxes:
|
||||
print("\nWarning: Argument 'boxes' is deprecated and will be removed in future versions. Use function image_to_boxes instead.\n")
|
||||
return image_to_boxes(image, lang, config, nice, output_type)
|
||||
else:
|
||||
args = [
|
||||
image, 'txt', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
return {'text': run_and_get_output(*args)}
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_boxes(image, lang=None, config='', nice=0, output_type=Output.STRING):
|
||||
"""
|
||||
Returns string containing recognized characters and their box boundaries
|
||||
"""
|
||||
config += ' batch.nochop makebox'
|
||||
args = [image, 'box', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
box_header = 'char left bottom right top page\n'
|
||||
return file_to_dict(box_header + run_and_get_output(*args), ' ', 0)
|
||||
else:
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_data(image, lang=None, config='', nice=0, output_type=Output.STRING):
|
||||
"""
|
||||
Returns string containing box boundaries, confidences,
|
||||
and other information. Requires Tesseract 3.05+
|
||||
"""
|
||||
if get_tesseract_version() < '3.05':
|
||||
raise TSVNotSupported()
|
||||
config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip()
|
||||
args = [image, 'tsv', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
return file_to_dict(run_and_get_output(*args), '\t', -1)
|
||||
else:
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_osd(image, lang='osd', config='', nice=0, output_type=Output.STRING):
|
||||
"""
|
||||
Returns string containing the orientation and script detection (OSD)
|
||||
"""
|
||||
config = '{}-psm 0 {}'.format('' if get_tesseract_version() < '3.05' else '-', config.strip()).strip()
|
||||
args = [
|
||||
image, 'osd', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
return osd_to_dict(run_and_get_output(*args))
|
||||
else:
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 2:
|
||||
filename, lang = sys.argv[1], None
|
||||
else:
|
||||
if len(sys.argv) == 4:
|
||||
if sys.argv[1] == '-l':
|
||||
filename, lang = sys.argv[3], sys.argv[2]
|
||||
sys.stderr.write('Usage: python pytesseract.py [-l lang] input_file\n')
|
||||
exit(2)
|
||||
try:
|
||||
print(image_to_string((Image.open(filename)), lang=lang))
|
||||
except IOError:
|
||||
sys.stderr.write('ERROR: Could not open file "%s"\n' % filename)
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
69
src/launcher/CNIRLauncher.py
Normal file
69
src/launcher/CNIRLauncher.py
Normal file
@ -0,0 +1,69 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher & updater main file *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import updater # updater.py
|
||||
import ihm # ihm.py
|
||||
import globs # globs.py
|
||||
import logger # logger.py
|
||||
|
||||
## Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
## Main function
|
||||
def main():
|
||||
|
||||
# Starting the main update thread
|
||||
mainThread.start()
|
||||
|
||||
# Hello world
|
||||
logfile.printdbg('*** CNIRLauncher LOGFILE. Hello World ! ***')
|
||||
#logfile.printdbg('Files in directory : ' + str(os.listdir(globs.CNIRFolder)))
|
||||
|
||||
# Hello user
|
||||
launcherWindow.progressBar.configure(mode='indeterminate', value=0, maximum=20)
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text='Starting...')
|
||||
launcherWindow.progressBar.start()
|
||||
|
||||
launcherWindow.mainloop()
|
||||
|
||||
logfile.printdbg('*** CNIRLauncher LOGFILE. Goodbye World ! ***')
|
||||
return
|
||||
|
||||
## Bootstrap
|
||||
try:
|
||||
mainThread = threading.Thread(target=updater.umain, daemon=False)
|
||||
main()
|
||||
except Exception:
|
||||
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
33
src/launcher/downloader.py
Normal file
33
src/launcher/downloader.py
Normal file
@ -0,0 +1,33 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher download stuff *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
from pypac import PACSession
|
||||
from requests.auth import HTTPProxyAuth
|
||||
|
||||
class newdownload():
|
||||
def __init__(url):
|
||||
self.url = url
|
||||
|
30
src/launcher/globs.py
Normal file
30
src/launcher/globs.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher global variables *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
import os
|
||||
|
||||
CNIRTesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8'
|
||||
CNIRFolder = os.getcwd()
|
||||
CNIRLColor = "#006699"
|
||||
CNIRName = "CNIRevelator Launcher 3"
|
BIN
src/launcher/id-card.ico
Normal file
BIN
src/launcher/id-card.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
119
src/launcher/ihm.py
Normal file
119
src/launcher/ihm.py
Normal file
@ -0,0 +1,119 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher graphical interface *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from tkinter import *
|
||||
from tkinter.messagebox import *
|
||||
from tkinter import filedialog
|
||||
from tkinter import ttk
|
||||
|
||||
import logger # logger.py
|
||||
import globs # globs.py
|
||||
|
||||
class LoginDialog(Toplevel):
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.title('Connexion')
|
||||
Label(self, text='IPN : ').pack()
|
||||
self.entry_login = Entry(self)
|
||||
self.entry_login.insert(0, '')
|
||||
self.entry_login.pack()
|
||||
Label(self, text='Mot de passe : ').pack()
|
||||
self.entry_pass = Entry(self, show='*')
|
||||
self.entry_pass.insert(0, '')
|
||||
self.entry_pass.pack()
|
||||
Button(self, text='Connexion', command=(self.connecti)).pack()
|
||||
self.resizable(width=False, height=False)
|
||||
w = 150
|
||||
h = 110
|
||||
self.update()
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
if getattr(sys, 'frozen', False):
|
||||
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
||||
else:
|
||||
self.iconbitmap('id-card.ico')
|
||||
upwin.update()
|
||||
x = ws / 2 - w / 2
|
||||
y = hs / 2 - h / 2
|
||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
|
||||
def logingin(self):
|
||||
global key
|
||||
global login
|
||||
login = self.entry_login.get().strip()
|
||||
key = self.entry_pass.get().strip()
|
||||
self.destroy()
|
||||
|
||||
class LauncherWindow(Tk):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the tkinter main class
|
||||
Tk.__init__(self)
|
||||
self.configure(bg=globs.CNIRLColor)
|
||||
self.resizable(width=False, height=False)
|
||||
self.queue = []
|
||||
|
||||
# Setting up the geometry
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
wheight = hs /4
|
||||
wwidth = ws /4
|
||||
#self.update()
|
||||
|
||||
# Creating objects
|
||||
self.mainCanvas = Canvas(self, width=wwidth, height=wheight*9/10, bg=globs.CNIRLColor, highlightthickness=0)
|
||||
self.pBarZone = Canvas(self, width=wwidth, height=wheight/10, bg=globs.CNIRLColor)
|
||||
|
||||
self.progressBar = ttk.Progressbar(self.pBarZone, orient=HORIZONTAL, length=wwidth-10, mode='determinate')
|
||||
#self.update()
|
||||
|
||||
self.mainCanvas.create_text((wwidth / 2), (wheight / 3), text=(globs.CNIRName), font='Helvetica 30', fill='white')
|
||||
self.msg = self.mainCanvas.create_text((wwidth / 2.05), (wheight / 1.20), text='', font='Helvetica 9', fill='white')
|
||||
#self.update()
|
||||
|
||||
self.wm_title(globs.CNIRName)
|
||||
|
||||
# Centering
|
||||
x = ws / 2 - wwidth / 2
|
||||
y = hs / 2 - wheight / 2
|
||||
self.geometry('%dx%d+%d+%d' % (wwidth, wheight, x, y))
|
||||
self.mainCanvas.grid()
|
||||
self.pBarZone.grid()
|
||||
self.progressBar.grid()
|
||||
|
||||
#self.after(2000, updating)
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
||||
else:
|
||||
self.iconbitmap('id-card.ico')
|
||||
logfile = logger.logCur
|
||||
logfile.printdbg('Launcher IHM successful')
|
||||
self.protocol('WM_DELETE_WINDOW', lambda : self.destroy())
|
||||
|
||||
## Global Handler
|
||||
launcherWindowCur = LauncherWindow()
|
||||
|
64
src/launcher/logger.py
Normal file
64
src/launcher/logger.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher logging stuff *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
import logging
|
||||
import globs
|
||||
import os
|
||||
|
||||
## The logging class
|
||||
class NewLoggingSystem:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Deleting the error log
|
||||
try:
|
||||
os.remove(globs.CNIRFolder + '\\error.log') # The deletion does not working
|
||||
except Exception as e:
|
||||
#print(str(e) + " : " + str(globs.CNIRFolder + '\\error.log'))
|
||||
pass
|
||||
|
||||
# Create new logging handle
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO) # To make sure we can have a debug channel
|
||||
|
||||
# Create channels
|
||||
formatter = logging.Formatter("\n[ %(module)s/%(funcName)s ] %(asctime)s :: %(levelname)s :: %(message)s")
|
||||
error_handler = logging.FileHandler((globs.CNIRFolder + '\\error.log'), mode='w', encoding='utf-8', delay=True)
|
||||
info_handler = logging.FileHandler((globs.CNIRFolder + '\\launcher.log'), mode='w', encoding='utf-8')
|
||||
|
||||
error_handler.setLevel(logging.ERROR)
|
||||
error_handler.setFormatter(formatter)
|
||||
logger.addHandler(error_handler)
|
||||
|
||||
info_handler.setLevel(logging.DEBUG)
|
||||
info_handler.setFormatter(formatter)
|
||||
logger.addHandler(info_handler)
|
||||
|
||||
self.logger = logger
|
||||
self.printerr = logger.error
|
||||
self.printdbg = logger.info
|
||||
|
||||
## Global Handler
|
||||
logCur = NewLoggingSystem()
|
91
src/launcher/updater.py
Normal file
91
src/launcher/updater.py
Normal file
@ -0,0 +1,91 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher updating system *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from win32com.client import Dispatch
|
||||
import traceback
|
||||
import sys
|
||||
import time
|
||||
|
||||
import logger # logger.py
|
||||
import globs # globs.py
|
||||
import ihm # ihm.py
|
||||
|
||||
def createShortcut(path, target='', wDir='', icon=''):
|
||||
ext = path[-3:]
|
||||
if ext == 'url':
|
||||
shortcut = file(path, 'w')
|
||||
shortcut.write('[InternetShortcut]\n')
|
||||
shortcut.write('URL=%s' % target)
|
||||
shortcut.close()
|
||||
else:
|
||||
shell = Dispatch('WScript.Shell')
|
||||
shortcut = shell.CreateShortCut(path)
|
||||
shortcut.Targetpath = target
|
||||
shortcut.WorkingDirectory = wDir
|
||||
if icon == '':
|
||||
pass
|
||||
else:
|
||||
shortcut.IconLocation = icon
|
||||
shortcut.save()
|
||||
|
||||
## Main Batch Function
|
||||
def batch():
|
||||
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
for i in range(0,10000):
|
||||
if i % 1000 : launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text=('Starting... ' + str(i)))
|
||||
return
|
||||
|
||||
## Main Function
|
||||
def umain():
|
||||
try:
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
try:
|
||||
batch()
|
||||
except Exception as e:
|
||||
logfile.printerr("An error occured on the thread : " + str(traceback.format_exc()))
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text=('ERROR : ' + str(e)))
|
||||
time.sleep(3)
|
||||
launcherWindow.destroy()
|
||||
return 1
|
||||
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text='Software is up-to-date !')
|
||||
time.sleep(2)
|
||||
launcherWindow.destroy()
|
||||
return 0
|
||||
|
||||
except:
|
||||
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
||||
launcherWindow.destroy()
|
||||
sys.exit(2)
|
||||
return 2
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user