Merge pull request #14 from neox95/v3.1

V3.1.1 release
This commit is contained in:
Adrien Bourmault 2019-08-20 10:29:59 +02:00 committed by GitHub
commit 9bc808d785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2159 additions and 894 deletions

View File

@ -1,2 +1,2 @@
# ver|url|checksum, and | as separator, one version per ||
# ver|url|checksum, and | as separator, one version per ||
3.1.2|https://github.com/neox95/CNIRevelator/releases/download/3.1.1/CNIRevelator.zip|cb2f4999e7265297008074e0e2beacbb2943a004||

View File

@ -23,22 +23,26 @@
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
********************************************************************************
"""
import sys
# Import critical files
import os
import subprocess
import threading
import traceback
import psutil
import launcher # launcher.py"
import updater # updater.py
import globs # globs.py
import pytesseract # pytesseract.py
import lang # lang.py
import logger # logger.py
import globs # globs.py
import critical # critical.py
# Import all other files and crash if necessary
try:
from main import * # main.py
import launcher # launcher.py"
import updater # updater.py
import pytesseract # pytesseract.py
import ihm # ihm.py
from tkinter.messagebox import *
from tkinter import *
except:
critical.crashCNIR()
# Global handler
logfile = logger.logCur
@ -59,15 +63,17 @@ 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"]))
# changelog
if globs.CNIRNewVersion:
showinfo('Changelog : résumé de mise à jour', ('Version du logiciel : CNIRevelator ' + globs.verstring_full + '\n\n' + globs.changelog), parent=mainw)
mainw.after_idle(mainw.showChangeLog)
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 ! ***')
@ -75,30 +81,45 @@ def main():
## BOOTSTRAP OF CNIREVELATOR
try:
launcherThread = threading.Thread(target=updater.umain, daemon=False)
launcher.lmain(launcherThread)
except Exception:
updater.exitProcess(1)
if updater.UPDATE_IS_MADE:
# Launch app !
args = updater.UPATH + '\\CNIRevelator.exe ' + globs.CNIRFolder
cd = updater.UPATH
for i in range(0,3):
try:
updater.spawnProcess(args, cd)
except:
time.sleep(3)
continue
break
updater.exitProcess(0)
try:
# LANGUAGE
lang.readLang()
except:
critical.crashCNIR()
updater.exitProcess(1)
# Here we go !
try:
main()
except Exception as e:
traceback.print_exc(file=sys.stdout)
from main import * # main.py
# GO
try:
launcherThread = threading.Thread(target=updater.umain, daemon=False)
launcher.lmain(launcherThread)
except Exception:
critical.crashCNIR()
updater.exitProcess(1)
if updater.UPDATE_IS_MADE:
# Launch app !
args = updater.UPATH + '\\CNIRevelator.exe' + " DELETE " + globs.CNIRFolder
cd = updater.UPATH
for i in range(0,3):
try:
updater.spawnProcess(args, cd)
except:
time.sleep(3)
continue
break
updater.exitProcess(0)
# Here we go !
try:
main()
except Exception as e:
critical.crashCNIR()
updater.exitProcess(1)
except:
critical.crashCNIR()
updater.exitProcess(0)

66
src/critical.py Normal file
View File

@ -0,0 +1,66 @@
"""
********************************************************************************
* CNIRevelator *
* *
* Desc: Critical Stuff for CNIRevelator *
* *
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
* *
* This file is part of CNIRevelator. *
* *
* CNIRevelator is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* any later version. *
* *
* CNIRevelator is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
********************************************************************************
"""
from tkinter.messagebox import *
from importlib import reload
from tkinter import *
import webbrowser
import traceback
import psutil
import os
import lang # lang.py
import logger # logger.py
import globs # globs.py
def crashCNIR(shutdown=True):
"""
very last solution
"""
try:
root = Tk()
root.withdraw()
logfile = logger.logCur
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"], parent=root)
res = askquestion(lang.all[globs.CNIRlang]["CNIRevelator Fatal Eror"], lang.all[globs.CNIRlang]["Would you like to open the log file ?"], parent=root)
if res == "yes":
webbrowser.open_new(globs.CNIRErrLog)
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 ?"], parent=root)
if res == "yes":
webbrowser.open_new("https://github.com/neox95/CNIRevelator/issues")
root.destroy()
# Quit ?
if not shutdown:
return
# Quit totally without remain in memory
for process in psutil.process_iter():
if process.pid == os.getpid():
process.terminate()
sys.exit(arg)
except:
traceback.print_exc()

View File

@ -32,9 +32,11 @@ from Crypto.Cipher import AES
from requests import Session
from time import time
import critical # critical.py
import logger # logger.py
import globs # globs.py
import ihm # ihm.py
import lang # lang.py
class AESCipher(object):
@ -184,8 +186,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 +203,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

View File

@ -26,12 +26,9 @@ import os
# CNIRevelator version
verType = "final release"
version = [3, 1, 0]
version = [3, 1, 1]
verstring_full = "{}.{}.{} {}".format(version[0], version[1], version[2], verType)
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"
CNIRTesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8'
CNIRFolder = os.getcwd()
@ -47,3 +44,28 @@ CNIRMainLog = CNIRFolder + '\\logs\\main.log'
CNIRUrlConfig = CNIRFolder + '\\config\\urlconf.ig'
CNIRVerStock = CNIRFolder + '\\downloads\\versions.lst'
CNIREnv = CNIRFolder + '\\Data\\'
CNIRTesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8'
CNIRFolder = os.path.dirname(os.path.realpath(__file__))
CNIRLColor = "#006699"
CNIRName = "CNIRevelator {}".format(verstring)
CNIRCryptoKey = '82Xh!efX3#@P~2eG'
CNIRLangFile = CNIRFolder + '\\config\\lang.ig'
CNIRlang = "fr"
CNIRConfig = CNIRFolder + '\\config\\conf.ig'
CNIRTesser = CNIRFolder + '\\Tesseract-OCR4\\'
CNIRErrLog = CNIRFolder + '\\logs\\error.log'
CNIRMainLog = CNIRFolder + '\\logs\\main.log'
CNIRUrlConfig = CNIRFolder + '\\config\\urlconf.ig'
CNIRVerStock = CNIRFolder + '\\downloads\\versions.lst'
CNIREnv = CNIRFolder + '\\Data\\'
CNIRBetaURL = "https://raw.githubusercontent.com/neox95/CNIRevelator/v3.1/VERSIONS.LST"
CNIRDefaultURL = "https://raw.githubusercontent.com/neox95/CNIRevelator/master/VERSIONS.LST"
CNIRNewVersion = False
CNIROpenFile = False
debug = True

View File

@ -1,4 +1,4 @@
"""
"""
********************************************************************************
* CNIRevelator *
* *
@ -23,19 +23,22 @@
********************************************************************************
"""
from tkinter import *
import webbrowser
from tkinter.messagebox import *
from tkinter import *
from tkinter import filedialog
from tkinter import ttk
import cv2
import PIL.Image, PIL.ImageTk
import traceback
import webbrowser
import cv2
import critical # critical.py
import logger # logger.py
import globs # globs.py
import lang # lang.py
import updater # updater.py
import critical # critical.py
controlKeys = ["Escape", "Right", "Left", "Up", "Down", "Home", "End", "BackSpace", "Delete", "Inser", "Shift_L", "Shift_R", "Control_R", "Control_L"]
@ -117,21 +120,22 @@ class LoginDialog(Toplevel):
self.login = ''
super().__init__(parent)
self.title(lang.all[globs.CNIRlang]["Connection"])
Label(self, text='IPN : ').pack()
self.entry_login = Entry(self)
self["background"] = "white"
Label(self, text='IPN : ', bg="white").pack(fill=Y)
self.entry_login = ttk.Entry(self)
self.entry_login.insert(0, '')
self.entry_login.pack()
Label(self, text='{} : '.format(lang.all[globs.CNIRlang]["Password"])).pack()
self.entry_pass = Entry(self, show='*')
Label(self, text='{} : '.format(lang.all[globs.CNIRlang]["Password"]), bg="white").pack(fill=Y)
self.entry_pass = ttk.Entry(self, show='*')
self.entry_pass.insert(0, '')
self.entry_pass.pack()
Button(self, text=lang.all[globs.CNIRlang]["Connection"], command=(self.connecti)).pack()
self.entry_pass.pack(fill=Y)
ttk.Button(self, text=lang.all[globs.CNIRlang]["Connection"], command=(self.connecti)).pack(fill=Y, pady=10)
self.update()
self.resizable(width=False, height=False)
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
w = hs / 10
h = ws / 18
self.update()
w = self.winfo_reqwidth() + 5
h = self.winfo_reqheight()
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
@ -146,6 +150,124 @@ class LoginDialog(Toplevel):
self.key = self.entry_pass.get().strip()
self.destroy()
class ChangelogDialog(Toplevel):
def __init__(self, parent, text):
super().__init__(parent)
self.title(lang.all[globs.CNIRlang]["Show Changelog"])
self["background"] = "white"
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
self.text = Text(self, wrap='word', width=55, height=15, borderwidth=0, font="TkDefaultFont", bg="white")
self.text.grid(column=0, row=0, sticky='EWNS', padx=5, pady=5)
ttk.Button(self, text="OK", command=(self.oki)).grid(column=0, row=1, pady=5)
self.scrollb = ttk.Scrollbar(self, command=(self.text.yview))
self.scrollb.grid(column=1, row=0, sticky='EWNS', padx=5, pady=5)
self.text['yscrollcommand'] = self.scrollb.set
self.text.insert('end', text)
self.text['state'] = 'disabled'
self.update()
self.resizable(width=False, height=False)
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
w = self.winfo_reqwidth() + 5
h = self.winfo_reqheight()
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
x = ws / 2 - w / 2
y = hs / 2 - h / 2
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.bind("<Return>", self.oki)
def oki(self, event=None):
self.destroy()
class langDialog(Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title(lang.all[globs.CNIRlang]["Language"])
Label(self, text=lang.all[globs.CNIRlang]["Please choose your language : "]).pack(fill=Y)
vals = [i for i in lang.all]
for i in range(len(lang.all)):
ttk.Radiobutton(self, text=vals[i], command=self.createRegister(i), value=vals[i]).pack(fill=Y)
ttk.Button(self, text="OK", command=(self.oki)).pack(fill=Y, pady=5)
self.update()
self.resizable(width=False, height=False)
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
w = self.winfo_reqwidth() + 5
h = self.winfo_reqheight()
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
x = ws / 2 - w / 2
y = hs / 2 - h / 2
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.bind("<Return>", self.oki)
def oki(self, event=None):
self.destroy()
def createRegister(self, i):
def register():
lang.updateLang([j for j in lang.all][i])
return register
class updateSetDialog(Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title(lang.all[globs.CNIRlang]["Update options"])
Label(self, text=lang.all[globs.CNIRlang]["Please choose your update channel : "]).pack(fill=Y)
self.vals = ["Stable", "Beta"]
vals = self.vals
for i in range(len(vals)):
ttk.Radiobutton(self, text=vals[i], command=self.createRegister(i), value=vals[i]).pack(fill=Y)
ttk.Button(self, text="OK", command=(self.oki)).pack(fill=Y, pady=5)
self.update()
self.resizable(width=False, height=False)
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
w = self.winfo_reqwidth() + 5
h = self.winfo_reqheight()
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
x = ws / 2 - w / 2
y = hs / 2 - h / 2
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.bind("<Return>", self.oki)
def oki(self, event=None):
self.destroy()
def createRegister(self, i):
def register():
updater.updateChannel(self.vals[i])
return register
class LauncherWindow(Tk):
def __init__(self):
@ -153,6 +275,12 @@ class LauncherWindow(Tk):
Tk.__init__(self)
self.resizable(width=False, height=False)
# icon
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
# Setting up the geometry
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
@ -201,11 +329,6 @@ class LauncherWindow(Tk):
self.update()
self.progressBar.grid(row=1, sticky='S')
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
logfile = logger.logCur
logfile.printdbg('Launcher IHM successful')
self.protocol('WM_DELETE_WINDOW', lambda : 0)
@ -233,22 +356,20 @@ class ResizeableCanvas(Canvas):
# 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()
class StatusBar(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
self.label.pack(fill=X)
def set(self, text):
self.label.config(text="Document : " + text.lower())
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
## Global Handler
launcherWindowCur = LauncherWindow()

1465
src/lang.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -26,12 +26,13 @@
import sys
import os
import threading
import traceback
import critical # critical.py
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 +45,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

View File

@ -26,6 +26,7 @@
import logging
import os
import critical # critical.py
import globs # globs.py
## The logging class

View File

@ -31,29 +31,37 @@ from tkinter import filedialog
from tkinter import ttk
import threading
from datetime import datetime
from importlib import reload
import re
import traceback
import cv2
import PIL.Image, PIL.ImageTk
import os, shutil
import webbrowser
import sys, os
import critical # critical.py
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
class mainWindow(Tk):
## App Pattern
def __init__(self):
Tk.__init__(self)
self.initialize()
def initialize(self):
"""
Initializes the main window
"""
self.mrzChar = ""
self.mrzDecided = False
self.Tags = []
@ -61,6 +69,12 @@ class mainWindow(Tk):
self.corners = []
self.validatedtext = ""
# The icon
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
# Hide during construction
self.withdraw()
@ -70,15 +84,16 @@ class mainWindow(Tk):
logfile.printdbg('Launching main window with resolution' + str(ws) + 'x' + str(hs))
# Configuring the size of each part of the window
self.grid_columnconfigure(0, weight=1, minsize=(ws / 2 * 0.3333333333333333))
self.grid_columnconfigure(1, weight=1, minsize=(ws / 2 * 0.3333333333333333))
self.grid_columnconfigure(0, minsize=(ws / 2 * 0.3333333333333333))
self.grid_columnconfigure(1, minsize=(ws / 2 * 0.3333333333333333))
self.grid_columnconfigure(2, weight=1, minsize=(ws / 2 * 0.3333333333333333))
self.grid_rowconfigure(0, weight=1, minsize=(hs / 2 * 0.5))
self.grid_rowconfigure(1, weight=1, minsize=(hs / 2 * 0.10))
self.grid_rowconfigure(2, weight=1, minsize=(hs / 2 * 0.35))
self.grid_rowconfigure(0, minsize=(hs / 2 * 0.5))
self.grid_rowconfigure(1, minsize=(hs / 2 * 0.10))
self.grid_rowconfigure(2, minsize=(hs / 2 * 0.35))
self.grid_rowconfigure(3, minsize=10)
# 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)
@ -90,52 +105,57 @@ class mainWindow(Tk):
self.lecteur_ci.grid_rowconfigure(3, weight=1)
self.lecteur_ci.grid_rowconfigure(4, weight=1)
self.lecteur_ci.grid_rowconfigure(5, weight=1)
# And what about the status bar ?
self.statusbar = ihm.StatusBar(self)
self.statusbar.grid(row=3, columnspan=3, sticky="NSEW")
# 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.statusbar.set(lang.all[globs.CNIRlang]["IDLE"])
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 +173,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 +278,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 +292,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 +314,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,41 +333,45 @@ 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)
menu2 = Menu(menubar, tearoff=0)
menu2.add_command(label=lang.all[globs.CNIRlang]["Update options"], command=(self.updateSet))
menu2.add_command(label=lang.all[globs.CNIRlang]["Show Changelog"], command=(self.showChangeLog))
menu2.add_separator()
menu2.add_command(label=lang.all[globs.CNIRlang]["Language"], command=(self.languageSet))
menubar.add_cascade(label=lang.all[globs.CNIRlang]["Settings"], menu=menu2)
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)
menubar.add_command(label="<|>", command=(self.panelResize))
self.config(menu=menubar)
# The title
self.wm_title(globs.CNIRName)
# The icon
if getattr(sys, 'frozen', False):
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
else:
self.iconbitmap('id-card.ico')
# Make this window resizable and set her size
self.resizable(width=True, height=True)
self.resizable(0, 0)
self.update()
w = int(self.winfo_reqwidth())
h = int(self.winfo_reqheight())
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
x = (ws - w)/2
y = (hs - h)/2
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.w = int(self.winfo_reqwidth())
self.h = int(self.winfo_reqheight())
self.ws = self.winfo_screenwidth()
self.hs = self.winfo_screenheight()
self.x = (self.ws - self.w)/2
self.y = (self.hs - self.h)/2
self.geometry('%dx%d+%d+%d' % (self.w, self.h, self.x, self.y))
self.update()
self.deiconify()
self.minsize(self.winfo_width(), self.winfo_height())
self.attributes("-topmost", 1)
self.maxsize(self.w, self.h)
self.minsize(int(2.15 * (self.ws / 2 * 0.3333333333333333)), self.h)
self.currentw = self.w
# Set image
self.imageViewer.image = None
@ -364,27 +388,33 @@ class mainWindow(Tk):
self.imageViewer.ZONE.bind("<Button-1>", self.rectangleSelectScan)
logfile.printdbg('Initialization successful')
def statusUpdate(self, image=None, setplace=False):
if image:
self.imageViewer.image = image
self.imageViewer.ZONE.itemconfigure(self.STATUSimg, image=(self.imageViewer.image))
self.imageViewer.ZONE.configure(scrollregion=self.imageViewer.ZONE.bbox("all"))
if globs.CNIROpenFile:
self.after_idle(lambda : self.openScanFile(sys.argv[1]))
## OCR related functions
def rectangleSelectScan(self, event):
"""
Realises the selection of the MRZ to make OCR possible
"""
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)
def goOCRDetection(self):
"""
Realises the OCR detection and get the text in self.mrzChar (and prints it)
"""
if self.imageViewer.image:
cv_img = cv2.imreadmulti(self.imageViewer.imagePath)[1][self.imageViewer.pagenumber]
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
@ -434,7 +464,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 +475,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,16 +483,20 @@ 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)
## Regex and document detection + control related functions
def stringValidation(self, keysym):
"""
Analysis of the already typed document
"""
# analysis
# If we must decide the type of the document
if not self.mrzDecided:
@ -478,11 +512,13 @@ 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.statusbar.set(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.statusbar.set(candidates[0][2])
self.mrzDecided = candidates[0]
else:
# corrects some problems
@ -569,11 +605,13 @@ 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.statusbar.set(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.statusbar.set(candidates[0][2])
self.mrzDecided = candidates[0]
return "break"
@ -583,7 +621,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]
@ -625,35 +663,147 @@ class mainWindow(Tk):
char = self.speed731text.get()
self.speedResultPrint(str(mrz.computeControlSum(char)))
return "break"
def computeSigma(self):
"""
Launch the checksum computation, infos validation and print/display the results
"""
# the regex
regex = re.compile("[^A-Z0-9<]")
code = re.sub(regex, '', self.mrzChar)
self.compliance = True
allSums = mrz.computeAllControlSum(self.mrzDecided, code)["ctrlSumList"]
#print("Code : _{}_ | Sums : {}".format(code, allSums))
self.termtext.tag_remove("conforme", "1.0", "end")
self.termtext.tag_remove("nonconforme", "1.0", "end")
self.clearTerm()
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
y = sum[1] % len(self.mrzDecided[0][0])
#print("index : {}.{}".format(x,y))
#print("{} == {}".format(code[sum[1]], sum[2]))
if sum[3]:
self.logOnTerm(lang.all[globs.CNIRlang]["Checksum position {}: Lu {} VS Calculated {} [facultative]\n"].format(sum[1], code[sum[1]], sum[2]))
else:
self.logOnTerm(lang.all[globs.CNIRlang]["Checksum position {}: Lu {} VS Calculated {}\n"].format(sum[1], code[sum[1]], sum[2]))
# if sum is facultative or if sum is ok
try:
if sum[3] or int(code[sum[1]]) == int(sum[2]):
self.termtext.tag_add("conforme", "{}.{}".format(x,y), "{}.{}".format(x,y+1))
self.termtext.tag_configure("conforme", background="green", foreground="white")
else:
self.termtext.tag_add("nonconforme", "{}.{}".format(x,y), "{}.{}".format(x,y+1))
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
self.compliance = False
except ValueError:
self.termtext.tag_add("nonconforme", "{}.{}".format(x,y), "{}.{}".format(x,y+1))
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
self.compliance = False
# get the infos
docInfos = mrz.getDocInfos(self.mrzDecided, code)
#print(docInfos)
# display the infos
for key in [ e for e in docInfos ]:
#print(docInfos[key])
if key in ["CODE", "CTRL", "CTRLF"]:
continue
if not docInfos[key] == False:
self.infoList[key]['text'] = docInfos[key]
self.infoList[key]['background'] = self['background']
self.infoList[key]['foreground'] = "black"
else:
self.infoList[key]['background'] = "red"
self.infoList[key]['foreground'] = "white"
self.infoList[key]['text'] = "NC"
self.compliance = False
if self.compliance == True:
self.STATUStxt["text"] = lang.all[globs.CNIRlang]["COMPLIANT"]
self.STATUStxt["foreground"] = "green"
self.statusbar.set(lang.all[globs.CNIRlang]["COMPLIANT"])
else:
self.STATUStxt["text"] = lang.all[globs.CNIRlang]["IMPROPER"]
self.STATUStxt["foreground"] = "red"
self.statusbar.set(lang.all[globs.CNIRlang]["IMPROPER"])
## Print functions
def logOnTerm(self, text):
"""
Writes on the monitor
"""
self.monlog['state'] = 'normal'
self.monlog.insert('end', text)
self.monlog['state'] = 'disabled'
self.monlog.yview(END)
def clearTerm(self):
"""
Clears the monitor
"""
self.monlog['state'] = 'normal'
self.monlog.delete('1.0', 'end')
self.monlog['state'] = 'disabled'
self.monlog.yview(END)
def speedResultPrint(self, text):
"""
Prints a result in the quick entry terminal
"""
self.speedResult['state'] = 'normal'
self.speedResult.delete("1.0", 'end')
self.speedResult.insert('end', text)
self.speedResult['state'] = 'disabled'
## Document display related functions
def DisplayUpdate(self, image=None, setplace=False):
"""
Reload the displayer to display the image or not
"""
if image:
self.imageViewer.image = image
self.imageViewer.ZONE.itemconfigure(self.STATUSimg, image=(self.imageViewer.image))
self.imageViewer.ZONE.configure(scrollregion=self.imageViewer.ZONE.bbox("all"))
def goPageChoice(self, event):
"""
Change the current viewed page of the multipage tiff if needed
"""
self.imageViewer.pagenumber = int(self.toolbar.pageChooser.get()) - 1
self.resizeScan()
def openingScan(self):
"""
Open the scan, ask its path and displays it
"""
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):
"""
Open an image file at path to display it on the displayer
"""
# 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 +835,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.DisplayUpdate(photo)
def zoomInScan50(self, quantity = 50):
if self.imageViewer.image:
@ -746,6 +896,9 @@ class mainWindow(Tk):
self.resizeScan()
def negativeScan(self):
"""
Invert the bits to make a negative of the scan (and highlight the contrasts)
"""
if self.imageViewer.image:
# Load an image using OpenCV
cv_img = cv2.imreadmulti(self.imageViewer.imagePath)[1][self.imageViewer.pagenumber]
@ -760,6 +913,9 @@ class mainWindow(Tk):
self.resizeScan(cv_img)
def resizeScan(self, cv_img = None):
"""
Reloads the image according to settings
"""
if self.imageViewer.image:
try:
if not hasattr(cv_img, 'shape'):
@ -789,10 +945,10 @@ class mainWindow(Tk):
cv_img = cv2.resize(cv_img, dim, interpolation = cv2.INTER_AREA)
# 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.DisplayUpdate( photo)
except Exception as e:
logfile.printerr("Error with opencv : {}".format(e))
traceback.print_exc(file=sys.stdout)
critical.crashCNIR()
try:
# Reload an image using OpenCV
path = self.imageViewer.imagePath
@ -807,144 +963,97 @@ class mainWindow(Tk):
height, width, channels_no = cv_img.shape
# 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.DisplayUpdate(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")
critical.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()
## IHM and user interface related functions
def newEntry(self):
"""
Reinits the IHM and invite
"""
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):
"""
Shows the About dialog
"""
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 à <https://www.gnu.org/licenses/>. "
"\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 <https://github.com/neox95/CNIRevelator>.\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):
"""
Shows the keyboard help summary
"""
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")
"""
Opens the Github Issue Repository page
"""
webbrowser.open_new("https://github.com/neox95/CNIRevelator/issues")
def openBrowser(self, url):
webbrowser.open_new(url)
def showChangeLog(self):
changelogWin = ihm.ChangelogDialog(self, ('{} : CNIRevelator {}\n\n{}'.format(lang.all[globs.CNIRlang]["Program version"], globs.verstring_full, lang.all[globs.CNIRlang]["CHANGELOG"])))
changelogWin.transient(self)
changelogWin.grab_set()
changelogWin.focus_force()
self.wait_window(changelogWin)
def computeSigma(self):
def updateSet(self):
"""
Launch the checksum computation, infos validation and display the results
Update Settings
"""
# the regex
regex = re.compile("[^A-Z0-9<]")
code = re.sub(regex, '', self.mrzChar)
self.compliance = True
allSums = mrz.computeAllControlSum(self.mrzDecided, code)["ctrlSumList"]
#print("Code : _{}_ | Sums : {}".format(code, allSums))
self.termtext.tag_remove("conforme", "1.0", "end")
self.termtext.tag_remove("nonconforme", "1.0", "end")
self.clearTerm()
self.logOnTerm("Examen du document : {}\n\n".format(self.mrzDecided[2]))
for sum in allSums:
x = sum[1] // len(self.mrzDecided[0][0]) +1
y = sum[1] % len(self.mrzDecided[0][0])
#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]))
# if sum is facultative or if sum is ok
try:
if sum[3] or int(code[sum[1]]) == int(sum[2]):
self.termtext.tag_add("conforme", "{}.{}".format(x,y), "{}.{}".format(x,y+1))
self.termtext.tag_configure("conforme", background="green", foreground="white")
else:
self.termtext.tag_add("nonconforme", "{}.{}".format(x,y), "{}.{}".format(x,y+1))
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
self.compliance = False
except ValueError:
self.termtext.tag_add("nonconforme", "{}.{}".format(x,y), "{}.{}".format(x,y+1))
self.termtext.tag_configure("nonconforme", background="red", relief='raised', foreground="white")
self.compliance = False
# get the infos
docInfos = mrz.getDocInfos(self.mrzDecided, code)
#print(docInfos)
# display the infos
for key in [ e for e in docInfos ]:
#print(docInfos[key])
if key in ["CODE", "CTRL"]:
continue
if not docInfos[key] == False:
self.infoList[key]['text'] = docInfos[key]
self.infoList[key]['background'] = self['background']
self.infoList[key]['foreground'] = "black"
else:
self.infoList[key]['background'] = "red"
self.infoList[key]['foreground'] = "white"
self.infoList[key]['text'] = "NC"
self.compliance = False
if self.compliance == True:
self.STATUStxt["text"] = "CONFORME"
self.STATUStxt["foreground"] = "green"
changeupdateWin = ihm.updateSetDialog(self)
changeupdateWin.transient(self)
changeupdateWin.grab_set()
changeupdateWin.focus_force()
self.wait_window(changeupdateWin)
def languageSet(self):
"""
Lang Settings
"""
changelangWin = ihm.langDialog(self)
changelangWin.transient(self)
changelangWin.grab_set()
changelangWin.focus_force()
self.wait_window(changelangWin)
global mrz
mrz = reload(mrz)
self.initialize()
def panelResize(self):
"""
Shows or hides the panel
"""
if self.currentw > int(2.15 * (self.ws / 2 * 0.3333333333333333)):
self.currentw = int(2.15 * (self.ws / 2 * 0.3333333333333333))
self.geometry('%dx%d+%d+%d' % (self.currentw, self.h, self.x, self.y))
self.update()
else:
self.STATUStxt["text"] = "NON CONFORME"
self.STATUStxt["foreground"] = "red"
return
self.currentw = self.w
self.geometry('%dx%d+%d+%d' % (self.currentw, self.h, self.x, self.y))
self.update()

