fiche-ppi 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fiche_ppi-0.1.0/PKG-INFO +135 -0
- fiche_ppi-0.1.0/README.md +117 -0
- fiche_ppi-0.1.0/fiche_ppi/__init__.py +0 -0
- fiche_ppi-0.1.0/fiche_ppi/fiche_ppi.py +325 -0
- fiche_ppi-0.1.0/fiche_ppi/format_excel.py +522 -0
- fiche_ppi-0.1.0/fiche_ppi/gui.py +407 -0
- fiche_ppi-0.1.0/fiche_ppi.egg-info/PKG-INFO +135 -0
- fiche_ppi-0.1.0/fiche_ppi.egg-info/SOURCES.txt +12 -0
- fiche_ppi-0.1.0/fiche_ppi.egg-info/dependency_links.txt +1 -0
- fiche_ppi-0.1.0/fiche_ppi.egg-info/entry_points.txt +2 -0
- fiche_ppi-0.1.0/fiche_ppi.egg-info/requires.txt +4 -0
- fiche_ppi-0.1.0/fiche_ppi.egg-info/top_level.txt +1 -0
- fiche_ppi-0.1.0/pyproject.toml +30 -0
- fiche_ppi-0.1.0/setup.cfg +4 -0
fiche_ppi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fiche-ppi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate PPI sheets from Excel templates
|
|
5
|
+
Author-email: Your Name <you@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/fiche-ppi
|
|
8
|
+
Project-URL: Bug-tracker, https://github.com/yourusername/fiche-ppi/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: pandas>=1.5.0
|
|
15
|
+
Requires-Dist: numpy>=1.24.0
|
|
16
|
+
Requires-Dist: requests>=2.28.0
|
|
17
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
18
|
+
|
|
19
|
+
# Générateur de Fiches PPI
|
|
20
|
+
|
|
21
|
+
Générateur de fiches PPI (Phrases Préfabriquées des Interactions) à partir de grilles d'analyse de PPI pré-remplies.
|
|
22
|
+
|
|
23
|
+
## 📋 Description
|
|
24
|
+
|
|
25
|
+
Cet outil permet de générer automatiquement des fiches d'analyse PPI à partir de paires de grilles Excel (oral/écrit) pré-remplies. Les fiches produites suivent la grille d'analyse développée dans le cadre du projet ANR PREFAB et résument certaines informations pour faciliter la saisie des fiches finales.
|
|
26
|
+
|
|
27
|
+
Les fonctionnalités principales :
|
|
28
|
+
|
|
29
|
+
- **Mode simple** : génération d'une fiche à partir d'une paire de fichiers (oral + écrit)
|
|
30
|
+
- **Mode batch** : génération de plusieurs fiches à partir d'un dossier contenant des paires de fichiers `*_Or.xlsx` / `*_Ph.xlsx`
|
|
31
|
+
- **Interface graphique** intuitive (Tkinter)
|
|
32
|
+
- **Formatage automatique** des cellules (gras, couleurs, tags XML)
|
|
33
|
+
|
|
34
|
+
## 🚀 Installation
|
|
35
|
+
|
|
36
|
+
### Depuis PyPI (recommandé)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install fiche-ppi
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Depuis les sources
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/yourusername/fiche-ppi.git
|
|
46
|
+
cd fiche-ppi
|
|
47
|
+
pip install -e .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Dépendances
|
|
51
|
+
|
|
52
|
+
- Python ≥ 3.8
|
|
53
|
+
- pandas ≥ 1.5.0
|
|
54
|
+
- numpy ≥ 1.24.0
|
|
55
|
+
- requests ≥ 2.28.0
|
|
56
|
+
- beautifulsoup4 ≥ 4.12.0
|
|
57
|
+
- ppi_analyser (dépendance interne)
|
|
58
|
+
|
|
59
|
+
**Note** : Sur Linux, `tkinter` peut nécessiter l'installation d'un paquet système :
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
sudo apt-get install python3-tk # Debian/Ubuntu
|
|
63
|
+
sudo dnf install python3-tkinter # Fedora
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 💻 Utilisation
|
|
67
|
+
|
|
68
|
+
### Interface graphique
|
|
69
|
+
|
|
70
|
+
Lancez l'interface graphique :
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
fiche-ppi-gui
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Mode graphique - Onglet Simple
|
|
77
|
+
|
|
78
|
+
1. Sélectionnez le fichier oral (`*_Or.xlsx`)
|
|
79
|
+
2. Sélectionnez le fichier écrit (`*_Ph.xlsx`)
|
|
80
|
+
3. Le chemin de sortie est automatiquement généré (modifiable)
|
|
81
|
+
4. Cliquez sur "⚡ Générer la fiche"
|
|
82
|
+
|
|
83
|
+
### Mode graphique - Onglet Batch
|
|
84
|
+
|
|
85
|
+
1. Sélectionnez un dossier contenant des paires de fichiers
|
|
86
|
+
2. Les paires `*_Or.xlsx` / `*_Ph.xlsx` sont automatiquement détectées
|
|
87
|
+
3. Cliquez sur "⚡ Générer toutes les fiches"
|
|
88
|
+
|
|
89
|
+
### Ligne de commande
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python -m fiche_ppi.fiche_ppi fichier_oral.xlsx fichier_ecrit.xlsx -o fiche.xlsx
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 📁 Structure des fichiers d'entrée
|
|
96
|
+
|
|
97
|
+
Les fichiers Excel doivent contenir les colonnes suivantes (issues de l'export Lexicoscope) :
|
|
98
|
+
|
|
99
|
+
- `sentId`, `left`, `node`, `right`, `author`, `collection`
|
|
100
|
+
- `corpusId`, `pubdate`, `publisher`, `pubplace`, `puburl`
|
|
101
|
+
- `source_language`, `sourcefilename`, `sub_genre`, `title`
|
|
102
|
+
- `type`, `wordsnumber`, `year`
|
|
103
|
+
|
|
104
|
+
et les colonnes d'annotation PPI :
|
|
105
|
+
|
|
106
|
+
- `Lemme`, `Forme PPI`, `Acception`, `Type de phrase`
|
|
107
|
+
- `Modalité d'énonciation`, `Expansion`, `Modifieurs`
|
|
108
|
+
- `Cooccurrents`, `Fonction globale`, `Fonctions spécifiques`
|
|
109
|
+
- `milieu`, `secteur`, `Remarques`
|
|
110
|
+
|
|
111
|
+
## Génération des colonnes de la fiche PPI
|
|
112
|
+
|
|
113
|
+
| Propriété | Comment c'est généré |
|
|
114
|
+
|-----------|---------------------|
|
|
115
|
+
| **Fe_1a PPI** | Prend la valeur de la colonne `Forme PPI` du premier enregistrement du DataFrame combiné (oral + écrit) |
|
|
116
|
+
| **Fe_1b Acception** | Joint les valeurs uniques non-vides de la colonne `Acception` (triées, séparées par ", ") |
|
|
117
|
+
| **Fe_1c Variantes formelles** | Calcule les variantes formelles pour l'oral et l'écrit séparément :<br>- Extrait les modifieurs de la colonne `Modifieurs`<br>- Nettoie les tokens de modifieurs<br>- Applique `remove_modifier()` pour retirer les modifieurs de la colonne `node`<br>- Formate : `\n- Oral : var1, var2\n- Écrit : var3, var4` |
|
|
118
|
+
| **Fe_1e Prononciation** | Pour le lemme (premier `Lemme` du DataFrame oral) :Scrape la page Wiktionary du lemme pour extraire les URLs des fichiers audio MP3 - Extrait les URLs des fichiers MP3<br>- Retourne une liste d'URLs séparées par des sauts de ligne |
|
|
119
|
+
| **Fe_2a Statut syntaxique phrase** | Joint les valeurs uniques de la colonne `Type de phrase` |
|
|
120
|
+
| **Fe_2c Modalité de phrase** | Joint les valeurs uniques de la colonne `Modalité d'énonciation` |
|
|
121
|
+
| **Fe_2e Expansion éventuelle** | Joint les valeurs uniques de la colonne `Expansion` |
|
|
122
|
+
| **Fe_3a Fonction globale** | Joint les valeurs uniques de la colonne `Fonction globale` |
|
|
123
|
+
| **Fe_3b Fonctions spécifiques** | Joint les valeurs uniques de la colonne `Fonctions spécifiques` |
|
|
124
|
+
| **Fe_3c Codes Fonction globale** | Joint les valeurs uniques de la colonne `Fonction globale` (identique à Fe_3a) |
|
|
125
|
+
| **Fe_3d Codes Fonctions spécifiques** | Joint les valeurs uniques de la colonne `Fonctions spécifiques` (identique à Fe_3b) |
|
|
126
|
+
| **Fe_3f Structure interactionnelle** | Pour l'oral, l'écrit et le combiné :<br>- Calcule les fréquences des colonnes `Déclenchement`, `Portée`, `Position`<br>- Formate : `Oral :\n\t- Déclenchement : val1 (n), val2 (n)\n\t- Portée : ...` |
|
|
127
|
+
| **Fe_3g Contexte spécifique** | Joint les valeurs uniques de la colonne `milieu` |
|
|
128
|
+
| **Fe_3h Modalité écrite et orale** | Joint les valeurs uniques de la colonne `secteur` |
|
|
129
|
+
| **Fe_4a Cooccurrents privilégiés** | Pour l'oral, l'écrit et le combiné :<br>- Parse la colonne `Cooccurrents`<br>- Sépare les éléments antéposés (a) et postposés (p)<br>- Compte les fréquences avec `Counter()`<br>- Calcule les pourcentages d'antéposés/postposés<br>- Formate : `Oral :\n\t- Cooccurrents antéposés (X%) : ...\n\t- Cooccurrents postposés (Y%) : ...` |
|
|
130
|
+
| **Fe_4b Modifieurs de la PPI** | Joint les valeurs uniques de la colonne `Modifieurs` |
|
|
131
|
+
| **Fe_9a Remarques** | Joint les valeurs uniques de la colonne `Remarques` |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
**LIDILEM · ANR PREFAB**
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Générateur de Fiches PPI
|
|
2
|
+
|
|
3
|
+
Générateur de fiches PPI (Phrases Préfabriquées des Interactions) à partir de grilles d'analyse de PPI pré-remplies.
|
|
4
|
+
|
|
5
|
+
## 📋 Description
|
|
6
|
+
|
|
7
|
+
Cet outil permet de générer automatiquement des fiches d'analyse PPI à partir de paires de grilles Excel (oral/écrit) pré-remplies. Les fiches produites suivent la grille d'analyse développée dans le cadre du projet ANR PREFAB et résument certaines informations pour faciliter la saisie des fiches finales.
|
|
8
|
+
|
|
9
|
+
Les fonctionnalités principales :
|
|
10
|
+
|
|
11
|
+
- **Mode simple** : génération d'une fiche à partir d'une paire de fichiers (oral + écrit)
|
|
12
|
+
- **Mode batch** : génération de plusieurs fiches à partir d'un dossier contenant des paires de fichiers `*_Or.xlsx` / `*_Ph.xlsx`
|
|
13
|
+
- **Interface graphique** intuitive (Tkinter)
|
|
14
|
+
- **Formatage automatique** des cellules (gras, couleurs, tags XML)
|
|
15
|
+
|
|
16
|
+
## 🚀 Installation
|
|
17
|
+
|
|
18
|
+
### Depuis PyPI (recommandé)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install fiche-ppi
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Depuis les sources
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/yourusername/fiche-ppi.git
|
|
28
|
+
cd fiche-ppi
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Dépendances
|
|
33
|
+
|
|
34
|
+
- Python ≥ 3.8
|
|
35
|
+
- pandas ≥ 1.5.0
|
|
36
|
+
- numpy ≥ 1.24.0
|
|
37
|
+
- requests ≥ 2.28.0
|
|
38
|
+
- beautifulsoup4 ≥ 4.12.0
|
|
39
|
+
- ppi_analyser (dépendance interne)
|
|
40
|
+
|
|
41
|
+
**Note** : Sur Linux, `tkinter` peut nécessiter l'installation d'un paquet système :
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
sudo apt-get install python3-tk # Debian/Ubuntu
|
|
45
|
+
sudo dnf install python3-tkinter # Fedora
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 💻 Utilisation
|
|
49
|
+
|
|
50
|
+
### Interface graphique
|
|
51
|
+
|
|
52
|
+
Lancez l'interface graphique :
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
fiche-ppi-gui
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Mode graphique - Onglet Simple
|
|
59
|
+
|
|
60
|
+
1. Sélectionnez le fichier oral (`*_Or.xlsx`)
|
|
61
|
+
2. Sélectionnez le fichier écrit (`*_Ph.xlsx`)
|
|
62
|
+
3. Le chemin de sortie est automatiquement généré (modifiable)
|
|
63
|
+
4. Cliquez sur "⚡ Générer la fiche"
|
|
64
|
+
|
|
65
|
+
### Mode graphique - Onglet Batch
|
|
66
|
+
|
|
67
|
+
1. Sélectionnez un dossier contenant des paires de fichiers
|
|
68
|
+
2. Les paires `*_Or.xlsx` / `*_Ph.xlsx` sont automatiquement détectées
|
|
69
|
+
3. Cliquez sur "⚡ Générer toutes les fiches"
|
|
70
|
+
|
|
71
|
+
### Ligne de commande
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python -m fiche_ppi.fiche_ppi fichier_oral.xlsx fichier_ecrit.xlsx -o fiche.xlsx
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 📁 Structure des fichiers d'entrée
|
|
78
|
+
|
|
79
|
+
Les fichiers Excel doivent contenir les colonnes suivantes (issues de l'export Lexicoscope) :
|
|
80
|
+
|
|
81
|
+
- `sentId`, `left`, `node`, `right`, `author`, `collection`
|
|
82
|
+
- `corpusId`, `pubdate`, `publisher`, `pubplace`, `puburl`
|
|
83
|
+
- `source_language`, `sourcefilename`, `sub_genre`, `title`
|
|
84
|
+
- `type`, `wordsnumber`, `year`
|
|
85
|
+
|
|
86
|
+
et les colonnes d'annotation PPI :
|
|
87
|
+
|
|
88
|
+
- `Lemme`, `Forme PPI`, `Acception`, `Type de phrase`
|
|
89
|
+
- `Modalité d'énonciation`, `Expansion`, `Modifieurs`
|
|
90
|
+
- `Cooccurrents`, `Fonction globale`, `Fonctions spécifiques`
|
|
91
|
+
- `milieu`, `secteur`, `Remarques`
|
|
92
|
+
|
|
93
|
+
## Génération des colonnes de la fiche PPI
|
|
94
|
+
|
|
95
|
+
| Propriété | Comment c'est généré |
|
|
96
|
+
|-----------|---------------------|
|
|
97
|
+
| **Fe_1a PPI** | Prend la valeur de la colonne `Forme PPI` du premier enregistrement du DataFrame combiné (oral + écrit) |
|
|
98
|
+
| **Fe_1b Acception** | Joint les valeurs uniques non-vides de la colonne `Acception` (triées, séparées par ", ") |
|
|
99
|
+
| **Fe_1c Variantes formelles** | Calcule les variantes formelles pour l'oral et l'écrit séparément :<br>- Extrait les modifieurs de la colonne `Modifieurs`<br>- Nettoie les tokens de modifieurs<br>- Applique `remove_modifier()` pour retirer les modifieurs de la colonne `node`<br>- Formate : `\n- Oral : var1, var2\n- Écrit : var3, var4` |
|
|
100
|
+
| **Fe_1e Prononciation** | Pour le lemme (premier `Lemme` du DataFrame oral) :Scrape la page Wiktionary du lemme pour extraire les URLs des fichiers audio MP3 - Extrait les URLs des fichiers MP3<br>- Retourne une liste d'URLs séparées par des sauts de ligne |
|
|
101
|
+
| **Fe_2a Statut syntaxique phrase** | Joint les valeurs uniques de la colonne `Type de phrase` |
|
|
102
|
+
| **Fe_2c Modalité de phrase** | Joint les valeurs uniques de la colonne `Modalité d'énonciation` |
|
|
103
|
+
| **Fe_2e Expansion éventuelle** | Joint les valeurs uniques de la colonne `Expansion` |
|
|
104
|
+
| **Fe_3a Fonction globale** | Joint les valeurs uniques de la colonne `Fonction globale` |
|
|
105
|
+
| **Fe_3b Fonctions spécifiques** | Joint les valeurs uniques de la colonne `Fonctions spécifiques` |
|
|
106
|
+
| **Fe_3c Codes Fonction globale** | Joint les valeurs uniques de la colonne `Fonction globale` (identique à Fe_3a) |
|
|
107
|
+
| **Fe_3d Codes Fonctions spécifiques** | Joint les valeurs uniques de la colonne `Fonctions spécifiques` (identique à Fe_3b) |
|
|
108
|
+
| **Fe_3f Structure interactionnelle** | Pour l'oral, l'écrit et le combiné :<br>- Calcule les fréquences des colonnes `Déclenchement`, `Portée`, `Position`<br>- Formate : `Oral :\n\t- Déclenchement : val1 (n), val2 (n)\n\t- Portée : ...` |
|
|
109
|
+
| **Fe_3g Contexte spécifique** | Joint les valeurs uniques de la colonne `milieu` |
|
|
110
|
+
| **Fe_3h Modalité écrite et orale** | Joint les valeurs uniques de la colonne `secteur` |
|
|
111
|
+
| **Fe_4a Cooccurrents privilégiés** | Pour l'oral, l'écrit et le combiné :<br>- Parse la colonne `Cooccurrents`<br>- Sépare les éléments antéposés (a) et postposés (p)<br>- Compte les fréquences avec `Counter()`<br>- Calcule les pourcentages d'antéposés/postposés<br>- Formate : `Oral :\n\t- Cooccurrents antéposés (X%) : ...\n\t- Cooccurrents postposés (Y%) : ...` |
|
|
112
|
+
| **Fe_4b Modifieurs de la PPI** | Joint les valeurs uniques de la colonne `Modifieurs` |
|
|
113
|
+
| **Fe_9a Remarques** | Joint les valeurs uniques de la colonne `Remarques` |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
**LIDILEM · ANR PREFAB**
|
|
File without changes
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"""
|
|
2
|
+
fiche_ppi.py
|
|
3
|
+
Génère une fiche PPI consolidée à partir de deux fichiers Excel (oral / écrit).
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python fiche_ppi.py <file_oral.xlsx> <file_ecrit.xlsx>
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# ── Imports ────────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from collections import Counter
|
|
15
|
+
from urllib.parse import quote
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
import pandas as pd
|
|
19
|
+
import requests
|
|
20
|
+
from bs4 import BeautifulSoup
|
|
21
|
+
|
|
22
|
+
from .format_excel import format_ppi_bold
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ── CLI ────────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
def parse_args() -> argparse.Namespace:
|
|
28
|
+
parser = argparse.ArgumentParser(
|
|
29
|
+
description="Génère une fiche PPI consolidée à partir de deux fichiers Excel."
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument("file_oral", help="Fichier Excel oral (.xlsx)")
|
|
32
|
+
parser.add_argument("file_ecrit", help="Fichier Excel écrit (.xlsx)")
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"-o", "--output",
|
|
35
|
+
default="fiche.xlsx",
|
|
36
|
+
help="Fichier de sortie (défaut: fiche.xlsx)"
|
|
37
|
+
)
|
|
38
|
+
return parser.parse_args()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ── Wiktionary ─────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
def get_wiktionary_pronunciation(expression: str, lang: str = "fr") -> list[str]:
|
|
44
|
+
"""
|
|
45
|
+
Récupère les URLs audio MP3 pour une expression depuis Wiktionary.
|
|
46
|
+
Retourne une liste d'URLs ou une liste vide en cas d'échec.
|
|
47
|
+
"""
|
|
48
|
+
url = f"https://{lang}.wiktionary.org/wiki/{quote(expression)}"
|
|
49
|
+
headers = {"User-Agent": "Mozilla/5.0 (pronunciation-fetcher/1.0)"}
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
response = requests.get(url, headers=headers, timeout=10)
|
|
53
|
+
response.raise_for_status()
|
|
54
|
+
|
|
55
|
+
soup = BeautifulSoup(response.text, "html.parser")
|
|
56
|
+
audio_urls = []
|
|
57
|
+
|
|
58
|
+
for source in soup.find_all("source"):
|
|
59
|
+
src = source.get("src", "")
|
|
60
|
+
if src and src.startswith("//") and src.endswith(".mp3"):
|
|
61
|
+
audio_urls.append("https:" + src)
|
|
62
|
+
|
|
63
|
+
if not audio_urls:
|
|
64
|
+
print(f"[warn] Aucun audio trouvé pour '{expression}'.", file=sys.stderr)
|
|
65
|
+
|
|
66
|
+
return list(set(audio_urls))
|
|
67
|
+
|
|
68
|
+
except requests.exceptions.ConnectionError:
|
|
69
|
+
print("[warn] Impossible de joindre Wiktionary.", file=sys.stderr)
|
|
70
|
+
except requests.exceptions.Timeout:
|
|
71
|
+
print(f"[warn] Timeout pour '{expression}'.", file=sys.stderr)
|
|
72
|
+
except requests.exceptions.HTTPError as e:
|
|
73
|
+
print(f"[warn] HTTP {e.response.status_code} pour '{expression}'.", file=sys.stderr)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"[warn] Erreur inattendue : {e}", file=sys.stderr)
|
|
76
|
+
|
|
77
|
+
return []
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# ── Nettoyage des données ──────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
def stringify(df: pd.DataFrame) -> pd.DataFrame:
|
|
83
|
+
"""
|
|
84
|
+
Remplace les NaN / None / chaînes vides par "" et convertit toutes
|
|
85
|
+
les colonnes en str. Ne modifie pas le DataFrame en place — retourne
|
|
86
|
+
une copie.
|
|
87
|
+
"""
|
|
88
|
+
df = df.copy()
|
|
89
|
+
for col in df.columns:
|
|
90
|
+
df[col] = df[col].apply(
|
|
91
|
+
lambda x: ""
|
|
92
|
+
if (x is None or (isinstance(x, float) and np.isnan(x)))
|
|
93
|
+
else x
|
|
94
|
+
)
|
|
95
|
+
df[col] = df[col].astype(str).str.strip()
|
|
96
|
+
# Remplace les chaînes vides ou purement blanches par ""
|
|
97
|
+
df[col] = df[col].apply(lambda x: "" if not x.split() else x)
|
|
98
|
+
return df
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def clean_list(series) -> list[str]:
|
|
102
|
+
"""Retourne les valeurs non-vides d'une Series ou d'une liste."""
|
|
103
|
+
return [str(v) for v in series if isinstance(v, str) and v.strip()]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def join_unique(series) -> str:
|
|
107
|
+
"""Joint les valeurs uniques non-vides triées d'une Series."""
|
|
108
|
+
values = sorted(
|
|
109
|
+
set(series.dropna().astype(str).str.strip()) - {"", "nan"}
|
|
110
|
+
)
|
|
111
|
+
return ", ".join(values)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def clean_modifieurs(modifieurs: list[str]) -> list[str]:
|
|
115
|
+
return [re.sub(r"[.,»]", "", m).strip().lower() for m in modifieurs]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ── Variantes formelles ────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
def remove_modifier(modifier_tokens: list[str], forme: str) -> str:
|
|
121
|
+
"""Retire les tokens de modifieur présents dans la forme."""
|
|
122
|
+
modifier_lower = {t.lower() for t in modifier_tokens}
|
|
123
|
+
tokens = forme.split()
|
|
124
|
+
filtered = [t for t in tokens if t.lower() not in modifier_lower]
|
|
125
|
+
result = " ".join(filtered).replace(" -", "-")
|
|
126
|
+
return result.strip()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def compute_variantes_formelles(df: pd.DataFrame) -> list[str]:
|
|
130
|
+
modifieurs = [m for m in df["Modifieurs"] if isinstance(m, str) and m.strip()]
|
|
131
|
+
modifier_tokens = " ".join(modifieurs).lower().split()
|
|
132
|
+
|
|
133
|
+
nodes = df["node"].str.replace(" -", "-", regex=False)
|
|
134
|
+
variantes = nodes.apply(lambda x: remove_modifier(modifier_tokens, x))
|
|
135
|
+
variantes = clean_modifieurs(variantes.tolist())
|
|
136
|
+
return list(set(variantes))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ── Cooccurrents ───────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
def count_cooccurrents(df: pd.DataFrame) -> dict:
|
|
142
|
+
"""
|
|
143
|
+
Parse la colonne Cooccurrents et calcule les stats
|
|
144
|
+
antéposés (a) / postposés (p).
|
|
145
|
+
"""
|
|
146
|
+
raw = ", ".join(clean_list(df["Cooccurrents"]))
|
|
147
|
+
items = [c.strip() for c in raw.replace(";", ",").split(",") if c.strip()]
|
|
148
|
+
|
|
149
|
+
cooc_a = [c.lower() for c in items if "(a)" in c]
|
|
150
|
+
cooc_p = [c.lower() for c in items if "(p)" in c]
|
|
151
|
+
all_coocs = [
|
|
152
|
+
re.sub(r"\((a|p)\)", "", c).strip().lower()
|
|
153
|
+
for c in items
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
total = len(cooc_a) + len(cooc_p)
|
|
157
|
+
pct_a = round(len(cooc_a) / total * 100, 2) if total > 0 else 0.0
|
|
158
|
+
pct_p = round(len(cooc_p) / total * 100, 2) if total > 0 else 0.0
|
|
159
|
+
|
|
160
|
+
def fmt(counter_items, tag):
|
|
161
|
+
pattern = f"({tag})"
|
|
162
|
+
return ", ".join(
|
|
163
|
+
f"{cooc.replace(pattern, '').strip()} ({n})"
|
|
164
|
+
for cooc, n in counter_items
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
"all": ", ".join(f"{c} ({n})" for c, n in Counter(all_coocs).most_common()),
|
|
169
|
+
"anteposés": fmt(Counter(cooc_a).most_common(), "a"),
|
|
170
|
+
"postposés": fmt(Counter(cooc_p).most_common(), "p"),
|
|
171
|
+
"pct_a": pct_a,
|
|
172
|
+
"pct_p": pct_p,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def format_cooc_stats(stats: dict, label: str) -> str:
|
|
177
|
+
return (
|
|
178
|
+
f"<bold>{label}</bold>:\n"
|
|
179
|
+
f"\t- <bold>Cooccurrents antéposés</bold> ({stats['pct_a']}%) : {stats['anteposés']}\n"
|
|
180
|
+
f"\t- <bold>Cooccurrents postposés</bold> ({stats['pct_p']}%) : {stats['postposés']}\n"
|
|
181
|
+
f"\t- <bold>Total</bold> : {stats['all']}\n"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ── Structure interactionnelle ─────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
def get_interaction_stats(df: pd.DataFrame) -> str:
|
|
188
|
+
"""Comptages sur Déclenchement, Portée, Position."""
|
|
189
|
+
|
|
190
|
+
def fmt_counts(series):
|
|
191
|
+
counts = Counter(clean_list(series.str.lower().str.strip()))
|
|
192
|
+
return ", ".join(f"{val} ({n})" for val, n in counts.most_common())
|
|
193
|
+
|
|
194
|
+
decl = fmt_counts(df["Déclenchement"])
|
|
195
|
+
port = fmt_counts(df["Portée"])
|
|
196
|
+
pos = fmt_counts(df["Position"])
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
f"\t- <bold>Déclenchement</bold> : {decl}\n"
|
|
200
|
+
f"\t- <bold>Portée</bold> : {port}\n"
|
|
201
|
+
f"\t- <bold>Position</bold> : {pos}\n"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ── Construction de la fiche ───────────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
FICHE_COLS = [
|
|
208
|
+
"Fe_1a PPI",
|
|
209
|
+
"Fe_1b Acception",
|
|
210
|
+
"Fe_1c Variantes formelles",
|
|
211
|
+
"Fe_1e Prononciation",
|
|
212
|
+
"Fe_2a Statut syntaxique phrase",
|
|
213
|
+
"Fe_2b Type phrase",
|
|
214
|
+
"Fe_2c Modalité de phrase",
|
|
215
|
+
"Fe_2d Structure syntaxique globale",
|
|
216
|
+
"Fe_2e Expansion éventuelle",
|
|
217
|
+
"Fe_2f Construction syntaxique détaillée",
|
|
218
|
+
"Fe_2g Alternances syntaxiques",
|
|
219
|
+
"Fe_3a Fonction globale",
|
|
220
|
+
"Fe_3b Fonctions spécifiques",
|
|
221
|
+
"Fe_3c Codes Fonction globale",
|
|
222
|
+
"Fe_3d Codes Fonctions spécifiques",
|
|
223
|
+
"Fe_3e Fonctionnement pragma-sémantique",
|
|
224
|
+
"Fe_3f Structure interactionnelle",
|
|
225
|
+
"Fe_3g Contexte spécifique",
|
|
226
|
+
"Fe_3h Modalité écrite et orale",
|
|
227
|
+
"Fe_4a Cooccurrents privilégiés communs à la PPI",
|
|
228
|
+
"Fe_4b Modifieurs de la PPI",
|
|
229
|
+
"Fe_4c Renvois synonymiques",
|
|
230
|
+
"Fe_5a Marques d'usage de la PPI",
|
|
231
|
+
"Fe_6a Définitions et sources",
|
|
232
|
+
"Fe_7a Gestes/comportements associés",
|
|
233
|
+
"Fe_9a Remarques",
|
|
234
|
+
"Fe_9b Références",
|
|
235
|
+
"Fe_10a Noms des rédacteurs",
|
|
236
|
+
"Fe_10b Date de mise à jour",
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def build_fiche(
|
|
241
|
+
df_oral: pd.DataFrame,
|
|
242
|
+
df_ecrit: pd.DataFrame,
|
|
243
|
+
df_combined: pd.DataFrame,
|
|
244
|
+
) -> pd.DataFrame:
|
|
245
|
+
|
|
246
|
+
# Variantes formelles
|
|
247
|
+
var_oral = compute_variantes_formelles(df_oral)
|
|
248
|
+
var_ecrit = compute_variantes_formelles(df_ecrit)
|
|
249
|
+
variantes_str = (
|
|
250
|
+
f"\n- <bold>Oral</bold> : {', '.join(var_oral)}"
|
|
251
|
+
f"\n- <bold>Écrit</bold> : {', '.join(var_ecrit)}\n"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Prononciation
|
|
255
|
+
lemme = df_oral["Lemme"].iloc[0]
|
|
256
|
+
prononciation = "\n".join(get_wiktionary_pronunciation(lemme))
|
|
257
|
+
|
|
258
|
+
# Cooccurrents
|
|
259
|
+
cooc_oral = count_cooccurrents(df_oral)
|
|
260
|
+
cooc_ecrit = count_cooccurrents(df_ecrit)
|
|
261
|
+
cooc_combined = count_cooccurrents(df_combined)
|
|
262
|
+
|
|
263
|
+
cooc_str = (
|
|
264
|
+
format_cooc_stats(cooc_oral, "Oral")
|
|
265
|
+
+ format_cooc_stats(cooc_ecrit, "Écrit")
|
|
266
|
+
+ format_cooc_stats(cooc_combined, "Combiné")
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Structure interactionnelle
|
|
270
|
+
stats_oral = get_interaction_stats(df_oral)
|
|
271
|
+
stats_ecrit = get_interaction_stats(df_ecrit)
|
|
272
|
+
stats_combined = get_interaction_stats(df_combined)
|
|
273
|
+
|
|
274
|
+
stats_str = (
|
|
275
|
+
"<bold>Oral</bold> :\n" + stats_oral
|
|
276
|
+
+ "<bold>Écrit</bold> :\n" + stats_ecrit
|
|
277
|
+
+ "<bold>Combiné</bold> :\n" + stats_combined
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Remplissage
|
|
281
|
+
df_fiche = pd.DataFrame(columns=FICHE_COLS, index=[0])
|
|
282
|
+
|
|
283
|
+
df_fiche["Fe_1a PPI"] = df_combined["Forme PPI"].values[0]
|
|
284
|
+
df_fiche["Fe_1b Acception"] = join_unique(df_combined["Acception"])
|
|
285
|
+
df_fiche["Fe_1c Variantes formelles"] = variantes_str
|
|
286
|
+
df_fiche["Fe_1e Prononciation"] = prononciation
|
|
287
|
+
df_fiche["Fe_2a Statut syntaxique phrase"] = join_unique(df_combined["Type de phrase"])
|
|
288
|
+
df_fiche["Fe_2c Modalité de phrase"] = join_unique(df_combined["Modalité d'énonciation"])
|
|
289
|
+
df_fiche["Fe_2e Expansion éventuelle"] = join_unique(df_combined["Expansion"])
|
|
290
|
+
df_fiche["Fe_3a Fonction globale"] = join_unique(df_combined["Fonction globale"])
|
|
291
|
+
df_fiche["Fe_3b Fonctions spécifiques"] = join_unique(df_combined["Fonctions spécifiques"])
|
|
292
|
+
df_fiche["Fe_3c Codes Fonction globale"] = join_unique(df_combined["Fonction globale"])
|
|
293
|
+
df_fiche["Fe_3d Codes Fonctions spécifiques"] = join_unique(df_combined["Fonctions spécifiques"])
|
|
294
|
+
df_fiche["Fe_3f Structure interactionnelle"] = stats_str
|
|
295
|
+
df_fiche["Fe_3g Contexte spécifique"] = join_unique(df_combined["milieu"])
|
|
296
|
+
df_fiche["Fe_3h Modalité écrite et orale"] = join_unique(df_combined["secteur"])
|
|
297
|
+
df_fiche["Fe_4a Cooccurrents privilégiés communs à la PPI"] = cooc_str
|
|
298
|
+
df_fiche["Fe_4b Modifieurs de la PPI"] = join_unique(df_combined["Modifieurs"])
|
|
299
|
+
df_fiche["Fe_9a Remarques"] = join_unique(df_combined["Remarques"])
|
|
300
|
+
|
|
301
|
+
# Pivot
|
|
302
|
+
df_fiche = df_fiche.T.reset_index()
|
|
303
|
+
df_fiche.rename(columns={"index": "Propriétés", 0: "Valeurs"}, inplace=True)
|
|
304
|
+
|
|
305
|
+
return df_fiche
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# ── Main ───────────────────────────────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
def main():
|
|
311
|
+
args = parse_args()
|
|
312
|
+
|
|
313
|
+
df_oral = stringify(pd.read_excel(args.file_oral))
|
|
314
|
+
df_ecrit = stringify(pd.read_excel(args.file_ecrit))
|
|
315
|
+
df_combined = pd.concat([df_oral, df_ecrit], ignore_index=True)
|
|
316
|
+
|
|
317
|
+
df_fiche = build_fiche(df_oral, df_ecrit, df_combined)
|
|
318
|
+
|
|
319
|
+
print(df_fiche.to_string(index=False))
|
|
320
|
+
format_ppi_bold(df_fiche, args.output)
|
|
321
|
+
print(f"\n[ok] Fiche exportée → {args.output}")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
if __name__ == "__main__":
|
|
325
|
+
main()
|