Répondre

Je kife le SuperTool !

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Bon, j'ai continué à jouer avec le SuperTool

Il va, je pense, remplacer à l'avenir quelques uns (beaucoup ??) de mes filtres (en attendant de faire plus avec).

Ce que j'ai appris en bricolant ce soir:
1) on peut s'appuyer sur des filtres existants
2) il permet d'en redéfinir d'autres
3) les include permettent de se définir des morceaux de code standard qu'on peut réutiliser dans les différentes vues Gramps plutôt que de définir autant de fois que de vue les mêmes actions pour le filtres, en particulier pour vérifier la présence de tags
4) on a accès dans une vue à des données de filtrage pour lesquels il n'existe pas de filtre dans celle-ci habituellement

Illustration. Ce script ST, qui ne me sert à rien d'autre qu'à m'exercer mais fait ceci:
- il filtre mes sources 1) sur celles qui ont une partie particulière du nom de dépôt, défini dans un filtre classique pré-existant, 2) sur celles qui ont un tag particulier et 3) sur celle dont une citation contient le prénom 'André' (juste impossible à faire habituellement)
- il m'affiche 1) les citations correspondant à ces sources, 2) les dépôts, 3 et 4) moins intéressant mais ça me permet de voir ce que je fais, le titre des sources et leurs tags
- comme j'ai normalisé le format de mes tags, j'en ai profité pour faire un include dans lequel je définie 3 fonctions, disons 2: 1) Fait un dictionnaire des tags, 2) recherche, pour s'en servir dans le filtre sur les tags, si chaque source dispose du tag qui me sert de filtre

Le code du script ST:

Code : Tout sélectionner

[title]
SuperTool-Sources-test

[category]
Sources

[initial_statements]
@include tag_decom_def.py
filtre_fonds = filter("90. Dépôt.Nom['Fonds Legoux, André & Audrain, Marcelle Lucie Mathilde'] - 099p")

def citation_filter(cit, filtre):
	flag = False
	for i in [citation.page for citation in cit]:
		if filtre in i:
			flag = True
	return flag

[statements]
repo_names = [(repo.name) for repo in repositories]
tags_exploded = tag_explosion(tags)


[filter]
filtre_fonds(obj) and (tag_search(tags_exploded, 'Gouvernance', 'Objets Gramps normés, V2021', 'Sources | Etat civil') or 
tag_search(tags_exploded, 'Gouvernance', 'Objets Gramps normés, V2021', 'Sources | Photographies')) and citation_filter(citations, 'André')

[expressions]
[citation.page for citation in citations], [(repo.name) for repo in repositories], title, tags

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

Le petit include:

Code : Tout sélectionner

#Include start

import re
def tag_explode(tag):
	# décompose les tags au format 'typologie[sujet] valeur" ex: "Gouvernance[Objet de la source] Sans objet"

	#print('1', tag)
	if tag is None:
		return []
	tag_decom = re.search('(^.{1,})(\[)(.{1,})(\]\s)(.{1,}$)', tag)
	#print('2', tag_decom)
	if tag_decom is None:
		return []
	#print('3', tag_decom.group(1), tag_decom.group(2), tag_decom.group(3), tag_decom.group(4), tag_decom.group(5))
	typo = tag_decom.group(1)
	sujet = tag_decom.group(3)
	valeur = tag_decom.group(5)
	#print('4', typo, sujet, valeur)
	return [dict([(typo, sujet)]), valeur]

def tag_explosion(tags):
	# Lance la décomposition de tags en dictionnaires

	tags_exploded = []
	for tag in tags:
		tags_exploded.append(tag_explode(tag))
	return tags_exploded

def tag_search(tags_exploded, typo, sujet, valeur):
	# Recherche un tags au format 'typologie[sujet] valeur" dans le dictionnaire des objets courants
	return ([dict([(typo, sujet)]), valeur] in tags_exploded)

#Include end
On peut donc sauvegarder le script ST sous deux formes :
- un fichier script qu'on peut recharger dans la fenêtre autant de fois qu'on veut pour le rejouer
- un filtre qui a cette tête la dans custom_filters.xml :

Code : Tout sélectionner

    <filter name="SuperTool-Sources-test" function="and">
      <rule class="GenericFilterRule_Source" use_regex="False">
        <arg value="filtre_fonds(obj) and (tag_search(tags_exploded, 'Gouvernance', 'Objets Gramps normés, V2021', 'Sources | Etat civil') or 
tag_search(tags_exploded, 'Gouvernance', 'Objets Gramps normés, V2021', 'Sources | Photographies')) and citation_filter(citations, 'André')"/>
        <arg value="@include tag_decom_def.py&lt;br&gt;filtre_fonds = filter(&quot;90. Dépôt.Nom['Fonds Legoux, André &amp; Audrain, Marcelle Lucie Mathilde'] - 099p&quot;)&lt;br&gt;&lt;br&gt;def citation_filter(cit, filtre):&lt;br&gt;	flag = False&lt;br&gt;	for i in [citation.page for citation in cit]:&lt;br&gt;		if filtre in i:&lt;br&gt;			flag = True&lt;br&gt;	return flag"/>
        <arg value="repo_names = [(repo.name) for repo in repositories]&lt;br&gt;tags_exploded = tag_explosion(tags)"/>
      </rule>
    </filter>
Les morceaux de code python s'y retrouvent. Du coup, je ne sais pas (encore) ce que ça donne si on fait un code ST qui modifie les données de la base vu que le "commit changes" n'est pas setté. A suivre...

Sinon pour conclure sur l'objet de mon essai, le filtrage, il est désormais gonflé aux stéroïdes. J'ai vu par exemple que des objets pour lesquels il n'existe pas de filtre étaient désormais accessibles. Ca va permette d'apporter une nouveau coup de booster.

Impressionné par la puissance du truc. Les soi-disant requêtes intelligentes d'Heredis n'ont qu'à bien se tenir :D

Pour terminer qq captures d'écran:
Image

Image
Pièces jointes
Capture d’écran 2021-07-08 022708.jpg
Capture d’écran 2021-07-08 022730.jpg
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Bonjour,

Je vais partager ici les quelques créations de filtres ST assez génériques qui pourraient être utiles à d'autres, et qui n'ont pas d'équivalence en filtre Gramps classique.

Je vous fournie le script ST car c'est bien plus pratique que de transmettre le XML du filtre Gramps mais une fois chargé dans ST il est à enregistrer en tant que filtre ('Save as filter') dans les filtres de citations.

Celui-ci filtre donc les citations par rapport à leurs attributs. En l'occurrence ici, il cherche un attribut "Consulté le" avec la valeur 'A définir', type et valeur qui peuvent être changés par ce qu'on veut ('*' pour rechercher tout)

Il fonctionne aussi s'il est enregistré sans modification dans les filtres sources qui ne disposent pas non plus de moyen de filtrer les sources en fonction de leurs attributs.

Code : Tout sélectionner

[title]
90. Attribut[{'Consulté le': 'A définir'}] - 105p

[category]
Sources

[initial_statements]
# Valeurs modifiables - début
# Valeurs possibles:  'texte' ou '*'

attribut_type    = 'Consulté le'
attribut_valeur = 'A définir'

# Valeurs modifiables - fin

gid = []

[statements]
for attribute in attributes:
 if attribut_type == '*' and attribut_valeur == '*':
  gid.append(gramps_id)
  break
 
 if attribut_type == '*':
  if attribute[1] == attribut_valeur:
   gid.append(gramps_id)
   break
 elif attribut_valeur == '*':
  if attribute[0] == attribut_type:
   gid.append(gramps_id)
   break
 else:
  if attribute == (attribut_type, attribut_valeur):
   gid.append(gramps_id)
   break

