Compare commits

...

28 Commits
v2.0 ... master

Author SHA1 Message Date
151ef275c8 MAJ CHALGELOG 2020-09-30 13:46:45 +02:00
2d803d8d59 Merge branch 'optimise_bdd_insee' into 'master'
Optimisation de l abase INSEE pour accélérer les recherches.

Closes #14

See merge request sdjgeek/purge-registres-deces-insee!6
2020-09-30 13:36:45 +02:00
19126e57aa Optimisation de l abase INSEE pour accélérer les recherches. 2020-09-30 13:33:59 +02:00
35d82173c6 Merge branch '10-code-geo' into 'master'
Convertir les code insee en nom de ville

Closes #10

See merge request sdjgeek/purge-registres-deces-insee!5
2020-08-04 08:47:46 +02:00
bb709eb2c1 release 7.0 2020-08-04 08:46:04 +02:00
cfdab63598 Convertir les code insee en nom de ville 2020-08-03 22:40:21 +02:00
6e4f587cce Minor release 6.1 2020-07-30 20:25:39 +02:00
88a94c6983 BUG: Ne crash plus si maiden_name est vide dans les registres 2020-07-30 12:01:48 +02:00
35fbec4fff Merge branch 'excel_in' into 'master'
Release 6.0

Closes #11, #12, and #13

See merge request sdjgeek/purge-registres-deces-insee!4
2020-07-29 13:21:39 +02:00
e0d2949b00 MAJ Changelog, ajout des entêtes 2020-07-29 13:19:35 +02:00
a26450d98d Tentative de creation d'un launcher pour windows 2020-07-29 12:56:27 +02:00
308ed11077 Lecture et écriture des fichiers Excel + quelques bugs
- Mise en place de la lecture de la liste des membres à
partir d'un fichier Excel
- Mise en place de l'écriture de la liste des membres décédés
dans un fichier Excel
- Correction du bug: le traitement ne rend pas la main à l'IHM
losque terminé
- Vider le texte de l'IHM lorsqu'on clique sur OK
2020-07-29 09:56:37 +02:00
9febc4cc55 Mettre la barre de progression à zéro lorsqu'on donne la valeur max. 2020-07-28 16:51:42 +02:00
87093bc92c Envoyer l'info que le traitement est terminé à l'IHM 2020-07-28 16:50:35 +02:00
8856641003 WIP préparation du lecteur de fichier excel 2020-07-18 16:49:02 +02:00
3fec540b67 Abstraction de site_eglise pour donner possibilité de recevoir une liste de membre par un autre moyen 2020-07-18 16:18:17 +02:00
b83958fd51 Mise à jour des entêtes 2020-07-17 21:49:45 +02:00
3e04e9f29a Release 5.0 2020-07-17 16:45:53 +02:00
b438d3a7a5 Merge branch 'ihm-tkinter' into 'master'
Création d'une IHM avec tkinter

Closes #4

See merge request sdjgeek/purge-registres-deces-insee!3
2020-07-13 15:09:59 +02:00
b9beb304e6 Ajout du multiprocessing pour que l'IHM ne freez pas 2020-07-07 16:07:04 +02:00
602ead50a9 Ajout d'une IHM utilisant tkinter 2020-07-01 23:33:55 +02:00
9d216600f8 Merge branch 'cookie-fetch' into 'master'
Simplification de la manipulation des cookies

See merge request sdjgeek/purge-registres-deces-insee!2
2020-06-16 23:36:01 +02:00
e798bbcc91 Version 4.0 2020-06-16 23:32:13 +02:00
ac82d33c3a Recherche des cookies OK. Utilisation d'argparse et suppression de settings.py 2020-06-16 23:28:56 +02:00
6f7028bb0e WIP: Aller chercher les cookies directement dans le navigateur 2020-06-16 23:28:27 +02:00
3734eeed87 Ajout de commentaires 2020-06-09 00:16:00 +02:00
7c8ce98489 Ajout du programme permettant de créer la base SQLite 2020-06-07 22:57:19 +02:00
5f281b1511 Réorganisation du code en modules 2020-06-07 00:52:15 +02:00
16 changed files with 1107 additions and 246 deletions

View File

@ -1,5 +1,40 @@
# Changelog
### [v8.0] 2020-09-30
- Optimisation de la base INSEE
- Création d'une table pour les prénoms, et d'une pour les noms
- Ajout d'index
- Ajout d'un mécanisme de mise à jour de la base insee
### [v7.0] 2020-08-04
- Convertit les Codes Officiels Géographiques en nom de commune
### [v6.1] 2020-07-30
- BUG: Lorsque maiden_name est None, ne pas essayer de le strip
### [v6.0] 2020-07-29
- Lecture de la liste des membres à partir d'un fichier Excel
- Écriture de la liste des membres décédés dans un fichier Excel
- Vider le texte de l'IHM lorsqu'on clique sur OK
- Fichier BAT pour installer et lancer l'IHM sous Windows
- BUG: le traitement rend la main à l'IHM losque terminé
- BUG: la barre de progression est remise à zero entre chaque traitement
### [v5.0] 2020-07-17
- Mise en place d'une interface graphique
- Ajout d'un fichier install.py
### [v4.0] - 2020-06-16
- Recherche automatique des cookies dans firefox
- Supression du fichier settings.py, remplacé par argparse
### [v3.1] - 2020-06-08
- Amélioration de la documentation
### [v3.0] - 2020-06-07
- Réorganisation du code en modules
- Ajout du script permettant de créer la base de donnée SQLite
### [v2.0] - 2020-06-01
- Le fichier de sortie contient plus d'informations pour faciliter la comparaison (lieu de naissance)
- Sortie des paramètres du code et création du fichier settings.py
@ -7,4 +42,4 @@
- Création du changelog
### [v1.0] - 2020-05-16
- Première publication
- Première publication

130
README.md
View File

