1
0
mirror of https://gitlab.os-k.eu/neox/CNIRevelator.git synced 2023-08-25 14:03:10 +02:00
This commit is contained in:
Adrien Bourmault 2019-06-30 17:54:41 +02:00
parent b3587b770c
commit 6c25739051
6 changed files with 1511 additions and 1948 deletions

View File

@ -10,41 +10,38 @@
******************************************************************************** ********************************************************************************
""" """
CST_REV = "8"
CST_VERTITLE = "2.2" class changelog:
CST_TAB_VER = ["2","2","1"]
CST_VER = "{0}.{1}.{2}".format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2]) def __init__(self):
CST_TYPE = "Final Release" self.isOn = False
CST_NAME = "CNIRevelator" 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_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 CST_REV = '0'
import hashlib 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 import Random
from Crypto.Cipher import AES from Crypto.Cipher import AES
from tkinter import * from tkinter import *
from tkinter.messagebox import * from tkinter.messagebox import *
from tkinter import filedialog from tkinter import filedialog
from tkinter import ttk as ttk from tkinter import ttk
import os import os, time, threading, sys, urllib.request as urllib2, urllib.error as URLExcept, random
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 datetime import datetime
from PIL import Image, ImageFont, ImageDraw, ImageTk, ImageEnhance, ImageFilter
import math
import warnings
import string
CST_FOLDER = os.getenv('APPDATA') + "/CNIRevelator/" CST_FOLDER = os.getenv('APPDATA') + '/CNIRevelator/'
CST_CRYPTOKEY = '82Xh!efX3#@P~2eG'
CST_CHANGELOG = changelog()

View File

@ -10,122 +10,97 @@
******************************************************************************** ********************************************************************************
""" """
from CNI_GLOBALVAR import *
###IMPORTS GLOBAUX
from CNI_GLOBALVAR import *
##LOGGING
try: try:
os.remove("error.log") os.remove('error.log')
os.remove("conf.ig") os.remove('conf.ig')
except: except:
print("pass log deletion")
pass pass
import logging
CST_NIVEAU_LOG = logging.ERROR if not os.path.exists(CST_FOLDER):
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: try:
os.makedirs(CST_FOLDER)
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\" except IOError:
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata" print("pass IO ERROR")
tesser_version = pytesseract.get_tesseract_version() pass
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: except Exception as e:
logger.error("main() : " +"**** ERROR WITH TESSERACT MODULE " + str(e) + " ****") logger.info('launcher : *****FATAL ERROR*****' + 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() os.abort()
sys.exit(0) 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)

View File