[filter]
gramps_id in gid

[expressions]

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

A noter que chez moi je l'ai enregistré comme ci-dessous, avec deux includes, un pour l'initialisation et un autre pour les statements, ce qui me permettra de modifier le code des filtres Gramps déjà créés sur cette base sans devoir repasser dans chacun d'eux. Il fait exactement la même chose (pour l'instant - les includes évolueront probablement pour permettre de rechercher des expressions rationnelles).

G:\5. Conception\Gramps\Isotammi\C90.105p.script :

Code : Tout sélectionner

[title]
90. Attribut[{'Consulté le': 'A définir'}] - 105p

[category]
Sources

[initial_statements]
# Valeurs modifiables - début
# Valeurs possibles:  'texte' ou '*'

attribut_type    = 'Consulté le'
attribut_valeur = 'A définir'

# Valeurs modifiables - fin

@include SC_attr_init.py

[statements]
@include SC_attr_statement.py

[filter]
gramps_id in gid

[expressions]

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

C:\Users\Patrice\supertool\SC_attr_init.py:

Code : Tout sélectionner

# Include SC_attr_init
gid = []

C:\Users\Patrice\supertool\SC_attr_statement.py :

Code : Tout sélectionner

# Include SC_attr_statement

for attribute in attributes:
 if attribut_type == '*' and attribut_valeur == '*':
  gid.append(gramps_id)
  break
 
 if attribut_type == '*':
  if attribute[1] == attribut_valeur:
   gid.append(gramps_id)
   break
 elif attribut_valeur == '*':
  if attribute[0] == attribut_type:
   gid.append(gramps_id)
   break
 else:
  if attribute == (attribut_type, attribut_valeur):
   gid.append(gramps_id)
   break
Vous pouvez utiliser la version que vous voulez, la première en un seul script ou la seconde avec un script et 2 includes. Elles font la même chose. Je vous conseille toutefois la seconde même si elle est un peu plus compliquée à installer.

Vous pouvez utiliser l'une ou l'autre des versions sans crainte de modifications de vos données, ça ne fait que filtrer comme n'importe quel filtre Gramps classique.
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Un second qui peut se marier avec le précédent. Il recherche les sources avec un support de la source (électronique, photo, livre, etc.) dans le dépôt particulier.

Code : Tout sélectionner

[title]
90. DépotSupport['Électronique'] - 149p

[category]
Sources

[initial_statements]
# Valeurs modifiables - début

type_support = 'Électronique'

# Valeurs modifiables - fin

@include S_SupportType_init.py

[statements]
@include S_SupportType_statement.py

[filter]
repository_media_type == [type_support]

[expressions]
title

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

S_SupportType_init.py:

Code : Tout sélectionner

# Include S_SupportType_init
S_SupportType_statement.py:

Code : Tout sélectionner

# Include S_SupportType_statement

repository_media_type = [(repo.media_type.string) for repo in source.reporef_list]
Ainsi, en filtrant les citations qui ont une source type électronique et pas d'attribut URL, je peux corriger mes citations pour lesquelles il manque le permalien

Image
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Bonjour,

Un nouveau filtre supertool. Il recherche les événements avec une note A faire.

Le script supertool:

Code : Tout sélectionner

[title]
90. Note['A faire'] - 363p

[category]
Events

[initial_statements]
# Valeurs modifiables - début
note_type = 'À faire'
# Valeurs modifiables - fin

@include note_type_search_init.py

[statements]
@include note_type_search_statement.py

[filter]
gramps_id in records_with_note

[expressions]
[note.text for note in notes]

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

l'include note_type_search_init.py:

Code : Tout sélectionner

# note_type_search_init

records_with_note = []
l'include note_type_search_statement.py:

Code : Tout sélectionner

# note_type_search_statement

for note in notes:
	read_note = db.get_note_from_gramps_id(note.gramps_id)
	if read_note.type == note_type:
		records_with_note.append(gramps_id)
		break