@ -1,40 +1,43 @@
# Purge Registres Décès INSEE
Ce programme s'adresse essentiellement aux greffiers des pieux, paroisse et
branches de France, de l'Église de Jésus Christ des Saints des Derniers Jours.
Ce programme s'adresse essentiellement aux greffiers des pieux,
paroisse et branches de France, de l'Église de Jésus Christ des Saints
des Derniers Jours.
## Objectif
La tenue à jour des registres de l'Église n'est pas une tache aisée. Une des
difficultés est liée au fait que lorsqu'un membre s'éloigne de l'Église, ses
informations personnelles (adresse, téléphone, etc) ne sont plus mises à jour.
La tenue à jour des registres de l'Église n'est pas une tache
aisée. Une des difficultés est liée au fait que lorsqu'un membre
s'éloigne de l'Église, ses informations personnelles (adresse,
téléphone, etc) ne sont plus mises à jour.
L'INSEE tient un fichier des personnes décédées en France de 1970 à
aujourd'hui. Ce fichier est disponible en [données
ouvertes](https://fr.wikipedia.org/wiki/Donn%C3%A9es_ouvertes) sur [le site du
ouvertes](https://fr.wikipedia.org/wiki/Donn%C3%A9es_ouvertes) sur [le
site du
gouvernement](https://www.data.gouv.fr/fr/datasets/fichier-des-personnes-decedees/).
Ce fichier est une aubaine, car il permet de savoir si un membre de l'Église
est décédé sans que nous le sachions. Si une personne figure à la fois dans
les registres de l'Église et dans le fichier de l'INSEE, alors une mise à jour
du registre de l'Église est nécessaire.
Ce fichier est une aubaine, car il permet de savoir si un membre de
l'Église est décédé sans que nous le sachions. Si une personne figure
à la fois dans les registres de l'Église et dans le fichier de
l'INSEE, alors une mise à jour du registre de l'Église est nécessaire.
Parcourir à la main le registre de membres de paroisse est une tache longue et
fastidieuse. Ce programme permet de comparer automatiquement le registre de
paroisse avec le fichier de l'INSEE, et donne la liste des membres trouvés
dans le fichier de l'INSEE.
Parcourir à la main le registre de membres de paroisse est une tache
longue et fastidieuse. Ce programme permet de comparer automatiquement
le registre de paroisse avec le fichier de l'INSEE, et donne la liste
des membres trouvés dans le fichier de l'INSEE.
Le fichier de l'INSEE est actualisé de façon mensuelle. Il semble intéressant
d'utiliser ce programme régulièrement (une fois par an par exemple) pour
s'assurer que les registres restent à jour.
Le fichier de l'INSEE est actualisé de façon mensuelle. Il semble
intéressant d'utiliser ce programme régulièrement (une fois par an par
exemple) pour s'assurer que les registres restent à jour.
## Pré-requis
### Logiciels
- Git
- Python 3.6
- Python 3.6+
- Virtualenv
Testé uniquement sur GNU/Linux (Ubuntu 18.4)
Testé uniquement sur GNU/Linux (Ubuntu 18.4) et Windows
### Données
@ -45,6 +48,12 @@ ici](https://git.roflcopter.fr/sdjgeek/purge-registres-deces-insee/-/wikis/home#
## Installation
### Windows
Utiliser le script ```install.py```
### Linux
- Cloner le code
```bash
@ -56,49 +65,72 @@ cd purge-registres-deces-insee
```bash
python3 -m venv localenv
. localenv/bin/activate
source localenv/bin/activate
pip install -r requirements.txt
```
## Utilisation
Le début du fichier ```trouver_deces.py``` contient les paramètres à configurer :
Aller sur le [site de l'Église](https://lcr.churchofjesuschrist.org),
et se connecter au compte SDJ. Cette étape est essencielle pour que
le programme puisse accéder aux listes des membres.
- chemin_base_donnees : Le chemin vers le fichier téléchargé (me contacter)
- numeros_unites : La liste des unités que le programme doit tester. La liste commence par le caractère '['. Les
numéros d'unités sont séparés par des virgules. La liste se termine par le caractère ']'. Par exemple
[47823, 67745, 31456]
- cookie : Les cookies de la session au compte SDJ. Pour obtenir ces cookies il faut
1- Ouvrir Firefox
2- Ouvrir les outils de développement : CTRL + MAJ + E
3- Aller sur le site des outils pour greffiers : https://lds.org/lcr
4- Entrer identifiant et mot de passe SDJ
5- Aller dans la liste des membres
6- Dans l'outil de développement, cliquer sur la dernière ligne
7- Dans le panneau de droite, dans la section "En-tête de la requête", rechercher le champ "cookie"
8- Copier la valeur contenue dans ce champ
- chemin_repertoire_sortie : Le chemin vers le répertoire qui contiendra la fichiers de sortie
Pour lancer le prgramme, vous pouvez double cliquer
sur ```gui_trouver_decedes.py```, ce qui ouvrira une interface
graphique.
Une fois la configuration effectuée, lancer le script :
Si vous préférez la console, vous pouvez aussi faire :
```bash
. localenv/bin/activate
python trouver_deces.py
source localenv/bin/activate
python purge-registres-deces-insee/trouver_deces.py chemin_base_donnees chemin_repertoire_sortie numeros_unites
```
Le script mettra un peu de temps à s'exécuter. En fin d'exécution, le fichier
liste_membres_decedes_unite_{unite}.txt contiendra la liste des membres
trouvés dans le fichier de l'INSEE. Il est recommandé de contrôler les
informations du membres avant de le déclarer comme décédé dans les registres
de l'Église.
Avec :
- chemin_base_donnees : Le chemin vers le [fichier
téléchargé](https://git.roflcopter.fr/sdjgeek/purge-registres-deces-insee/-/wikis/home#t%C3%A9l%C3%A9charger-la-base-de-donn%C3%A9e)
- chemin_repertoire_sortie : Le chemin vers le répertoire qui
contiendra la fichiers de sortie
- numeros_unites : La liste des unités que le programme doit
tester. La liste commence par le caractère '['. Les numéros d'unités
sont séparés par des virgules. La liste se termine par le caractère
']'. Par exemple [47823, 67745, 31456]
Le script devrait trouver seul le cookie de session permettant de
consulter la liste des membres. Mais en cas d'échec, il est possible
de lui donner le chemin vers le fichier SQLite que Firefox utilise
pour stocker les cookies avec l'option '-c'. En règle générale ce
fichier se trouve le répertoire :
- sur linux :
"~/.mozilla/firefox/<un_code_étrange>.default/cookies.sqlite"
- sur windows :
"C:\Users\<nom_utilisateur>\AppData\Local\Mozilla\Firefox\Profiles\<un_code_étrange>.default\cookies.sqlite"
Par exemple :
```bash
python purge-registres-deces-insee/trouver_deces.py ~/Documents/fichier-des-personnes-decedees.sqlite ~/Documents/ 123,854 -c ~/.mozilla/firefox/8h3jsedf.default/cookies.sqlite
```
Le script mettra un peu de temps à s'exécuter. En fin d'exécution, le
fichier liste_membres_decedes_unite_{unite}.txt contiendra la liste
des membres trouvés dans le fichier de l'INSEE. Il est recommandé de
contrôler les informations du membres avant de le déclarer comme
décédé dans les registres de l'Église.
## Licence
Copyright (c) 2020 Sdj Geek
Ce logiciel est sous licence MIT/X Consortium. Elle donne à toute personne
recevant le logiciel (et ses fichiers) le droit illimité de l'utiliser, le
copier, le modifier, le fusionner, le publier, le distribuer, le vendre et le
"sous-licencier" (l'incorporer dans une autre licence). La seule obligation
est d'incorporer la notice de licence et de copyright dans toutes les
copies. Voir le fichier [LICENSE](LICENSE) pour le texte complet.
Ce logiciel est sous licence MIT/X Consortium. Elle donne à toute
personne recevant le logiciel (et ses fichiers) le droit illimité de
l'utiliser, le copier, le modifier, le fusionner, le publier, le
distribuer, le vendre et le "sous-licencier" (l'incorporer dans une
autre licence). La seule obligation est d'incorporer la notice de
licence et de copyright dans toutes les copies. Voir le fichier
[LICENSE](LICENSE) pour le texte complet.

52
install.py Normal file
View File

@ -0,0 +1,52 @@
import sys
import subprocess
try:
import peewee
except ImportError:
print("Installing peewee")
p = subprocess.Popen([sys.executable, "-m", "pip", "install", "-U", "peewee"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(p.communicate())
try:
import requests
except ImportError:
print("Installing requests")
p = subprocess.Popen([sys.executable, "-m", "pip", "install", "-U", "requests"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(p.communicate())
import requests
try:
import browser_cookie3
except ImportError:
print("Installing browser_cookie3")
p = subprocess.Popen([sys.executable, "-m", "pip", "install", "-U", "browser_cookie3"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(p.communicate())
try:
import pandas
except ImportError:
print("Installing pandas")
p = subprocess.Popen([sys.executable, "-m", "pip", "install", "-U", "pandas"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(p.communicate())
try:
import xlrd
except ImportError:
print("Installing xlrd")
p = subprocess.Popen([sys.executable, "-m", "pip", "install", "-U", "xlrd"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(p.communicate())
try:
import numpy
except ImportError:
print("Installing numpy")
p = subprocess.Popen([sys.executable, "-m", "pip", "install", "-U", "numpy"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(p.communicate())

View File

@ -0,0 +1,255 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Classe d'accès aux données INSEE dans la base SQLite
"""
import os
import peewee
import requests
import datetime
# Objet de connexion à la base
database = peewee.SqliteDatabase(None)
# Définition du modèle de donnée
class BaseModel(peewee.Model):
class Meta:
database = database
class LastName(BaseModel):
"""Classe contenant tous les noms de famille
"""
last_name = peewee.CharField(unique=True)
class FirstName(BaseModel):
"""Classe contenant tous les prénoms
"""
first_name = peewee.CharField(unique=True)
class Person(BaseModel):
"""Classe représentant une personne dans la base
"""
first_name = peewee.ForeignKeyField(FirstName)
last_name = peewee.ForeignKeyField(LastName)
is_woman = peewee.BooleanField()
date_naissance = peewee.DateField(index=True)
code_lieu_naissance = peewee.CharField()
commune_naissance = peewee.CharField()
pays_naissance = peewee.CharField()
date_deces = peewee.DateField()
code_lieu_deces = peewee.CharField()
numero_act_deces = peewee.CharField()
class ImportedDataset(BaseModel):
"""
"""
dataset = peewee.CharField(unique=True)
# Gestion de l'accès aux données
class BddInsee:
"""Classe encapsulant les accès aux données.
"""
def __init__(self, chemin_base_donnees=None):
"""Initialisation
:param chemin_base_donnees: chemin vers le fichier SQLite
"""
if not chemin_base_donnees:
chemin_base_donnees = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bdd_insee.sqlite")
new = not os.path.isfile(chemin_base_donnees)
database.init(chemin_base_donnees)
if new:
database.create_tables([LastName, FirstName, Person, ImportedDataset])
# Fonctions d'accès aux données
def find_person(self, first_name, last_name, maiden_name, annee_naissance, mois_naissance, jour_naissance):
"""Rechercher une personne dans la base
:param first_name: prénom de la personne à rechercher
:param last_name: nom de famille de la personne à rechercher
:param maiden_name: nom de jeune fille de la personne à rechercher (None si non défini)
:param annee_naissance: année de naissance de la personne à rechercher
:param mois_naissance: mois de naissance de la personne à rechercher
:param jour_naissance: jour de naissance de la personne à rechercher
:returns: liste contenant un dictionnaire par personne trouvée
"""
if maiden_name:
# return Person.select().where((Person.annee_naissance == int(annee_naissance))
# & (Person.mois_naissance == int(mois_naissance))
# & (Person.jour_naissance == int(jour_naissance))
# & (Person.first_name.contains(first_name.upper()))
# & ((Person.last_name.contains(last_name.upper()))
# | Person.last_name.contains(maiden_name.upper())))
query = Person.select().join(FirstName).switch(Person).join(LastName)\
.where((Person.date_naissance == datetime.date(int(annee_naissance), int(mois_naissance), int(jour_naissance)))
& (FirstName.first_name.contains(first_name.upper()))
& ((LastName.last_name.contains(last_name.upper()))
| LastName.last_name.contains(maiden_name.upper())))
else:
# return Person.select().where((Person.annee_naissance == int(annee_naissance))
# & (Person.mois_naissance == int(mois_naissance))
# & (Person.jour_naissance == int(jour_naissance))
# & (Person.first_name.contains(first_name.upper()))
# & (Person.last_name.contains(last_name.upper())))
query = Person.select().join(FirstName).switch(Person).join(LastName)\
.where((Person.date_naissance == datetime.date(int(annee_naissance), int(mois_naissance), int(jour_naissance)))
& (FirstName.first_name.contains(first_name.upper()))
& (LastName.last_name.contains(last_name.upper())))
result = list()
if query:
for row in query:
result.append({
'first_name': row.first_name.first_name,
'last_name': row.last_name.last_name,
'date_naissance': row.date_naissance,
'code_lieu_naissance': row.code_lieu_naissance,
'date_deces': row.date_deces,
'code_lieu_deces': row.code_lieu_deces
})
return result
# Fonctions d'import des données
def parse_insee_data(self, data_text):
"""Parse le texte d'un fichier de l'INSEE
:param data_text: texte contenu dans un fichier de l'INSEE
:returns: liste de dictionnaires contenant les informations à insérer
"""
data_list = []
for line_number, line in enumerate(data_text.split('\n')):
if not line:
continue
try:
try:
last_name, first_name = line[0:80].strip()[:-1].split("*")
except ValueError:
if '*' not in line[0:80]:
continue
raise
date_naissance = self.parse_date(int(line[81:85]), int(line[85:87]), int(line[87:89]))
date_deces = self.parse_date(int(line[154:158]), int(line[158:160]), int(line[160:162]))
data_list.append({'last_name': last_name.upper(),
'first_name': first_name.strip('/').upper(),
'is_woman': (line[80] == "2"),
'date_naissance': date_naissance,
'code_lieu_naissance': line[89:94].upper(),
'commune_naissance': line[94: 124].strip().upper(),
'pays_naissance': line[124: 154].strip().upper(),
'date_deces': date_deces,
'code_lieu_deces': line[162:167].upper(),
'numero_act_deces': line[167:].strip().upper()})
except ValueError as e:
print(f"Erreur pour parser ligne numéro {line_number}.")
print(line)
raise
return data_list
def parse_date(self, year, month, day):
if year == 0:
return datetime.date.min
if month == 0:
month = 1
elif month > 12:
month = 12
if day == 0:
day = 1
elif day > 30 and month in [4, 6, 9, 11]:
day = 30
elif day > 31 and month in [1, 3, 5, 7, 8, 10, 12]:
day = 31
elif day > 29 and month == 2:
day = 29
try:
date = datetime.date(year, month, day)
except ValueError:
if month == 2 and day == 29 and year not in [1844, 1848, 1852, 1856, 1860, 1864, 1868, 1872, 1876, 1880,
1884, 1888, 1892, 1896, 1904, 1908, 1912, 1916, 1920,
1924, 1928, 1932, 1936, 1940, 1944, 1948, 1952, 1956, 1960,
1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000,
2004, 2008, 2012, 2016, 2020]:
day = 28
date = datetime.date(year, month, day)
return date
def import_url_list(self, file_path):
"""Importer les données à partir d'un fichier contenant les URL
Un fichier d'URL contient un nom de dataset et une URL par ligne, sapéras par une espace. Chaque URL conduit
vers un fichier de l'insée qui doit être importé.
:param file_path: chemin vers le fichier d'URL
"""
if not os.path.isfile(file_path):
print(f"Le fichier {file_path} est introuvable.")
return
with open(file_path, 'r') as file_content:
for line in file_content.readlines():
try:
dataset, url = line.split(' ')
url = url.strip('\n')
except:
print(f"Le fichier {file_path} contient une ligne non conforme : {line}.")
continue
dataset_id = ImportedDataset.select().where(ImportedDataset.dataset == dataset)
if not len(dataset_id) == 0:
print(f"Le dataset {dataset} ne sera pas traité, car il a déjà été importé.")
continue
print(f"Importation du dataset {dataset}")
print(f"Téléchargement de : '{url}'.")
result = requests.get(url)
try:
result.raise_for_status()
except:
print(f"Erreur lors du téléchargement du fichier {url}.")
continue
print("Parse des données téléchargées")
data_list = self.parse_insee_data(result.text)
self.import_data_list(data_list, dataset)
def import_data_list(self, data_list, dataset_name):
"""Insérer des données dans la base
:param data_list: liste de dictionnaires contenant les informations à insérer
:param dataset_name: nom du jeu de données
"""
dataset_id = ImportedDataset.select().where(ImportedDataset.dataset == dataset_name)
if not len(dataset_id) == 0:
print(f"Le dataset {dataset_name} a déjà été importé.")
return
print(f"Import en base des {len(data_list)} personnes.")
with database.atomic():
for data in data_list:
# Gestion de la clé étrangère prénom
first_name = data.pop('first_name')
try:
first_name_id = FirstName.get(FirstName.first_name == first_name)
except peewee.DoesNotExist:
first_name_id = FirstName.create(first_name=first_name)
data['first_name'] = first_name_id
# Gestion de la clé étrangère nom de famille
last_name = data.pop('last_name')
try:
last_name_id = LastName.get(LastName.last_name == last_name)
except peewee.DoesNotExist:
last_name_id = LastName.create(last_name=last_name)
data['last_name'] = last_name_id
Person.insert(**data).execute()
ImportedDataset.insert(dataset=dataset_name).execute()
print("Import terminé")

View File

@ -0,0 +1,83 @@
"""Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
------------------------------------------------------------------------------------------------------------------------
Ce programme permet d'insérer les fichiers fournis par l'INSEE dans
une base de donnée SQLite.
Les fichiers fournis par l'INSEE sont disponibles en open data à
[cette
adresse](https://www.data.gouv.fr/fr/datasets/fichier-des-personnes-decedees/#_). Leur
formatage n'est pas très optimisé pour une utilisation dans un
programme. L'objet de ce programme est de transformer ces fichiers en
une base de donnée SDLite.
------------------------------------------------------------------------------------------------------------------------
Auteur : SDJ GeeK <sdjgeek@protonmail.com>
Date : 7 juin 2020
"""
import argparse
import glob
import os
import peewee
from bdd_insee import BddInsee
def parse_insee_file(data_file_path):
"""Parse un fichier de l'INSEE
:param data_file_path: chemin vers le fichier à parser
:returns: liste de dictionnaires contenant les informations à insérer
"""
data_list = []
with open(data_file_path, 'r', encoding='latin1') as data_file:
for line in data_file.readlines():
try:
last_name, first_name = line[0:80].strip()[:-1].split("*")
data_list.append({'last_name': last_name.upper(),
'first_name': first_name.upper(),
'is_woman': (line[80] == "2"),
'annee_naissance': int(line[81:85]),
'mois_naissance': int(line[85:87]),
'jour_naissance': int(line[87:89]),
'code_lieu_naissance': line[89:94].upper(),
'commune_naissance': line[94: 124].strip().upper(),
'pays_naissance': line[124: 154].strip().upper(),
'annee_deces': int(line[154:158]),
'mois_deces': int(line[158:160]),
'jour_deces': int(line[160:162]),
'code_lieu_deces': line[162:167].upper(),
'numero_act_deces': line[167:].strip().upper()})
except ValueError:
print("Erreur pour parser ligne :")
print(line)
continue
return data_list
def completer_bdd_insee(chemin_base_donnees, chemin_repertoire_donnees):
"""Ajouter des données dans la base
:param chemin_base_donnees: chemin vers le fichier SQLite
:param chemin_repertoire_donnees: chemin vers le répertoire contenant les fichiers de l'INSEE
"""
base_insee = BddInsee(chemin_base_donnees)
for file_path in sorted(glob.glob(os.path.join(chemin_repertoire_donnees, "deces-*.txt"))):
print(f"Import du fichier {file_path}")
data_list = parse_insee_file(file_path)
base_insee.import_data_list(data_list)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("bdd_path", type=str, help="Chemin vers le fichier de base de donnée")
parser.add_argument("input_dir_path", type=str, help="Chemin vers le répertoire contenant les fichiers de l'INSEE")
args = parser.parse_args()
completer_bdd_insee(args.bdd_path, args.input_dir_path)

View File

@ -0,0 +1,54 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Classe d'accès aux données du site de l'Église
"""
import os
import pandas as pd
import numpy as np
from membre_base import MembreBase, MembreProvider
class ExcelIn(MembreProvider):
def __init__(self, excel_path):
self.excel_path = excel_path
self.dataframe = None
def __len__(self):
return len(self.dataframe)
class Member(MembreBase):
def __init__(self, provider, row):
super().__init__(provider)
self.r_id = str(row["id"])
self.r_first_name = str(row["first_name"]).strip().split(' ')[0].upper()
self.r_last_name = str(row["last_name"]).strip().split(' ')[0].upper()
self.r_maiden_name = str(row["maiden_name"]).strip().split(' ')[0].upper() if type(row["maiden_name"]) == np.str else None
self.r_annee = str(row["annee"])
self.r_mois = str(row["mois"])
self.r_jour = str(row["jour"])
self.r_ville = "<VIDE>"
self.r_sexe = "F" if str(row["status"]) == "Female" else "M"
def get_name(self):
return os.path.basename(self.excel_path)
def load(self):
self.dataframe = pd.read_excel(self.excel_path, skiprows=[0, 1], usecols="B:E,G:I,K", header=None,
names=["last_name", "first_name", "maiden_name", "id", "jour", "mois", "annee",
"status"])
return len(self.dataframe)
def get_member_list(self):
for index, row in self.dataframe.iterrows():
try:
yield self.Member(self, row)
except ValueError:
print(f"Error with member [{row}]")
continue

View File

@ -0,0 +1,30 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Classe d'accès aux données INSEE dans la base SQLite
"""
import pandas as pd
class ExcelOut:
def __init__(self, out_path):
self.out_path = out_path
self.data = list()
def add_member(self, member):
self.data.append({
'nom_registres': member.get_nom_registres(),
'nom_insee': member.get_nom_insee(),
'mrn': member.r_id,
'death_year': member.i_annee_deces,
'death_month': member.i_mois_deces,
'death_day': member.i_jour_deces
})
def generate_output(self):
df = pd.DataFrame(self.data)
df.to_excel(self.out_path)

View File

@ -0,0 +1,160 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Ce programme permet de comparer le fichier des personnes décédées établi par l'INSEE avec les registre de membres de
l'Église, afin de déterminer si figurent dans nos registre des personnes décédées.
L'INSEE reçoit des communes les décès enregistrés. Le fichier des personnes décédées établi par l'INSEE est en accès
libre sur le site https://www.data.gouv.fr.
"""
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.filedialog as tkfiledialog
from multiprocessing import Process, Pipe
from trouver_decedes import trouver_decedes
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
# Worker
self.run = False
self.pipe = None
# GUI
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.frame = tk.Frame(parent)
self.frame.pack()
# Sélection base INSEE
self.label_bdd_insee = tk.Label(self.frame, text="Fichier de l'INSEE")
self.label_bdd_insee.grid(row=0, column=0, sticky='e')
self.value_bdd_insee = tk.StringVar()
self.entry_bdd_insee = tk.Entry(self.frame, state='disabled', textvariable=self.value_bdd_insee)
self.entry_bdd_insee.grid(row=0, column=1, sticky='ew')
self.button_bdd_insee = tk.Button(self.frame, text="...", command=self.command_button_bdd_insee)
self.button_bdd_insee.grid(row=0, column=2, sticky='w')
# Sélection fichier Excel
self.label_fichier_excel = tk.Label(self.frame, text="Fichier Excel")
self.label_fichier_excel.grid(row=1, column=0, sticky='e')
self.value_fichier_excel = tk.StringVar()
self.entry_fichier_excel = tk.Entry(self.frame, state='disabled', textvariable=self.value_fichier_excel)
self.entry_fichier_excel.grid(row=1, column=1, sticky='ew')
self.button_fichier_excel = tk.Button(self.frame, text="...", command=self.command_button_fichier_excel)
self.button_fichier_excel.grid(row=1, column=2, sticky='w')
# Sélection répertoire sortie
self.label_dir_out = tk.Label(self.frame, text="Répertoire de sortie")
self.label_dir_out.grid(row=2, column=0, sticky='e')
self.value_dir_out = tk.StringVar()
self.entry_dir_out = tk.Entry(self.frame, state='disabled', textvariable=self.value_dir_out)
self.entry_dir_out.grid(row=2, column=1, sticky='ew')
self.button_dir_out = tk.Button(self.frame, text="...", command=self.command_button_dir_out)
self.button_dir_out.grid(row=2, column=2, sticky='w')
# Sélection des unités à traiter
self.label_units = tk.Label(self.frame, text="Unités à purger")
self.label_units.grid(row=3, column=0, sticky='e')
self.value_units = tk.StringVar()
self.entry_units = tk.Entry(self.frame, textvariable=self.value_units)
self.entry_units.grid(row=3, column=1, sticky='ew', columnspan="2")
# Bouton validation
self.button_valid = tk.Button(self.frame, text="Ok", command=self.command_button_valid)
self.button_valid.grid(row=4, column=0, columnspan=3)
# Barre de progression
self.progressbar = ttk.Progressbar(self.frame, orient=tk.HORIZONTAL, mode='determinate')
self.progressbar.grid(row=5, column=0, columnspan=3, sticky='ew')
# Affichage des logs
self.text_log = tk.Text(self.frame, state='disabled')
self.text_log.grid(row=0, column=3, rowspan=6, sticky='nesw')
def command_button_bdd_insee(self):
self.value_bdd_insee.set(tkfiledialog.askopenfilename(title="Fichier de l'INSEE"))
def command_button_fichier_excel(self):
self.value_fichier_excel.set(tkfiledialog.askopenfilename(title="Fichier Excel"))
def command_button_dir_out(self):
self.value_dir_out.set(tkfiledialog.askdirectory(title="Répertoire de sortie"))
def add_log(self, text):
self.text_log.configure(state='normal')
self.text_log.insert(tk.END, text)
self.text_log.configure(state='disabled')
def clear_log(self):
self.text_log.configure(state='normal')
self.text_log.delete('1.0', tk.END)
self.text_log.configure(state='disabled')
def watch(self):
if self.run:
if self.pipe.poll():
message = self.pipe.recv()
if message.get('step', False):
self.progressbar.step(message['step'])
elif message.get('text', False):
self.add_log(message['text'])
elif 'running' in message:
self.run = message['running']
elif message.get('set_max', False):
self.progressbar["value"] = 0
self.progressbar['maximum'] = message['set_max']
self.parent.after(100, self.watch)
else:
self.add_log("\nRecherche terminée\n")
self.button_valid.configure(state='normal')
def command_button_valid(self):
self.button_valid.configure(state='disabled')
self.clear_log()
self.run = True
(conn1, conn2) = Pipe()
self.pipe = conn1
unite = self.value_units.get().split(',')
if unite == ['']:
unite = None
Worker(conn2, self.value_bdd_insee.get(), self.value_fichier_excel.get(), self.value_dir_out.get(), unite).start()
self.watch()
class Worker(Process):
def __init__(self, pipe, bdd_insee, fichier_excel, dir_out, units):
Process.__init__(self)
self.pipe = pipe
self.bdd_insee = bdd_insee
self.fichier_excel = fichier_excel
self.dir_out = dir_out
self.units = units
def tracker(self, step=None, text=None, set_max=None, running=None):
if step is not None:
self.pipe.send({'step': step})
elif text is not None:
self.pipe.send({'text': text})
elif set_max is not None:
self.pipe.send({'set_max': set_max})
elif running is not None:
self.pipe.send({'running': running})
def run(self):
trouver_decedes(chemin_base_donnees=self.bdd_insee,
excel_path=self.fichier_excel,
chemin_repertoire_sortie=self.dir_out,
numeros_unites=self.units,
tracker=self.tracker)
self.pipe.send({'running': False})
if __name__ == "__main__":
root = tk.Tk()
root.title('Recherche des personnes décédées dans les registres')
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()

View File

@ -0,0 +1,116 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
"""
from abc import ABC, abstractmethod
from site_opendatasoft import get_ville_from_cog
class MembreBase(ABC):
def __init__(self, provider):
self.provider = provider
# Données issues des registres
self.r_id = None
self.r_first_name = None
self.r_last_name = None
self.r_maiden_name = None
self.r_annee = None
self.r_mois = None
self.r_jour = None
self.r_ville = None
self.r_sexe = None
# Données issues de l'INSEE
self.i_first_name = None
self.i_last_name = None
self.i_annee_naissance = None
self.i_mois_naissance = None
self.i_jour_naissance = None
self.i_ville_naissance = None
self.i_annee_deces = None
self.i_mois_deces = None
self.i_jour_deces = None
self.i_ville_deces = None
def get_nom_registres(self):
nom_registres = f"{self.r_last_name}, {self.r_first_name}"
if self.r_maiden_name:
nom_registres = f"{nom_registres} née {self.r_maiden_name.upper()}"
return nom_registres
def get_nom_insee(self):
return f"{self.i_last_name}, {self.i_first_name}"
def set_insee(self, insee):
self.i_first_name = insee['first_name']
self.i_last_name = insee['last_name']
self.i_annee_naissance = insee['date_naissance'].year
self.i_mois_naissance = insee['date_naissance'].month
self.i_jour_naissance = insee['date_naissance'].day
self.i_ville_naissance = insee['code_lieu_naissance']
self.i_annee_deces = insee['date_deces'].year
self.i_mois_deces = insee['date_deces'].month
self.i_jour_deces = insee['date_deces'].day
self.i_ville_deces = insee['code_lieu_deces']
def convertir_villes_insee(self):
old_value = self.i_ville_naissance
new_value = None
try:
int(self.i_ville_naissance)
except ValueError:
pass
else:
new_value = get_ville_from_cog(self.i_ville_naissance)
if new_value is not None:
self.i_ville_naissance = new_value
if old_value == self.i_ville_deces:
self.i_ville_deces = new_value
else:
try:
int(self.i_ville_deces)
except ValueError:
pass
else:
new_value = get_ville_from_cog(self.i_ville_deces)
if new_value is not None:
self.i_ville_deces = new_value
def get_texte_decede(self):
if self.r_sexe == "F":
feminin = "e"
elif self.r_sexe == "M":
feminin = ""
else:
feminin = "(e)"
self.convertir_villes_insee()
return f"""
Le membre {self.get_nom_registres()} ({self.r_id}),
{feminin} le {self.r_jour:0>2}/{self.r_mois:0>2}/{self.r_annee:0>4} à {self.r_ville.upper()}
semble être décédé{feminin}.
Dans le fichier de l'INSEE on peut trouver {self.get_nom_insee()}
{feminin} le {self.i_jour_naissance:0>2}/{self.i_mois_naissance:0>2}/{self.i_annee_naissance:0>4} à {self.i_ville_naissance}
décédé{feminin} le {self.i_jour_deces:0>2}/{self.i_mois_deces:0>2}/{self.i_annee_deces:0>4} à {self.i_ville_deces}
"""
class MembreProvider(ABC):
@abstractmethod
def load(self):
pass
@abstractmethod
def get_name(self):
pass
@abstractmethod
def __len__(self):
pass
@abstractmethod
def get_member_list(self):
pass

View File

@ -0,0 +1,99 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Classe d'accès aux données du site de l'Église
"""
import requests
import browser_cookie3
from membre_base import MembreBase, MembreProvider
class SiteEglise(MembreProvider):
def __init__(self, unite, cookie_path=None):
"""
:param unite: numéro de l'unité (paroisse, branche)
:param cookie_path: chemin vers le répertoire inscrire les fichiers de sortie
"""
self.unite = unite
self.cookie_jar = browser_cookie3.firefox(cookie_file=cookie_path)
self.as_json = None
def __len__(self):
return len(self.as_json)
class Membre(MembreBase):
def __init__(self, provider, data):
super().__init__(provider)
self.completed = False
# Données fournies
self.r_id = data['legacyCmisId']
self.r_last_name, self.r_first_name = data['nameListPreferredLocal'].upper().split(',')
self.r_last_name = self.r_last_name.strip().split(' ')[0]
self.r_first_name = self.r_first_name.strip().split(' ')[0]
self.r_annee, self.r_mois, self.r_jour = data['birth']['date']['date'].split('-')
self.r_sexe = data['sex']
# Rechercher le nom de jeune fille si besoin
if data['isSpouse']:
self.complete()
def complete(self):
if not self.completed:
member_profile = self.provider.get_member_profile(self.r_id)
self.r_maiden_name = member_profile['individual']['maidenNameGroup']['name1']['family']
if self.r_maiden_name is not None:
self.r_maiden_name = self.r_maiden_name.strip().split(' ')[0].upper()
self.r_ville = member_profile['individual']['birthPlace']
if not self.r_ville:
self.r_ville = "<VIDE>"
self.completed = True
def set_insee(self, insee):
self.complete()
super().set_insee(insee)
def get_name(self):
return f"Unité_{self.unite}"
def load(self):
r = requests.get('https://lcr.churchofjesuschrist.org/services/umlu/report/member-list',
params={'lang': "fra", 'unitNumber': self.unite},
headers={'Accept': "application/json"},
cookies=self.cookie_jar)
r.raise_for_status()
self.as_json = r.json()
return len(self.as_json)
def get_member_list(self):
"""Recevoir la liste des membres
:returns: la liste des membres sous forme d'objet JSON
"""
for member in self.as_json:
try:
yield self.Membre(self, member)
except ValueError:
print(f"Error with member [{member['nameListPreferredLocal']}, {member['birth']['date']['date']}]")
continue
def get_member_profile(self, member_id):
"""Recevoir les informations sur un membre
:param member_id: ID du membre
:returns: les informations sur le membre sous forme d'objet JSON
"""
r = requests.get(f'https://lcr.churchofjesuschrist.org/records/member-profile/service/{member_id}',
params={'lang': "fra"},
headers={'Accept': "application/json",
'Referer': f"https://lcr.churchofjesuschrist.org/records/member-profile/{member_id}?lang=fra"},
cookies=self.cookie_jar)
r.raise_for_status()
return r.json()

View File

@ -0,0 +1,38 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Classe d'accès aux données du site https://public.opendatasoft.com
"""
import requests
def get_ville_from_cog(cog):
"""
Donne le nom de la commune (et son code postal) correspondant au code officiel géographique (cog) donné en entrée.
:param cog: le code officiel géographique à convertie
:return: le nom de la commune (et son code postal). None si la recherche a échoué.
"""
# Contacter l'API
r = requests.get("https://public.opendatasoft.com/api/records/1.0/search/",
params={'dataset': "correspondance-code-insee-code-postal",
'q': f"insee_com={cog}",
'lang': "fr"},
headers={'Accept': "application/json"})
# Tester la validité de la réponse
try:
r.raise_for_status()
except requests.HTTPError:
print(f"Warning get_ville_from_cog: requests return status {r.headers}")
return None
# Récupérer le résultat
result = r.json()
# Si résultat non vide
if result['nhits'] > 0:
# COMMUNE (CODE POSTAL)
return f"{result['records'][0]['fields']['nom_comm']} ({result['records'][0]['fields']['postal_code']})"
print(f"Warning get_ville_from_cog: no result returned for cog {cog}")
return None

View File

@ -0,0 +1,97 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
Ce programme permet de comparer le fichier des personnes décédées établi par l'INSEE avec les registre de membres de
l'Église, afin de déterminer si figurent dans nos registre des personnes décédées.
L'INSEE reçoit des communes les décès enregistrés. Le fichier des personnes décédées établi par l'INSEE est en accès
libre sur le site https://www.data.gouv.fr.
"""
import argparse
import os
from bdd_insee import BddInsee
from site_eglise import SiteEglise
from excel_in import ExcelIn
from excel_out import ExcelOut
def default_tracker(step=None, text=None, set_max=None, running=None):
if text:
print(text)
def trouver_decedes(chemin_base_donnees, numeros_unites, chemin_repertoire_sortie, cookie_path=None, excel_path=None,
tracker=None):
"""Recherche les personnes décédées dans les registres
:param chemin_base_donnees: chemin vers le fichier SQLite
:param numeros_unites: liste des numéros d'unités à analyser
:param chemin_repertoire_sortie: chemin vers le répertoire inscrire les fichiers de sortie
:param cookie_path: chemin vers la base de donnée des cookies
:param excel_path: chemin vers le fichier Excel contenant la liste des membres à rechercher
:param tracker: Objet permettant de suivre l'avancée du traitement
"""
# Tracker par défaut
if tracker is None:
tracker = default_tracker
# Initialiser les accès aux données INSEE
base_insee = BddInsee(chemin_base_donnees)
# Initialiser les fournisseurs de liste de membres
fournisseurs_membres = list()
if excel_path:
fournisseurs_membres.append(ExcelIn(excel_path))
if numeros_unites:
for unite in numeros_unites:
fournisseurs_membres.append(SiteEglise(unite, cookie_path))
# Boucler sur la liste fournisseurs
for member_provider in fournisseurs_membres:
tracker(set_max=member_provider.load())
tracker(text=f"Recherche dans {member_provider.get_name()}\n")
# Récupérer la liste des membres
members = member_provider.get_member_list()
# Préparer les fichiers de sortie
output_base_name = os.path.join(chemin_repertoire_sortie, f"liste_membres_decedes_{member_provider.get_name()}")
output_txt = open(output_base_name + ".txt", 'w')
output_xls = ExcelOut(output_base_name + ".xlsx")
output_txt.write("Les lieux dans le fichier de l'INSEE sont donnés en Code Officiel Géographique en vigueur au moment de la prise en compte du décès\n")
# Boucler sur la liste des membres
for member in members:
query = base_insee.find_person(member.r_first_name, member.r_last_name, member.r_maiden_name,
member.r_annee, member.r_mois, member.r_jour)
for person in query:
member.set_insee(person)
text = member.get_texte_decede()
tracker(text=text)
output_txt.write(text)
output_xls.add_member(member)
tracker(step=1)
# Clore les fichiers de sortie
output_txt.close()
output_xls.generate_output()
tracker(running=False)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Recherche des personnes décédées dans les registres.')
parser.add_argument('chemin_base_donnees', type=str, help="chemin vers la base de données")
parser.add_argument('chemin_repertoire_sortie', type=str, help="chemin vers le répertoire de sortie")
parser.add_argument('--numeros_unites', '-u', type=str, help="numéros des l'unités à traiter, séparés par des virgules (ex: 123,753,469)")
parser.add_argument('--cookie', '-c', type=str, help="chemin vers la base de donnée des cookies de Firefox")
parser.add_argument('--excel', '-e', type=str, help="chemin vers le fichier Excel contenant la liste des membres à rechercher")
args = parser.parse_args()
if args.numeros_unites:
numeros_unites = args.numeros_unites.split(',')
else:
numeros_unites = list()
trouver_decedes(chemin_base_donnees=args.chemin_base_donnees,
numeros_unites=numeros_unites,
chemin_repertoire_sortie=args.chemin_repertoire_sortie,
cookie_path=args.cookie,
excel_path=args.excel)

View File

@ -1,2 +1,6 @@
peewee
requests
browser_cookie3
pandas
xlrd
numpy

View File

@ -1,31 +0,0 @@
"""
Paramètres de l'application
- chemin_base_donnees : Le chemin vers le fichier téléchargé sur le site data.gouv.fr
- numeros_unites : La liste des unités que le programme doit tester. La liste commence par le caractère '['. Les
numéros d'unités sont séparés par des virgules. La liste se termine par le caractère ']'. Par exemple
[47823, 67745, 31456]
- cookie : Les cookies de la session au compte SDJ. Pour obtenir ces cookies il faut
1- Ouvrir Firefox
2- Ouvrir les outils de développement : CTRL + MAJ + E
3- Aller sur le site des outils pour greffiers : https://lds.org/lcr
4- Entrer identifiant et mot de passe SDJ
5- Aller dans la liste des membres
6- Dans l'outil de développement, cliquer sur la dernière ligne
7- Dans le paneau de droite, dans la section "En-tête de la requête", rechercher le champ "cookie"
8- Copier la valeur contenue dans ce champ
- chemin_repertoire_sortie : Le chemin vers le répertoire qui contiendra la fichiers de sortie
"""
# Chemin complet vers le fichier contenant les registres de l'INSEE.
# Pour le télécharger, rendez-vous ici :
# https://git.roflcopter.fr/sdjgeek/purge-registres-deces-insee/-/wikis/home#t%C3%A9l%C3%A9charger-la-base-de-donn%C3%A9e
chemin_base_donnees = "/chemin/vers/fichier-des-personnes-decedees.sqlite"
# Numéro des l'unités à contrôler
numeros_unites = [47823, 67745, 31456]
# Cookie de session compte SDJ
cookie = ""
# Chemin vers le répertoire de sortie
chemin_repertoire_sortie = "/chemin/vers/repertoire-de-sortie"

2
start_windows.bat Normal file
View File

@ -0,0 +1,2 @@
python install.py
python purge-registres-deces-insee\gui_trouver_decedes.py

View File

@ -1,165 +0,0 @@
"""
Copyright (c) 2020 Sdj Geek
Voir le fichier LICENSE
------------------------------------------------------------------------------------------------------------------------
Ce programme permet de comparer le fichier des personnes décédées établi par l'INSEE avec les registre de membres de
l'Église, afin de déterminer si figurent dans nos registre des personnes décédées.
L'INSEE reçoit des communes les décès enregistrés. Le fichier des personnes décédées établi par l'INSEE est en accès
libre sur le site https://www.data.gouv.fr.
Comment utilise ce programme :
1) Demander le ficher de l'INSEE à cette adresse : sdjgeek@protonmail.com
2) Installer les dépendance du programme : "pip install requests peewee"
3) Configurer les paramètres du programme (fichier settings.py)
- chemin_base_donnees : Le chemin vers [le fichier téléchargé ici](https://git.roflcopter.fr/sdjgeek/purge-registres-deces-insee/-/wikis/home#t%C3%A9l%C3%A9charger-la-base-de-donn%C3%A9e)
- numeros_unites : La liste des unités que le programme doit tester. La liste commence par le caractère '['. Les
numéros d'unités sont séparés par des virgules. La liste se termine par le caractère ']'. Par exemple
[47823, 67745, 31456]
- cookie : Les cookies de la session au compte SDJ. Pour obtenir ces cookies il faut
1- Ouvrir Firefox
2- Ouvrir les outils de développement : CTRL + MAJ + E
3- Aller sur le site des outils pour greffiers : https://lds.org/lcr
4- Entrer identifiant et mot de passe SDJ
5- Aller dans la liste des membres
6- Dans l'outil de développement, cliquer sur la dernière ligne
7- Dans le paneau de droite, dans la section "En-tête de la requête", rechercher le champ "cookie"
8- Copier la valeur contenue dans ce champ
- chemin_repertoire_sortie : Le chemin vers le répertoire qui contiendra la fichiers de sortie
4) Exécuter le programme : "python3.6 trouver_decedes.py"
------------------------------------------------------------------------------------------------------------------------
Auteur : SDJ GeeK <sdjgeek@protonmail.com>
Date : 1 juin 2020
Version : 2.0
"""
import os
import peewee
import requests
import settings as param
###
# Base de donnée INSEE
database = peewee.SqliteDatabase(param.chemin_base_donnees)
class Person(peewee.Model):
last_name = peewee.CharField()
first_name = peewee.CharField()
is_woman = peewee.BooleanField()
annee_naissance = peewee.IntegerField()
mois_naissance = peewee.IntegerField()
jour_naissance = peewee.IntegerField()
code_lieu_naissance = peewee.CharField()
commune_naissance = peewee.CharField()
pays_naissance = peewee.CharField()
annee_deces = peewee.IntegerField()
mois_deces = peewee.IntegerField()
jour_deces = peewee.IntegerField()
code_lieu_deces = peewee.CharField()
numero_act_deces = peewee.CharField()
class Meta:
database = database
def find_person(first_name, last_name, maiden_name, annee_naissance, mois_naissance, jour_naissance):
if maiden_name:
return Person.select().where((Person.annee_naissance == int(annee_naissance))
& (Person.mois_naissance == int(mois_naissance))
& (Person.jour_naissance == int(jour_naissance))
& (Person.first_name.contains(first_name.upper()))
& ((Person.last_name.contains(last_name.upper()))
| Person.last_name.contains(maiden_name.upper())))
else:
return Person.select().where((Person.annee_naissance == int(annee_naissance))
& (Person.mois_naissance == int(mois_naissance))
& (Person.jour_naissance == int(jour_naissance))
& (Person.first_name.contains(first_name.upper()))
& (Person.last_name.contains(last_name.upper())))
###
# Registres de l'Église
def get_member_list(unit_number, cookie):
r = requests.get('https://lcr.churchofjesuschrist.org/services/umlu/report/member-list',
params={'lang': "fra", 'unitNumber': unit_number},
headers={'Accept': "application/json",
'Cookie': cookie})
r.raise_for_status()
return r.json()
def get_member_profile(member_id, cookie):
r = requests.get(f'https://lcr.churchofjesuschrist.org/records/member-profile/service/{member_id}',
params={'lang': "fra"},
headers={'Accept': "application/json",
'Cookie': cookie,
'Referer': f"https://lcr.churchofjesuschrist.org/records/member-profile/{member_id}?lang=fra"})
r.raise_for_status()
return r.json()
###
# Programme principal
# Boucler sur la liste des unités
for unite in param.numeros_unites:
print(f"Unité {unite}")
# Récupérer la liste des membres
members = get_member_list(unite, cookie=param.cookie)
# Préparer le fichier de sortie
output_file = os.path.join(param.chemin_repertoire_sortie, f"liste_membres_decedes_unite_{unite}.txt")
with open(output_file, 'w') as out_file:
out_file.write("Les lieux dans le fichier de l'INSEE sont donnés en Code Officiel Géographique en vigueur au moment de la prise en compte du décès\n")
# Boucler sur la liste des membres
for member in members:
# Lire les noms et date de naissance
name_registre = member['nameListPreferredLocal']
full_birthdate = member['birth']['date']['date']
maiden_name = None
ville_registre = None
# S'il s'agit d'une femme mariée, trouver son nom de jeune fille
if member['isSpouse']:
member_profile = get_member_profile(member['legacyCmisId'], cookie=param.cookie)
maiden_name = member_profile['individual']['maidenNameGroup']['name1']['family']
ville_registre = member_profile['individual']['birthPlace']
try:
last_name, first_name = name_registre.split(',')
annee_registre, mois_registre, jour_registre = full_birthdate.split('-')
except ValueError:
print(f"Error with member [{name_registre}, {full_birthdate}]")
continue
first_name = first_name.strip().split(' ')[0]
last_name = last_name.strip().split(' ')[0]
query = find_person(first_name, last_name, maiden_name, annee_registre, mois_registre, jour_registre)
name_registre = name_registre.upper()
if maiden_name:
name_registre = f"{name_registre} née {maiden_name.upper()}"
if member['sex'] == "F":
feminin = "e"
else:
feminin = ""
for person in query:
if not ville_registre:
member_profile = get_member_profile(member['legacyCmisId'], cookie=param.cookie)
ville_registre = member_profile['individual']['birthPlace']
if not ville_registre:
ville_registre = "<VIDE>"
text = f"""
Le membre {name_registre},
{feminin} le {jour_registre:0>2}/{mois_registre:0>2}/{annee_registre:0>4} à {ville_registre.upper()}
semble être décédé{feminin}.
Dans le fichier de l'INSEE on peut trouver {person.last_name}, {person.first_name}
{feminin} le {person.jour_naissance:0>2}/{person.mois_naissance:0>2}/{person.annee_naissance:0>4} à {person.code_lieu_naissance}
décédé{feminin} le {person.jour_deces:0>2}/{person.mois_deces:0>2}/{person.annee_deces:0>4} à {person.code_lieu_deces}
"""
print(text)
out_file.write(text)