@ -10,559 +10,484 @@
******************************************************************************** ********************************************************************************
""" """
from CNI_GLOBALVAR import *
###IMPORTS GLOBAUX
from CNI_GLOBALVAR import *
###IMPORTS LOCAUX
from CNI_classes import * from CNI_classes import *
import hashlib
from pypac import PACSession
from requests.auth import HTTPProxyAuth
import subprocess
def SoftUpdate(logger):
###BEGIN global ret
global upwin
def SoftUpdate(logger): #Fonction de mise à jour de l'application
import zipfile import zipfile
# zip_ref = zipfile.ZipFile(path_to_zip_file, 'r') for f in os.listdir(CST_FOLDER):
# zip_ref.extractall(directory_to_extract_to) if f[-4:] == '.tif':
# zip_ref.close() try:
os.remove(CST_FOLDER + '\\' + f)
logger.info("SoftUpdate() : " +"Verifying local data dir...") except PermissionError as e:
if not os.path.exists(CST_FOLDER): logger.info('SoftUpdate() : Failing to purge : ' + str(e))
os.makedirs(CST_FOLDER)
logger.info("SoftUpdate() : " +"Data dir created !") logger.info('SoftUpdate() : Looking for older version in dir...')
logger.info("SoftUpdate() : " +"Looking for older version in dir...")
list = os.listdir('.') list = os.listdir('.')
for file in list: for file in list:
if file.startswith('CNIRevelator_'):
if file.startswith("CNIRevelator_"): temp = ['0', '0', '0']
ver = file[13:].split('.')
temp = ["0","0","0"]
ver = file[13:].split(".")
for i in range(len(ver)): for i in range(len(ver)):
if ver[i] != 'exe':
if ver[i] != "exe" :
try: try:
temp[i] = ver[i] temp[i] = ver[i]
except: except:
None pass
ver = temp.copy() ver = temp.copy()
try:
try : sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2])
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
if sum_ver < CST_SUM_VER: if sum_ver < CST_SUM_VER:
if file[-3:] == 'exe':
os.remove(file) os.remove(file)
logger.info("SoftUpdate() : " + "Removed old version : " + str(file) ) logger.info('SoftUpdate() : Removed old version : ' + str(file))
CST_CHANGELOG.isOn = True
except: except Exception as e:
logger.error('SoftUpdate() : Failing to remove old version ' + str(file) + ' : ' + str(e))
logger.error("SoftUpdate() : " +"Failing to remove old version : " + str(file) )
def updating():
None
def updator():
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 global ret
logger.info('[updator() thread] : Welcome !')
ret = 11 ret = 11
canvas.itemconfigure(message, text="Recherche de mises-à-jour...") canvas.itemconfigure(message, text='Recherche de mises-à-jour...')
p.configure(mode = "indeterminate", value = 0, maximum = 20) p.configure(mode='indeterminate', value=0, maximum=20)
p.start() p.start()
upwin.update() upwin.update()
logger.info("[updator() thread] : " + "Looking for updates...") logger.info('[updator() thread] : Looking for updates...')
try:
try:#Essai par proxy
def download(url, filename): def download(url, filename):
global key
try: # if identifiants disponibles dans un fichier global login
try:
logger.info("[download() thread] : " + "Trying getting credentials in the config file") logger.info('[download() thread] : Trying getting credentials in the config file')
with open(CST_FOLDER + 'conf.ig', 'rb') as (config):
with open(CST_FOLDER +'conf.ig', "rb") as config: AESObj = AESCipher(CST_CRYPTOKEY)
try:
AESObj = AESCipher("<clé>") tempone = AESObj.decrypt(config.read())
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2] if tempone != '||':
logger.info("[download() thread] : " + "Got credentials !") if tempone.find('||') != -1:
IPN, IPN_PASS = tempone.split('||')[0:2]
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS)) else:
logger.info("[download() thread] : " + "Authenticated to proxy successfully") raise ValueError('Cryptokey is bad !')
else:
IPN = ''
except IOError as e: #Else : on vérifie les identifiants de proxy IPN_PASS = ''
except Exception as e:
logger.error("[download() thread] : " + "False or absent credentials in the config file : " + str(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 NoConnect = True
while NoConnect: while NoConnect:
class LoginDialog(Toplevel): class LoginDialog(Toplevel):
global login
global key
def __init__(self, parent): def __init__(self, parent):
super().__init__(parent) super().__init__(parent)
self.title("Connexion") self.title('Connexion')
# --------------------------------------- Label(self, text='IPN : ').pack()
Label(self, text="IPN : ").pack()
self.entry_login = Entry(self) self.entry_login = Entry(self)
self.entry_login.insert(0, "") self.entry_login.insert(0, '')
self.entry_login.pack() self.entry_login.pack()
# --------------------------------------- Label(self, text='Mot de passe : ').pack()
Label(self, text="Mot de passe : ").pack()
self.entry_pass = Entry(self, show='*') self.entry_pass = Entry(self, show='*')
self.entry_pass.insert(0, "") self.entry_pass.insert(0, '')
self.entry_pass.pack() self.entry_pass.pack()
# --------------------------------------- Button(self, text='Connexion', command=(self.connecti)).pack()
Button(self, text="Connexion", command=self.connecti).pack()
self.resizable(width=False, height=False) self.resizable(width=False, height=False)
#taille souhaite de la fenetre
w = 150 w = 150
h = 110 h = 110
#pour centrer la fenetre self.update()
#taille de l'ecran
self.update()
ws = self.winfo_screenwidth() ws = self.winfo_screenwidth()
hs = self.winfo_screenheight() hs = self.winfo_screenheight()
if getattr(sys, 'frozen', False):
if getattr( sys, 'frozen', False ) : self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
self.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
else: else:
self.iconbitmap("id-card.ico") self.iconbitmap('id-card.ico')
upwin.update()
upwin.update() x = ws / 2 - w / 2
#calcul la position de la fenetre y = hs / 2 - h / 2
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)) self.geometry('%dx%d+%d+%d' % (w, h, x, y))
def connecti(self): def connecti(self):
global login
global key global key
global login
login = self.entry_login.get().strip() login = self.entry_login.get().strip()
key = self.entry_pass.get().strip() key = self.entry_pass.get().strip()
self.destroy() self.destroy()
session = PACSession() session = PACSession()
if session.get_pac() == None: if session.get_pac() == None:
IPN = "" IPN = ''
IPN_PASS = "" IPN_PASS = ''
NoConnect = False NoConnect = False
break break
canvas.itemconfigure(message, text='En attente de connexion au serveur proxy...')
canvas.itemconfigure(message, text="En attente de connexion au serveur proxy...") login = ''
global login key = ''
global key
login = ""
key = ""
result = LoginDialog(upwin) result = LoginDialog(upwin)
result.transient(upwin) result.transient(upwin)
result.grab_set() result.grab_set()
upwin.wait_window(result) upwin.wait_window(result)
IPN = login IPN = login
IPN_PASS = key IPN_PASS = key
session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS)))
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS)) Ans = session.get('http://www.google.com')
Ans = session.get("http://www.google.com") if str(Ans) == '<Response [407]>':
canvas.itemconfigure(message, text='Identifiants erronés, accès refusé')
if str(Ans) == "<Response [407]>": logger.info('[download() thread] : 407 Error')
canvas.itemconfigure(message, text="Identifiants erronés, accès refusé")
logger.info("[download() thread] : " + "407 Error")
time.sleep(1) time.sleep(1)
elif str(Ans) == "<Response [200]>":
logger.info("[download() thread] : " + "Connection ok !")
NoConnect = False
else: else:
raise(IOError()) if str(Ans) == '<Response [200]>':
logger.info('[download() thread] : Connection ok !')
AESObj = AESCipher("<clé>") NoConnect = False
else:
with open(CST_FOLDER +"conf.ig","wb+") as f: raise IOError()
logger.info("[download() thread] : " + "Saving credentials in encrypted config file")
f.write(AESObj.encrypt(IPN + "||" + IPN_PASS)) 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 !") if IPN == 'i005316':
elif IPN == "i020251": canvas.itemconfigure(message, text='Bienvenue Thierry !')
canvas.itemconfigure(message, text="Bienvenue Samia !") else:
elif IPN == "i018410": if IPN == 'i020251':
canvas.itemconfigure(message, text="Bienvenue Adrien !") canvas.itemconfigure(message, text='Bienvenue Samia !')
elif IPN == "i003067": else:
canvas.itemconfigure(message, text="Bienvenue Remy !") if IPN == 'i018410':
elif IPN == "i018422": canvas.itemconfigure(message, text='Bienvenue Adrien !')
canvas.itemconfigure(message, text="Bienvenue Eloise !") else:
if IPN == 'i003067':
canvas.itemconfigure(message, text='Bienvenue Remy !')
else:
if IPN == 'i018422':
canvas.itemconfigure(message, text='Bienvenue Eloise !')
time.sleep(1) time.sleep(1)
try: try:
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu") Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu')
PROXY_USABLE = Prox_us[6:-1].split(";")[0] PROXY_USABLE = Prox_us[6:-1].split(';')[0]
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } ) ph = urllib2.ProxyHandler({'http': proxy_server_url})
auth = urllib2.ProxyBasicAuthHandler() auth = urllib2.ProxyBasicAuthHandler()
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler ) server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler)
urllib2.install_opener(server) urllib2.install_opener(server)
logger.info("[download() thread] : " + "Proxy connection initiated successfully") logger.info('[download() thread] : Proxy connection initiated successfully')
except: except:
logger.info('[download() thread] : Proxy connection not initiated')
logger.info("[download() thread] : " + "Proxy connection not initiated")
try: try:
urllib2.urlretrieve(url, filename) urllib2.urlretrieve(url, filename)
return True return True
except Exception as e: except Exception as e:
logger.error("[download() thread] : " + "HTTP ERROR " ) logger.error('[download() thread] : HTTP ERROR ')
return e return e
##updator() logger.info('[updator() thread] : Prepare downloading the version recap file...')
logger.info("[updator() thread] : " + "Prepare downloading the version recap file...") tempfile = CST_FOLDER + 'temp' + str(random.randint(11111, 99999)) + '.cniu'
tempfile = CST_FOLDER + 'temp' + str(random.randint(11111,99999)) + ".cniu" isOk = download(CST_LINK + 'cnir.ver', tempfile)
isOk = download(CST_LINK+"Version.txt", tempfile)
if not isOk: if not isOk:
raise(isOk) raise isOk
urllib2.urlcleanup() urllib2.urlcleanup()
logger.info("[updator() thread] : " + "Opening version recap file...") logger.info('[updator() thread] : Opening version recap file...')
file_ver = open(tempfile, "r") file_ver = open(tempfile, 'r')
logger.info("[updator() thread] : " + "Reading version recap file...") logger.info('[updator() thread] : Reading version recap file...')
version = file_ver.read() version = file_ver.read()
logger.info("[updator() thread] : " + "Closing version recap file...") logger.info('[updator() thread] : Closing version recap file...')
repert = version.split("|") repert = version.split('|')
file_ver.close() file_ver.close()
logger.info("[updator() thread] : " + "Deleting version recap file...") logger.info('[updator() thread] : Deleting version recap file...')
os.remove(tempfile) os.remove(tempfile)
logger.info("[updator() thread] : " + "Parsing informations about version...") logger.info('[updator() thread] : Parsing informations about version...')
final_f = "CNI_file" final_f = 'CNI_file'
final_ver = ['0','0','0'] final_ver = ['0', '0', '0']
final_hash = ''
for file in repert: for sentence 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: try:
file, hashref = sentence.split(':')
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu") except ValueError:
PROXY_USABLE = Prox_us[6:-1].split(";")[0] pass
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE else:
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } ) if str.startswith(file, CST_NAME):
auth = urllib2.ProxyBasicAuthHandler() ver = file.replace(CST_NAME + '_', '').split('.')
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler ) temp = [
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !") '0', '0', '0']
for i in range(len(ver)):
temp[i] = ver[i]
except:
ver = temp.copy()
canvas.itemconfigure(message, text="Téléchargement en connexion directe...") sum_fver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2])
server= urllib2.build_opener() sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2])
logger.info("[updator() thread] : " + "Direct connection initiated successfully") if sum_ver > sum_fver:
final_ver = ver.copy()
logger.info("[updator() thread] : " + "Launching download of " + final_f) final_f = file
Statut = Download( CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger ) final_hash = hashref
if Statut.success: 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: try:
os.rename(final_f, final_f + ".exe") Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu')
except IOError: PROXY_USABLE = Prox_us[6:-1].split(';')[0]
logger.error("[updator() thread] : " + "Unable to rename the file ! Wait 3 sec and retry") proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE
time.sleep(3) 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: try:
os.rename(final_f, final_f + ".exe") os.rename(final_f, final_f + '.exe')
except IOError: except IOError:
logger.critical("[updator() thread] : " + "Unable to rename the file !") logger.error('[updator() thread] : Unable to rename the file ! Wait 3 sec and retry')
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) time.sleep(3)
try: try:
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True) os.rename(final_f, final_f + '.exe')
except Exception as e: except IOError:
logger.critical("[updator() thread] : " + "Unable to start the new version ! Stopping : " + str(e)) logger.critical('[updator() thread] : Unable to rename the file !')
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:
else: canvas.itemconfigure(message, text='Téléchargement terminé ! Préparation du lancement...')
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP. Préparation du lancement...") logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully')
logger.error("[updator() thread] : " + "Update has failed with HTTP error") p.configure(mode='indeterminate', value=0, maximum=20)
time.sleep(1) p.start()
time.sleep(1)
logger.info('[updator() thread] : Launching ' + final_f)
try:
else: proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True)
canvas.itemconfigure(message, text="Logiciel déjà à jour. Préparation du lancement...") except:
logger.info("[updator() thread] : " + "CNIRevelator is up to date !") 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) time.sleep(1)
ret = 11 ret = 11
if os.path.exists(CST_FOLDER + 'Tesseract-OCR4\\tesseract.exe'):
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
#Tesseract os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
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: else:
final_f = 'tesseract_4'
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...')
#On a une maj logger.info('[updator() thread] : Preparing download')
logger.info("[updator() thread] : " + "Downloading tesseract 4 !") with open(CST_FOLDER + 'conf.ig', 'rb') as (config):
canvas.itemconfigure(message, text="Mise à jour du module OCR ! Préparation du téléchargement...") logger.info('[updator() thread] : Reading credentials for proxy in config file...')
logger.info("[updator() thread] : " + "Preparing download") AESObj = AESCipher(CST_CRYPTOKEY)
IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2]
with open(CST_FOLDER +'conf.ig', "rb") as config: session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS)))
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: try:
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu')
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu") PROXY_USABLE = Prox_us[6:-1].split(';')[0]
PROXY_USABLE = Prox_us[6:-1].split(";")[0] proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE ph = urllib2.ProxyHandler({'http': proxy_server_url})
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
auth = urllib2.ProxyBasicAuthHandler() auth = urllib2.ProxyBasicAuthHandler()
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler ) server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler)
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !") logger.info('[updator() thread] : Connection to the proxy initiated successfully !')
except: except:
canvas.itemconfigure(message, text='Téléchargement en connexion directe...')
canvas.itemconfigure(message, text="Téléchargement en connexion directe...") server = urllib2.build_opener()
server= urllib2.build_opener() logger.info('[updator() thread] : Direct connection initiated successfully')
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
logger.info('[updator() thread] : Launching download of ' + final_f)
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 = ''
Statut = Download( CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger ) 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: if Statut.success:
canvas.itemconfigure(message, text='Téléchargement terminé ! Installation...')
canvas.itemconfigure(message, text="Téléchargement terminé ! Installation...") logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully')
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully") p.configure(mode='indeterminate', value=0, maximum=20)
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start() p.start()
try: try:
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r') zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
zip_ref.extractall(CST_FOLDER) zip_ref.extractall(CST_FOLDER)
zip_ref.close() zip_ref.close()
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\" os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata" os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
canvas.itemconfigure(message, text="Installation terminée !") canvas.itemconfigure(message, text='Installation terminée !')
except: except:
logger.error("[updator() thread] : " + "Unable to install the module. Wait and retry") logger.error('[updator() thread] : Unable to install the module. Wait and retry')
time.sleep(3) time.sleep(3)
try: try:
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r') zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
zip_ref.extractall(CST_FOLDER) zip_ref.extractall(CST_FOLDER)
zip_ref.close() zip_ref.close()
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\" os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata" os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
canvas.itemconfigure(message, text="Installation terminée !") canvas.itemconfigure(message, text='Installation terminée !')
except Exception as e: except Exception as e:
logger.critical("[updator() thread] : " + "Unable to install the module ! Stopping : " + str(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.") showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.")
ret = 11 ret = 11
else: else:
logger.critical('[updator() thread] : Unable to download the module ! ')
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")
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')))
path = filedialog.askopenfilename(title = "Indiquez le chemin d'accès à tesseract_4...",filetypes = (("Tesseract_4","*.cni4"), ("Tesseract_4","*.cni4"))) if path != '':
if path != "":
try: try:
canvas.itemconfigure(message, text="Installation...") canvas.itemconfigure(message, text='Installation...')
zip_ref = zipfile.ZipFile(path, 'r') zip_ref = zipfile.ZipFile(path, 'r')
zip_ref.extractall(CST_FOLDER) zip_ref.extractall(CST_FOLDER)
zip_ref.close() zip_ref.close()
logger.error("[updator() thread] : " + "Manual installation successed") logger.error('[updator() thread] : Manual installation successed')
canvas.itemconfigure(message, text="Installation terminée !") canvas.itemconfigure(message, text='Installation terminée !')
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\" os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata" os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
except Exception as e: except Exception as e:
logger.error('[updator() thread] : Manual installation has failed' + str(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")
showerror("Erreur de lecture", "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner")
else: else:
showerror('Opération annulée', "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner")
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: except URLExcept.HTTPError as e:
canvas.itemconfigure(message, text=('Echec de la mise à jour : Erreur HTTP ' + str(e.code) + ' . Préparation du lancement...'))
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))
logger.error("[updator() thread] : " + "Update has failed with HTTP error" + str(e.code) )
if int(e.code) == 407: 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é.") 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...") logger.info('[updator() thread] : Credential error. Deleting the config file...')
os.remove(CST_FOLDER +"conf.ig") os.remove(CST_FOLDER + 'conf.ig')
p.configure(mode = "indeterminate", value = 0, maximum = 20) p.configure(mode='indeterminate', value=0, maximum=20)
p.start() p.start()
time.sleep(3) time.sleep(3)
except Exception as e: except Exception as e:
canvas.itemconfigure(message, text='Echec de la mise à jour. Préparation du lancement...')
canvas.itemconfigure(message, text="Echec de la mise à jour. Préparation du lancement...") logger.error('[updator() thread] : Error from the updating system : ' + str(e))
logger.error("[updator() thread] : " + "Error from the updating system : " + str(e)) p.configure(mode='indeterminate', value=0, maximum=20)
p.configure(mode = "indeterminate", value = 0, maximum = 20)
p.start() p.start()
time.sleep(2) time.sleep(2)
p.stop() p.stop()
upwin.destroy() upwin.destroy()
root.destroy() root.destroy()
return ret 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() logger.info('updating() : Launching updator() thread...')
threading.Thread(target=updator, daemon=True).start()
global ret logger.info('updating() [Thread] : Ending updator() thread')
ret = 11 ret = 11
global upwin
root = Tk() root = Tk()
root.attributes('-alpha', 0.0) #For icon root.attributes('-alpha', 0.0)
#root.lower()
root.iconify() root.iconify()
upwin = Toplevel(root) upwin = Toplevel(root)
upwin.overrideredirect(1) upwin.overrideredirect(1)
upwin.configure(bg = CST_COLOR) upwin.configure(bg=CST_COLOR)
upwin.resizable(width=False, height=False) upwin.resizable(width=False, height=False)
#taille souhaite de la fenetre
w = 600 w = 600
h = 300 h = 300
#pour centrer la fenetre upwin.update()
#taille de l'ecran
upwin.update()
canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0) canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0)
pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR) pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR)
p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate') p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate')
upwin.update() upwin.update()
ws = upwin.winfo_screenwidth() ws = upwin.winfo_screenwidth()
hs = upwin.winfo_screenheight() hs = upwin.winfo_screenheight()
canvas.create_text(w/2, h/3, text=CST_NAME + " " + CST_VERTITLE, font="Calibri 30 bold", fill="white") 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") message = canvas.create_text((w / 2), (h / 1.15), text=' ', font='Calibri 9', fill='white')
upwin.update() upwin.update()
#calcul la position de la fenetre x = ws / 2 - w / 2
x = (ws/2) - (w/2) y = hs / 2 - h / 2
y = (hs/2) - (h/2)
#applique la taille et la position
upwin.geometry('%dx%d+%d+%d' % (w, h, x, y)) upwin.geometry('%dx%d+%d+%d' % (w, h, x, y))
canvas.grid() canvas.grid()
pbar.grid() pbar.grid()
p.grid() p.grid()
upwin.after(2000, updating) upwin.after(2000, updating)
if getattr(sys, 'frozen', False):
if getattr( sys, 'frozen', False ) : root.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
root.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
else: else:
root.iconbitmap("id-card.ico") root.iconbitmap('id-card.ico')
logger.info('SoftUpdate() : Entering upwin mainloop()')
# On démarre la boucle Tkinter qui s'interrompt quand on ferme la fenêtre upwin.protocol('WM_DELETE_WINDOW', lambda : root.destroy())
logger.info("SoftUpdate() : " + "Entering upwin mainloop()")
upwin.protocol('WM_DELETE_WINDOW', lambda: root.destroy())
upwin.mainloop() upwin.mainloop()
logger.info("SoftUpdate() : " + "Exiting upwin mainloop()") logger.info('SoftUpdate() : Exiting upwin mainloop()')
if ret == 11: if ret == 11:
logger.info("SoftUpdate() : " + "OK to start to main() normally !") logger.info('SoftUpdate() : OK to start to main() normally !')
return True return True
else: else:
logger.info("SoftUpdate() : " + "Program will stop !") logger.info('SoftUpdate() : Program will stop !')
return False return False