View File

@ -24,537 +24,22 @@
********************************************************************************
"""
import re
import logger # logger.py
import re
import datetime
import logger # logger.py
import globs # globs.py
import lang # lang.py
import critical # critical.py
## SEX CODES
sexcode = {'M':'Homme', 'F':'Femme', 'X':'Non spécifié'}
## COUNTRY CODES
landcode2 = {
'AW': 'Aruba',
'AF': 'Afghanistan',
'AO': 'Angola',
'AI': 'Anguilla',
'AL': 'Albanie',
'AD': 'Andorre',
'AE': 'Emirats arabes unis',
'AR': 'Argentine',
'AM': 'Arménie',
'AS': 'Samoa américaines',
'AQ': 'Antarctique',
'TF': 'Terres australes et antarctiques françaises',
'AG': 'Antigua-et-Barbuda',
'AU': 'Australie',
'AT': 'Autriche',
'AZ': 'Azerbaidjan',
'BI': 'Burundi',
'BE': 'Belgique',
'BJ': 'Benin',
'BQ': 'Pays-Bas caribéens',
'BF': 'Burkina Faso',
'BD': 'Bangladesh',
'BG': 'Bulgarie',
'BH': 'Bahrein',
'BS': 'Bahamas',
'BA': 'Bosnie-Herzegovine',
'BL': 'Saint-Barthélemy',
'BY': 'Bielorussie',
'BZ': 'Belize',
'BM': 'Bermudes',
'BO': 'Bolivie',
'BR': 'Brésil',
'BB': 'Barbade',
'BN': 'Brunei',
'BT': 'Bhoutan',
'BW': 'Botswana',
'CF': 'République Centrafricaine',
'CA': 'Canada',
'CC': 'Îles Cocos',
'CH': 'Suisse',
'CL': 'Chili',
'CN': 'Chine',
'CI': "Côte d'Ivoire",
'CM': 'Cameroun',
'CD': 'Congo (République démocratique)',
'CG': 'Congo (République)',
'CK': 'Îles Cook',
'CO': 'Colombie',
'KM': 'Comores',
'CV': 'Cap-Vert',
'CR': 'Costa Rica',
'CU': 'Cuba',
'CW': 'Curaçao',
'CX': 'Île Christmas',
'KY': 'Caimans',
'CY': 'Chypre',
'CZ': 'Tchéquie',
'DE': 'Allemagne',
'DJ': 'Djibouti',
'DM': 'Dominique',
'DK': 'Danemark',
'DO': 'République dominicaine',
'DZ': 'Algérie',
'EC': 'Equateur',
'EG': 'Egypte',
'ER': 'Erythrée',
'EH': 'Sahara occidental',
'ES': 'Espagne',
'EE': 'Estonie',
'ET': 'Ethiopie',
'FI': 'Finlande',
'FJ': 'Fidji',
'FK': 'Îles Malouines',
'FR': 'France',
'FO': 'Féroé',
'FM': 'Micronésie',
'GA': 'Gabon',
'GB': 'Royaume-Uni',
'GE': 'Géorgie',
'GG': 'Guernesey',
'GH': 'Ghana',
'GI': 'Gibraltar',
'GN': 'Guinée',
'GP': 'Guadeloupe',
'GM': 'Gambie',
'GW': 'Guinée-Bissau',
'GQ': 'Guinée équatoriale',
'GR': 'Grèce',
'GD': 'Grenade',
'GL': 'Groenland',
'GT': 'Guatemala',
'GF': 'Guyane',
'GU': 'Guam',
'GY': 'Guyana',
'HK': 'Hong Kong',
'HN': 'Honduras',
'HR': 'Croatie',
'HT': 'Haïti',
'HU': 'Hongrie',
'ID': 'Indonésie',
'IM': 'Île de Man',
'IN': 'Inde',
'IO': "Territoire britannique de l'océan Indien",
'IE': 'Irlande',
'IR': 'Irak',
'IQ': 'Iran',
'IS': 'Islande',
'IL': 'Israël',
'IT': 'Italie',
'JM': 'Jamaïque',
'JE': 'Jersey',
'JO': 'Jordanie',
'JP': 'Japon',
'KZ': 'Kazakhstan',
'KE': 'Kenya',
'KG': 'Kirghizistan',
'KH': 'Cambodge',
'KI': 'Kiribati',
'KN': 'Saint-Christophe-et-Niévès',
'KR': 'Corée du Sud',
'KW': 'Koweït',
'LA': 'Laos',
'LB': 'Liban',
'LR': 'Liberia',
'LY': 'Libye',
'LC': 'Sainte-Lucie',
'LI': 'Liechtenstein',
'LK': 'Sri Lanka',
'LS': 'Lesotho',
'LT': 'Lituanie',
'LU': 'Luxembourg',
'LV': 'Lettonie',
'MO': 'Macao',
'MF': 'Sint-Maarten',
'MA': 'Maroc',
'MC': 'Monaco',
'MD': 'Moldavie',
'MG': 'Madagascar',
'MV': 'Maldives',
'MX': 'Mexique',
'MH': 'Marshall',
'MK': 'Macedoine',
'ML': 'Mali',
'MT': 'Malte',
'MM': 'Birmanie',
'ME': 'Monténégro',
'MN': 'Mongolie',
'MP': 'Îles Mariannes du Nord',
'MZ': 'Mozambique',
'MR': 'Mauritanie',
'MS': 'Montserrat',
'MQ': 'Martinique',
'MU': 'Maurice',
'MW': 'Malawi',
'MY': 'Malaisie',
'YT': 'Mayotte',
'NA': 'Namibie',
'NC': 'Nouvelle-Calédonie',
'NE': 'Niger',
'NF': 'Île Norfolk',
'NG': 'Nigeria',
'NI': 'Nicaragua',
'NU': 'Niue',
'NL': 'Pays-Bas',
'NO': 'Norvège',
'NP': 'Nepal',
'NR': 'Nauru',
'NZ': 'Nouvelle-Zélande',
'OM': 'Oman',
'PK': 'Pakistan',
'PA': 'Panama',
'PN': 'Îles Pitcairn',
'PE': 'Pérou',
'PH': 'Philippines',
'PW': 'Palaos',
'PG': 'Papouasie-Nouvelle-Guinée',
'PL': 'Pologne',
'PR': 'Porto Rico',
'KP': 'Corée du Nord',
'PT': 'Portugal',
'PY': 'Paraguay',
'PS': 'Palestine',
'PF': 'Polynésie française',
'QA': 'Qatar',
'RE': 'Réunion',
'RO': 'Roumanie',
'RU': 'Russie',
'RW': 'Rwanda',
'SA': 'Arabie saoudite',
'SD': 'Soudan',
'SN': 'Sénégal',
'SG': 'Singapour',
'GS': 'Georgie du Sud-et-les iles Sandwich du Sud',
'SH': 'Sainte-Hélène, Ascension et Tristan da Cunha',
'SJ': 'Svalbard et île Jan Mayen',
'SB': 'Salomon',
'SL': 'Sierra Leone',
'SV': 'Salvador',
'SM': 'Saint-Marin',
'SO': 'Somalie',
'PM': 'Saint-Pierre-et-Miquelon',
'RS': 'Serbie',
'SS': 'Soudan du Sud',
'ST': 'Sao Tomé-et-Principe',
'SR': 'Suriname',
'SK': 'Slovaquie',
'SI': 'Slovénie',
'SE': 'Suède',
'SZ': 'eSwatani',
'SX': 'Saint-Martin ',
'SC': 'Seychelles',
'SY': 'Syrie',
'TC': 'Îles Turques-et-Caïques',
'TD': 'Tchad',
'TG': 'Togo',
'TH': 'Thaïlande',
'TJ': 'Tadjikistan',
'TK': 'Tokelau',
'TM': 'Turkmenistan',
'TL': 'Timor oriental',
'TO': 'Tonga',
'TT': 'Trinité-et-Tobago',
'TN': 'Tunisie',
'TR': 'Turquie',
'TV': 'Tuvalu',
'TW': 'Taiwan',
'TZ': 'Tanzanie',
'UG': 'Ouganda',
'UA': 'Ukraine',
'UY': 'Uruguay',
'US': 'Etats-Unis',
'UZ': 'Ouzbékistan',
'VA': 'Saint-Siège (État de la Cité du Vatican)',
'VC': 'Saint-Vincent-et-les-Grenadines',
'VE': 'Venezuela',
'VG': 'Îles Vierges britanniques',
'VI': 'Îles Vierges des États-Unis',
'VN': 'Viêt Nam',
'VU': 'Vanuatu',
'WF': 'Wallis-et-Futuna',
'WS': 'Samoa',
'XK': 'Kosovo',
'YE': 'Yémen',
'ZA': 'Afrique du Sud',
'ZM': 'Zambie',
'ZW': 'Zimbabwe'
}
landcode2 = lang.all[globs.CNIRlang]["LANDCODE2"]
landcode3 = {
'ABW': 'Aruba',
'AFG': 'Afghanistan',
'AGO': 'Angola',
'AIA': 'Anguilla',
'ALB': 'Albanie',
'AND': 'Andorre',
'ARE': 'Emirats arabes unis',
'ARG': 'Argentine',
'ARM': 'Arménie',
'ASM': 'Samoa américaines',
'ATA': 'Antarctique',
'ATF': 'Terres australes et antarctiques françaises',
'ATG': 'Antigua-et-Barbuda',
'AUS': 'Australie',
'AUT': 'Autriche',
'AZE': 'Azerbaidjan',
'BDI': 'Burundi',
'BEL': 'Belgique',
'BEN': 'Benin',
'BES': 'Pays-Bas caribéens',
'BFA': 'Burkina Faso',
'BGD': 'Bangladesh',
'BGR': 'Bulgarie',
'BHR': 'Bahrein',
'BHS': 'Bahamas',
'BIH': 'Bosnie-Herzegovine',
'BLM': 'Saint-Barthélemy',
'BLR': 'Bielorussie',
'BLZ': 'Belize',
'BMU': 'Bermudes',
'BOL': 'Bolivie',
'BRA': 'Brésil',
'BRB': 'Barbade',
'BRN': 'Brunei',
'BTN': 'Bhoutan',
'BWA': 'Botswana',
'CAF': 'République Centrafricaine',
'CAN': 'Canada',
'CCK': 'Îles Cocos',
'CHE': 'Suisse',
'CHL': 'Chili',
'CHN': 'Chine',
'CIV': "Côte d'Ivoire",
'CMR': 'Cameroun',
'COD': 'Congo (République démocratique)',
'COG': 'Congo (République)',
'COK': 'Îles Cook',
'COL': 'Colombie',
'COM': 'Comores',
'CPV': 'Cap-Vert',
'CRI': 'Costa Rica',
'CUB': 'Cuba',
'CUW': 'Curaçao',
'CXR': 'Île Christmas',
'CYM': 'Caimans',
'CYP': 'Chypre',
'CZE': 'Tchéquie',
'DEU': 'Allemagne',
'DJI': 'Djibouti',
'DMA': 'Dominique',
'DNK': 'Danemark',
'DOM': 'République dominicaine',
'DZA': 'Algérie',
'ECU': 'Equateur',
'EGY': 'Egypte',
'ERI': 'Erythrée',
'ESH': 'Sahara occidental',
'ESP': 'Espagne',
'EST': 'Estonie',
'ETH': 'Ethiopie',
'FIN': 'Finlande',
'FJI': 'Fidji',
'FLK': 'Îles Malouines',
'FRA': 'France',
'FRO': 'Féroé',
'FSM': 'Micronésie',
'GAB': 'Gabon',
'GBR': 'Royaume-Uni',
'GEO': 'Géorgie',
'GGY': 'Guernesey',
'GHA': 'Ghana',
'GIB': 'Gibraltar',
'GIN': 'Guinée',
'GLP': 'Guadeloupe',
'GMB': 'Gambie',
'GNB': 'Guinée-Bissau',
'GNQ': 'Guinée équatoriale',
'GRC': 'Grèce',
'GRD': 'Grenade',
'GRL': 'Groenland',
'GTM': 'Guatemala',
'GUF': 'Guyane',
'GUM': 'Guam',
'GUY': 'Guyana',
'HKG': 'Hong Kong',
'HND': 'Honduras',
'HRV': 'Croatie',
'HTI': 'Haïti',
'HUN': 'Hongrie',
'IDN': 'Indonésie',
'IMN': 'Île de Man',
'IND': 'Inde',
'IOT': "Territoire britannique de l'océan Indien",
'IRL': 'Irlande',
'IRN': 'Irak',
'IRQ': 'Iran',
'ISL': 'Islande',
'ISR': 'Israël',
'ITA': 'Italie',
'JAM': 'Jamaïque',
'JEY': 'Jersey',
'JOR': 'Jordanie',
'JPN': 'Japon',
'KAZ': 'Kazakhstan',
'KEN': 'Kenya',
'KGZ': 'Kirghizistan',
'KHM': 'Cambodge',
'KIR': 'Kiribati',
'KNA': 'Saint-Christophe-et-Niévès',
'KOR': 'Corée du Sud',
'KWT': 'Koweït',
'LAO': 'Laos',
'LBN': 'Liban',
'LBR': 'Liberia',
'LBY': 'Libye',
'LCA': 'Sainte-Lucie',
'LIE': 'Liechtenstein',
'LKA': 'Sri Lanka',
'LSO': 'Lesotho',
'LTU': 'Lituanie',
'LUX': 'Luxembourg',
'LVA': 'Lettonie',
'MAC': 'Macao',
'MAF': 'Sint-Maarten',
'MAR': 'Maroc',
'MCO': 'Monaco',
'MDA': 'Moldavie',
'MDG': 'Madagascar',
'MDV': 'Maldives',
'MEX': 'Mexique',
'MHL': 'Marshall',
'MKD': 'Macedoine',
'MLI': 'Mali',
'MLT': 'Malte',
'MMR': 'Birmanie',
'MNE': 'Monténégro',
'MNG': 'Mongolie',
'MNP': 'Îles Mariannes du Nord',
'MOZ': 'Mozambique',
'MRT': 'Mauritanie',
'MSR': 'Montserrat',
'MTQ': 'Martinique',
'MUS': 'Maurice',
'MWI': 'Malawi',
'MYS': 'Malaisie',
'MYT': 'Mayotte',
'NAM': 'Namibie',
'NCL': 'Nouvelle-Calédonie',
'NER': 'Niger',
'NFK': 'Île Norfolk',
'NGA': 'Nigeria',
'NIC': 'Nicaragua',
'NIU': 'Niue',
'NLD': 'Pays-Bas',
'NOR': 'Norvège',
'NPL': 'Nepal',
'NRU': 'Nauru',
'NZL': 'Nouvelle-Zélande',
'OMN': 'Oman',
'PAK': 'Pakistan',
'PAN': 'Panama',
'PCN': 'Îles Pitcairn',
'PER': 'Pérou',
'PHL': 'Philippines',
'PLW': 'Palaos',
'PNG': 'Papouasie-Nouvelle-Guinée',
'POL': 'Pologne',
'PRI': 'Porto Rico',
'PRK': 'Corée du Nord',
'PRT': 'Portugal',
'PRY': 'Paraguay',
'PSE': 'Palestine',
'PYF': 'Polynésie française',
'QAT': 'Qatar',
'REU': 'Réunion',
'ROU': 'Roumanie',
'RUS': 'Russie',
'RWA': 'Rwanda',
'SAU': 'Arabie saoudite',
'SDN': 'Soudan',
'SEN': 'Sénégal',
'SGP': 'Singapour',
'SGS': 'Georgie du Sud-et-les iles Sandwich du Sud',
'SHN': 'Sainte-Hélène, Ascension et Tristan da Cunha',
'SJM': 'Svalbard et île Jan Mayen',
'SLB': 'Salomon',
'SLE': 'Sierra Leone',
'SLV': 'Salvador',
'SMR': 'Saint-Marin',
'SOM': 'Somalie',
'SPM': 'Saint-Pierre-et-Miquelon',
'SRB': 'Serbie',
'SSD': 'Soudan du Sud',
'STP': 'Sao Tomé-et-Principe',
'SUR': 'Suriname',
'SVK': 'Slovaquie',
'SVN': 'Slovénie',
'SWE': 'Suède',
'SWZ': 'eSwatani',
'SXM': 'Saint-Martin ',
'SYC': 'Seychelles',
'SYR': 'Syrie',
'TCA': 'Îles Turques-et-Caïques',
'TCD': 'Tchad',
'TGO': 'Togo',
'THA': 'Thaïlande',
'TJK': 'Tadjikistan',
'TKL': 'Tokelau',
'TKM': 'Turkmenistan',
'TLS': 'Timor oriental',
'TON': 'Tonga',
'TTO': 'Trinité-et-Tobago',
'TUN': 'Tunisie',
'TUR': 'Turquie',
'TUV': 'Tuvalu',
'TWN': 'Taiwan',
'TZA': 'Tanzanie',
'UGA': 'Ouganda',
'UKR': 'Ukraine',
'URY': 'Uruguay',
'USA': 'Etats-Unis',
'UZB': 'Ouzbékistan',
'VAT': 'Saint-Siège (État de la Cité du Vatican)',
'VCT': 'Saint-Vincent-et-les-Grenadines',
'VEN': 'Venezuela',
'VGB': 'Îles Vierges britanniques',
'VIR': 'Îles Vierges des États-Unis',
'VNM': 'Viêt Nam',
'VUT': 'Vanuatu',
'WLF': 'Wallis-et-Futuna',
'WSM': 'Samoa',
'XKX': 'Kosovo',
'YEM': 'Yémen',
'ZAF': 'Afrique du Sud',
'ZMB': 'Zambie',
'ZWE': 'Zimbabwe',
'NTZ': 'Zone neutre',
'UNO': 'Fonctionnaire des Nations Unies',
'UNA': "Fonctionnaire d'une organisation affiliée aux Nations Unies",
'UNK': 'Représentant des Nations Unies au Kosovo',
'XXA': 'Apatride Convention 1954',
'XXB': 'Réfugié Convention 1954',
'XXC': 'Réfugié autre',
'XXX': 'Résident Légal de Nationalité Inconnue',
'D': 'Allemagne',
'EUE': 'Union Européenne',
'GBD': "Citoyen Britannique d'Outre-mer (BOTC)",
'GBN': 'British National (Overseas)',
'GBO': 'British Overseas Citizen',
'GBP': 'British Protected Person',
'GBS': 'British Subject',
'XBA': 'Banque Africaine de Développement',
'XIM': "Banque Africaine d'ExportImport",
'XCC': 'Caribbean Community or one of its emissaries',
'XCO': 'Common Market for Eastern and Southern Africa',
'XEC': 'Economic Community of West African States',
'XPO': 'International Criminal Police Organization',
'XOM': 'Sovereign Military Order of Malta',
'RKS': 'Kosovo',
'WSA': 'World Service Authority World Passport'
}
landcode3 = lang.all[globs.CNIRlang]["LANDCODE3"]
## DOCUMENTS TYPES
@ -576,7 +61,7 @@ P = [
"D": ["1", "CTRLF", "[0-9]", "C"],
"E": ["1", "CTRL", "[0-9]", "4578ABCD"]
},
"Passeport"
lang.all[globs.CNIRlang]["Passeport"]
]
IP = [
@ -596,7 +81,7 @@ IP = [
"C": ["11", "FACULT", ".+"],
"D": ["1", "CTRL", "[0-9]", "345679AC"]
},
"Carte-passeport"
lang.all[globs.CNIRlang]["Carte-passeport"]
]
I_ = [
@ -616,7 +101,7 @@ I_ = [
"C": ["11", "FACULT", ".+"],
"D": ["1", "CTRL", "[0-9]", "345679AC"]
},
"Titre d'identité/de voyage"
lang.all[globs.CNIRlang]["Titre d'identité/de voyage"]
]
AC = [
@ -637,7 +122,7 @@ AC = [
"C": ["11", "FACULT", ".+"],
"D": ["1", "CTRL", "[0-9]","345679AC"]
},
"Certificat de membre d'équipage"
lang.all[globs.CNIRlang]["Certificat de membre d'équipage"]
]
VA = [
@ -656,7 +141,7 @@ VA = [
"B": ["1", "CTRL", "[0-9]", "A"],
"C": ["16", "FACULT", ".+"]
},
"Visa de type A"
lang.all[globs.CNIRlang]["Visa de type A"]
]
VB = [
@ -675,7 +160,7 @@ VB = [
"B": ["1", "CTRL", "[0-9]", "A"],
"C": ["8", "FACULT", ".+"]
},
"Visa de type B"
lang.all[globs.CNIRlang]["Visa de type B"]
]
TSF = [
@ -694,7 +179,7 @@ TSF = [
"B": ["1", "CTRL", "[0-9]", "A"],
"C": ["8", "FACULT", ".+"]
},
"Carte de séjour"
lang.all[globs.CNIRlang]["Carte de séjour"]
]
I__ = [
@ -714,7 +199,7 @@ I__ = [
"C": ["7", "FACULT", ".+"],
"D": ["1", "CTRL", "[0-9]", "4578ABC"]
},
"Pièce d'identité/de voyage"
lang.all[globs.CNIRlang]["Pièce d'identité/de voyage"]
]
IDFR = [
@ -734,7 +219,7 @@ IDFR = [
"C": ["1", "SEX", "[A-Z]"],
"D": ["1", "CTRL", "[0-9]", "123456789ABCE"]
},
"Pièce d'identité FR"
lang.all[globs.CNIRlang]["Pièce d'identité FR"]
]
DL = [
@ -748,7 +233,7 @@ DL = [
"6": ["8", "NOM", "([A-Z]|<)+"],
"7": ["1", "CTRL", "[0-9]", "123456"]
},
"Permis de conduire"
lang.all[globs.CNIRlang]["Permis de conduire"]
]
TYPES = [IDFR, I__, VB, VA, AC, I_, IP, P, DL, TSF]
@ -1010,6 +495,14 @@ def getDocInfos(doc, code):
res["INDIC"] += value
except KeyError:
res["INDIC"] = value
# Sex
elif field[0] == 'SEX':
if not value in "MF":
res[field[0]] = False
else:
res[field[0]] = value
# All other cases
else:
if value != "":

View File

@ -24,7 +24,9 @@
"""
from win32com.client import Dispatch
import traceback
from tkinter.messagebox import *
from tkinter import *
import pythoncom
import sys
import time
import os
@ -34,10 +36,12 @@ import hashlib
import subprocess
import psutil
import critical # critical.py
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 = ' '
@ -80,52 +84,21 @@ def exitProcess(arg):
if process.pid == os.getpid():
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 updateChannel(choice):
if choice == "Beta":
with open(globs.CNIRUrlConfig, 'w') as (configFile):
configFile.write("{}\n0\n0".format(globs.CNIRBetaURL))
else:
with open(globs.CNIRUrlConfig, 'w') as (configFile):
configFile.write("{}\n0\n0".format(globs.CNIRDefaultURL))
def getLatestVersion(credentials):
"""
Returns the latest version of the software
"""
finalver, finalurl, finalchecksum = [None]*3
# Global Handlers
logfile = logger.logCur
@ -155,7 +128,7 @@ def getLatestVersion(credentials):
except:
pass
with open(globs.CNIRUrlConfig, 'w') as (configFile):
configFile.write("https://raw.githubusercontent.com/neox95/CNIRevelator/master/VERSIONS.LST\n0\n0")
configFile.write("{}\n0\n0".format(globs.CNIRDefaultURL))
# Getting the list of versions of the software
logfile.printdbg('Retrieving the software versions')
@ -188,7 +161,7 @@ def getLatestVersion(credentials):
else:
finalurl = url
finalchecksum = None
return (finalver, finalurl, finalchecksum)
@ -200,6 +173,11 @@ def tessInstall(PATH, credentials):
# Verifying that Tesseract is installed
if not os.path.exists(PATH + '\\Tesseract-OCR4\\'):
finalver, finalurl, finalchecksum = getLatestVersion(credentials)
if finalurl == None:
logfile.printerr('Unable to get the Tesseract url')
return False
tesseracturl = finalurl.replace("CNIRevelator.zip", "tesseract_4.zip")
# WE ASSUME THAT THE MAIN FILE IS CNIRevelator.zip AND THAT THE TESSERACT PACKAGE IS tesseract_4.zip
@ -228,7 +206,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 +239,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 +264,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 +276,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 +287,16 @@ def batch(credentials):
logfile.printdbg('Extracted :' + UPATH + '\\CNIRevelator.exe')
# Make a shortcut
createShortcut("CNIRevelator.lnk", UPATH + '\\CNIRevelator.exe', UPATH)
# hide main window
pythoncom.CoInitialize()
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 +304,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 +320,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 +346,24 @@ 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
print(sys.argv)
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()))
critical.crashCNIR()
launcherWindow.printmsg('ERROR : ' + str(e))
time.sleep(3)
launcherWindow.exit()
@ -410,7 +374,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 +383,7 @@ def umain():
launcherWindow.exit()
return 0
except:
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
critical.crashCNIR()
launcherWindow.exit()
sys.exit(2)
return 2
@ -429,7 +393,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()))
critical.crashCNIR()
launcherWindow.printmsg('ERROR : ' + str(e))
time.sleep(3)
launcherWindow.exit()
@ -437,16 +401,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()))
critical.crashCNIR()
launcherWindow.exit()
sys.exit(2)
return 2