diff --git a/src/CNIRevelator.py b/src/CNIRevelator.py index 3fe7fb0..d332afe 100644 --- a/src/CNIRevelator.py +++ b/src/CNIRevelator.py @@ -28,14 +28,17 @@ import sys import os import subprocess import threading -import traceback import psutil +from tkinter import * +from tkinter.messagebox import * import launcher # launcher.py" import updater # updater.py import globs # globs.py import pytesseract # pytesseract.py import logger # logger.py +import lang # lang.py +import ihm # ihm.py from main import * # main.py @@ -59,15 +62,15 @@ def main(): text = 'Tesseract version ' + str(tesser_version) + ' Licensed Apache 2004 successfully initiated\n' mainw.logOnTerm(text) - mainw.logOnTerm('\n\nEntrez la première ligne de MRZ svp \n') + mainw.logOnTerm('\n\n{} \n'.format(lang.all[globs.CNIRlang]["Please type a MRZ or open a scan"])) if globs.CNIRNewVersion: - showinfo('Changelog : résumé de mise à jour', ('Version du logiciel : CNIRevelator ' + globs.verstring_full + '\n\n' + globs.changelog), parent=mainw) + showinfo("Changelog : update summary", ('{} : CNIRevelator {}\n\n{}'.format(lang.all[globs.CNIRlang]["Program version"], globs.verstring_full, globs.changelog)), parent=mainw) logfile.printdbg('main() : **** Launching App_main() ****') try: mainw.mainloop() except Exception as e: - showerror("CNIRevelator Fatal Error", "An error has occured : {}".format(e), parent=mainw) + showerror(lang.all[globs.CNIRlang]["CNIRevelator Fatal Error"], "{} : {}".format(lang.all[globs.CNIRlang]["An error has occured"],e), parent=mainw) logfile.printdbg('main() : **** Ending App_main() ****') logfile.printdbg('*** CNIRevelator LOGFILE. Goodbye World ! ***') @@ -76,15 +79,36 @@ def main(): ## BOOTSTRAP OF CNIREVELATOR + +# LANGUAGE +if os.path.isfile(globs.CNIRLangFile): + with open(globs.CNIRLangFile, 'r') as (configFile): + try: + # Reading it + globs.CNIRlang = configFile.read() + except Exception as e: + ihm.crashCNIR() + raise IOError(str(e)) +else: + with open(globs.CNIRLangFile, 'w') as (configFile): + try: + # Writing it + configFile.write(globs.CNIRlang) + except Exception as e: + ihm.crashCNIR() + raise IOError(str(e)) + +# GO try: launcherThread = threading.Thread(target=updater.umain, daemon=False) launcher.lmain(launcherThread) except Exception: + ihm.crashCNIR() updater.exitProcess(1) if updater.UPDATE_IS_MADE: # Launch app ! - args = updater.UPATH + '\\CNIRevelator.exe ' + globs.CNIRFolder + args = updater.UPATH + '\\CNIRevelator.exe' + " DELETE " + globs.CNIRFolder cd = updater.UPATH for i in range(0,3): try: @@ -99,6 +123,6 @@ if updater.UPDATE_IS_MADE: try: main() except Exception as e: - traceback.print_exc(file=sys.stdout) + ihm.crashCNIR() updater.exitProcess(0) \ No newline at end of file diff --git a/src/downloader.py b/src/downloader.py index c10a7e2..a72c8f8 100644 --- a/src/downloader.py +++ b/src/downloader.py @@ -35,6 +35,7 @@ from time import time import logger # logger.py import globs # globs.py import ihm # ihm.py +import lang # lang.py class AESCipher(object): @@ -184,8 +185,8 @@ class newdownload: reducedFilename = filename.split("\\")[-1] - launcherWindow.printmsg('Downloading {}'.format(title)) - logfile.printdbg('Requesting download of {}'.format(reducedFilename)) + launcherWindow.printmsg('{} {}'.format(lang.all[globs.CNIRlang]["Downloading"], title)) + logfile.printdbg('{} {}'.format("Downloading", reducedFilename)) try: os.remove(filename) @@ -201,12 +202,12 @@ class newdownload: launcherWindow.progressBar.stop() launcherWindow.progressBar.configure(mode='determinate', value=(int(Percent)), maximum=100) - launcherWindow.printmsg('Downloading {}'.format(title) + ' : {:4.2f} %'.format(Percent)) + launcherWindow.printmsg('{} {}'.format(lang.all[globs.CNIRlang]["Downloading"], title) + ' : {:4.2f} %'.format(Percent)) launcherWindow.progressBar.configure(mode='indeterminate', value=0, maximum=20) launcherWindow.progressBar.start() - logfile.printdbg('Successful retrieved {}'.format(filename)) + logfile.printdbg('{} {}'.format(lang.all[globs.CNIRlang]["Successful retrieved"], filename)) return filename diff --git a/src/globs.py b/src/globs.py index 42b1e6c..e183884 100644 --- a/src/globs.py +++ b/src/globs.py @@ -31,7 +31,7 @@ verstring_full = "{}.{}.{} {}".format(version[0], version[1], version[2], v verstring = "{}.{}".format(version[0], version[1]) debug = True -changelog = "Version 3.1.0 \nMise-à-jour majeure avec les progressions suivantes :\n- Modifications cosmétiques de l'interface utilisateur\n- Stabilisation des changements effectués sur la version mineure 3.0 : interface utilisateur, OCR, VISA A et B, logging" +changelog = "Version 3.1.0 \nMise-à-jour majeure avec les progressions suivantes :\n- Modifications cosmétiques de l'interface utilisateur\n- Stabilisation des changements effectués sur la version mineure 3.0 : interface utilisateur, OCR, VISA A et B, logging\n- Rationalisation du système de langues" CNIRTesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8' CNIRFolder = os.getcwd() @@ -39,6 +39,8 @@ CNIRLColor = "#006699" CNIRName = "CNIRevelator {}".format(verstring) CNIRCryptoKey = '82Xh!efX3#@P~2eG' CNIRNewVersion = False +CNIRLangFile = CNIRFolder + '\\config\\lang.ig' +CNIRlang = "fr" CNIRConfig = CNIRFolder + '\\config\\conf.ig' CNIRTesser = CNIRFolder + '\\Tesseract-OCR4\\' @@ -47,3 +49,5 @@ CNIRMainLog = CNIRFolder + '\\logs\\main.log' CNIRUrlConfig = CNIRFolder + '\\config\\urlconf.ig' CNIRVerStock = CNIRFolder + '\\downloads\\versions.lst' CNIREnv = CNIRFolder + '\\Data\\' + +CNIROpenFile = True \ No newline at end of file diff --git a/src/ihm.py b/src/ihm.py index 9c7622c..6759960 100644 --- a/src/ihm.py +++ b/src/ihm.py @@ -24,14 +24,17 @@ """ from tkinter import * +import webbrowser from tkinter.messagebox import * from tkinter import filedialog from tkinter import ttk import cv2 import PIL.Image, PIL.ImageTk +import traceback import logger # logger.py import globs # globs.py +import lang # lang.py controlKeys = ["Escape", "Right", "Left", "Up", "Down", "Home", "End", "BackSpace", "Delete", "Inser", "Shift_L", "Shift_R", "Control_R", "Control_L"] @@ -42,7 +45,7 @@ class DocumentAsk(Toplevel): self.choice = 0 vals = [0, 1] super().__init__(parent) - self.title("Choisir le document d'identité :") + self.title("{} :".format(lang.all[globs.CNIRlang]["Choose the identity document"])) ttk.Radiobutton(self, text=choices[0], command=self.register0, value=vals[0]).pack() ttk.Radiobutton(self, text=choices[1], command=self.register1, value=vals[1]).pack() @@ -68,19 +71,19 @@ class DocumentAsk(Toplevel): self.choice = 1 def ok(self): self.destroy() - - + + class OpenScanDialog(Toplevel): def __init__(self, parent, text): super().__init__(parent) self.parent = parent - self.title('Validation de la MRZ détectée par OCR') + self.title(lang.all[globs.CNIRlang]["OCR Detection Validation"]) self.resizable(width=False, height=False) self.termtext = Text(self, state='normal', width=45, height=2, wrap='none', font='Terminal 17', fg='#121f38') self.termtext.grid(column=0, row=0, sticky='NEW', padx=5, pady=5) self.termtext.insert('end', text + '\n') - self.button = Button(self, text='Valider', command=(self.valid)) + self.button = Button(self, text=lang.all[globs.CNIRlang]["Validate"], command=(self.valid)) self.button.grid(column=0, row=1, sticky='S', padx=5, pady=5) self.update() hs = self.winfo_screenheight() @@ -102,27 +105,27 @@ class OpenScanDialog(Toplevel): for i in range(len(texting)): for char in texting[i]: if char not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789<': - showerror('Erreur de validation', 'La MRZ soumise contient des caractères invalides', parent=self) + showerror(lang.all[globs.CNIRlang]["Validation Error"], lang.all[globs.CNIRlang]["The submitted MRZ contains invalid characters"], parent=self) self.parent.validatedtext = '' self.destroy() - + class LoginDialog(Toplevel): def __init__(self, parent): self.key = '' self.login = '' super().__init__(parent) - self.title('Connexion') + self.title(lang.all[globs.CNIRlang]["Connection"]) 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() + Label(self, text='{} : '.format(lang.all[globs.CNIRlang]["Password"])).pack() self.entry_pass = Entry(self, show='*') self.entry_pass.insert(0, '') self.entry_pass.pack() - Button(self, text='Connexion', command=(self.connecti)).pack() + Button(self, text=lang.all[globs.CNIRlang]["Connection"], command=(self.connecti)).pack() self.resizable(width=False, height=False) ws = self.winfo_screenwidth() hs = self.winfo_screenheight() @@ -185,7 +188,7 @@ class LauncherWindow(Tk): self.mainCanvas.create_text((wwidth / 2), (wheight / 3), text=(globs.CNIRName), font='Helvetica 30', fill='white') self.mainCanvas.create_text((wwidth / 2), (wheight / 2), text="version " + (globs.verstring_full), font='Helvetica 8', fill='white') - self.msg = self.mainCanvas.create_text((wwidth / 2), (wheight / 1.20), text='Booting up...', font='Helvetica 9', fill='white') + self.msg = self.mainCanvas.create_text((wwidth / 2), (wheight / 1.20), text=lang.all[globs.CNIRlang]["Booting up..."], font='Helvetica 9', fill='white') #self.pBarZone = Frame(self.mainCanvas, width=wwidth, height=wheight/10) self.update() @@ -205,7 +208,8 @@ class LauncherWindow(Tk): self.iconbitmap('id-card.ico') logfile = logger.logCur logfile.printdbg('Launcher IHM successful') - self.protocol('WM_DELETE_WINDOW', lambda : self.destroy()) + self.protocol('WM_DELETE_WINDOW', lambda : 0) + self.attributes("-topmost", 1) self.update() @@ -230,7 +234,24 @@ class ResizeableCanvas(Canvas): self.height = event.height # rescale all the objects tagged with the "all" tag self.scale("all",0,0,wscale,hscale) - + +def crashCNIR(): + """ + last solution + """ + # Global handler + logfile = logger.logCur + # hide main window + root = Tk() + root.withdraw() + logfile.printerr("FATAL ERROR : see traceback below.\n{}".format(traceback.format_exc())) + showerror(lang.all[globs.CNIRlang]["CNIRevelator Fatal Eror"], lang.all[globs.CNIRlang]["CNIRevelator crashed because a fatal error occured. View log for more infos and please open an issue on Github"]) + res = askquestion(lang.all[globs.CNIRlang]["CNIRevelator Fatal Eror"], lang.all[globs.CNIRlang]["Would you like to open an issue on Github to report this bug ?"]) + if res == "yes": + webbrowser.open_new("https://github.com/neox95/CNIRevelator/issues") + root.destroy() + + ## Global Handler launcherWindowCur = LauncherWindow() diff --git a/src/lang.py b/src/lang.py new file mode 100644 index 0000000..100706c --- /dev/null +++ b/src/lang.py @@ -0,0 +1,299 @@ +""" +******************************************************************************** +* CNIRevelator * +* * +* Desc: Application langage 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 . * +******************************************************************************** +""" + +import globs # globs.py + +## FRENCH LANGUAGE +french = \ +{ +"Please type a MRZ or open a scan" : "Veuillez taper une MRZ ou ouvrir un scan svp", +"Changelog : update summary" : "Changelog : résumé de mise à jour", +"Program version" : "Version du logiciel", +"CNIRevelator Fatal Error" : "Erreur fatale de CNIRevelator", +"An error has occured" : "Une erreur s'est produite", +"Downloading" : "Téléchargement de", +"Successful retrieved" : "Réussite du téléchargement de", +"Choose the identity document" : "Choisir le document d'identité", +"OCR Detection Validation" : "Validation de la MRZ détectée par OCR", +"Validate" : "Valider", +"Validation Error" : "Erreur de validation", +"The submitted MRZ contains invalid " + "characters" : "La MRZ soumise contient des caractères invalides", +"Connection" : "Connexion", +"Password" : "Mot de passe", +"Booting up..." : "Démarrage", +"CNIRevelator Fatal Eror" : "Erreur Fatale de CNIRevelator", +"CNIRevelator crashed because a " +"fatal error occured. View log for " +"more infos and please open " +"an issue on Github" : "CNIRevelator s'est arrêté car une erreur fatale s'est produite. Consultez le journal pour plus d'informations et ouvrez s'il vous plaît un ticket sur Github.", +"Would you like to open an issue " +"on Github to report this bug ?" : "Souhaitez-vous ouvrir un ticket sur Github pour signaler ce bogue?", +"Starting..." : "Lancement...", +"Informations about the current " +"document" : "Informations sur la pièce d'identité", +"IDLE" : "EN ATTENTE", +"Status" : "Statut", +"Name" : "Nom", +"Birth date" : "Date de naissance", +"Issue date" : "Date de délivrance", +"Expiration date" : "Date d'expiration", +"Sex" : "Sexe", +"Issuing country" : "Pays émetteur", +"Nationality" : "Nationalité", +"Registration" : "Immatriculation", +"Document number" : "Numéro de document", +"Unknown" : "Inconnu(e)", +"Display and processing of " +"documents" : "Affichage et traitement de documents", +"Complete MRZ capture terminal" : "Terminal de saisie de MRZ complète", +"Quick entry terminal (731)" : "Terminal de saisie rapide (731)", +"Monitor" : "Moniteur", +"New" : "Nouveau", +"Open scan..." : "Ouvrir scan...", +"Quit" : "Quitter", +"File" : "Fichier", +"Keyboard commands" : "Commandes au clavier", +"Report a bug" : "Signaler un problème", +"About CNIRevelator" : "A propos de CNIRevelator", +"Help" : "Aide", +"OCR module error" : "Erreur du module OCR", +"The OCR module located at {} " +"can not be found or corrupted. " +"It will be reinstalled at " +"the next run" : "Le module OCR localisé en {} est introuvable ou corrompu. Il sera réinstallé à la prochaine exécution", +"The Tesseract module " +"encountered a problem: {}" : "Le module Tesseract a rencontré un problème : {}", +"Tesseract error : {}. " +"Will be reinstallated" : "Erreur de Tesseract : {}. Le module sera réinstallé", +"Document detected: {}\n" : "Document detecté : {}\n", +"Document detected again: {}\n" : "Document re-detecté : {}\n", +"Character not accepted !\n" : "Caractère non accepté !\n", +"Open a scan of document..." : "Ouvrir un scan de document...", +"OpenCV error (image processing)" : "Erreur OpenCV (traitement d'images)", +"A critical error has occurred in " +"the OpenCV image processing " +"manager used by CNIRevelator, the " +"application will reset itself" : "Une erreur critique s'est produite dans le gestionnaire de traitement d'images OpenCV utilisé par CNIRevelator. L'application va se réinitialiser", +"ABOUT" : 'Version du logiciel : CNIRevelator ' + globs.verstring_full + '\n\n' + "Copyright © 2018-2019 Adrien Bourmault (neox95)" + "\n\n" + "CNIRevelator est un logiciel libre : vous avez le droit de le modifier et/ou le distribuer " + "dans les termes de la GNU General Public License telle que publiée par " + "la Free Software Foundation, dans sa version 3 ou " + "ultérieure. " + "\n\n" + "CNIRevelator est distribué dans l'espoir d'être utile, sans toutefois " + "impliquer une quelconque garantie de " + "QUALITÉ MARCHANDE ou APTITUDE À UN USAGE PARTICULIER. Référez vous à la " + "GNU General Public License pour plus de détails à ce sujet. " + "\n\n" + "Vous devriez avoir reçu une copie de la GNU General Public License " + "avec CNIRevelator. Si cela n'est pas le cas, jetez un oeil à . " + "\n\n" + "Le module d'OCR Tesseract 4.0 est soumis à l'Apache License 2004." + "\n\n" + "Les bibliothèques python et l'environnement Anaconda 3 sont soumis à la licence BSD 2018-2019." + "\n\n" + "Le code source de ce programme est disponible sur Github à l'adresse .\n" + "Son fonctionnement est conforme aux normes et directives du document 9303 de l'OACI régissant les documents de voyages et d'identité." + '\n\n' + " En cas de problèmes ou demande particulière, ouvrez-y une issue ou bien envoyez un mail à neox@os-k.eu !\n\n", + +"KEYBHELP" : "Terminal de saisie rapide (731) : \n\n" + "Caractères autorisés : Alphanumériques en majuscule et le caractère '<'. Pas de minuscules ni caractères spéciaux, autrement la somme est mise à zéro \n\n" + "Calculer résultat :\t\t\tTouche Ctrl droite \n" + "Copier :\t\t\t\tCtrl-C \n" + "Coller :\t\t\t\tCtrl-V \n" + "\n\n" + "Terminal de saisie MRZ complète : \n\n" + "Caractères autorisés : Alphanumériques en majuscule et le caractère '<'. Pas de minuscules ni caractères spéciaux, autrement la somme est mise à zéro \n\n" + "Calculer résultat :\t\t\tTouche Ctrl droite \n" + "Compléter champ :\t\t\tTouche Tab \n" + "Copier :\t\t\t\tCtrl-C \n" + "Coller :\t\t\t\tCtrl-V \n" + "Forcer une nouvelle détection du document :\tEchap\n", +"Document Review: {}\n\n" : "Examen du document : {}\n\n", +"Check sum position {}: Lu {} VS " +"Calculated {} and {}\n" : "Check sum position {}: Lu {} VS Calculated {} and {}\n", +"COMPLIANT" : "CONFORME", +"IMPROPER" : "NON CONFORME", +"Installing the updates" : "Installation des mises-à-jour", +"Verifying download..." : "Vérification du téléchargement ...", +"Preparing installation..." : "Préparation de l'installation", +"Success !" : "Installation terminée !", +"Launching the new version..." : "Lancement de la nouvelle version...", +"Credentials Error. No effective " +"update !" : "Identifiants incorrects. Pas de mise-à-jour !", +"Deleting old version" : "Suppression de l'ancienne version", +'Software is up-to-date !' : "Logiciel à jour !", +"An error occured. " +"No effective update !" : "Une erreur s'est produite. Pas de mise-à-jour !", +"Shortcut creation" : "Création de raccourci", +"Would you like to create/update " +"the shortcut for CNIRevelator " +"on your desktop ?" : "Souhaitez vous créer/mettre à jour le raccourci pour CNIRevelator sur votre bureau ?", +"The file you provided is " +"not found : {}" : "Fichier transmis non trouvé : {}" +} + +## ENGLISH LANGUAGE + +english = \ +{ +"Please type a MRZ or open a scan" : "Please type a MRZ or open a scan", +"Changelog : update summary" : "Changelog : update summary", +"Program version" : "Program version", +"CNIRevelator Fatal Error" : "CNIRevelator Fatal Error" , +"An error has occured" : "An error has occured", +"Downloading" : "Downloading", +"Successful retrieved" : "Successful retrieved", +"Choose the identity document" : "Choose the identity document" , +"OCR Detection Validation" : "OCR Detection Validation", +"Validate" : "Validate", +"Validation Error" : "Validation Error", +"The submitted MRZ contains invalid " + "characters" : "The submitted MRZ contains invalid characters", +"Connection" : "Connection", +"Password" : "Password", +"Booting up..." : "Booting up...", +"CNIRevelator Fatal Eror" : "CNIRevelator Fatal Eror", +"CNIRevelator crashed because a " +"fatal error occured. View log for " +"more infos and please open " +"an issue on Github" : "CNIRevelator crashed because a fatal error occured. View log for more infos and please open an issue on Github", +"Would you like to open an issue " +"on Github to report this bug ?" : "Would you like to open an issue on Github to report this bug ?", +"Starting..." : "Starting...", +"Informations about the current " +"document" : "Informations about the current document", +"IDLE" : "IDLE", +"Status" : "Status", +"Name" : "Name", +"Birth date" : "Birth date", +"Issue date" : "Date de délivrance", +"Expiration date" : "Issue date", +"Sex" : "Sex", +"Issuing country" : "Issuing country", +"Nationality" : "Nationality", +"Registration" : "Registration", +"Document number" : "Document number", +"Unknown" : "Unknown", +"Display and processing of " +"documents" : "Display and processing of documents", +"Complete MRZ capture terminal" : "Complete MRZ capture terminal", +"Quick entry terminal (731)" : "Quick entry terminal (731)", +"Monitor" : "Monitor", +"New" : "New", +"Open scan..." : "Open scan...", +"Quit" : "Quit", +"File" : "File", +"Keyboard commands" : "Keyboard commands", +"Report a bug" : "Report a bug", +"About CNIRevelator" : "About CNIRevelator", +"Help" : "Help", +"OCR module error" : "OCR module error", +"The OCR module located at {} " +"can not be found or corrupted. " +"It will be reinstalled at " +"the next run" : "The OCR module located at {} can not be found or corrupted. It will be reinstalled at the next run", +"The Tesseract module " +"encountered a problem: {}" : "The Tesseract module encountered a problem: {}", +"Tesseract error : {}. " +"Will be reinstallated" : "Tesseract error : {}. Will be reinstallated", +"Document detected: {}\n" : "Document detected : {}\n", +"Document detected again: {}\n" : "Document detected again : {}\n", +"Character not accepted !\n" : "Character not accepted !\n", +"Open a scan of document..." : "Open a scan of document...", +"OpenCV error (image processing)" : "OpenCV error (image processing)", +"A critical error has occurred in " +"the OpenCV image processing " +"manager used by CNIRevelator, the " +"application will reset itself" : "A critical error has occurred in the OpenCV image processing manager used by CNIRevelator, the application will reset itself", + +"ABOUT" : 'Software Version: CNIRevelator' + globs.verstring_full + '\n\n' + "Copyright © 2018-2019 Adrien Bourmault (neox95)" + "\n\n" + "CNIRevelator is free software: you have the right to modify and / or distribute it" + "in the terms of the GNU General Public License as published by" + "Free Software Foundation, version 3 or" + "later." + "\n\n" + "CNIRevelator is distributed in the hope of being useful, without however" + "imply any guarantee of" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE Refer to the" + "GNU General Public License for more details about this." + "\n\n" + "You should have received a copy of the GNU General Public License" + "with CNIRevelator, if this is not the case, take a look at ." + "\n\n" + "The Tesseract 4.0 OCR module is subject to the 2004 Apache License." + "\n\n" + "Python libraries and the Anaconda 3 environment are subject to the BSD 2018-2019 license." + "\n\n" + "The source code for this program is available on Github at . \n" + "Its operation is in accordance with the standards and guidelines of ICAO document 9303 governing travel and identity documents." + '\n\n' + "In case of problems or special request, open an issue or send an email to neox@os-k.eu! \n\n", + + +"KEYBHELP" : "Fast entry terminal (731): \n\n" + "Allowed characters: Alphanumeric uppercase and the character '<' No lowercase or special characters, otherwise the sum is set to zero \n\n" + "Calculate Result:\t\t\tControl Right\n" + "Copy:\t\t\t\tCtrl-C\n" + "Paste:\t\t\t\tCtrl-V\n" + "\n\n" + "MRZ input terminal complete: \n\n" + "Allowed characters: Alphanumeric uppercase and the character '<' No lowercase or special characters, otherwise the sum is set to zero \n\n" + "Calculate Result:\t\t\tControl Right\n" + "Complete field:\t\t\tTab button\n" + "Copy:\t\t\t\tCtrl-C\n" + "Paste:\t\t\t\tCtrl-V\n" + "Force a new document detection:\tEchap\n", + +"Document Review: {}\n\n" : "Document Review: {}\n\n", +"Check sum position {}: Lu {} VS " +"Calculated {} and {}\n" : "Check sum position {}: Lu {} VS Calculated {} and {}\n", +"COMPLIANT" : "COMPLIANT", +"IMPROPER" : "IMPROPER", +"Installing the updates" : "Installing the updates", +"Verifying download..." : "Verifying download...", +"Preparing installation..." : "Preparing installation...", +"Success !" : "Success !", +"Launching the new version..." : "Launching the new version...", +"Credentials Error. No effective " +"update !" : "Credentials Error. No effective update !", +"Deleting old version" : "Deleting old version", +'Software is up-to-date !' : "Software is up-to-date !", +"An error occured. " +"No effective update !" : "An error occured. No effective update !", +"Shortcut creation" : "Shortcut creation", +"Would you like to create/update " +"the shortcut for CNIRevelator on " +"your desktop ?" : "Would you like to create/update the shortcut for CNIRevelator on your desktop ?", +"The file you provided is not " +"found : {}" : "The file you provided is not found : {}" +} + +## MAIN DICT +all = \ +{ +"fr" : french, +"en" : english +} \ No newline at end of file diff --git a/src/launcher.py b/src/launcher.py index 3edcfaa..aa35b8d 100644 --- a/src/launcher.py +++ b/src/launcher.py @@ -26,12 +26,12 @@ import sys import os import threading -import traceback import updater # updater.py import ihm # ihm.py import globs # globs.py import logger # logger.py +import lang # lang.py ## Main function def lmain(mainThread): @@ -44,7 +44,7 @@ def lmain(mainThread): # Hello user launcherWindow.progressBar.configure(mode='indeterminate', value=0, maximum=20) - launcherWindow.printmsg('Starting...') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Starting..."]) launcherWindow.progressBar.start() # Starting the main update thread diff --git a/src/main.py b/src/main.py index b921838..18ec34d 100644 --- a/src/main.py +++ b/src/main.py @@ -32,17 +32,18 @@ from tkinter import ttk import threading from datetime import datetime import re -import traceback import cv2 import PIL.Image, PIL.ImageTk import os, shutil import webbrowser +import sys, os import ihm # ihm.py import logger # logger.py import mrz # mrz.py import globs # globs.py import pytesseract # pytesseract.py +import lang # lang.py # Global handler logfile = logger.logCur @@ -78,7 +79,7 @@ class mainWindow(Tk): self.grid_rowconfigure(2, weight=1, minsize=(hs / 2 * 0.35)) # Prepare the data sections - self.lecteur_ci = ttk.Labelframe(self, text="Informations sur la pièce d'identité") + self.lecteur_ci = ttk.Labelframe(self, text=lang.all[globs.CNIRlang]["Informations about the current document"]) self.lecteur_ci.grid_columnconfigure(0, weight=1) self.lecteur_ci.grid_columnconfigure(1, weight=1) self.lecteur_ci.grid_columnconfigure(2, weight=1) @@ -92,50 +93,50 @@ class mainWindow(Tk): self.lecteur_ci.grid_rowconfigure(5, weight=1) # Fill the data sections - ttk.Label((self.lecteur_ci), text='Statut : ').grid(column=0, row=0, padx=5, pady=5) - self.STATUStxt = ttk.Label((self.lecteur_ci), text='EN ATTENTE', font=("TkDefaultFont", 13, "bold"), foreground="orange", anchor=CENTER) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Status"])).grid(column=0, row=0, padx=5, pady=5) + self.STATUStxt = ttk.Label((self.lecteur_ci), text=lang.all[globs.CNIRlang]["IDLE"], font=("TkDefaultFont", 13, "bold"), foreground="orange", anchor=CENTER) self.STATUStxt.grid(column=1, row=0, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Nom : ').grid(column=0, row=1, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Name"])).grid(column=0, row=1, padx=5, pady=5) self.nom = ttk.Label((self.lecteur_ci), text=' ') self.nom.grid(column=1, row=1, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Nom (2) : ').grid(column=0, row=2, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} (2) : '.format(lang.all[globs.CNIRlang]["Name"])).grid(column=0, row=2, padx=5, pady=5) self.prenom = ttk.Label((self.lecteur_ci), text=' ') self.prenom.grid(column=1, row=2, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Date de naissance : ').grid(column=0, row=3, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Birth date"])).grid(column=0, row=3, padx=5, pady=5) self.bdate = ttk.Label((self.lecteur_ci), text=' ') self.bdate.grid(column=1, row=3, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Date de délivrance : ').grid(column=0, row=4, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Issue date"])).grid(column=0, row=4, padx=5, pady=5) self.ddate = ttk.Label((self.lecteur_ci), text=' ') self.ddate.grid(column=1, row=4, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text="Date d'expiration : ").grid(column=0, row=5, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text="{} : ".format(lang.all[globs.CNIRlang]["Expiration date"])).grid(column=0, row=5, padx=5, pady=5) self.edate = ttk.Label((self.lecteur_ci), text=' ') self.edate.grid(column=1, row=5, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Sexe du porteur : ').grid(column=4, row=1, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Sex"])).grid(column=4, row=1, padx=5, pady=5) self.sex = ttk.Label((self.lecteur_ci), text=' ') self.sex.grid(column=5, row=1, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Pays de délivrance : ').grid(column=4, row=2, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Issuing country"])).grid(column=4, row=2, padx=5, pady=5) self.pays = ttk.Label((self.lecteur_ci), text=' ') self.pays.grid(column=5, row=2, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Nationalité du porteur : ').grid(column=4, row=3, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Nationality"])).grid(column=4, row=3, padx=5, pady=5) self.nat = ttk.Label((self.lecteur_ci), text=' ') self.nat.grid(column=5, row=3, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Immatriculation : ').grid(column=4, row=4, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Registration"])).grid(column=4, row=4, padx=5, pady=5) self.indic = ttk.Label((self.lecteur_ci), text=' ') self.indic.grid(column=5, row=4, padx=5, pady=5) - ttk.Label((self.lecteur_ci), text='Numéro de document : ').grid(column=4, row=5, padx=5, pady=5) + ttk.Label((self.lecteur_ci), text='{} : '.format(lang.all[globs.CNIRlang]["Document number"])).grid(column=4, row=5, padx=5, pady=5) self.no = ttk.Label((self.lecteur_ci), text=' ') self.no.grid(column=5, row=5, padx=5, pady=5) - self.nom['text'] = 'Inconnu(e)' - self.prenom['text'] = 'Inconnu(e)' - self.bdate['text'] = 'Inconnu(e)' - self.ddate['text'] = 'Inconnu(e)' - self.edate['text'] = 'Inconnu(e)' - self.no['text'] = 'Inconnu(e)' - self.sex['text'] = 'Inconnu(e)' - self.nat['text'] = 'Inconnu(e)' - self.pays['text'] = 'Inconnu(e)' - self.indic['text'] = 'Inconnu(e)' + self.nom['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.prenom['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.bdate['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.ddate['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.edate['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.no['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.sex['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.nat['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.pays['text'] = lang.all[globs.CNIRlang]["Unknown"] + self.indic['text'] = lang.all[globs.CNIRlang]["Unknown"] self.infoList = \ @@ -153,7 +154,7 @@ class mainWindow(Tk): } # The the image viewer - self.imageViewer = ttk.Labelframe(self, text='Affichage et traitement de documents') + self.imageViewer = ttk.Labelframe(self, text=lang.all[globs.CNIRlang]["Display and processing of documents"]) self.imageViewer.grid_columnconfigure(0, weight=1) self.imageViewer.grid_columnconfigure(1, weight=0) self.imageViewer.grid_rowconfigure(0, weight=1) @@ -258,7 +259,7 @@ class mainWindow(Tk): # The terminal to enter the MRZ - self.terminal = ttk.Labelframe(self, text='Terminal de saisie de MRZ complète') + self.terminal = ttk.Labelframe(self, text=lang.all[globs.CNIRlang]["Complete MRZ capture terminal"]) self.terminal.grid_columnconfigure(0, weight=1) self.terminal.grid_rowconfigure(0, weight=1) self.termframe = Frame(self.terminal) @@ -272,7 +273,7 @@ class mainWindow(Tk): self.termtext.grid(column=0, row=0, sticky='SW', padx=5, pady=25) # Speed Entry Zone for 731 - self.terminal2 = ttk.Labelframe(self, text='Terminal de saisie rapide (731)') + self.terminal2 = ttk.Labelframe(self, text=lang.all[globs.CNIRlang]["Quick entry terminal (731)"]) self.terminal2.grid_columnconfigure(0, weight=1) self.terminal2.grid_rowconfigure(0, weight=1) self.speed731 = Frame(self.terminal2) @@ -294,7 +295,7 @@ class mainWindow(Tk): self.speedResult.grid(column=2, row=0, sticky='NEW', padx=5, pady=5) # The monitor that indicates some useful infos - self.monitor = ttk.Labelframe(self, text='Moniteur') + self.monitor = ttk.Labelframe(self, text=lang.all[globs.CNIRlang]["Monitor"]) self.monlog = Text((self.monitor), state='disabled', width=60, height=10, wrap='word') self.monlog.grid(column=0, row=0, sticky='EWNS', padx=5, pady=5) self.scrollb = ttk.Scrollbar((self.monitor), command=(self.monlog.yview)) @@ -313,17 +314,17 @@ class mainWindow(Tk): # What is a window without a menu bar ? menubar = Menu(self) menu1 = Menu(menubar, tearoff=0) - menu1.add_command(label='Nouveau', command=(self.newEntry)) - menu1.add_command(label='Ouvrir scan...', command=(self.openingScan)) + menu1.add_command(label=lang.all[globs.CNIRlang]["New"], command=(self.newEntry)) + menu1.add_command(label=lang.all[globs.CNIRlang]["Open scan..."], command=(self.openingScan)) menu1.add_separator() - menu1.add_command(label='Quitter', command=(self.destroy)) - menubar.add_cascade(label='Fichier', menu=menu1) + menu1.add_command(label=lang.all[globs.CNIRlang]["Quit"], command=(self.destroy)) + menubar.add_cascade(label=lang.all[globs.CNIRlang]["File"], menu=menu1) menu3 = Menu(menubar, tearoff=0) - menu3.add_command(label='Commandes au clavier', command=(self.helpbox)) - menu3.add_command(label='Signaler un problème', command=(self.openIssuePage)) + menu3.add_command(label=lang.all[globs.CNIRlang]["Keyboard commands"], command=(self.helpbox)) + menu3.add_command(label=lang.all[globs.CNIRlang]["Report a bug"], command=(self.openIssuePage)) menu3.add_separator() - menu3.add_command(label='A propos de CNIRevelator', command=(self.infobox)) - menubar.add_cascade(label='Aide', menu=menu3) + menu3.add_command(label=lang.all[globs.CNIRlang]["About CNIRevelator"], command=(self.infobox)) + menubar.add_cascade(label=lang.all[globs.CNIRlang]["Help"], menu=menu3) self.config(menu=menubar) # The title @@ -347,6 +348,7 @@ class mainWindow(Tk): self.geometry('%dx%d+%d+%d' % (w, h, x, y)) self.update() self.deiconify() + self.attributes("-topmost", 1) self.minsize(self.winfo_width(), self.winfo_height()) # Set image @@ -364,6 +366,9 @@ class mainWindow(Tk): self.imageViewer.ZONE.bind("", self.rectangleSelectScan) logfile.printdbg('Initialization successful') + + if globs.CNIROpenFile: + self.after_idle(lambda : self.openScanFile(sys.argv[1])) def statusUpdate(self, image=None, setplace=False): if image: @@ -374,12 +379,12 @@ class mainWindow(Tk): def rectangleSelectScan(self, event): if self.imageViewer.image: canvas = event.widget - print("Get coordinates : [{}, {}], for [{}, {}]".format(canvas.canvasx(event.x), canvas.canvasy(event.y), event.x, event.y)) + #print("Get coordinates : [{}, {}], for [{}, {}]".format(canvas.canvasx(event.x), canvas.canvasy(event.y), event.x, event.y)) self.corners.append([canvas.canvasx(event.x), canvas.canvasy(event.y)]) if len(self.corners) == 2: self.select = self.imageViewer.ZONE.create_rectangle(self.corners[0][0], self.corners[0][1], self.corners[1][0], self.corners[1][1], outline ='cyan', width = 2) - print("Get rectangle : [{}, {}], for [{}, {}]".format(self.corners[0][0], self.corners[0][1], self.corners[1][0], self.corners[1][1])) + #print("Get rectangle : [{}, {}], for [{}, {}]".format(self.corners[0][0], self.corners[0][1], self.corners[1][0], self.corners[1][1])) if len(self.corners) > 2: self.corners = [] self.imageViewer.ZONE.delete(self.select) @@ -434,7 +439,7 @@ class mainWindow(Tk): invite.focus_force() self.wait_window(invite) - print("text : {}".format(self.validatedtext)) + #print("text : {}".format(self.validatedtext)) self.mrzChar = "" @@ -445,7 +450,7 @@ class mainWindow(Tk): self.mrzChar = self.mrzChar + char self.stringValidation("") - print(self.mrzChar) + #print(self.mrzChar) # Reinstall tesseract except pytesseract.TesseractNotFoundError as e: @@ -453,13 +458,13 @@ class mainWindow(Tk): shutil.rmtree(globs.CNIRTesser) except Exception: pass - showerror('Erreur de module OCR', ('Le module OCR localisé en ' + str(os.environ['PATH']) + 'est introuvable ou corrompu. Il sera réinstallé à la prochaine exécution'), parent=self) - logfile.printerr("Tesseract error : {}. Will be reinstallated".format(e)) + showerror(lang.all[globs.CNIRlang]["OCR module error"], (lang.all[globs.CNIRlang]["The OCR module located at {} can not be found or corrupted. It will be reinstalled at the next run"].format(os.environ['PATH'])), parent=self) + logfile.printerr(lang.all[globs.CNIRlang]["Tesseract error : {}. Will be reinstallated"].format(e)) # Tesseract error except pytesseract.TesseractError as e: logfile.printerr("Tesseract error : {}".format(e)) - showerror('Erreur de module OCR', ("Le module Tesseract a rencontré un problème : {}".format(e)), parent=self) + showerror(lang.all[globs.CNIRlang]["OCR module error"], (lang.all[globs.CNIRlang]["The Tesseract module encountered a problem: {}"].format(e)), parent=self) def stringValidation(self, keysym): @@ -478,11 +483,11 @@ class mainWindow(Tk): self.wait_window(invite) - self.logOnTerm("Document detecté : {}\n".format(candidates[invite.choice][2])) + self.logOnTerm(lang.all[globs.CNIRlang]["Document detected: {}\n"].format(candidates[invite.choice][2])) self.mrzDecided = candidates[invite.choice] elif len(candidates) == 1: - self.logOnTerm("Document detecté : {}\n".format(candidates[0][2])) + self.logOnTerm(lang.all[globs.CNIRlang]["Document detected: {}\n"].format(candidates[0][2])) self.mrzDecided = candidates[0] else: # corrects some problems @@ -569,11 +574,11 @@ class mainWindow(Tk): self.wait_window(invite) - self.logOnTerm("Document re-detecté : {}\n".format(candidates[invite.choice][2])) + self.logOnTerm(lang.all[globs.CNIRlang]["Document detected again: {} \ n"].format(candidates[invite.choice][2])) self.mrzDecided = candidates[invite.choice] elif len(candidates) == 1: - self.logOnTerm("Document re-detecté : {}\n".format(candidates[0][2])) + self.logOnTerm(lang.all[globs.CNIRlang]["Document detected again: {} \ n"].format(candidates[0][2])) self.mrzDecided = candidates[0] return "break" @@ -583,7 +588,7 @@ class mainWindow(Tk): regex = re.compile("[A-Z]|<|[0-9]") # match ! if not regex.fullmatch(event.char): - self.logOnTerm("Caractère non accepté !\n") + self.logOnTerm(lang.all[globs.CNIRlang]["Character not accepted !\n"]) return "break" # Adds the entry tempChar = self.termtext.get("1.0", "end")[:-1] @@ -650,10 +655,22 @@ class mainWindow(Tk): def openingScan(self): path = '' - path = filedialog.askopenfilename(parent=self, title='Ouvrir un scan de CNI...', filetypes=(('TIF files', '*.tif'), + path = filedialog.askopenfilename(parent=self, title=lang.all[globs.CNIRlang]["Open a scan of document..."], filetypes=(('TIF files', '*.tif'), ('TIF files', '*.tiff'), ('JPEG files', '*.jpg'), ('JPEG files', '*.jpeg'))) + self.openScanFile(path) + + def openScanFile(self, path): + + # Check if the file is valid + if ( path[-3:] != 'jpg' + and path[-3:] != 'tif' + and path[-4:] != 'jpeg' + and path[-4:] != 'tiff' ) or not os.path.isfile(path): + showerror(lang.all[globs.CNIRlang]["Open a scan of document..."], lang.all[globs.CNIRlang]["The file you provided is not valid : {}"].format(path)) + return + # Load an image using OpenCV self.imageViewer.imagePath = path self.imageViewer.imgZoom = 1 @@ -685,7 +702,7 @@ class mainWindow(Tk): # Use PIL (Pillow) to convert the NumPy ndarray to a PhotoImage photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(cv_img)) - self.statusUpdate(photo) + self.statusUpdate(photo) def zoomInScan50(self, quantity = 50): if self.imageViewer.image: @@ -792,7 +809,7 @@ class mainWindow(Tk): self.statusUpdate( photo) except Exception as e: logfile.printerr("Error with opencv : {}".format(e)) - traceback.print_exc(file=sys.stdout) + ihm.crashCNIR() try: # Reload an image using OpenCV path = self.imageViewer.imagePath @@ -810,70 +827,37 @@ class mainWindow(Tk): self.statusUpdate(photo) except Exception as e: logfile.printerr("Critical error with opencv : ".format(e)) - traceback.print_exc(file=sys.stdout) - showerror("Erreur OpenCV (traitement d'images)", "Une erreur critique s'est produite dans le gestionnaire de traitement d'images OpenCV utilisé par CNIRevelator. L'application va se réinitialiser") + ihm.crashCNIR() + showerror(lang.all[globs.CNIRlang]["OpenCV error (image processing)"], lang.all[globs.CNIRlang]["A critical error has occurred in the OpenCV image processing manager used by CNIRevelator, the application will reset itself"]) self.initialize() def newEntry(self): self.initialize() - self.logOnTerm('\n\nEntrez la première ligne de MRZ svp \n') + self.logOnTerm('\n\n{}\n'.format(lang.all[globs.CNIRlang]["Please type a MRZ or open a scan"])) def infobox(self): + Tk().withdraw() - showinfo('A propos de CNIRevelator', - ( 'Version du logiciel : CNIRevelator ' + globs.verstring_full + '\n\n' - "Copyright © 2018-2019 Adrien Bourmault (neox95)" + "\n\n" - "CNIRevelator est un logiciel libre : vous avez le droit de le modifier et/ou le distribuer " - "dans les termes de la GNU General Public License telle que publiée par " - "la Free Software Foundation, dans sa version 3 ou " - "ultérieure. " + "\n\n" - "CNIRevelator est distribué dans l'espoir d'être utile, sans toutefois " - "impliquer une quelconque garantie de " - "QUALITÉ MARCHANDE ou APTITUDE À UN USAGE PARTICULIER. Référez vous à la " - "GNU General Public License pour plus de détails à ce sujet. " - "\n\n" - "Vous devriez avoir reçu une copie de la GNU General Public License " - "avec CNIRevelator. Si cela n'est pas le cas, jetez un oeil à . " - "\n\n" - "Le module d'OCR Tesseract 4.0 est soumis à l'Apache License 2004." - "\n\n" - "Les bibliothèques python et l'environnement Anaconda 3 sont soumis à la licence BSD 2018-2019." - "\n\n" - "Le code source de ce programme est disponible sur Github à l'adresse .\n" - "Son fonctionnement est conforme aux normes et directives du document 9303 de l'OACI régissant les documents de voyages et d'identité." + '\n\n' - " En cas de problèmes ou demande particulière, ouvrez-y une issue ou bien envoyez un mail à neox@os-k.eu !\n\n" + showinfo( lang.all[globs.CNIRlang]["About CNIRevelator"], + ( + lang.all[globs.CNIRlang]["ABOUT"] ), - parent=self) def helpbox(self): Tk().withdraw() - showinfo('Aide sur les contrôles au clavier', - ( "Terminal de saisie rapide (731) : \n\n" - " Caractères autorisés : Alphanumériques en majuscule et le caractère '<'. Pas de minuscules ni caractères spéciaux, autrement la somme est mise à zéro \n\n" - " Calculer résultat :\t\t\tTouche Ctrl droite \n" - " Copier :\t\t\t\tCtrl-C \n" - " Coller :\t\t\t\tCtrl-V \n" - "\n\n" - "Terminal de saisie MRZ complète : \n\n" - " Caractères autorisés : Alphanumériques en majuscule et le caractère '<'. Pas de minuscules ni caractères spéciaux, autrement la somme est mise à zéro \n\n" - " Calculer résultat :\t\t\tTouche Ctrl droite \n" - " Compléter champ :\t\t\tTouche Tab \n" - " Copier :\t\t\t\tCtrl-C \n" - " Coller :\t\t\t\tCtrl-V \n" - " Forcer une nouvelle détection du document :\tEchap\n" + showinfo( lang.all[globs.CNIRlang]["Keyboard commands"], + ( + lang.all[globs.CNIRlang]["KEYBHELP"] ), parent=self) def openIssuePage(self): - self.openBrowser("https://github.com/neox95/CNIRevelator/issues") + webbrowser.open_new("https://github.com/neox95/CNIRevelator/issues") - def openBrowser(self, url): - webbrowser.open_new(url) - def computeSigma(self): """ Launch the checksum computation, infos validation and display the results @@ -890,7 +874,7 @@ class mainWindow(Tk): self.termtext.tag_remove("nonconforme", "1.0", "end") self.clearTerm() - self.logOnTerm("Examen du document : {}\n\n".format(self.mrzDecided[2])) + self.logOnTerm(lang.all[globs.CNIRlang]["Document Review: {}\n\n"].format(self.mrzDecided[2])) for sum in allSums: x = sum[1] // len(self.mrzDecided[0][0]) +1 @@ -898,7 +882,7 @@ class mainWindow(Tk): #print("index : {}.{}".format(x,y)) #print("{} == {}".format(code[sum[1]], sum[2])) - self.logOnTerm("Somme de contrôle position {} : Lu {} VS Calculé {}\n".format(sum[1], code[sum[1]], sum[2])) + self.logOnTerm(lang.all[globs.CNIRlang]["Check sum position {}: Lu {} VS Calculated {} and {}\n"].format(sum[1], code[sum[1]], sum[2], sum[3])) # if sum is facultative or if sum is ok try: @@ -920,7 +904,7 @@ class mainWindow(Tk): # display the infos for key in [ e for e in docInfos ]: #print(docInfos[key]) - if key in ["CODE", "CTRL"]: + if key in ["CODE", "CTRL", "CTRLF"]: continue if not docInfos[key] == False: self.infoList[key]['text'] = docInfos[key] @@ -933,10 +917,10 @@ class mainWindow(Tk): self.compliance = False if self.compliance == True: - self.STATUStxt["text"] = "CONFORME" + self.STATUStxt["text"] = lang.all[globs.CNIRlang]["COMPLIANT"] self.STATUStxt["foreground"] = "green" else: - self.STATUStxt["text"] = "NON CONFORME" + self.STATUStxt["text"] = lang.all[globs.CNIRlang]["IMPROPER"] self.STATUStxt["foreground"] = "red" return diff --git a/src/mrz.py b/src/mrz.py index e51fad6..1ce646d 100644 --- a/src/mrz.py +++ b/src/mrz.py @@ -24,11 +24,12 @@ ******************************************************************************** """ -import re -import logger # logger.py import re import datetime +import logger # logger.py +import lang # lang.py + ## SEX CODES sexcode = {'M':'Homme', 'F':'Femme', 'X':'Non spécifié'} diff --git a/src/updater.py b/src/updater.py index b41c16b..0840c78 100644 --- a/src/updater.py +++ b/src/updater.py @@ -24,7 +24,6 @@ """ from win32com.client import Dispatch -import traceback import sys import time import os @@ -38,6 +37,7 @@ import ihm # ihm.py import logger # logger.py import globs # globs.py import downloader # downloader.py +import lang # lang.py UPDATE_IS_MADE = False UPATH = ' ' @@ -81,47 +81,6 @@ def exitProcess(arg): process.terminate() sys.exit(arg) -def runPowershell(scriptblock, cwd=os.getcwd()): - """ - Executes a powershell command - """ - log.debug("Running PowerShell Block:\r\n%s", scriptblock) - log.debug("Current Directory: %s\r\n" % cwd) - psProc = subprocess.Popen([r'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe', - '-ExecutionPolicy', 'Bypass', - '-noprofile', - '-c', '-',], - cwd=cwd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdoutdata, stderrdata = psProc.communicate(scriptblock) - - if stdoutdata: - log.debug("Script Output:\r\n%s" % stdoutdata) - elif not stderrdata: - log.debug("Script completed succssfully (no stdout/stderr).") - if stderrdata: - log.error("Script Error:\r\n%s" % stderrdata) - - return stdoutdata, stderrdata - - -def getCertificates(server_list, location="LocalMachine", store="My"): - """ - Returns the json data of all installed certificates - """ - cmd = ''' -$sb = { ls Cert:\%s\%s | Select Subject,ThumbPrint } -$Servers = '%s' | ConvertFrom-Json - -Invoke-Command -ComputerName $Servers -ScriptBlock $sb -Authentication Negotiate | ConvertTo-Json -Depth 999 - ''' % (location, store, json.dumps(server_list)) - stdoutdata, stderrdata = runPowershell(cmd) - return json.loads(stdoutdata) - - - def getLatestVersion(credentials): """ Returns the latest version of the software @@ -228,7 +187,7 @@ def tessInstall(PATH, credentials): # Unzip Tesseract logfile.printdbg("Unzipping the package") - launcherWindow.printmsg('Installing the updates') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Installing the updates"]) zip_ref = zipfile.ZipFile(PATH + '\\downloads\\TsrtPackage.zip', 'r') zip_ref.extractall(PATH) zip_ref.close() @@ -261,7 +220,7 @@ def batch(credentials): getTheUpdate = downloader.newdownload(credentials, finalurl, globs.CNIRFolder + '\\downloads\\CNIPackage.zip', "CNIRevelator {}.{}.{}".format(finalver[0], finalver[1], finalver[2])).download() - launcherWindow.printmsg('Verifying download...') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Verifying download..."]) # CHECKSUM BUF_SIZE = 65536 # lets read stuff in 64kb chunks! @@ -286,7 +245,7 @@ def batch(credentials): global UPATH UPATH = globs.CNIRFolder + '\\..\\CNIRevelator' + "{}.{}.{}".format(finalver[0], finalver[1], finalver[2]) logfile.printdbg("Make place") - launcherWindow.printmsg('Preparing installation...') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Preparing installation..."]) # Cleanup try: shutil.rmtree(UPATH + 'temp') @@ -298,7 +257,7 @@ def batch(credentials): logfile.printdbg('Unable to cleanup : ' +str(e)) # Unzip logfile.printdbg("Unzipping the package") - launcherWindow.printmsg('Installing the updates') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Installing the updates"]) zip_ref = zipfile.ZipFile(globs.CNIRFolder + '\\downloads\\CNIPackage.zip', 'r') zip_ref.extractall(UPATH + "temp") zip_ref.close() @@ -309,9 +268,15 @@ def batch(credentials): logfile.printdbg('Extracted :' + UPATH + '\\CNIRevelator.exe') # Make a shortcut - createShortcut("CNIRevelator.lnk", UPATH + '\\CNIRevelator.exe', UPATH) + # hide main window + root = Tk() + root.withdraw() + res = askquestion(lang.all[globs.CNIRlang]["Shortcut creation"], lang.all[globs.CNIRlang]["Would you like to create/update the shortcut for CNIRevelator on your desktop ?"]) + if res == "yes": + createShortcut("CNIRevelator.lnk", UPATH + '\\CNIRevelator.exe', UPATH) + root.destroy() - launcherWindow.printmsg('Success !') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Success !"]) # Cleanup try: @@ -319,7 +284,7 @@ def batch(credentials): except: pass # Time to quit - launcherWindow.printmsg('Launched the new process.') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Launching the new version..."]) global UPDATE_IS_MADE UPDATE_IS_MADE = True return True @@ -335,19 +300,19 @@ def umain(): if not credentials.valid: logfile.printerr("Credentials Error. No effective update !") - launcherWindow.printmsg('Credentials Error. No effective update !') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Credentials Error. No effective update !"]) time.sleep(2) launcherWindow.exit() return 0 # Cleaner for the old version if detected - if len(sys.argv) > 2: + if len(sys.argv) > 2 and str(sys.argv[1]) == "DELETE": globs.CNIRNewVersion = True - launcherWindow.printmsg('Deleting old version !') + launcherWindow.printmsg(lang.all[globs.CNIRlang]["Deleting old version"]) logfile.printdbg("Old install detected : {}".format(sys.argv[1])) while os.path.exists(str(sys.argv[2])): try: - os.remove(str(sys.argv[2])) + shutil.rmtree(str(sys.argv[2])) except Exception as e: logfile.printerr(str(e)) logfile.printdbg('Trying stop the process !') @@ -361,45 +326,23 @@ def umain(): else: logfile.printdbg('Terminating process !') process.terminate() - os.remove(str(sys.argv[2])) + shutil.rmtree(str(sys.argv[2])) break except Exception as e: logfile.printerr(str(e)) launcherWindow.printmsg('Fail :{}'.format(e)) - launcherWindow.printmsg('Starting...') - elif len(sys.argv) > 1: - globs.CNIRNewVersion = True - launcherWindow.printmsg('Deleting old version !') - logfile.printdbg("Old install detected : {}".format(sys.argv[1])) - while os.path.exists(str(sys.argv[1])): - try: - shutil.rmtree(str(sys.argv[1])) - except Exception as e: - logfile.printerr(str(e)) - logfile.printdbg('Trying stop the process !') - launcherWindow.printmsg('Fail :{}'.format(e)) - try: - for process in psutil.process_iter(): - if process.name() == 'CNIRevelator.exe': - logfile.printdbg('Process found. Command line: {}'.format(process.cmdline())) - if process.pid == os.getpid(): - logfile.printdbg("Don't touch us ! {} = {}".format(process.pid, os.getpid())) - else: - logfile.printdbg('Terminating process !') - process.terminate() - shutil.rmtree(str(sys.argv[1])) - break - except Exception as e: - logfile.printerr(str(e)) - launcherWindow.printmsg('Fail :{}'.format(e)) - launcherWindow.printmsg('Starting...') + launcherWindow.printmsg(lang.all[globs.CNIRlang]['Starting...']) + + # check we want open a file + elif len(sys.argv) > 1 and str(sys.argv[1]) != "DELETE": + globs.CNIROpenFile = True try: try: # EXECUTING THE UPDATE BATCH success = batch(credentials) except Exception as e: - logfile.printerr("An error occured on the thread : " + str(traceback.format_exc())) + ihm.crashCNIR() launcherWindow.printmsg('ERROR : ' + str(e)) time.sleep(3) launcherWindow.exit() @@ -410,7 +353,7 @@ def umain(): launcherWindow.printmsg('Software is up-to-date !') else: logfile.printerr("An error occured. No effective update !") - launcherWindow.printmsg('An error occured. No effective update !') + launcherWindow.printmsg(lang.all[globs.CNIRlang]['An error occured. No effective update !']) time.sleep(2) launcherWindow.exit() return 0 @@ -419,7 +362,7 @@ def umain(): launcherWindow.exit() return 0 except: - logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc())) + ihm.crashCNIR() launcherWindow.exit() sys.exit(2) return 2 @@ -429,7 +372,7 @@ def umain(): # INSTALLING TESSERACT OCR success = tessInstall(globs.CNIRFolder, credentials) except Exception as e: - logfile.printerr("An error occured on the thread : " + str(traceback.format_exc())) + ihm.crashCNIR() launcherWindow.printmsg('ERROR : ' + str(e)) time.sleep(3) launcherWindow.exit() @@ -437,16 +380,16 @@ def umain(): if success: logfile.printdbg("Software is up-to-date !") - launcherWindow.printmsg('Software is up-to-date !') + launcherWindow.printmsg(lang.all[globs.CNIRlang]['Software is up-to-date !']) else: logfile.printerr("An error occured. No effective update !") - launcherWindow.printmsg('An error occured. No effective update !') + launcherWindow.printmsg(lang.all[globs.CNIRlang]['An error occured. No effective update !']) time.sleep(2) launcherWindow.exit() return 0 except: - logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc())) + ihm.crashCNIR() launcherWindow.exit() sys.exit(2) return 2