Comme précédemment:
  • - créer les 3 fichiers E363p.script, note_type_search_statement.py et note_type_search_init.py dans le répertoire supertool que vous vous avec préalablement créé (ex dans mon cas: C:\Users\Patrice\supertool. NB: il n'y a que 3 répertoires bien définis où peut être créé ce répertoire supertool),
    - dans la vue Evénements lancer supertool et charger (load) le fichier E363p.script,
    - à partir de là vous pouvez:
    • - soit l'utiliser dans supertool lui-même (cliquez sur "Execute")
      - soit cliquer sur "Save as filter" et vous disposez d'un filtre nommé "90. Note['A faire'] - 363p" (que vous pouvez le renommer si vous le souhaitez)
On doit pouvoir le mettre dans n'importe quel autre module -citations, individus...- pour lui faire chercher les mêmes ou d'autres types de notes sans trop de difficultés (en gros sans changer une ligne de code, juste le type de note dans la première ligne que j'ai indiquée "modifiable"): note_type = 'À faire', par exemple note_type = 'Transcription' pour créer un filtre sur ce type de notes dans les citations. Pensez alors à changer le nom du script supertool afin de créer le filtre avec le bon nom (champ Title, par exemple 90. Note['Transcription'] - nnnp)


P.S: j'ai ajouté une rubrique Supertool à ma page sur Gramps incluant ces filtres.
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Filtrer les sources qui ont ou pas une certaine citation.

Ici ma citation contient la chaîne de caractères "_Objet de la source:", et elle doit être présente dans la source (du fait de la variable "existe = True") elle pourrait contenir "Acte de naissance" ou n'importe quoi d'autre. Modifiable dans la variable modifiable "citation_page" comme d'habitude.
Avec la variable "existe = False", le filtre recherche toutes les sources qui n'ont pas de citation contenant la chaîne de caractères indiquée.

S152p.script:

Code : Tout sélectionner

[title]
90. CitationPage['_Objet de la source:'] - 152p

[category]
Sources

[initial_statements]
# Valeurs modifiables - début
citation_page = "_Objet de la source:"
existe = True #valeurs possibles: True / False, en fonction retource toutes les sources avec ou sans la citation contenant la valeur dans citation_page
# Valeurs modifiables - fin

@include Source_CitationSearch_init.py


[statements]
@include Source_CitationSearch_statements.py

[filter]
gramps_id in gid_list

[expressions]
gramps_id

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

Source_CitationSearch_init.py:

Code : Tout sélectionner

# Source_CitationSearch_init.py

gid_list=[]
Source_CitationSearch_statements.py:

Code : Tout sélectionner

# Source_CitationSearch_statements.py

trouve = False
for citation in citations:
	if citation_page in citation.page:
		trouve = True
		break

if (existe and trouve) or (not existe and not trouve):
	gid_list.append(gramps_id)
A retrouver ici.
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Filtrage de l'attribut Heure des événement décès:
Image

Un petit export du résultat dans excel via un csv (évitez le format utf-8) et deux mini formules:
Image

Un tableau croisé dynamique, et hop je connais si les décès (ou l'heure où on les a découverts) ont plutôt lieu la nuit ou le jour, en journée donc (avant 7h ou après 20h):
Image

sous un autre format:
Image
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Trier définitivement les individus groupés par leurs prénoms en deux lignes de code (j'en parle ici et avec quelques copies d'écran du résultat):

Code : Tout sélectionner

[Gramps SuperTool script file]
version=1

[title]
Individus-Sort_as des individus groupés

[category]
People

[initial_statements]

[statements]
for n in nameobjs:
	n.sort_as = -1

[filter]

[expressions]
([n.group_as, n.sort_as, n.display_as] for n in nameobjs)

[scope]
selected

[unwind_lists]
False

[commit_changes]
True

[summary_only]
False

Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
J'avais eu (et j'ai toujours, enfin plus maintenant) besoin d'un filtre qui permettait de chercher les valeurs dans les attributs dans les références des événements associées à un individu que j'utilise beaucoup avec les recensements ou les mariages. Grace à SuperTool c'est fait.

Si vous en avez aussi besoin, ce script est à enregistrer en tant que filtre individu. Les valeurs modifiables à rechercher sont à indiquer au début; la valeur (True ou False) utilisée par 'rech_exacte' porte sur la recherche du contenu de 'attr_value' (soit son contenu est présent (False) dans la description de l'attribut, soit le contenu est exactement celui de la valeur indiquée (True)):

I511p.script:

Code : Tout sélectionner

[Gramps SuperTool script file]
version=1

[title]
90. RefEventAttribut[{'Attribut': 'Présence'}, {'Valeur': 'Absent'}, {'Recherche exacte': False}] - 511p

[category]
People

[initial_statements]
# Valeurs modifiables - début
attribut_type = "Présence"
attribut_value = "Absent"
rech_exacte = False # Valeur possible: True ou False
# Valeurs modifiables - fin

objet_type = 'event'
@include ref_attr_init.py

[statements]
objet = person
@include ref_attr_statement.py

[filter]
gramps_id in gid

[expressions]
gramps_id

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

Et ses deux includes:

ref_attr_init.py:

Code : Tout sélectionner

# Include ref_attr_init.py

def ref_attr(attr_list, type=None, value=None, exact=False):
	#Recherche dans les attributs de le référence si le type et ou la valeur correspondent aux arguments
	if type != None or value != None:
		for attr in attr_list:
			if type:
				if value:
					if exact:
						if type == attr.type and value == attr.value:
							return True
					else:
						if type == attr.type and value in attr.value:
							return True
				else:
					if type == attr.type:
						return True
			else:
				if exact:
					if value == attr.value:
						return True
				else:
					if value in attr.value:
						return True
	
	return False

gid = []
ref_attr_statement.py:

Code : Tout sélectionner

# Include ref_attr_statement.py

if objet_type == 'event':
	#Recherche dans les événements d'une personne ou d'une famille (et à l'avenir d'un lieu)
	for obj_rech in objet.get_event_ref_list():
		if ref_attr(obj_rech.attribute_list, attribut_type, attribut_value, rech_exacte):
			gid.append(gramps_id)
elif objet_type == 'media':
	#Recherche dans les médias d'une personne, d'une famille, d'une source', d'une citation ou d'un lieu
	pass
J'ai conçu les includes afin qu'ils supportent à l'avenir la recherche des attributs dans les événements de la famille (je ne l'ai pas testé mais ça doit fonctionner sans modif, juste, je suppose, en changeant 'objet = person' par 'objet = family', ça reste tout de même à vérifier) ou des médias associés à une personne (ou une source, une citation ou un lieu) mais je ne l'ai pas encore mis ce troisième type de recherche dans le code de ref_attr_statement.py (je le ferai aussi car j'utilise les attributs des références des médias pour sourcer d'où vient l'identification des personnes sur les photos, j'ai donc besoin d'un filtre qui vérifie si l'info est bien présente).

Je présents qu'une autre mise à jour de ce script Supertool sera de filtrer d'abord par type d'événement afin de ne vérifier le contenu de l'attribut que pour un type d'événement donné (attribut présence dans un événement mariage, attribut âge lors d'un événement recensement, etc...). Je ne sais pas encore...
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Hello,

Ça faisait un moment que j'avais pas répondu à ce fil (bon ensuite, y a pas de réaction, je sais pas trop s'il vous intéresse) pour parler d'un nouveau filtre SuperTool. Mais d'abord vous allez avoir droit à une assez longue introduction pour expliquer ce que fait le filtre, qui n'est pas plus compliqué que les autres mais qui a un prérequis qui mérite explication. L'utilité de ce filtre? Trouver tous les individus qui ont une ou plusieurs sources concernant (associées à; provenant d') un lieu donné mais qui n'ont pas d'événement lié à ce lieu.

Ici donc je vous présenter un nouveau filtre qui demande un peu de préparation. Il est basé sur l'idée du GEPS 015 (c'est tante Martha qui va être contente) qui grosso-modo vise à trouver des pistes par ricochet: qu'est ce qu'un dépôt de source peut m'apporter d'intéressant comme informations complémentaires. Dans le contexte de cette question, le filtre permet de trouver des individus (dans ce premier jet) dont la source d'une citation d'un de leurs événements est commune avec les sources des citations liées à un ou plusieurs lieux (dans mon filtre vous verrez indiqué les ID d'un lieu, la commune de Saint-Pierre-des-Loges (SPDL), et de ses sous-lieux).

Exemple: j'ai créé une source "livre d'or 14/18" pour cette commune et j'ai aussi créé des citations pour les MPF de cette source et enfin créé des événements associés à ces personnes et ces citations. Tous ces individus ne sont pas nés dans la commune et sont morts ailleurs, sur le champ de bataille. Un filtre individus basé sur le lieux des événements dans la commune de SPDL ne les affiche pas, et pour cause, ils n'y ont pas d'événement, je ne connais ces personnes qu'au travers de cette source livre d'or de SPDL.

J'ai pour chacune de mes sources une citation spéciale (créée par un script supertool aussi, déjà évoquée un peu plus haut) "Objet de la source: xyz" où xyz reprend le titre de la source, dans le cas de l'exemple précédent ; "_Objet de la source: FR-61446, Saint-Pierre-des-Loges. Livre d'or. Relevé collaboratif". C'est cette citation spéciale que j'ai associée au lieu SPDL.

Le filtre sources Supertool qui suit recherche pour les lieux que j'y ai indiqués ce type de citation pour chacune des sources et retourne les sources correspondantes, dont la source "FR-61446, Saint-Pierre-des-Loges. Livre d'or. Relevé collaboratif".

Un filtre événements recherche tout événement associé aux sources retournées par ce filtre supertool et un filtre individus retourne tous les individus correspondants aux événements filtrés.

Les individus qui ne sont ni nés ni décédés à SPDL et n'y ont aucun événement, mais disposent de citations d'une source originaire de cette commune (probablement parce qu'ils y habitaient) remontent maintenant avec ce filtre individus. CQFD. Une nouvelle piste pour tante Martha et pour moi-même pour trouver des personnes du lieu dans la base.

Le filtre Supertool à enregistrer comme filtre sources:

Code : Tout sélectionner

[Gramps SuperTool script file]
version=1

[title]
90. Citations['_Objet de la source:'].References[{'Lieu': 'FR-61446, Saint-Pierre-des-Loges"}] - 157p

[category]
Sources

[initial_statements]
# derived from original @kku import and function - https://github.com/Taapeli/isotammi-addons/issues/1
from supertool_engine import PlaceProxy

def place_citators(handle, ic):
 for _, rhandle in db.find_backlink_handles(handle, include_classes=[ic]):
  yield PlaceProxy(db, rhandle)

#Valeurs modifiables - Début
ref_ids = ["P12073", "P24110", "P00875", "P00874", "P00420", "P23151", "P00870", "P00422", "P23147", "P00866", "P00868", "P00867", "P00864", "P12516", "P00865", "P00863"] # Saint-Pierre-des-Loges et ses sous-lieux
ref_cit = "_Objet de la source:" # Tout ou partie du nom de la citation à rechercher dans ces lieux
#Valeurs modifiables - Fin

ref_type = "Place"
gid = []

[statements]
add_page = False
for citation in citations:
 for referent in place_citators(citation.handle, ref_type):
  for ref_id in ref_ids:
   if f'{ref_type}[{ref_id}]' in str(referent) and ref_cit in citation.page:
    add_page = True
    break
  if add_page:
   break

if add_page:
 gid.append(gramps_id)

[filter]
gramps_id in gid

[expressions]

[scope]
selected

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False

Les filtres classiques qui remontent les individus évoqués avant:

Le filtre événements qui appelle le filtre supertool défini dans les filtres sources:

Code : Tout sélectionner

    <filter name="90. Sources.Citations['_Objet de la source:'].References[{'Lieu': 'FR-61446, Saint-Pierre-des-Loges&quot;}] - 422p" function="and" comment="422p (E90.422p) - Filtre == [S90.157p]">
      <rule class="MatchesSourceFilter" use_regex="False">
        <arg value="90. Citations['_Objet de la source:'].References[{'Lieu': 'FR-61446, Saint-Pierre-des-Loges&quot;}] - 157p"/>
      </rule>
    </filter>
Le filtre individus qui appelle le filtre événements précédent:

Code : Tout sélectionner

    <filter name="88. Evenements.Sources.Citations['_Objet de la source:'].References[{'Lieu': 'FR-61446, Saint-Pierre-des-Loges&quot;}] &amp; ^Evénements.Lieux['FR-61446, Saint-Pierre-des-Loges'].Self.SousLieux - 514p" function="and" comment="514p (I88.514p) - Filtre(&amp;) == [E90.422p, I90.513i]">
      <rule class="MatchesEventFilter" use_regex="False">
        <arg value="90. Sources.Citations['_Objet de la source:'].References[{'Lieu': 'FR-61446, Saint-Pierre-des-Loges&quot;}] - 422p"/>
      </rule>
    </filter>
Quelques copies d'écran pour terminer ce long post:
Exemples de sources et citations:
Image

Citations appliquées à un lieu (ce sont les sources de toutes ces citations débutant par "_Objet de la source:" que le filtre ST va retourner au filtre événements. Tout événement lié à toute citation de l'une de ces sources remontera au filtre individus pour l'afficher):
Image

Une des personnes trouvée par le filtre individus (on voit qu'il n'a pas d'événement à SPDL):
Image

Et le détail d'un de ses événements qui a permis au filtre ST de le trouver (on retrouve la citation du livre d'or 14/18 "_Objet de la source: FR-61446, Saint-Pierre-des-Loges. Livre d'or. Relevé collaboratif":
Image

Un second exemple pour terminer; parmi les individus il y a bien sur ceux du livre d'or mais aussi tout ceux qui ont des événements nulle part ou ailleurs dépendant d'une source à SPDL:
Image
on encore là:
Image

Voilà. En espérant que ce long post et ce filtre pourront être utile à quelqu'un.
Pièces jointes
Untitled (3).png
Untitled (4).png
Capture4.JPG
Capture5.JPG
Capture6.JPG
Capture7.JPG
Dernière modification par patricelegoux le 26 août 2021, 09:16, modifié 1 fois.
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

llhduflot
male
Messages : 221
Saisie : Standard
Voir son arbre
patricelegoux a écrit :
25 août 2021, 12:50

Ça faisait un moment que j'avais pas répondu à ce fil (bon ensuite, y a pas de réaction, je sais pas trop s'il vous intéresse) ....
Perso, je suis ce fil mais mon utilisation de gramps est beaucoup plus basique donc j'apprend sur ta façon d'utiliser gramps plus que de réutiliser tes outils.

Laurent

glopglop
glopglop
Messages : 2751
Saisie : Geneweb
Voir son arbre
Bonjour Patrice

ne perds pas esport
a chaque que je te lis je me dis qu'il faut que je m y mette plutot que de programmer a chaque fois de A a Z des petits outils en python
mais il faut que je trouve le courage :)
Developpeur du greffon GedcomforGeneanet pour GRAMPS
https://github.com/grocanar/GedcomforGeneanet
Sans aucun lien avec l'entreprise Geneanet que celui d’être un abonné premium comme les autres.

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Bonjour,

J'ai réécrit en un seul include ST_filters_init.py l'ensemble des filtres précédents et probablement un ou deux que je n'avais pas publié (je pense notamment à la recherche de doublons dans les lieux STF_dupplicate_place ou le filtre qui affiche les objets de la catégorie en fonction de la date de modification STF_dtf, il y en a peut-être d'autres que j'oublie):

Code : Tout sélectionner

##################################################################################################################
#
# Include ST_filters_init.py 
# Filtres Supertool complémentaires à ceux de Gramps
#
# Usage: @include ST_filters_init.py (dans Supertool Initial Statements)
#
# Version: # V1.3
#
# Changes:
# - V1.3:
#   - Généralisation de la possibilité d'utiliser un filtre Gramps en ajoutant l'argument facultatif objet=obj à 
#     chaque méthode "recherche"
# - V1.2:
#   - Ajout de la classe ST_Spaces et transfert de la méthode _place_citators de la classe
#     STF_source_citations_in_objects vers celle-ci
# - V1.1: 
#   - Délégation des recherches vers la classe ST_Filters
# - V1.0:
#   - Regroupement de tous les fichiers individuels de filtres Supertool en classes au sein de ce fichier unique
#
# Author: Patrice Legoux <Twitter: @plegoux>
#
##################################################################################################################

'''Include des classes d'utilisation des filtres SuperTool
SuperTool filters classes include

Filtres sources / Source filters:
    - STF_source_citations :
        Filtre les sources ayant la citation <page> si <existe> est vrai, ou toutes celles qui n'ont pas cette citation
        si <existe> est faux
    - STF_source_citations_in_objects :
        Filtre les sources ayant la citation <page> si cette citation est associée à un des lieux <ids>
    - STF_depot_support_type :
        Filtre les sources ayant le type de support <type_support> parmi leurs références de dépôt

Filtres individus / Person filters:
    - STF_role_in_event :
        Filtre les individus porteurs du rôle <role> dans leurs événements personnels de type <type>

Filtres événements / Event filters:
    - STF_participant_role:

Filtres lieux / Place filters:
    - STF_dupplicate_place:

Filtres communs à différents types d'objets / Common filters:
    - STF_refattr :
    - STF_attrs :
    - STF_note_type

Filtres généralistes / General filters:
    - STF_dtf :
    - STF_refcount_is :
'''

from datetime import date
from abc import ABC, abstractmethod
from collections.abc import Callable

from supertool_engine import PlaceProxy

ST_FILTER = "ST Filter"

ST_GET = "GET"
ST_ASK = "ASK"

ST_TODAY = "TODAY"
ST_ON = "ON"
ST_AFTER = "AFTER"

ST_PERSON = "Person"
ST_EVENT = "Event"
ST_FAMILY = "Family"
ST_CITATION = "Citation"
ST_SOURCE = "Source"
ST_REPOSITORY = "Repository"
ST_NOTE = "Note"
ST_PLACE = "Place"
ST_MEDIA = "Media"
ST_FR_NS = {
    "Individu": ST_PERSON,
    "Evénement": ST_EVENT,
    "Famille": ST_FAMILY,
    "Citation": ST_CITATION,
    "Source": ST_SOURCE,
    "Dépôt": ST_REPOSITORY,
    "Note": ST_NOTE,
    "Lieu": ST_PLACE,
    "Média": ST_MEDIA
}

ST_LESS_THAN = "less than"
ST_GREATER_THAN = "greater than"
ST_EQUAL_TO = "equal to"

# Classes d'exceptions
class Error(Exception):
    """Base class for other excpetions"""
    
    def __str__(self):
        return f'{ST_FILTER} exception: {self.message}'

class FilterNotProvidedInNamespace(Error):
    """Raised when a filter is called from a namespace it is not intented for"""
    
    def __init__(self, caller, namespace, auth_ns):
        self.caller = caller
        self.namespace = namespace
        self.auth_ns = auth_ns
        self.message = f'Filter not provided in {namespace} namespace' + '\n' + f'{caller} is intended to be used only in {auth_ns} namespace(s)'
        super().__init__(self.message)

class SearchFilterNotProvided(Error):
    """Raised when a filter want to search into a namespace not provided"""
   
    def __init__(self, caller, type, auth_ns):
        self.caller = caller
        self.type = type
        self.auth_ns = auth_ns
        self.message = f'{type} search filter not provided' + '\n' + f'{caller} is intended for searching only into {auth_ns} namespace(s)'
        super().__init__(self.message)

# Classes génériques
class ST_Filters(ABC):
    '''Classe d'abstraction des différents filtres'''
    
    _ns: list = []
    
    def __init__(self, auth_ns: list, caller: object) -> None:
        self._ns_checked = False
        self._auth_ns = auth_ns
        self._caller = caller
        self.gid = Gid()
    
    def _checkns(self, namespace: list) -> [bool, Exception]:
        if self._ns_checked:
            return True
            
        self._ns_checked = namespace in self._auth_ns
        if not self._ns_checked:
            raise FilterNotProvidedInNamespace(self._caller, namespace, self._auth_ns)
            
    @abstractmethod
    def recherche(self, namespace: str, gramps_id: str) -> bool:
        self._checkns(namespace)
        if self._filtre and not self._filtre(self._objet):
            return False
        return self.gid._addGidIf(gramps_id, self._search_method, *self._search_args)

class ST_Spaces:
    """Moyens d'accès aux objets d'autres espaces
    Access methods to other spaces objects
    """
    
    def _place_citators(self, handle, ic):
        # derived from original @kku import and function - https://github.com/Taapeli/isotammi-addons/issues/1
        for _, rhandle in db.find_backlink_handles(handle, include_classes=[ic]):
            yield PlaceProxy(db, rhandle)

class Gid:
    '''Stockage et restitution des Gramps ID'''
    def __init__(self) -> None:
        self._gid = []
        
    def __iter__(self):
        return iter(self._gid)
        
    def __next__(self):
        return next(self._gid)

    def _addGidIf(self, gid: str, method, *args) -> bool:
        """Méthode de stockage d'un Gramps ID par l'appel d'une méthode retournant un booléin.
        
        Arguments:
        - gid : Gramps ID qui sera stocké si la méthode retourne True
        - method : une méthode ou fonction à laquelle sont passés les arguments args. Si le retour est True, gid sera stocké
        - *arg : liste d'arguments qui est passée à method
        
        Retourne:
        - True ou False en fonction du retour de method
        """
        if method(*args):
            self.append(gid)
            return True
        else:
            return False
    
    def append(self, gid: str) -> None:
        # Méthode définie pour rester compatible avec la V0.0
        
        if gid not in self._gid:
            self._gid.append(gid)

# Classes de filtres sources
class STF_source_citations(ST_Filters):
    '''STF_source_citations - Filtre les sources ayant la citation <page> si <existe> est vrai, toutes celles qui n'ont pas cette sitation s'il est faux
    
    Rule:
        gramps_id in rcitation.gid
    Initial statements:
        @include ST_filters_init.py
        
        rcitation = STF_source_citations(
            # Valeurs modifiables - début
                page   = "",    # Citation Page (ex.: "Acte de décès")
                existe = True   #valeurs possibles: True / False, en fonction retourne toutes les sources avec ou sans la citation contenant la valeur dans page
            # Valeurs modifiables - fin
         )
    Statements:
        rcitation.recherche(namespace=namespace, gramps_id=gramps_id, citations=citations)
    
    Exemples:
        - nom du filtre Gramps: S90.158p: 90. CitationPage["_Objet de la source: Histoire de Saint-Chinian de la Corne et de ses environs (Hérault)"] - 158p
        - contenu du fitre supertool "Generic filter rule" inclu dans le fitre Gramps:
        
            Rule:
                gramps_id in rcitation.gid
                
            Initial statements:
                @include ST_filters_init.py
                
                rcitation = STF_source_citations(
                    # Valeurs modifiables - début
                         
                    page   = "_Objet de la source: Histoire de Saint-Chinian de la Corne et de ses environs (Hérault)",
                    existe = True   #valeurs possibles: True / False, en fonction retourne toutes les sources avec ou sans la citation contenant la valeur dans page
                    
                    # Valeurs modifiables - fin
                )
            Statements:
                rcitation.recherche(citations=citations, gramps_id=gramps_id, namespace=namespace)
    '''
    
    _ns: list = [ST_SOURCE]
    
    def _recherche_1(self, citations: list) -> bool:
        add_page: bool = False
        for citation in citations:
            if self._page in citation.page:
                add_page = True
                break
        return(self._existe and add_page) or (not self._existe and not add_page)
        
    def __init__(self, page: str, existe: bool, filtre: object = None) -> None:
        self._page: str = page
        self._existe: bool = existe
        self._filtre = filtre
        self._search_method = self._recherche_1
        super().__init__(self._ns, self.__class__.__name__)
        
    def recherche(self, namespace: str, gramps_id: str, citations: list, objet = None) -> bool:
        self._objet = objet
        self._search_args = [citations]
        return super().recherche(namespace, gramps_id)

class STF_source_citations_in_objects(ST_Filters, ST_Spaces):
    '''STF_source_citations_in_objects - Filtre les sources ayant la citation <page> si cette citation est associée à un des lieux <ids>
    
    Rule:
        gramps_id in rplaceCitation.gid
    Initial statements:
        @include ST_filters_init.py
        
        rplaceCitation = STF_source_citations_in_objects(
            o_type = ST_PLACE,
            # Valeurs modifiables - début
                o_ids = [],   # Place ID list (ex: ["P00001", "P00002"])
                c_page = ""   # Citation Page (ex.: "Acte de décès")
            # Valeurs modifiables - fin
        )
    Statements:
        rplaceCitation.recherche(namespace=namespace, gramps_id=gramps_id, citations=citations)
    
    Exemples:
        - S90.157p: 90. Citations['_Objet de la source:'].References[{'Lieu': 'FR-61446, Saint-Pierre-des-Loges"}] - 157p
    '''
    
    # note à moi-même: il y probablement moyen d'hériter de STF_source_citations (ou de n'utiliser qu'elle) à condition de réécrire ces deux classes
    
    _ns: list = [ST_SOURCE]
    _object_ns: list = [ST_PLACE]
    
    def _recherche_1(self, citations: list) -> bool:
        for citation in citations:
            for referent in self._place_citators(citation.handle, self._type):
                for id in self._ids:
                    if f'{self._type}[{id}]' in str(referent) and self._page in citation.page:
                        return True
        return False
    
    def __init__(self, o_type: str, o_ids: list, c_page: str, filtre: object = None) -> None:
        if o_type not in self._object_ns:
            raise SearchFilterNotProvided(self.__class__.__name__, o_type, self._object_ns)
        else:
            self._type = o_type
        self._ids = o_ids
        self._page = c_page
        self._filtre = filtre
        self._search_method = self._recherche_1
        super().__init__(self._ns, self.__class__.__name__)
        
    def recherche(self, namespace: str, gramps_id: str, citations: list, objet = None) -> bool:
        self._objet = objet
        self._search_args = [citations]
        return super().recherche(namespace, gramps_id)

class STF_depot_support_type(ST_Filters):
    '''STF_depot_support_type - Filtre les sources ayant le type de support <type_support> parmi leurs références de dépôt
    
        Rule:
        gramps_id in rsupportType.gid
    Initial statements:
        @include ST_filters_init.py
        
        rsupportType = STF_depot_support_type(
            # Valeurs modifiables - début
                type_support = 'Électronique'
            # Valeurs modifiables - fin
        )
    Statements:
        rsupportType.recherche(source=source, gramps_id=gramps_id, namespace=namespace)
    
    Exemples:
        - S90.149p: 90. DépotSupport['Électronique'] - 149p
    '''
    
    _ns: list = [ST_SOURCE]
    
    _recherche_1 = lambda self, source: [(repo.media_type.string) for repo in source.reporef_list] == [self._type_support]
    
    def __init__(self, type_support: str, filtre: object = None) -> None:
        self._type_support = type_support
        self._filtre = filtre
        self._search_method = self._recherche_1
        super().__init__(self._ns, self.__class__.__name__)
    
    def recherche(self, namespace: str, gramps_id: str, source: list, objet = None) -> bool:
        self._objet = objet
        self._search_args = [source]
        return super().recherche(namespace, gramps_id)

# Classes de filtres individus
class STF_role_in_event(ST_Filters):
    '''STF_role_in_event - Filtre les individus porteurs du rôle <role> dans leurs événements personnels de type <type>
    
    Rule:
        gramps_id in rroleEvenPerso.gid
    Initial statements:
        @include ST_filters_init.py
        
        rroleEvenPerso = STF_role_in_event(
            # Valeurs modifiables - début
                type = "",  # Type d'événement (ex. "Birth")
                ""          # Rôle dans l'évéement (ex. "Déclarant")
            # Valeurs modifiables - fin
        )
    Statements:
        rroleEvenPerso.recherche(namespace=namespace, gramps_id=gramps_id, events=events)
    '''
    
    _ns: list = [ST_PERSON] # en attendant les lieux
    
    _recherche_1 = lambda self, et, er: et == self.evenement and er == self.role
    
    def __init__(self, evenement: str, role: str, filtre: object = None) -> None:
        self.evenement = evenement
        self.role = role
        self.filtre = filtre
        self.search_method = self._recherche_1
        super().__init__(self._ns, self.__class__.__name__)
        
    def recherche(self, namespace: str, gramps_id: str, events: list, objet = None) -> bool:
        self._objet = objet
        for event in events:
            self.search_args = [event.type, event.role]
            search_result = super().recherche(namespace, gramps_id)
            if search_result:
                return search_result
        return False

# Classes de filtres événements
class STF_participant_role(ST_Filters):
    '''STF_participant_role - Filtre les événements du type <event_type> dont les individus ou les familles qui y participent on pour rôle <participant_role>. Facultativement, le filtre ne prend en compte que le sous-ensemble des événements correspondant au filtre Gramps <filtre>.
    
    Rule:
        gramps_id in rtypeRole.gid
    Initial statements:
        @include ST_filters_init.py
        
        rtypeRole = STF_participant_role(
            # Valeurs modifiables - début
                event_type = "", # Type d'événement (ex. "Birth")
                participant_role = "Déclarant", # Rôle des participants dans l'événement (ex. "Déclarant")
                filtre = <valeurs_possibles> # Valeurs possibles:
                                             # - None
                                             # - filter(<nom du filtre événements Gramps>)
            # Valeurs modifiables - fin
        )
    Statements:
        rtypeRole.recherche(namespace=namespace, gramps_id=gramps_id, participants=participants, type=type, objet=obj)
    
    
    Exemples:
        - Initial statements avec l'appel d'un filtre événements Gramps:
            
            @include ST_filters_init.py
            
            rtypeRole = STF_participant_role(
                # Valeurs modifiables - début
                    event_type = "Birth",
                    participant_role = "Déclarant",
                    filtre = filter("90. Individus.Signets.EvFamiliaux[Exclus] - 045p")
                # Valeurs modifiables - fin
            )
        
        - Initial statements sans l'appel d'un filtre événements Gramps:
            
            @include ST_filters_init.py
            
            rtypeRole = STF_participant_role(
                # Valeurs modifiables - début
                    event_type = "Birth",
                    participant_role = "Déclarant",
                    filtre = None
                # Valeurs modifiables - fin
            )
        
    '''
    
    _ns: list = [ST_EVENT]
    
    def _recherche_1(self, participants, type, objet):
        for part in participants:
            for pe in part.events:
                if pe == objet and pe.role == self._participant_role and type == self._event_type:
                    return True
        return False
    
    def __init__(self, event_type: str, participant_role: str, filtre: object = None) -> None:
        self._event_type = event_type
        self._participant_role = participant_role
        self._filtre = filtre
        self._search_method = self._recherche_1
        super().__init__(self._ns, self.__class__.__name__)
        
    def recherche(self, namespace: str, gramps_id: str, participants, type, objet: object) -> bool:
        self._objet = objet
        self._search_args = [participants, type, objet]
        return super().recherche(namespace, gramps_id)

# Classes de filtres lieux
class STF_dupplicate_place(ST_Filters):
    """STF_dupplicate_place - Recherche les éventuels lieux duppliqués
    A faire tourner dans la vue Lieux et voir lieu par lieu s'il y a un doublon dans la vue Groupe de lieux
    """
    
    _ns: list = [ST_PLACE]
    
    _recherche_1 = lambda self, x, y: x in y
    _recherche_2 = lambda self, x: x
    
    def __init__(self, filter_type: bool, filtre: object = None) -> None:
        self._filter_type = filter_type
        self._ln = {}
        self._filtre = filtre
        super().__init__(self._ns, self.__class__.__name__)
        
    def recherche(self, namespace: str, gramps_id: str, longname: str, type: str, objet = None) -> None:
        self._objet = objet
        self._search_method = self._recherche_1
        lnt = (longname, type) if self._filter_type else longname
        self._search_args = [lnt, self._ln]
        if super().recherche(namespace, gramps_id):
            self._search_method = self._recherche_2
            self._search_args = [self._ln[lnt][0]]
            if super().recherche(namespace, self._ln[lnt][1]):
                self._ln[lnt][0] = False
        else:
            self._ln[lnt] = [True, gramps_id]

# Classes de filtres multi-objets
class STF_refattr(ST_Filters):
    """STF_refattr - Filtre les objets ayant dans ses références à d'autres objets (par ex. une personne dans ses références aux événements ou un lieu dans ses références aux médias) les attributs <attribut_type> et/ou <attribut_value>"""
    
    _ns: list = [ST_PERSON, ST_PLACE, ST_SOURCE, ST_CITATION, ST_FAMILY, ST_EVENT]
    _supported_objet_type: list = [ST_EVENT, ST_MEDIA]
    
    def _recherche_1(self, objets: list) -> bool:
        # Exécute la recherche des arguments recherchés dans les attributs
        #
        # Arguments attendus:
        # - obj_ref_list: objet "attribute_list" dans lequel chercher la correspondance
        # - gramps_id: ID Gramps a ajouter à la liste "gid" d'éléments comportant l'attribut
        #
        # Retourne:
        # - True si la recherche a aboutie
        # - False si la recherche a échouée
        
        trouve = False
        for objet in objets:
            if not self._selected_obj(objet):
                continue
            
            trouve = self._ref_attr(objet.attribute_list)
            if trouve:
                break
            
        return trouve
    
    def __init__(self, attribut_type: str, attribut_value: str, objet_type: str, type_event: str, rech_exacte: bool = False, filtre: object = None) -> None:
        caller = self.__class__.__name__
        self._attribut_type: str = attribut_type
        self._attribut_value: str = attribut_value
        self._rech_exacte = rech_exacte
        if objet_type not in self._supported_objet_type:
            raise Exception(f'PLX FILTERS - {caller} facilities not provided for {objet_type} type', f'{caller} is only intended for {self._supported_objet_type} type(s)')
        self._objet_type: str = objet_type
        self._type_event: str = type_event if self.objet_type == ST_EVENT else None
        self._filtre = filtre
        self._search_method = self._recherche_1
        super().__init__(self._ns, caller)
        
    def recherche(self, namespace: str, gramps_id: str, objet = None) -> bool:
        self._objet = objet
        if self._objet_type == ST_EVENT:
            #Recherche dans les événements d'une personne ou d'une famille (et à l'avenir d'un lieu)
            self._search_args = [objet.get_event_ref_list()]
        elif self._objet_type == ST_MEDIA:
            #Recherche dans les médias d'une personne, d'une famille, d'une source', d'une citation ou d'un lieu
            self._search_args = [objet.get_media_list()]
        return super().recherche(namespace, gramps_id)
            
    def _selected_obj(self, obj_rech):
        if self._objet_type == ST_MEDIA or (self._objet_type == ST_EVENT and self._type_event == None):
            # Sont sélectionnés d'office tous les médias
            # ainsi que tous événements si le type d'événement à selectionner n'est pas renseigné
            return True
            
        event = db.get_event_from_handle(obj_rech.ref)
        if self._type_event[0] == '!':
            return event.type != self._type_event[1:]
        else:
            return event.type == self._type_event

    def _ref_attr(self, attr_list: list) -> bool:
    # Recherche dans les attributs de référence si le type et/ou la valeur correspondent aux arguments
    #
    # Arguments attendus:
    # - attr_list: objet attribute_list provenant des EventRef, MediaRef, Event, Family, Citation, Source
    #
    # Retourne:
    # True: si attribut trouvé, ou si attr_type et attr_value valent None et que attr_list est vide
    # False: dans les cas contraires
    
        if not (self._attribut_type or self._attribut_value or self._rech_exacte):
            # le cas où on ne recherche rien (erreur d'arguments; tous à None/False?)
            return False
            
        if not (self._attribut_type and self._attribut_value) and (self._rech_exacte and len(attr_list) == 0):
            # recherche d'une liste d'attributs vide, trouvée
            return True
            
        for attr in attr_list:
            # les possibilités restantes
            if self._attribut_type and self._attribut_value:
                # recherches sur le type et la valeur de l'attribut
                if self._rech_exacte and attr_type == attr.type and self._attribut_value == attr.value:
                    # le type et la valeur de l'attribut recherchés correspondent
                    return True
                
                if not self._rech_exacte and self._attribut_type == attr.type and (self._attribut_value in attr.value or self._attribut_value == "*"):
                    # le type correspond et la valeur recherchée fait partie de la valeur de l'attribut
                    return True
            
            if self._attribut_type and not self._attribut_value:
                if self.rech_exacte and self.attribut_type == attr.type:
                    # le type de l'attibut correspond
                    return True
                
                if not self._rech_exacte and self._attribut_type in attr.type:
                    # la valeur recherchée fait partie du type de l'attribut
                    return True
                
            if not self._attribut_type:
                # recherche sur la valeur seule de l'attribut
                if self._rech_exacte and self._attribut_value == attr.value:
                    # la valeur de l'attribut recherchés correspond
                    return True
                    
                if not self._rech_exacte and self._attribut_value in attr.value or self._attribut_value == "*":
                    # la valeur recherchée fait partie de la valeur de l'attribut
                    return True

class STF_attrs(ST_Filters):
    """STF_attrs - Filtre les objets ayant sur un attribut et/ou sa valeur"""
    
    _ns: list = [ST_SOURCE, ST_PERSON, ST_EVENT, ST_FAMILY, ST_CITATION, ST_MEDIA]
    
    _recherche_1 = lambda self, attribut_type, type, attribut_valeur, valeur: attribut_type == type and attribut_valeur == valeur
    _recherche_2 = lambda self, attribut, attribut_type, attribut_valeur: attribut == (attribut_type, attribut_valeur)
        
    _STAR = '*'

    def __init__(self, type: str, valeur: str, filtre: object = None) -> None:
        self._type = type
        self._valeur = valeur
        self._filtre = filtre
        super().__init__(self._ns, self.__class__.__name__)
    
    def recherche(self, namespace: str, gramps_id: str, attributes: list, objet = None) -> bool:
        self._objet = objet
        for attribute in attributes:
            self._search_method = self._recherche_1
            self._search_args = [self._type, self._STAR, self._valeur, self._STAR]
            if super().recherche(namespace, gramps_id):
                return True
            self._search_args = [self._type, self._STAR, self._valeur, attribute[1]]
            if super().recherche(namespace, gramps_id):
                return True
            self._search_args = [self._type, attribute[0], self._valeur, self._STAR]
            if super().recherche(namespace, gramps_id):
                return True
            self._search_method = self._recherche_2
            self._search_args = [attribute, self._type, self._valeur]
            if super().recherche(namespace, gramps_id):
                return True
        return False

class STF_note_type(ST_Filters):
    """STF_note_type - Filtres les objets ayant des notes avec le type <note_type>"""
    
    _ns: list = [ST_EVENT, ST_CITATION, ST_SOURCE, ST_REPOSITORY, ST_PERSON, ST_PLACE]
    
    _recherche_1 = lambda self, nid, nt: db.get_note_from_gramps_id(nid).type == nt
    
    def __init__(self, note_type: str, filtre: object = None) -> None:
        self._note_type = note_type
        self._filtre = filtre
        self._search_method = self._recherche_1
        super().__init__(self._ns, self.__class__.__name__)
        
    def recherche(self, namespace: str, gramps_id: str, notes: list, objet = None) -> bool:
        self._objet = objet
        for note in notes:
            self._search_args = [note.gramps_id, self._note_type]
            if super().recherche(namespace, gramps_id):
                return True
        return False

# Classes de filtres généralistes
class STF_dtf(ST_Filters):
    """STF_dtf - Filtre les objets modifiés suivant une date précisée ou à la demande"""
    
    _ns: list = ["Person", "Source", "Event", "Citation", "Repository", "Note", "Place", "Media", "Family"]
    
    def _changeOn(self, change_timestamp): # _recherche_1
        return str(date.fromtimestamp(change_timestamp)) == self._a_day
    
    def _changeAfter(self, change_timestamp): # _recherche_2
        return str(date.fromtimestamp(change_timestamp)) > self._a_day
    
    def __init__(self, method=ST_TODAY, a_day=None, filtre: object = None):
        if method == ST_TODAY:
            self._a_day = str(today())
        elif method == ST_GET:
            self._a_day = a_day
        elif method == ST_ASK:
            a_day = getargs(daytoSearch="Searched date (default today)")
            if a_day.daytoSearch:
                self._a_day = a_day.daytoSearch
            else:
                self._a_day = str(today())
        self._filtre = filtre
        super().__init__(self._ns, self.__class__.__name__)
    
    def recherche(self, obj, namespace, gramps_id, when=ST_ON, objet = None) -> bool:
        self._objet = objet
        self._search_method = self._changeOn if when == ST_ON else self._changeAfter
        self._search_args = [obj.change]
        return super().recherche(namespace, gramps_id)

class STF_refcount_is(ST_Filters):
    """STF_refcount_is - Filtre les objets qui ont un nombre donné de références"""
    
    _ns: list = ["Person", "Source", "Event", "Citation", "Repository", "Note", "Place", "Media", "Family"]
    
    def _recherche_1(self, handle: str) -> bool:
        count = len(db.find_backlink_handles(handle))
        
        if self._count_type ==  ST_LESS_THAN:
            return count < self._nb_obj
        elif self._count_type ==  ST_GREATER_THAN:
            return count > self._nb_obj
        return count == self._nb_obj # "equal to"
    
    def __init__(self, nb_obj: int, count_type: str, filtre: object = None) -> None:
        self._nb_obj = nb_obj
        self._count_type = count_type
        self._filtre = filtre
        super().__init__(self._ns, self.__class__.__name__)
    
    def recherche(self, namespace: str, gramps_id: str, obj_to_count: list, objet = None) -> bool:
        self._objet = objet
        for tc in obj_to_count:
            self._search_args = [tc.handle]
            if super().recherche(namespace, gramps_id):
                return True
        return False

Pour le récupérer, copier-coller ce qui précède dans un fichier ST_filters_init.py (faites attention de ne pas supprimer ou ajouter d'espaces aux lignes) que vous placez dans le répertoire des include Supertool (voir plus haut dans ce fil à propos de ce répertoire).

Au début (normalement) de chaque filtre (class <nom du filtre>) j'ai indiqué son titre et dans un commentaire son usage et comment s'en servir. Exemple:

Code : Tout sélectionner

class STF_source_citations(ST_Filters):
    '''STF_source_citations - Filtre les sources ayant la citation <page> si <existe> est vrai, toutes celles qui n'ont pas cette sitation s'il est faux
    
    Rule:
        gramps_id in rcitation.gid
    Initial statements:
        @include ST_filters_init.py
        
        rcitation = STF_source_citations(
            # Valeurs modifiables - début
                page   = "",    # Citation Page (ex.: "Acte de décès")
                existe = True   #valeurs possibles: True / False, en fonction retourne toutes les sources avec ou sans la citation contenant la valeur dans page
            # Valeurs modifiables - fin
         )
    Statements:
        rcitation.recherche(namespace=namespace, gramps_id=gramps_id, citations=citations)
    
    Exemples:
        - nom du filtre Gramps: S90.158p: 90. CitationPage["_Objet de la source: Histoire de Saint-Chinian de la Corne et de ses environs (Hérault)"] - 158p
        - contenu du fitre supertool "Generic filter rule" inclu dans le fitre Gramps:
        
            Rule:
                gramps_id in rcitation.gid
                
            Initial statements:
                @include ST_filters_init.py
                
                rcitation = STF_source_citations(
                    # Valeurs modifiables - début
                         
                    page   = "_Objet de la source: Histoire de Saint-Chinian de la Corne et de ses environs (Hérault)",
                    existe = True   #valeurs possibles: True / False, en fonction retourne toutes les sources avec ou sans la citation contenant la valeur dans page
                    
                    # Valeurs modifiables - fin
                )
            Statements:
                rcitation.recherche(citations=citations, gramps_id=gramps_id, namespace=namespace)
    '''
Nom et titre des différents filtres:
  • STF_source_citations_in_objects - Filtre les sources ayant la citation <page> si cette citation est associée à un des lieux <ids>
  • STF_depot_support_type - Filtre les sources ayant le type de support <type_support> parmi leurs références de dépôt
  • STF_role_in_event - Filtre les individus porteurs du rôle <role> dans leurs événements personnels de type <type>
  • STF_participant_role - Filtre les événements du type <event_type> dont les individus ou les familles qui y participent on pour rôle <participant_role>. Facultativement, le filtre ne prend en compte que le sous-ensemble des événements correspondant au filtre Gramps <filtre>.
  • STF_dupplicate_place - Recherche les éventuels lieux duppliqués
  • STF_refattr - Filtre les objets ayant des attributs <attribut_type> et/ou <attribut_value>
  • STF_attrs - STF_refattr - Filtre les objets ayant dans ses références à d'autres objets (par ex. une personne dans ses références aux événements ou un lieu dans ses références aux médias) les attributs <attribut_type> et/ou <attribut_value>
  • STF_note_type - Filtres les objets ayant des notes avec le type <note_type>
  • STF_dtf - Filtre les objets modifiés suivant une date précisée ou à la demande
  • STF_refcount_is - Filtre les objets qui ont un nombre donné de références
Si je le modifie, ce qui est fort possible, je le republierai désormais en entier en changeant sa version.

P.S: Merci aux Youtubeurs pour leurs tutos en lien dans ma playlist ;)
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

patricelegoux
patricelegoux
Messages : 1077
Saisie : Geneweb
Voir son arbre
Bonsoir,

Pour ceux que ça intéresse, un petit script supertool à faire tourner dans la vue Familles qui recherche les familles dont un enfant est né moins de 9 mois après le mariage des parents (ou le nb de mois qu'on indique dans la variable birth_months du début). Il n'est pas parfait mais fonctionne globalement bien.

Code : Tout sélectionner

[Gramps SuperTool script file]
version=1

[title]
Families - Procreation vs Mariage

[category]
Families

[initial_statements]
birth_months = 9

gid = []
birth_days = 30 * birth_months + int(birth_months/2)

[statements]
if reltype == "Married":
    for event  in events:
        if event.type == "Marriage" and str(event.date) != "" and str(event.date) != "0000-00-00" and "bef" not in str(event.date) and "aft" not in str(event.date) and len(children) > 0:
            marriage = event.date
            for child in children:
                child_bd = child.birth.date
                if child_bd is not None and str(child_bd) != "" and str(child_bd) != "0000-00-00" and child_bd > marriage and "abt" not in str(child_bd) and "aft" not in str(child_bd) and "bef" not in str(child_bd) and "est" not in str(child_bd) and "calc" not in str(child_bd):
                    if (child_bd - marriage) < birth_days:
                       gid.append(gramps_id)

[filter]
gramps_id in gid

[expressions]
father.name, mother.name, [event.date for event in events if event.type == "Marriage"], [child.birth.date for child in children]

[scope]
all

[unwind_lists]
False

[commit_changes]
False

[summary_only]
False
Pour certains il était temps, j'ai trouvé des naissances moins d'un mois après le mariage (onze jours pour la ligne bleue):
Capture d’écran 2021-11-26 004010.jpg
Patrice Legoux Publications: Logiciels utilisés / Version:
  • Windows: Windows 10 Pro
  • Gramps: AIO64-5.1.3-2
  • Geneanet Upload: 3.0.2
  • Chrome

Répondre

Revenir à « Gramps »