IFPass/IFPass.py

374 lines
15 KiB
Python
Raw Normal View History

2019-02-04 15:39:33 +01:00
#!python3
2018-04-12 14:32:42 +02:00
# Written by Jordan ERNST Q1 2018.
# Contact : pro.ernst@gmail.com
import configparser
2018-04-12 14:32:42 +02:00
import sys
import os
import re
from datetime import date, timedelta, datetime
2018-04-12 14:32:42 +02:00
import csv
import code128
import cv2
from pywinauto.findwindows import find_window
from pywinauto.win32functions import SetForegroundWindow
from PIL import Image, ImageDraw, ImageFont
from PyPDF2 import PdfFileReader, PdfFileWriter
import subprocess
2019-02-07 15:54:04 +01:00
from shutil import copyfile, move
2018-04-12 14:32:42 +02:00
from pyfiglet import Figlet
from colorama import init
from termcolor import colored
2019-02-07 16:51:27 +01:00
version = 'dev' # dev/devnocam
2018-04-12 14:32:42 +02:00
config = 'IFPass.conf'
2018-04-12 14:32:42 +02:00
def initialisation():
conf = configparser.ConfigParser()
conf.optionxform = str # For case sensitive config file
2018-04-12 14:32:42 +02:00
if not os.path.exists(config): # Check if config file exists
print('Fichier de configuration introuvable.')
2019-02-07 15:54:04 +01:00
IFPassDBdir = input(r'Quel est le répertoire de la base de données IFPass ? (Ex : \\192.168.1.1\IFPass) : ')
2018-12-10 16:18:23 +01:00
printername = input("Quel est le nom de l'imprimante à cartes ? : ")
AcrobatReader = input(r"Quel est le chemain vers Acrobat Reader ? ( Ex : C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe) : ")
2019-02-07 15:54:04 +01:00
conf['DEFAULT'] = {'IFPassDBdir': IFPassDBdir, 'printername': printername, 'AcrobatReader': AcrobatReader}
with open(config, 'w') as configfile:
conf.write(configfile)
else:
conf.read(config)
2019-02-07 15:54:04 +01:00
IFPassDBdir = conf['DEFAULT']['IFPassDBdir']
printername = conf['DEFAULT']['printername']
AcrobatReader = conf['DEFAULT']['AcrobatReader']
2018-04-12 14:32:42 +02:00
2019-02-07 15:54:04 +01:00
clientsfile = os.path.join(IFPassDBdir, 'Clients_IFPass.csv')
clientsbkpfile = os.path.join(IFPassDBdir, 'Clients_IFPass_backup.csv')
imgdir = os.path.join(IFPassDBdir, 'Cartes')
2019-02-07 16:20:08 +01:00
templatesdir = os.path.join(IFPassDBdir, 'Templates')
pdftemplate = os.path.join(templatesdir, 'Templates', 'IFPass_PDF_Template.pdf')
pngtemplate = os.path.join(templatesdir, 'Templates', 'IFPass_PNG_Template.png')
fonttemplate = os.path.join(templatesdir, 'Templates', 'Roboto-Bold.ttf')
2018-04-12 14:32:42 +02:00
if not os.path.exists(imgdir): # Cartes dir creation if it doesn't exist
2018-04-17 16:27:41 +02:00
os.makedirs(imgdir)
if not os.path.exists(clientsfile): # Creation Clients_File if it doesn't exist
2018-04-17 16:27:41 +02:00
with open(clientsfile, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile, delimiter=';')
writer.writerow(['Titre', 'Prénom', 'Nom', 'Numéro de client', "Date d'inscription", "Date d'expiration"])
2019-02-07 16:20:08 +01:00
if not os.path.exists(templatesdir):
move('Templates', IFPassDBdir)
2019-02-07 15:54:04 +01:00
return IFPassDBdir, printername, AcrobatReader, clientsfile, clientsbkpfile, imgdir, pdftemplate, pngtemplate, fonttemplate
2018-04-12 14:32:42 +02:00
def get_fullname():
2019-02-07 15:54:04 +01:00
while "Empty firstname":
firstname = input("Prénom : ").strip()
if len(firstname) == 0:
os.system('cls')
print(colored("\nLe Prénom ne peut pas être vide.", 'red'))
else:
break
2018-04-12 14:32:42 +02:00
firstname = firstname[0].upper() + firstname[1:].lower()
if '-' in firstname:
pos = firstname.find("-")
firstname = firstname[:pos + 1] + firstname[pos + 1].upper() + \
firstname[pos + 2:] # Check if compound name
if ' ' in firstname:
pos = firstname.find(" ")
firstname = firstname[:pos + 1] + firstname[pos + 1].upper() + \
firstname[pos + 2:] # Check if compound name
2019-02-07 15:54:04 +01:00
while "Empty surname":
surname = input("Nom : ").upper().strip()
if len(surname) == 0:
os.system('cls')
print(colored("\nLe Nom ne peut pas être vide.", 'red'))
else:
break
2018-04-12 14:32:42 +02:00
docteur = yes_or_no('Est-ce un Docteur ?')
if docteur:
titre = 'Dr.'
fullname = titre + ' ' + surname + ' ' + firstname
else:
titre = ''
fullname = surname + ' ' + firstname
return titre, firstname, surname, fullname
def yes_or_no(question):
while "the answer is invalid":
reply = input(question + " O/N : ").lower().strip()
if reply[:1] in ['oui', 'ou', 'o']:
return True
if reply[:1] in ['non', 'no', 'n']:
return False
def getclientID():
with open(clientsfile, 'r', newline='', encoding='utf-8') as csvfile:
lastline = csvfile.readlines()[-1]
lastID = lastline.split(';')[3]
if lastID == "Numéro de client":
clientID = "0000000001"
else:
clientID = str(int(lastID) + 1).zfill(10)
writeindb(clientID)
return clientID
def barcode_gen(clientID):
print("Génération du code barre... ", end='')
2018-12-10 16:18:23 +01:00
barcode = code128.image(clientID, thickness=4) # .save(imgdir + clientID + '.png')
2018-04-12 14:32:42 +02:00
print(colored('[OK]', 'green'))
return barcode
def getpic():
while True:
print("Prendre la photo... ", end='')
sys.stdout.flush()
cap = cv2.VideoCapture(0)
while True: # Loop stream webcam
try:
ret, frame = cap.read()
cv2.rectangle(frame, (170, 73), (470, 407), (0, 255, 0), 2)
2018-04-17 14:31:11 +02:00
cv2.imshow('IFCamera - Touche Espace pour prendre la photo, Echap pour une carte sans photo, Q pour quitter.', frame)
2018-04-12 14:32:42 +02:00
except cv2.error:
2018-04-17 14:31:11 +02:00
print(colored('\nLa webcam est débranchée. Branchez-la, puis relancez le programme.', 'red'))
2018-04-12 14:32:42 +02:00
os.system("pause")
2018-04-17 14:31:11 +02:00
sys.exit()
2018-04-12 14:32:42 +02:00
SetForegroundWindow(find_window(title='IFCamera - Touche Espace '
'pour prendre la photo, Echap pour une carte sans photo, Q pour quitter.'))
k = cv2.waitKey(1)
if k == 27: # Echap
2019-02-07 15:54:04 +01:00
cap.release()
2018-04-12 14:32:42 +02:00
print(colored('[OK]', 'green'))
cv2.destroyAllWindows()
2019-02-07 15:54:04 +01:00
defaultpicture = os.path.join(IFPassDBdir, 'Templates', 'default_avatar.jpg')
2018-04-12 14:32:42 +02:00
picture = Image.open(defaultpicture)
return picture
elif k & 0xFF == ord(' '): # Space
2019-02-07 15:54:04 +01:00
cap.release()
cv2.destroyAllWindows()
2018-04-12 14:32:42 +02:00
break
elif k & 0xFF == ord('q'): # Q or q to quit
cap.release()
cv2.destroyAllWindows()
sys.exit()
cropped = frame[75:405, 172:468]
cv2.imshow('IFCamera - Espace pour valider, Echap pour modifier, Q pour quitter', cropped)
while True:
k = cv2.waitKey(1)
if k == 27: # Echap
print(colored('[KO]', 'red'))
cv2.destroyAllWindows()
break
elif k & 0xFF == ord(' '): # Space
print(colored('[OK]', 'green'))
cv2.destroyAllWindows()
# Color conversion and cv2 img to Pillow img
cropped = cv2.cvtColor(cropped, cv2.COLOR_BGR2RGB)
picture = Image.fromarray(cropped)
return picture
elif k & 0xFF == ord('q'): # Q or q to quit
cv2.destroyAllWindows()
sys.exit()
def writeindb(clientID):
print("Ajout dans la base de données... ", end="")
with open(clientsfile, 'a', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile, delimiter=';')
writer.writerow([titre, firstname, surname, clientID, dateinsc, dateexp])
print(colored('[OK]', 'green'))
def bkpdb():
copyfile(clientsfile, clientsbkpfile)
def fillcard(barcode):
print("Création de la carte avec les informations...", end='')
im = Image.open(pngtemplate)
draw = ImageDraw.Draw(im)
# Name embedding :
font = ImageFont.truetype(fonttemplate, 45)
2018-12-10 16:18:23 +01:00
draw.text((401, 310), fullname, fill=(0, 0, 0), font=font)
2018-04-12 14:32:42 +02:00
# Date embedding :
font = ImageFont.truetype(fonttemplate, 30)
2018-12-10 16:18:23 +01:00
draw.text((401, 390), dateexp, fill=(0, 0, 0), font=font)
2018-04-12 14:32:42 +02:00
# ID embedding :
font = ImageFont.truetype('arial.ttf', 30)
2018-12-10 16:18:23 +01:00
draw.text((693, 560), clientID, fill=(0, 0, 0), font=font)
2018-04-12 14:32:42 +02:00
# Barcode + picture embedding :
2018-12-10 16:18:23 +01:00
im.paste(barcode, (556, 460))
if version != 'devnocam':
2018-12-10 16:18:23 +01:00
im.paste(picture, (47, 49))
2018-04-12 14:32:42 +02:00
# Create PDF :
im = im.convert("RGB")
im.save(imgdir + clientID + '_Front.pdf', 'PDF', resolution=299.0, quality=98)
print(colored('[OK]', 'green'))
def mergepdf():
cartefilename = os.path.join(imgdir, clientID + '.pdf')
2018-04-12 14:32:42 +02:00
output = PdfFileWriter()
pdf1 = PdfFileReader(imgdir + clientID + '_Front.pdf', 'rb')
recto = pdf1.getPage(0)
output.addPage(recto)
pdf2 = PdfFileReader(pdftemplate, 'rb')
verso = pdf2.getPage(1)
output.addPage(verso)
with open(cartefilename, 'wb') as f:
output.write(f)
os.remove(imgdir + clientID + '_Front.pdf')
return cartefilename
def printcard(cartefilename):
# Working : subprocess.Popen('"C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe" /h /n /t ' + cartefilename + ' '+ printername, shell=False)
2018-12-10 16:18:23 +01:00
subprocess.Popen('"' + AcrobatReader + '"' + ' /h /n /t ' + cartefilename + ' ' + printername, shell=False)
2018-04-12 14:32:42 +02:00
def membersearch():
with open(clientsfile, 'r', newline='', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile, delimiter=';')
csvlist = list(map(tuple, reader))
del csvlist[0] # We dele the first line (Prénom, Nom...)
research = input('Entrez une partie du nom, prénom, ou numéro de carte (ou flashez) : ').lower()
os.system('cls')
results = []
for member in csvlist:
resfirstname = member[1].lower()
ressurname = member[2].lower()
resnumber = member[3]
if any(research in data for data in [resfirstname, ressurname, resnumber]):
results.append(member)
if results:
if len(results) == 1:
member = results[0]
else:
print('-' * 56)
print(f'{"Choix":8} {"Prénom":15} {"Nom":15} {"Numéro de carte":15}')
print('-' * 56)
for index, result in enumerate(results, start=1):
print(f'{index:^8} {result[1]:15} {result[2]:15} {result[3]:^15}')
del member
while 'The choice is not valid':
try:
memberchoice = input("De quel membre s'agit il ? (Colonne Choix) : ")
member = results[int(memberchoice) - 1]
break
except (IndexError, ValueError):
print(colored('Choix invalide ! Veillez bien à sélectionner le numéro de la colonne "Choix"', 'red', attrs=['bold']))
os.system('cls')
print("Titre : ", colored(member[0], 'green'))
print("Prénom : ", colored(member[1], 'green'))
print("Nom : ", colored(member[2], 'green'))
print("Numéro de carte : ", colored(member[3], 'green'))
print("Date d'inscription :", member[4])
print("Date d'expiration : ", member[5])
dateexp = datetime.strptime(member[5], '%d/%m/%Y').date()
diff = (dateexp - date.today()).days
if diff > 0:
print(colored(f"L'abonnement est encore valable {diff} jours.", 'green', attrs=['bold']))
elif diff < 0:
2018-12-10 16:18:23 +01:00
print(colored("L'abonnement est expiré depuis {abs(diff)} jours.", 'red', attrs=['bold'])) # abs() to remove minus sign
elif diff == 0:
print(colored("Il s'agit du dernier jour de l'abonnement, il expirera demain.", 'yellow', attrs=['bold']))
else:
print(colored("Aucun membre n'a été trouvé.", 'red', attrs=['bold']))
2019-02-04 16:47:28 +01:00
def main():
2019-02-07 15:54:04 +01:00
global titre, firstname, surname, fullname, dateinsc, dateexp, clientID, clientsfile, IFPassDBdir, clientsfile, imgdir, clientsbkpfile, pngtemplate, fonttemplate, picture, pdftemplate, printername, AcrobatReader
2019-02-04 16:47:28 +01:00
while "The program is running":
init() # Initialisation of colorama
2019-02-07 15:54:04 +01:00
IFPassDBdir, printername, AcrobatReader, clientsfile, clientsbkpfile, imgdir, pdftemplate, pngtemplate, fonttemplate = initialisation()
2019-02-04 16:47:28 +01:00
os.system('cls')
f = Figlet(font='big')
print(colored(f.renderText('IFPass'), 'cyan', attrs=['bold']))
print('Version : ', version)
if version in ('dev', 'devnocam'):
print(colored("\nATTENTION : Il s'agit d'une version en cours de développement, potentiellement instable !", 'red'))
print("\nLe programme IFPass à été écrit par Jordan ERNST Q1 2018 pour l'Institut Français en Hongrie.")
print('Pour toute question, problème ou requête contactez-moi à pro.ernst@gmail.com.\n')
print('1 - Nouveau membre', '2 - Rechercher un membre', '3 - Quitter', sep='\n')
choix = input('Choix : ')
if choix == '1':
while "the informations are incorrect": # Loop Filling informations
os.system('cls')
titre, firstname, surname, fullname = get_fullname()
dateinsc = date.today()
dateexp = dateinsc + timedelta(days=365)
dateinsc = dateinsc.strftime('%d/%m/%Y')
dateexp = dateexp.strftime('%d/%m/%Y')
changeexp = yes_or_no("Voulez-vous choisir la date d'expiration ?")
if changeexp:
while True:
dateexp = input("Quelle date d'expiration voulez-vous mettre (Format : JJ/MM/AAAA)? : ")
match = re.fullmatch(r'^(0[1-9]|1[0-9]|2[0-9]|3[0-1])/(0[1-9]|1[0-2])/([0-9]){4}$', dateexp)
if match:
break
else:
print('Mauvais format ! JJ/MM/AAAA, exemple : 01/08/2042')
2018-04-12 15:11:48 +02:00
os.system('cls')
2019-02-04 16:47:28 +01:00
print("Titre : ", colored(titre, 'green'))
print("Prénom : ", colored(firstname, 'green'))
print("Nom : ", colored(surname, 'green'))
print("Date d'inscription :", colored(dateinsc, 'green'))
print("Date d'expiration : ", colored(dateexp, 'green'))
correct = yes_or_no("Ces informations sont elles correctes ?")
if correct:
os.system('cls')
if version != 'devnocam':
global picture
picture = getpic()
clientID = getclientID()
barcode = barcode_gen(clientID)
fillcard(barcode)
cartefilename = mergepdf()
if version not in ('dev', 'devnocam'):
bkpdb()
printcard(cartefilename)
2019-02-07 15:54:04 +01:00
break
2019-02-04 16:47:28 +01:00
elif choix == '2':
os.system('cls')
membersearch()
os.system("pause")
2018-04-12 15:11:48 +02:00
2019-02-04 16:47:28 +01:00
elif choix == '3':
sys.exit()
2018-04-12 15:11:48 +02:00
2019-02-04 16:47:28 +01:00
else:
os.system('cls')
print(colored('Choix incorrect !', 'red', attrs=['bold']))