diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9c1edd5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# 2 space indentation +[*.py] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..7c35741 --- /dev/null +++ b/.flake8 @@ -0,0 +1,8 @@ +[flake8] +# ignore = +# # indentation is not a multiple of four, +# E111,E114, +# # visually indented line with same indent as next logical line, +# E129 + +max-line-length=80 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9be00b --- /dev/null +++ b/.gitignore @@ -0,0 +1,193 @@ +# virtual env +.venv/ + +# Created by https://www.gitignore.io/api/emacs,python,visualstudiocode +# Edit at https://www.gitignore.io/?templates=emacs,python,visualstudiocode + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don’t work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +# End of https://www.gitignore.io/api/emacs,python,visualstudiocode diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..6adb653 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,6 @@ +[style] +# YAPF uses the chromium style +# BASED_ON_STYLE = pep8 +BASED_ON_STYLE = google +SPACES_BEFORE_COMMENT = 4 +# SPLIT_BEFORE_LOGICAL_OPERATOR = true diff --git a/const.py b/const.py new file mode 100644 index 0000000..aef0251 --- /dev/null +++ b/const.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +MESSAGE_BOX_TITLE = "GOLEM-Iscritti" \ No newline at end of file diff --git a/create_db.sql b/create_db.sql new file mode 100644 index 0000000..6269604 --- /dev/null +++ b/create_db.sql @@ -0,0 +1,30 @@ +-- +-- File generated with SQLiteStudio v3.2.1 on lun mag 6 20:53:06 2019 +-- +-- Text encoding used: UTF-8 +-- +PRAGMA foreign_keys = off; +BEGIN TRANSACTION; + +-- Table: iscritti +CREATE TABLE iscritti ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + nome VARCHAR (50) NOT NULL, + cognome VARCHAR (50) NOT NULL, + data_nascita DATE NOT NULL, + comune_nascita VARCHAR (255) NOT NULL, + email VARCHAR (255) NOT NULL, + occupazione VARCHAR (50), + fonte VARCHAR (255), + data_firma DATE NOT NULL, + firma BLOB NOT NULL +); + +-- Table: sqlite_sequence +CREATE TABLE sqlite_sequence ( + name, + seq +); + +COMMIT TRANSACTION; +PRAGMA foreign_keys = on; diff --git a/datepicker.py b/datepicker.py new file mode 100644 index 0000000..917429c --- /dev/null +++ b/datepicker.py @@ -0,0 +1,622 @@ +# Geraldo 20190506 +# File originale nel repository di ActiveState su GitBub +# https://github.com/ActiveState/code/tree/master/recipes/Python/580725_Tkinter_Datepicker_like_jQuery_UI + +# Log modifiche rispetto all'originale: +# ------------------------------------- +# 20190506 +# - Trim trailing space +# - Code style yapf->PEP8 + +# ---------------------------------------------------------------------------------------------------------- + +# Author: Miguel Martinez Lopez +# +# Version: 1.0.7 +# +# Uncomment the next line to see my email +# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex")) # noqa +""" +These are the default bindings: + Click button 1 on entry: Show calendar + Click button 1 outsite calendar and entry: Hide calendar + Escape: Hide calendar + CTRL + PAGE UP: Move to the previous month. + CTRL + PAGE DOWN: Move to the next month. + CTRL + SHIFT + PAGE UP: Move to the previous year. + CTRL + SHIFT + PAGE DOWN: Move to the next year. + CTRL + LEFT: Move to the previous day. + CTRL + RIGHT: Move to the next day. + CTRL + UP: Move to the previous week. + CTRL + DOWN: Move to the next week. + CTRL + END: Close the datepicker and erase the date. + CTRL + HOME: Move to the current month. + CTRL + SPACE: Show date on calendar + CTRL + Return: Set current selection to entry +""" + +import calendar +import datetime + +try: + import Tkinter + import tkFont + import ttk + + from Tkconstants import CENTER, LEFT, N, E, W, S + from Tkinter import StringVar +except ImportError: # py3k + import tkinter as Tkinter + import tkinter.font as tkFont + import tkinter.ttk as ttk + + from tkinter.constants import CENTER, LEFT, N, E, W, S + from tkinter import StringVar + + +def get_calendar(locale, fwday): + # instantiate proper calendar class + if locale is None: + return calendar.TextCalendar(fwday) + else: + return calendar.LocaleTextCalendar(fwday, locale) + + +class Calendar(ttk.Frame): + datetime = calendar.datetime.datetime + timedelta = calendar.datetime.timedelta + + def __init__(self, + master=None, + year=None, + month=None, + firstweekday=calendar.MONDAY, + locale=None, + activebackground='#b1dcfb', + activeforeground='black', + selectbackground='#003eff', + selectforeground='white', + command=None, + borderwidth=1, + relief="solid", + on_click_month_button=None): + """ + WIDGET OPTIONS + + locale, firstweekday, year, month, selectbackground, + selectforeground, activebackground, activeforeground, + command, borderwidth, relief, on_click_month_button + """ + + if year is None: + year = self.datetime.now().year + + if month is None: + month = self.datetime.now().month + + self._selected_date = None + + self._sel_bg = selectbackground + self._sel_fg = selectforeground + + self._act_bg = activebackground + self._act_fg = activeforeground + + self.on_click_month_button = on_click_month_button + + self._selection_is_visible = False + self._command = command + + ttk.Frame.__init__( + self, master, borderwidth=borderwidth, relief=relief) + + self.bind("", + lambda event: self.event_generate('<>')) + self.bind("", + lambda event: self.event_generate('<>')) + + self._cal = get_calendar(locale, firstweekday) + + # custom ttk styles + style = ttk.Style() + style.layout('L.TButton', ([('Button.focus', { + 'children': [('Button.leftarrow', None)] + })])) + style.layout('R.TButton', ([('Button.focus', { + 'children': [('Button.rightarrow', None)] + })])) + + self._font = tkFont.Font() + + self._header_var = StringVar() + + # header frame and its widgets + hframe = ttk.Frame(self) + lbtn = ttk.Button( + hframe, style='L.TButton', command=self._on_press_left_button) + lbtn.pack(side=LEFT) + + self._header = ttk.Label( + hframe, width=15, anchor=CENTER, textvariable=self._header_var) + self._header.pack(side=LEFT, padx=12) + + rbtn = ttk.Button( + hframe, style='R.TButton', command=self._on_press_right_button) + rbtn.pack(side=LEFT) + hframe.grid(columnspan=7, pady=4) + + self._day_labels = {} + + days_of_the_week = self._cal.formatweekheader(3).split() + + for i, day_of_the_week in enumerate(days_of_the_week): + Tkinter.Label( + self, text=day_of_the_week, background='grey90').grid( + row=1, column=i, sticky=N + E + W + S) + + for i in range(6): + for j in range(7): + self._day_labels[i, j] = label = Tkinter.Label( + self, background="white") + + label.grid(row=i + 2, column=j, sticky=N + E + W + S) + label.bind("", + lambda event: event.widget.configure( + background=self._act_bg, + foreground=self._act_fg)) + label.bind( + "", + lambda event: event.widget.configure(background="white")) + + label.bind("<1>", self._pressed) + + # adjust its columns width + font = tkFont.Font() + maxwidth = max(font.measure(text) for text in days_of_the_week) + for i in range(7): + self.grid_columnconfigure(i, minsize=maxwidth, weight=1) + + self._year = None + self._month = None + + # insert dates in the currently empty calendar + self._build_calendar(year, month) + + def _build_calendar(self, year, month): + if not (self._year == year and self._month == month): + self._year = year + self._month = month + + # update header text (Month, YEAR) + header = self._cal.formatmonthname(year, month, 0) + self._header_var.set(header.title()) + + # update calendar shown dates + cal = self._cal.monthdayscalendar(year, month) + + for i in range(len(cal)): + + week = cal[i] + fmt_week = [('%02d' % day) if day else '' for day in week] + + for j, day_number in enumerate(fmt_week): + self._day_labels[i, j]["text"] = day_number + + if len(cal) < 6: + for j in range(7): + self._day_labels[5, j]["text"] = "" + + if (self._selected_date is not None + and self._selected_date.year == self._year + and self._selected_date.month == self._month): + self._show_selection() + + def _find_label_coordinates(self, date): + first_weekday_of_the_month = (date.weekday() - date.day) % 7 + + return divmod( + (first_weekday_of_the_month - self._cal.firstweekday) % 7 + + date.day, 7) + + def _show_selection(self): + """Show a new selection.""" + + i, j = self._find_label_coordinates(self._selected_date) + + label = self._day_labels[i, j] + + label.configure(background=self._sel_bg, foreground=self._sel_fg) + + label.unbind("") + label.unbind("") + + self._selection_is_visible = True + + def _clear_selection(self): + """Show a new selection.""" + i, j = self._find_label_coordinates(self._selected_date) + + label = self._day_labels[i, j] + label.configure(background="white", foreground="black") + + label.bind("", + lambda event: event.widget.configure( + background=self._act_bg, foreground=self._act_fg)) + label.bind( + "", + lambda event: event.widget.configure(background="white")) + + self._selection_is_visible = False + + # Callback + + def _pressed(self, evt): + """Clicked somewhere in the calendar.""" + + text = evt.widget["text"] + + if text == "": + return + + day_number = int(text) + + new_selected_date = datetime.datetime(self._year, self._month, + day_number) + if self._selected_date != new_selected_date: + if self._selected_date is not None: + self._clear_selection() + + self._selected_date = new_selected_date + + self._show_selection() + + if self._command: + self._command(self._selected_date) + + def _on_press_left_button(self): + self.prev_month() + + if self.on_click_month_button is not None: + self.on_click_month_button() + + def _on_press_right_button(self): + self.next_month() + + if self.on_click_month_button is not None: + self.on_click_month_button() + + def select_prev_day(self): + """Updated calendar to show the previous day.""" + if self._selected_date is None: + self._selected_date = datetime.datetime(self._year, self._month, 1) + else: + self._clear_selection() + self._selected_date = self._selected_date - self.timedelta(days=1) + + self._build_calendar( + self._selected_date.year, + self._selected_date.month) # reconstruct calendar + + def select_next_day(self): + """Update calendar to show the next day.""" + + if self._selected_date is None: + self._selected_date = datetime.datetime(self._year, self._month, 1) + else: + self._clear_selection() + self._selected_date = self._selected_date + self.timedelta(days=1) + + self._build_calendar( + self._selected_date.year, + self._selected_date.month) # reconstruct calendar + + def select_prev_week_day(self): + """Updated calendar to show the previous week.""" + if self._selected_date is None: + self._selected_date = datetime.datetime(self._year, self._month, 1) + else: + self._clear_selection() + self._selected_date = self._selected_date - self.timedelta(days=7) + + self._build_calendar( + self._selected_date.year, + self._selected_date.month) # reconstruct calendar + + def select_next_week_day(self): + """Update calendar to show the next week.""" + if self._selected_date is None: + self._selected_date = datetime.datetime(self._year, self._month, 1) + else: + self._clear_selection() + self._selected_date = self._selected_date + self.timedelta(days=7) + + self._build_calendar( + self._selected_date.year, + self._selected_date.month) # reconstruct calendar + + def select_current_date(self): + """Update calendar to current date.""" + if self._selection_is_visible: + self._clear_selection() + + self._selected_date = datetime.datetime.now() + self._build_calendar(self._selected_date.year, + self._selected_date.month) + + def prev_month(self): + """Updated calendar to show the previous week.""" + if self._selection_is_visible: + self._clear_selection() + + date = self.datetime(self._year, self._month, + 1) - self.timedelta(days=1) + self._build_calendar(date.year, date.month) # reconstuct calendar + + def next_month(self): + """Update calendar to show the next month.""" + if self._selection_is_visible: + self._clear_selection() + + date = self.datetime(self._year, self._month, 1) + self.timedelta( + days=calendar.monthrange(self._year, self._month)[1] + 1) + + self._build_calendar(date.year, date.month) # reconstuct calendar + + def prev_year(self): + """Updated calendar to show the previous year.""" + + if self._selection_is_visible: + self._clear_selection() + + self._build_calendar(self._year - 1, + self._month) # reconstruct calendar + + def next_year(self): + """Update calendar to show the next year.""" + + if self._selection_is_visible: + self._clear_selection() + + self._build_calendar(self._year + 1, + self._month) # reconstruct calendar + + def get_selection(self): + """Return a datetime representing the current selected date.""" + return self._selected_date + + selection = get_selection + + def set_selection(self, date): + """Set the selected date.""" + if self._selected_date is not None and self._selected_date != date: + self._clear_selection() + + self._selected_date = date + + self._build_calendar(date.year, date.month) # reconstruct calendar + + +# see this URL for date format information: +# https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior + + +class Datepicker(ttk.Entry): + def __init__(self, + master, + entrywidth=None, + entrystyle=None, + datevar=None, + dateformat="%Y-%m-%d", + onselect=None, + firstweekday=calendar.MONDAY, + locale=None, + activebackground='#b1dcfb', + activeforeground='black', + selectbackground='#003eff', + selectforeground='white', + borderwidth=1, + relief="solid"): + + if datevar is not None: + self.date_var = datevar + else: + self.date_var = Tkinter.StringVar() + + entry_config = {} + if entrywidth is not None: + entry_config["width"] = entrywidth + + if entrystyle is not None: + entry_config["style"] = entrystyle + + ttk.Entry.__init__( + self, master, textvariable=self.date_var, **entry_config) + + self.date_format = dateformat + + self._is_calendar_visible = False + self._on_select_date_command = onselect + + self.calendar_frame = Calendar( + self.winfo_toplevel(), + firstweekday=firstweekday, + locale=locale, + activebackground=activebackground, + activeforeground=activeforeground, + selectbackground=selectbackground, + selectforeground=selectforeground, + command=self._on_selected_date, + on_click_month_button=lambda: self.focus()) + + self.bind_all("<1>", self._on_click, "+") + + self.bind("", lambda event: self._on_entry_focus_out()) + self.bind("", lambda event: self.hide_calendar()) + self.calendar_frame.bind("<>", + lambda event: self._on_calendar_focus_out()) + + # CTRL + PAGE UP: Move to the previous month. + self.bind("", + lambda event: self.calendar_frame.prev_month()) + + # CTRL + PAGE DOWN: Move to the next month. + self.bind("", + lambda event: self.calendar_frame.next_month()) + + # CTRL + SHIFT + PAGE UP: Move to the previous year. + self.bind("", + lambda event: self.calendar_frame.prev_year()) + + # CTRL + SHIFT + PAGE DOWN: Move to the next year. + self.bind("", + lambda event: self.calendar_frame.next_year()) + + # CTRL + LEFT: Move to the previous day. + self.bind("", + lambda event: self.calendar_frame.select_prev_day()) + + # CTRL + RIGHT: Move to the next day. + self.bind("", + lambda event: self.calendar_frame.select_next_day()) + + # CTRL + UP: Move to the previous week. + self.bind("", + lambda event: self.calendar_frame.select_prev_week_day()) + + # CTRL + DOWN: Move to the next week. + self.bind("", + lambda event: self.calendar_frame.select_next_week_day()) + + # CTRL + END: Close the datepicker and erase the date. + self.bind("", lambda event: self.erase()) + + # CTRL + HOME: Move to the current month. + self.bind("", + lambda event: self.calendar_frame.select_current_date()) + + # CTRL + SPACE: Show date on calendar + self.bind("", + lambda event: self.show_date_on_calendar()) + + # CTRL + Return: Set to entry current selection + self.bind("", + lambda event: self.set_date_from_calendar()) + + def set_date_from_calendar(self): + if self.is_calendar_visible: + selected_date = self.calendar_frame.selection() + + if selected_date is not None: + self.date_var.set(selected_date.strftime(self.date_format)) + + if self._on_select_date_command is not None: + self._on_select_date_command(selected_date) + + self.hide_calendar() + + @property + def current_text(self): + return self.date_var.get() + + @current_text.setter + def current_text(self, text): + return self.date_var.set(text) + + @property + def current_date(self): + try: + date = datetime.datetime.strptime(self.date_var.get(), + self.date_format) + return date + except ValueError: + return None + + @current_date.setter + def current_date(self, date): + self.date_var.set(date.strftime(self.date_format)) + + @property + def is_valid_date(self): + if self.current_date is None: + return False + else: + return True + + def show_date_on_calendar(self): + date = self.current_date + if date is not None: + self.calendar_frame.set_selection(date) + + self.show_calendar() + + def show_calendar(self): + if not self._is_calendar_visible: + self.calendar_frame.place(in_=self, relx=0, rely=1) + self.calendar_frame.lift() + + self._is_calendar_visible = True + + def hide_calendar(self): + if self._is_calendar_visible: + self.calendar_frame.place_forget() + + self._is_calendar_visible = False + + def erase(self): + self.hide_calendar() + self.date_var.set("") + + @property + def is_calendar_visible(self): + return self._is_calendar_visible + + def _on_entry_focus_out(self): + if not str(self.focus_get()).startswith(str(self.calendar_frame)): + self.hide_calendar() + + def _on_calendar_focus_out(self): + if self.focus_get() != self: + self.hide_calendar() + + def _on_selected_date(self, date): + self.date_var.set(date.strftime(self.date_format)) + self.hide_calendar() + + if self._on_select_date_command is not None: + self._on_select_date_command(date) + + def _on_click(self, event): + str_widget = str(event.widget) + + if str_widget == str(self): + if not self._is_calendar_visible: + self.show_date_on_calendar() + else: + if not str_widget.startswith(str( + self.calendar_frame)) and self._is_calendar_visible: + self.hide_calendar() + + +if __name__ == "__main__": + import sys + + try: + from Tkinter import Tk, Frame, Label + except ImportError: + from tkinter import Tk, Frame, Label + + root = Tk() + root.geometry("500x600") + + main = Frame(root, pady=15, padx=15) + main.pack(expand=True, fill="both") + + Label(main, justify="left", text=__doc__).pack(anchor="w", pady=(0, 15)) + + Datepicker(main).pack(anchor="w") + + if 'win' not in sys.platform: + style = ttk.Style() + style.theme_use('clam') + + root.mainloop() diff --git a/db.py b/db.py new file mode 100644 index 0000000..75195ca --- /dev/null +++ b/db.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +import io +import sqlite3 + +class Db(): + + def __init__(self): + self.dbfile = "iscritti.sqlite3" + + def insert_new(self, iscritto): + firma_stream = io.BytesIO() + iscritto.img_firma.save(firma_stream, format="PNG") + firma_bytes = firma_stream.getvalue() + conn = sqlite3.connect(self.dbfile) + cur = conn.cursor() + # Come salvo il contenuto della Canvas + # nel db che non ce' una "textvar" associata? + par = ( + iscritto.nome.get(), + iscritto.cognome.get(), + iscritto.data_nascita.get(), + iscritto.comune_nascita.get(), + iscritto.email.get(), + iscritto.occupazione.get(), + iscritto.fonte.get(), + iscritto.data_firma.get(), + firma_bytes) + sql = ( + "INSERT INTO iscritti (" + "nome" + ", cognome" + ", data_nascita" + ", comune_nascita" + ", email" + ", occupazione" + ", fonte" + ", data_firma" + ", firma" + ") values (" + "?" # nome + ", ?" # cognome + ", ?" # data_nascita + ", ?" # comune_nascita + ", ?" # email + ", ?" # occupazione + ", ?" # fonte + ", ?" # data_firma + ", ?" # firma + ");") + cur.execute(sql, par) + conn.commit() + conn.close() diff --git a/dev_requirements.txt b/dev_requirements.txt new file mode 100644 index 0000000..4452f79 --- /dev/null +++ b/dev_requirements.txt @@ -0,0 +1,15 @@ +appdirs==1.4.3 +attrs==19.1.0 +autopep8==1.4.4 +black==19.3b0 +Click==7.0 +entrypoints==0.3 +flake8==3.7.7 +jedi==0.13.3 +mccabe==0.6.1 +parso==0.4.0 +pycodestyle==2.5.0 +pyflakes==2.1.1 +rope==0.14.0 +toml==0.10.0 +yapf==0.27.0 diff --git a/iscritti.py b/iscritti.py new file mode 100644 index 0000000..cff1206 --- /dev/null +++ b/iscritti.py @@ -0,0 +1,502 @@ +# -*- coding: utf-8 -*- + +# Per la generazione dei reports: +# - https://www.reportlab.com/ +# - https://pythonhosted.org/PollyReports/tutorial.html +# - https://pbpython.com/pdf-reports.html + +# Smart Card +# - https://pyscard.sourceforge.io/ +# - https://www.mmxforge.net/index.php/sviluppo/python/item/9-lettura-dei-dati-della-tessera-sanitaria-con-python + + +import tkinter as tk +import tkinter.ttk as ttk +import tkinter.messagebox as tkMessagebox + +from locale import (setlocale, LC_ALL) + +from PIL import Image +from PIL import ImageDraw +from PIL import ImageTk + +from db import Db +from datepicker import Datepicker +from const import MESSAGE_BOX_TITLE +from iscritto import Iscritto + +FRM_PADX = 5 +FRM_PADY = 3 + +FRA_PADX = 5 +FRA_PADY = 3 + +LBL_PADX = 3 +LBL_PADY = 3 + +TXT_PADX = 5 +TXT_PADY = 3 + +IMG_PADX = 5 +IMG_PADY = 3 + +BTN_PADX = 5 +BTN_PADY = 3 + +setlocale(LC_ALL, '') + +class Iscritti(): + + def __init__(self, parent): + self.parent = parent + self.larghezza_firma = 800 + self.altezza_firma = 300 + self.img_firma_color_fg = "black" + self.img_firma_color_bg = "white" + self.img_firma_penwidth = 5 + self.iscritto = Iscritto(self.larghezza_firma, + self.altezza_firma, + self.img_firma_color_fg, + self.img_firma_color_bg) + self.create_widgets() + parent.update() + return + + def create_widgets(self): + self.configure_master() + + self.crea_master_widgets() + return + + def configure_master(self): + # Crea la finestra a tutto schermo + # (non funziona su Mint 19.1) + # self.parent.wm_state('zoomed') + self.parent.focus_set() + self.parent.title("GOLEM - Iscritti") + self.parent.rowconfigure(0, weight=1) + self.parent.columnconfigure(0, weight=1) + self.parent.protocol("WM_DELETE_WINDOW", self.esci) + return + + def crea_master_widgets(self): + self.fra_master = ttk.Frame(self.parent) + + row_number = 0 + + s1 = ttk.Separator(self.fra_master, orient=tk.HORIZONTAL) + s1.grid(row=row_number, column=0, sticky=tk.W + tk.E) + row_number += 1 + + self.crea_frame_pulsanti(self.fra_master, row_number) + self.fra_master.rowconfigure(row_number, weight=0) + row_number += 1 + + s2 = ttk.Separator(self.fra_master, orient=tk.HORIZONTAL) + s2.grid(row=row_number, column=0, sticky=tk.W + tk.E) + row_number += 1 + + self.crea_frame_dati(self.fra_master, row_number) + self.fra_master.rowconfigure(row_number, weight=1) + row_number += 1 + + self.fra_master.columnconfigure(0, weight=1) + + self.fra_master.grid(row=0, + column=0, + padx=FRA_PADX, + pady=FRA_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + return + + def crea_frame_pulsanti(self, parent, row_number): + self.fra_pulsanti = ttk.Frame(parent) + self.fra_pulsanti.grid(row=row_number, + column=0, + padx=FRA_PADX, + pady=FRA_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + + child_row = 0 + + self.crea_widgets_pulsanti(self.fra_pulsanti, child_row) + self.fra_pulsanti.rowconfigure(0, weight=1) + child_row += 1 + + return + + def crea_widgets_pulsanti(self, parent, row_number): + child_column = 0 + self.btn_nuovo = ttk.Button(parent, text="Nuovo", command=self.nuovo) + self.btn_nuovo.grid(row=row_number, + column=child_column, + padx=BTN_PADX, + pady=BTN_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + parent.columnconfigure(child_column, weight=1) + + # Funzionalita' ricerca da implementare (datagrid in tkinter???) + # child_column += 1 + # self.btn_cerca = ttk.Button(parent, text="Cerca", command=self.cerca) + # self.btn_cerca.grid(row=row_number, + # column=child_column, + # padx=BTN_PADX, + # pady=BTN_PADX, + # sticky=tk.N + tk.W + tk.S + tk.E) + # parent.columnconfigure(child_column, weight=1) + + child_column += 1 + self.btn_salva = ttk.Button(parent, text="Salva", command=self.salva) + self.btn_salva.grid(row=row_number, + column=child_column, + padx=BTN_PADX, + pady=BTN_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + parent.columnconfigure(child_column, weight=1) + + child_column += 1 + self.btn_esci = ttk.Button(parent, text="Esci", command=self.esci) + self.btn_esci.grid(row=row_number, + column=child_column, + padx=BTN_PADX, + pady=BTN_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + parent.columnconfigure(child_column, weight=1) + return + + def crea_frame_dati(self, parent, row_number): + self.fra_dati = ttk.Frame(parent) + self.fra_dati.grid(row=row_number, + column=0, + padx=FRA_PADX, + pady=FRA_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + + child_row = 0 + + self.crea_widgets_id(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_cognome(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_nome(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_data_nascita(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_comune_nascita(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_email(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_occupazione(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_fonte(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_data_firma(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.crea_widgets_firma(self.fra_dati, child_row) + self.fra_dati.rowconfigure(child_row, weight=1) + child_row += 1 + + self.fra_dati.columnconfigure(0, weight=0) + self.fra_dati.columnconfigure(1, weight=1) + self.fra_dati.columnconfigure(2, weight=1) + + return + + def crea_widgets_id(self, parent, row_number): + + self.lbl_id = ttk.Label(parent, text="Id", anchor=tk.W) + self.lbl_id.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_id = ttk.Entry(parent, + textvariable=self.iscritto.id, + state="readonly") + self.txt_id.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_cognome(self, parent, row_number): + + self.lbl_cognome = ttk.Label(parent, text="Cognome", anchor=tk.W) + self.lbl_cognome.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_cognome = ttk.Entry(parent, textvariable=self.iscritto.cognome) + self.txt_cognome.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_nome(self, parent, row_number): + + self.lbl_nome = ttk.Label(parent, text="Nome", anchor=tk.W) + self.lbl_nome.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_nome = ttk.Entry(parent, textvariable=self.iscritto.nome) + self.txt_nome.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_data_nascita(self, parent, row_number): + + self.lbl_data_nascita = ttk.Label(parent, + text="Data di nascita", + anchor=tk.W) + self.lbl_data_nascita.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_data_nascita = Datepicker(parent, + dateformat="%x", + datevar=self.iscritto.data_nascita) + self.txt_data_nascita.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_comune_nascita(self, parent, row_number): + + self.lbl_comune_nascita = ttk.Label(parent, + text="Comune di nascita", + anchor=tk.W) + self.lbl_comune_nascita.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_comune_nascita = ttk.Entry( + parent, textvariable=self.iscritto.comune_nascita) + self.txt_comune_nascita.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_email(self, parent, row_number): + + self.lbl_email = ttk.Label(parent, text="E-mail", anchor=tk.W) + self.lbl_email.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_email = ttk.Entry(parent, textvariable=self.iscritto.email) + self.txt_email.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_occupazione(self, parent, row_number): + + self.lbl_occupazione = ttk.Label(parent, + text="Occupazione", + anchor=tk.W) + self.lbl_occupazione.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_occupazione = ttk.Entry(parent, + textvariable=self.iscritto.occupazione) + self.txt_occupazione.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_fonte(self, parent, row_number): + + self.lbl_fonte = ttk.Label(parent, text="Fonte", anchor=tk.W) + self.lbl_fonte.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_fonte = ttk.Entry(parent, textvariable=self.iscritto.fonte) + self.txt_fonte.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_data_firma(self, parent, row_number): + + self.lbl_data_firma = ttk.Label(parent, text="Data firma", anchor=tk.W) + self.lbl_data_firma.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + + self.txt_data_firma = Datepicker(parent, + dateformat="%x", + datevar=self.iscritto.data_firma) + self.txt_data_firma.grid(row=row_number, + column=1, + columnspan=2, + padx=TXT_PADX, + pady=TXT_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + return + + def crea_widgets_firma(self, parent, row_number): + # https://www.youtube.com/watch?v=rhvJ_M218EQ + self.img_firma_old_x = None + self.img_firma_old_y = None + + self.lbl_firma = ttk.Label(parent, text="Firma", anchor=tk.N + tk.W) + self.lbl_firma.grid(row=row_number, + column=0, + padx=LBL_PADX, + pady=LBL_PADY, + sticky=tk.N + tk.S + tk.E + tk.W) + larghezza = self.larghezza_firma + altezza = self.altezza_firma + self.cv_firma = tk.Canvas( + parent, + width=larghezza, + height=altezza, + border=1, + background=self.img_firma_color_bg, + relief=tk.SUNKEN) + self.cv_firma.bind("", self.img_firma_paint) + self.cv_firma.bind("", self.img_firma_reset) + self.cv_firma.grid(row=row_number, + column=1, + padx=IMG_PADX, + pady=IMG_PADY, + sticky=tk.N + tk.W) + self.draw_frirma = ImageDraw.Draw(self.iscritto.img_firma) + + self.fra_pulsanti_firma = ttk.Frame(parent) + self.fra_pulsanti_firma.columnconfigure(0, weight=1) + self.fra_pulsanti_firma.grid(row=row_number, + column=2, + padx=FRA_PADX, + pady=FRA_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + + self.btn_firma_clear = ttk.Button(self.fra_pulsanti_firma, + text="Pulisci") + self.btn_firma_clear.bind("", self.img_firma_clear) + self.btn_firma_clear.grid(row=0, + column=0, + padx=BTN_PADX, + pady=BTN_PADX, + sticky=tk.N + tk.W + tk.S + tk.E) + return + + def img_firma_paint(self, e): + if self.img_firma_old_x and self.img_firma_old_y: + self.cv_firma.create_line(self.img_firma_old_x, + self.img_firma_old_y, + e.x, + e.y, + width=self.img_firma_penwidth, + fill=self.img_firma_color_fg, + capstyle=tk.ROUND, + smooth=True) + xy = [self.img_firma_old_x, self.img_firma_old_y, e.x, e.y] + self.draw_frirma.line(xy, + width=self.img_firma_penwidth, + fill=self.img_firma_color_fg) + self.img_firma_old_x = e.x + self.img_firma_old_y = e.y + return + + def img_firma_reset(self, e): + self.img_firma_old_x = None + self.img_firma_old_y = None + return + + def img_firma_clear(self, e): + self.cv_firma.delete("all") + return + + def nuovo(self): + self.iscritto.new() + # # Alla canvas non e' associata nessuna tkinger.Variable() + # # quindi lavoro direttamente sul widget + self.cv_firma.delete("all") + self.draw_frirma = ImageDraw.Draw(self.iscritto.img_firma) + return + + def cerca(self): + tkMessagebox.showinfo(MESSAGE_BOX_TITLE, "valido, salvo!") + pass + + def salva(self): + if self.iscritto.is_valid(): + salva = tkMessagebox.askyesno(MESSAGE_BOX_TITLE, + "Convermi salvataggio?") + if salva: + db = Db() + db.insert_new(self.iscritto) + self.nuovo() + else: + tkMessagebox.showinfo(MESSAGE_BOX_TITLE, + "non valido e quindi non posso salvare!") + self.parent.update() + + def esci(self): + if tkMessagebox.askyesno(MESSAGE_BOX_TITLE, "Esco?"): + self.parent.update() + self.parent.destroy() + return diff --git a/iscritti.sqlite3 b/iscritti.sqlite3 new file mode 100644 index 0000000..5facef6 Binary files /dev/null and b/iscritti.sqlite3 differ diff --git a/iscritto.py b/iscritto.py new file mode 100644 index 0000000..c90d905 --- /dev/null +++ b/iscritto.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- + +import tkinter as tk +import tkinter.messagebox as tkMessagebox + +from datetime import datetime + +from PIL import Image + +from const import MESSAGE_BOX_TITLE + +class Iscritto(): + + def __init__(self, larghezza_firma, altezza_firma, img_firma_color_fg, img_firma_color_bg): + self.larghezza_firma = larghezza_firma + self.altezza_firma = altezza_firma + self.img_firma_color_fg = img_firma_color_fg + self.img_firma_color_bg = img_firma_color_bg + self.id = tk.StringVar() + self.cognome = tk.StringVar() + self.nome = tk.StringVar() + self.data_nascita = tk.StringVar() + self.comune_nascita = tk.StringVar() + self.email = tk.StringVar() + self.occupazione = tk.StringVar() + self.fonte = tk.StringVar() + self.data_firma = tk.StringVar() + # In Image.new(), se non indicato il "color" e' nero + # "L" = 8 bit - B/N + self.img_firma = Image.new("L", (self.larghezza_firma, self.altezza_firma), self.img_firma_color_bg) + return + + def new(self): + self.id.set("") + self.cognome.set("") + self.nome.set("") + self.data_nascita.set("") + self.comune_nascita.set("") + self.email.set("") + self.occupazione.set("") + self.fonte.set("") + self.data_firma.set("") + # In Image.new(), se non indicato il "color" e' nero + # "L" = 8 bit - B/N + self.img_firma = Image.new("L", (self.larghezza_firma, self.altezza_firma), self.img_firma_color_bg) + + def is_valid(self): + is_valid = self.cognome_is_valid() + if is_valid: + is_valid = self.nome_is_valid() + if is_valid: + is_valid = self.data_nascita_is_valid() + if is_valid: + is_valid = self.comune_nascita_is_valid() + if is_valid: + is_valid = self.email_is_valid() + if is_valid: + is_valid = self.occupazione_is_valid() + if is_valid: + is_valid = self.fonte_is_valid() + if is_valid: + is_valid = self.data_firma_is_valid() + # Non si puo' convalidare la firma che e' + # una immagine + return is_valid + + def cognome_is_valid(self): + is_valid = True + if len(str.rstrip(self.cognome.get())) == 0: + is_valid = False + messaggio = "'Cognome' e' obbligatorio" + tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio) + return is_valid + + def nome_is_valid(self): + is_valid = True + messaggio = "" + if len(str.rstrip(self.nome.get())) == 0: + is_valid = False + messaggio = "'Nome' e' obbligatorio" + tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio) + return is_valid + + def data_nascita_is_valid(self): + is_valid = True + messaggio = "" + if self.data_nascita.get() is None: + is_valid = False + messaggio = "'Data di nascita' e' obbligatorio" + elif len(str.rstrip(self.data_nascita.get())) == 0: + is_valid = False + messaggio = "'Data di nascita' e' obbligatorio" + else: + try: + datetime.strptime(self.data_nascita.get(), '%x') + except (ValueError): + is_valid = False + messaggio = "'Data di nascita' non valida" + if not is_valid: + tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio) + return is_valid + + def comune_nascita_is_valid(self): + is_valid = True + messaggio = "" + if len(str.rstrip(self.comune_nascita.get())) == 0: + is_valid = False + messaggio = "'Comune di nascita' e' obbligatorio" + tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio) + return is_valid, messaggio + + def email_is_valid(self): + is_valid = True + messaggio = "" + if len(str.rstrip(self.email.get())) == 0: + is_valid = False + messaggio = "'E-mail' e' obbligatorio" + tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio) + return is_valid, messaggio + + def occupazione_is_valid(self): + # Non obbligatorio + return True + + def fonte_is_valid(self): + # Non obbligatorio + return True + + def data_firma_is_valid(self): + is_valid = True + messaggio = "" + if self.data_nascita.get() is None: + is_valid = False + messaggio = "'Data firma' e' obbligatorio" + elif len(str.rstrip(self.data_nascita.get())) == 0: + is_valid = False + messaggio = "'Data firma' e' obbligatorio" + else: + try: + datetime.strptime(self.data_nascita.get(), '%x') + except (ValueError): + is_valid = False + messaggio = "'Data firma' non valida" + if not is_valid: + tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio) + return is_valid diff --git a/main.py b/main.py new file mode 100644 index 0000000..e60c883 --- /dev/null +++ b/main.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import tkinter as tk + +from iscritti import Iscritti + +def main(): + root = tk.Tk() + Iscritti(root) + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/pysc.py b/pysc.py new file mode 100644 index 0000000..e323bdc --- /dev/null +++ b/pysc.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# https://www.mmxforge.net/index.php/sviluppo/python/item/9-lettura-dei-dati-della-tessera-sanitaria-con-python + +from smartcard.System import readers +import array +import string +import datetime + +r = readers() +reader = r[0] +print("Sto usando: {}".format(reader.name)) +connection = reader.createConnection() +connection.connect() + +#Seleziona del MF +#CLS 00, istruzione A4 (seleziona file), P1 = P2 = 0 (seleziona per ID), +#Lc: 2, Data: 3F00 (id del MF) +SELECT_MF = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00] +data, sw1, sw2 = connection.transmit(SELECT_MF) +#se tutto è andato a buon fine sw1 e sw2 contengono +#rispettivamente i valori 0x90 e 0x00 il corrispettivo del 200 in HTTP + +#Seleziona del DF1...vedi sopra +SELECT_DF1 = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x11, 0x00] +data, sw1, sw2 = connection.transmit(SELECT_DF1) + +#Seleziona del file EF.Dati_personali... vedi sopra sopra +SELECT_EF_PERS = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x11, 0x02] +data, sw1, sw2 = connection.transmit(SELECT_EF_PERS) + +#leggiamo i dati +#CLS 00, istruzione B0 (leggi i dati binari contenuti nel file +READ_BIN = [0x00, 0xB0, 0x00, 0x00, 0x00, 0x00] +data, sw1, sw2 = connection.transmit(READ_BIN) +#data contiene i dati anagrafici in formato binario +#trasformiamo il tutto in una stringa +stringa_dati_personali = array.array('B', data).tostring() + +dimensione = int(stringa_dati_personali[0:6],16) +print("Dimensione in byte dei dati: {}".format( dimensione)) +numero_field = 0 + +prox_field_size = int(stringa_dati_personali[6:8], 16) +da = 8 +a = da + prox_field_size +if prox_field_size > 0: + codice_emettitore = stringa_dati_personali[da:a] + print("Codice emettitore: {}".format(codice_emettitore.decode("utf-8"))) + numero_field += 1 + +da = a +a +=2 +prox_field_size = int(stringa_dati_personali[da:a], 16) +da=a +a += prox_field_size +if prox_field_size > 0: + data_rilascio_tessera = stringa_dati_personali[da:a] + year = int(data_rilascio_tessera[-4:]) + month = int(data_rilascio_tessera[2:4]) + day = int(data_rilascio_tessera[0:2]) + dtm_data_rliascio = datetime.date(year, month, day) + # print(string("Data rilascio tessera: ", data_rilascio_tessera[0:2], "/", data_rilascio_tessera[2:4], "/", data_rilascio_tessera[-4:]) + print("Data rilascio tessera: {}".format(dtm_data_rliascio)) + numero_field += 1 + +da = a +a +=2 +prox_field_size = int(stringa_dati_personali[da:a], 16) +da=a +a += prox_field_size +if prox_field_size > 0: + data_scadenza_tessera = stringa_dati_personali[da:a] + year = int(data_scadenza_tessera[-4:]) + month = int(data_scadenza_tessera[2:4]) + day = int(data_scadenza_tessera[0:2]) + dtm_data_scadenza = datetime.date(year, month, day) + # print("Data scadenza tessera: ", data_scadenza_tessera[0:2]+"/"+data_scadenza_tessera[2:4]+"/"+data_scadenza_tessera[-4:]) + print("Data scadenza tessera: {}".format(dtm_data_scadenza)) + numero_field += 1 + +# da = a +# a +=2 +# prox_field_size = int(stringa_dati_personali[da:a], 16) +# da=a +# a += prox_field_size +# if prox_field_size > 0: +# cognome = stringa_dati_personali[da:a] +# print("Cognome: {}".format(cognome)) +# numero_field += 1 + +while a < dimensione: + da = a + a += 2 + prox_field_size = int(stringa_dati_personali[da:a], 16) + da=a + a += prox_field_size + if prox_field_size > 0: + field = stringa_dati_personali[da:a] + print("Field#{}: {}".format(numero_field, field)) + numero_field += 1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8b7b8b7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +Pillow==6.0.0 +# Su Linux installare +# sudo apt install pcscd +# sudo apt install pcsc-tools +# sudo apt install python3-pyscard +# pyscard==1.9.8