File diff suppressed because one or more lines are too long

View File

@ -10,52 +10,42 @@
***** *****
""" """
#!/usr/bin/env python
'''
Python-tesseract. For more information: https://github.com/madmaze/pytesseract
'''
try: try:
import Image import Image
except ImportError: except ImportError:
from PIL import Image from PIL import Image
import os import os, sys, subprocess, tempfile, shlex, string
import sys
import subprocess
import tempfile
import shlex
import string
from glob import iglob from glob import iglob
from pkgutil import find_loader from pkgutil import find_loader
from distutils.version import LooseVersion from distutils.version import LooseVersion
from os.path import realpath, normpath, normcase from os.path import realpath, normpath, normcase
numpy_installed = find_loader('numpy') is not None numpy_installed = find_loader('numpy') is not None
if numpy_installed: if numpy_installed:
from numpy import ndarray from numpy import ndarray
# CHANGE THIS IF TESSERACT IS NOT IN YOUR PATH, OR IS NAMED DIFFERENTLY
tesseract_cmd = 'tesseract' tesseract_cmd = 'tesseract'
RGB_MODE = 'RGB' RGB_MODE = 'RGB'
OSD_KEYS = { OSD_KEYS = {'Page number':(
'Page number': ('page_num', int), 'page_num', int),
'Orientation in degrees': ('orientation', int), 'Orientation in degrees':(
'Rotate': ('rotate', int), 'orientation', int),
'Orientation confidence': ('orientation_conf', float), 'Rotate':(
'Script': ('script', str), 'rotate', int),
'Script confidence': ('script_conf', float) 'Orientation confidence':(
} 'orientation_conf', float),
'Script':(
'script', str),
'Script confidence':(
'script_conf', float)}
class Output: class Output:
STRING = "string" STRING = 'string'
BYTES = "bytes" BYTES = 'bytes'
DICT = "dict" DICT = 'dict'
class TesseractError(RuntimeError): class TesseractError(RuntimeError):
def __init__(self, status, message): def __init__(self, status, message):
self.status = status self.status = status
self.message = message self.message = message
@ -63,20 +53,19 @@ class TesseractError(RuntimeError):
class TesseractNotFoundError(EnvironmentError): class TesseractNotFoundError(EnvironmentError):
def __init__(self): def __init__(self):
super(TesseractNotFoundError, self).__init__( super(TesseractNotFoundError, self).__init__(tesseract_cmd + " is not installed or it's not in your path")
tesseract_cmd + " is not installed or it's not in your path"
)
class TSVNotSupported(EnvironmentError): class TSVNotSupported(EnvironmentError):
def __init__(self): def __init__(self):
super(TSVNotSupported, self).__init__( super(TSVNotSupported, self).__init__('TSV output not supported. Tesseract >= 3.05 required')
'TSV output not supported. Tesseract >= 3.05 required'
)
def run_once(func): def run_once(func):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if wrapper._result is wrapper: if wrapper._result is wrapper:
wrapper._result = func(*args, **kwargs) wrapper._result = func(*args, **kwargs)
@ -87,13 +76,11 @@ def run_once(func):
def get_errors(error_string): def get_errors(error_string):
return u' '.join( return ' '.join(line for line in error_string.decode('utf-8').splitlines()).strip()
line for line in error_string.decode('utf-8').splitlines()
).strip()
def cleanup(temp_name): def cleanup(temp_name):
''' Tries to remove files by filename wildcard path. ''' """ Tries to remove files by filename wildcard path. """
for filename in iglob(temp_name + '*' if temp_name else temp_name): for filename in iglob(temp_name + '*' if temp_name else temp_name):
try: try:
os.remove(filename) os.remove(filename)
@ -104,138 +91,88 @@ def cleanup(temp_name):
def prepare(image): def prepare(image):
if isinstance(image, Image.Image): if isinstance(image, Image.Image):
return image return image
if numpy_installed:
if numpy_installed and isinstance(image, ndarray): if isinstance(image, ndarray):
pass
return Image.fromarray(image) return Image.fromarray(image)
raise TypeError('Unsupported image object') raise TypeError('Unsupported image object')
def save_image(image): def save_image(image):
temp_name = tempfile.mktemp(prefix='tess_') temp_name = tempfile.mktemp(prefix='tess_')
if isinstance(image, str): if isinstance(image, str):
return temp_name, realpath(normpath(normcase(image))) return (temp_name, realpath(normpath(normcase(image))))
else:
image = prepare(image) image = prepare(image)
img_extension = image.format img_extension = image.format
if image.format not in {'JPEG', 'PNG', 'TIFF', 'BMP', 'GIF'}: if image.format not in frozenset({'BMP', 'JPEG', 'GIF', 'TIFF', 'PNG'}):
img_extension = 'PNG' img_extension = 'PNG'
if not image.mode.startswith(RGB_MODE):
if not image.mode.startswith(RGB_MODE): image = image.convert(RGB_MODE)
image = image.convert(RGB_MODE) if 'A' in image.getbands():
background = Image.new(RGB_MODE, image.size, (255, 255, 255))
if 'A' in image.getbands(): background.paste(image, (0, 0), image)
# discard and replace the alpha channel with white background image = background
background = Image.new(RGB_MODE, image.size, (255, 255, 255)) input_file_name = temp_name + os.extsep + img_extension
background.paste(image, (0, 0), image) (image.save)(input_file_name, format=img_extension, **image.info)
image = background return (
temp_name, input_file_name)
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): def subprocess_args(include_stdout=True):
# The following is true only on Windows.
if hasattr(subprocess, 'STARTUPINFO'): 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 = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# Windows doesn't search the path by default. Pass it an environment so
# it will.
env = os.environ env = os.environ
else: else:
si = None si = None
env = 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: if include_stdout:
ret = {'stdout': subprocess.PIPE} ret = {'stdout': subprocess.PIPE}
else: else:
ret = {} ret = {}
ret.update({'stdin':subprocess.PIPE, 'stderr':subprocess.PIPE,
# On Windows, running this from the binary produced by Pyinstaller 'startupinfo':si,
# with the ``--noconsole`` option requires redirecting everything 'env':env})
# (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 ret
return kwargs
def run_tesseract(input_filename, output_filename_base, extension, lang, config='', nice=0):
def run_tesseract(input_filename,
output_filename_base,
extension,
lang,
config='',
nice=0):
cmd_args = [] cmd_args = []
if not sys.platform.startswith('win32'):
if not sys.platform.startswith('win32') and nice != 0: if nice != 0:
cmd_args += ('nice', '-n', str(nice)) cmd_args += ('nice', '-n', str(nice))
cmd_args += (tesseract_cmd, input_filename, output_filename_base) cmd_args += (tesseract_cmd, input_filename, output_filename_base)
if lang is not None: if lang is not None:
cmd_args += ('-l', lang) cmd_args += ('-l', lang)
cmd_args += shlex.split(config) cmd_args += shlex.split(config)
if extension not in ('box', 'osd', 'tsv'): if extension not in ('box', 'osd', 'tsv'):
cmd_args.append(extension) cmd_args.append(extension)
try: try:
proc = subprocess.Popen(cmd_args, **subprocess_args()) proc = (subprocess.Popen)(cmd_args, **subprocess_args())
except OSError: except OSError:
raise TesseractNotFoundError() raise TesseractNotFoundError()
status_code, error_string = proc.wait(), proc.stderr.read() status_code, error_string = proc.wait(), proc.stderr.read()
proc.stderr.close() proc.stderr.close()
if status_code: if status_code:
raise TesseractError(status_code, get_errors(error_string)) raise TesseractError(status_code, get_errors(error_string))
return True return True
def run_and_get_output(image, def run_and_get_output(image, extension, lang=None, config='', nice=0, return_bytes=False):
extension, temp_name, input_filename = ('', '')
lang=None,
config='',
nice=0,
return_bytes=False):
temp_name, input_filename = '', ''
try: try:
temp_name, input_filename = save_image(image) temp_name, input_filename = save_image(image)
kwargs = { kwargs = {'input_filename':input_filename,
'input_filename': input_filename, 'output_filename_base':temp_name + '_out',
'output_filename_base': temp_name + '_out', 'extension':extension,
'extension': extension, 'lang':lang,
'lang': lang, 'config':config,
'config': config, 'nice':nice}
'nice': nice
}
run_tesseract(**kwargs) run_tesseract(**kwargs)
filename = kwargs['output_filename_base'] + os.extsep + extension filename = kwargs['output_filename_base'] + os.extsep + extension
with open(filename, 'rb') as output_file: with open(filename, 'rb') as (output_file):
if return_bytes: if return_bytes:
return output_file.read() return output_file.read()
return output_file.read().decode('utf-8').strip() return output_file.read().decode('utf-8').strip()
@ -248,166 +185,127 @@ def file_to_dict(tsv, cell_delimiter, str_col_idx):
rows = [row.split(cell_delimiter) for row in tsv.split('\n')] rows = [row.split(cell_delimiter) for row in tsv.split('\n')]
if not rows: if not rows:
return result 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]
header = rows.pop(0) return result
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): def is_valid(val, _type):
if _type is int: if _type is int:
return val.isdigit() return val.isdigit()
else:
if _type is float: if _type is float:
pass
try: try:
float(val) float(val)
return True return True
except ValueError: except ValueError:
return False return False
return True return True
def osd_to_dict(osd): def osd_to_dict(osd):
return { 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])}
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 @run_once
def get_tesseract_version(): def get_tesseract_version():
''' """
Returns LooseVersion object of the Tesseract version Returns LooseVersion object of the Tesseract version
''' """
try: try:
return LooseVersion( return LooseVersion((subprocess.check_output)([tesseract_cmd, '--version'], **subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:]))
subprocess.check_output([tesseract_cmd, '--version'],
**subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:])
)
except OSError: except OSError:
raise TesseractNotFoundError() raise TesseractNotFoundError()
def image_to_string(image, def image_to_string(image, lang=None, config='', nice=0, boxes=False, output_type=Output.STRING):
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 Returns the result of a Tesseract OCR run on the provided image to string
''' """
if boxes: 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")
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) return image_to_boxes(image, lang, config, nice, output_type)
else:
args = [image, 'txt', lang, config, nice] args = [
image, 'txt', lang, config, nice]
if output_type == Output.DICT: if output_type == Output.DICT:
return {'text': run_and_get_output(*args)} return {'text': run_and_get_output(*args)}
elif output_type == Output.BYTES: if output_type == Output.BYTES:
args.append(True) args.append(True)
return run_and_get_output(*args)
return run_and_get_output(*args)
def image_to_boxes(image, def image_to_boxes(image, lang=None, config='', nice=0, output_type=Output.STRING):
lang=None, """
config='',
nice=0,
output_type=Output.STRING):
'''
Returns string containing recognized characters and their box boundaries Returns string containing recognized characters and their box boundaries
''' """
config += ' batch.nochop makebox' config += ' batch.nochop makebox'
args = [image, 'box', lang, config, nice] args = [image, 'box', lang, config, nice]
if output_type == Output.DICT: if output_type == Output.DICT:
box_header = 'char left bottom right top page\n' box_header = 'char left bottom right top page\n'
return file_to_dict(box_header + run_and_get_output(*args), ' ', 0) return file_to_dict(box_header + run_and_get_output(*args), ' ', 0)
elif output_type == Output.BYTES: else:
args.append(True) if output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args) return run_and_get_output(*args)
def image_to_data(image, def image_to_data(image, lang=None, config='', nice=0, output_type=Output.STRING):
lang=None, """
config='',
nice=0,
output_type=Output.STRING):
'''
Returns string containing box boundaries, confidences, Returns string containing box boundaries, confidences,
and other information. Requires Tesseract 3.05+ and other information. Requires Tesseract 3.05+
''' """
if get_tesseract_version() < '3.05': if get_tesseract_version() < '3.05':
raise TSVNotSupported() raise TSVNotSupported()
config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip() config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip()
args = [image, 'tsv', lang, config, nice] args = [image, 'tsv', lang, config, nice]
if output_type == Output.DICT: if output_type == Output.DICT:
return file_to_dict(run_and_get_output(*args), '\t', -1) return file_to_dict(run_and_get_output(*args), '\t', -1)
elif output_type == Output.BYTES: else:
args.append(True) if output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args) return run_and_get_output(*args)
def image_to_osd(image, def image_to_osd(image, lang='osd', config='', nice=0, output_type=Output.STRING):
lang='osd', """
config='',
nice=0,
output_type=Output.STRING):
'''
Returns string containing the orientation and script detection (OSD) Returns string containing the orientation and script detection (OSD)
''' """
config = '{}-psm 0 {}'.format( config = '{}-psm 0 {}'.format('' if get_tesseract_version() < '3.05' else '-', config.strip()).strip()
'' if get_tesseract_version() < '3.05' else '-', args = [
config.strip() image, 'osd', lang, config, nice]
).strip()
args = [image, 'osd', lang, config, nice]
if output_type == Output.DICT: if output_type == Output.DICT:
return osd_to_dict(run_and_get_output(*args)) return osd_to_dict(run_and_get_output(*args))
elif output_type == Output.BYTES: else:
args.append(True) if output_type == Output.BYTES:
args.append(True)
return run_and_get_output(*args) return run_and_get_output(*args)
def main(): def main():
if len(sys.argv) == 2: if len(sys.argv) == 2:
filename, lang = sys.argv[1], None 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: else:
sys.stderr.write('Usage: python pytesseract.py [-l lang] input_file\n') if len(sys.argv) == 4:
exit(2) 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: try:
print(image_to_string(Image.open(filename), lang=lang)) print(image_to_string((Image.open(filename)), lang=lang))
except IOError: except IOError:
sys.stderr.write('ERROR: Could not open file "%s"\n' % filename) sys.stderr.write('ERROR: Could not open file "%s"\n' % filename)
exit(1) exit(1)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

BIN
id-card.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB