Commit 813c8b12 authored by Lucas Terriel's avatar Lucas Terriel 🐍
Browse files

update Readme + add dataset

parent 9e8e8f40
......@@ -8,7 +8,7 @@ On a à diverses reprises et vainement recherché l'origine et la cause de ce d
Les anciens inventaires du Trésor des Chartes ne permettent même pas d'établir de façon certaine à quelle époque il remonte.
Tout ce que l'on peut dire c'est qu'il existait déjà au XVII e siècle, puisque, dans les copies exécutées alors sur l'ordre de Colbert et conservées aujourd'hui à la Bibliothèque nationale, on constate l'existence de la même lacune.
Tout ce que l'on peut dire c'est qu'il existait déjà au XVIIe siècle, puisque, dans les copies exécutées alors sur l'ordre de Colbert et conservées aujourd'hui à la Bibliothèque nationale, on constate l'existence de la même lacune.
Il est d'ailleurs infiniment probable que, par suite d'une cause qu'on ne peut préciser, les registres perdus l'ont été avant même d'avoir été versés des bureaux de la chancellerie au Trésor des Chartes, et que par conséquent leur disparition n'est point imputable aux gardes de ce Trésor.
......@@ -22,7 +22,7 @@ On comprend d'après cela que les actes ainsi insinués soient les plus divers e
Donner de tant d'actes de nature si différente une analyse détaillée était une entreprise d'autant plus utile que les anciens inventaires que nous en avions conservés, et en particulier celui de Dupuy, étaient insuffisants, car les analyses des pièces y étaient d'une concision qui ne permettait pas souvent d'en apprécier la valeur historique ; inutilisables souvent, car les noms de personnes et de lieux y étant à peine identifiés, beaucoup de pièces se trouvaient entièrement perdues dans l'ensemble.
L'inventaire qui en est donné aujourd'hui a pour but et aura pour résultat de mettre en quelque manière toutes ces pièces en valeur, et c'est pourquoi il convient et il me sera aisé de faire ressortir en quelques lignes l'intérêt que présente ce recueil de 2039 analyses d'actes au triple point de vue de l'histoire même du règne de Louis XII, de l'histoire locale, de l'histoire de la société et des moeurs au commencement du XVI e siècle.
L'inventaire qui en est donné aujourd'hui a pour but et aura pour résultat de mettre en quelque manière toutes ces pièces en valeur, et c'est pourquoi il convient et il me sera aisé de faire ressortir en quelques lignes l'intérêt que présente ce recueil de 2039 analyses d'actes au triple point de vue de l'histoire même du règne de Louis XII, de l'histoire locale, de l'histoire de la société et des moeurs au commencement du XVIe siècle.
C'est d'abord tout le début du règne de Louis XII qui est contenu là, avec mille détails sur l'histoire administrative, religieuse, financière, commerciale et industrielle des premières années de ce règne.
......@@ -46,7 +46,7 @@ Or comme ceux-ci ne craignaient pas d'entrer dans les détails les plus circonst
Toutes les classes de la société défilent d'abord là : sous nos yeux dans le plus pittoresque cortège, gentilshommes que nous surprenons dans leurs occupations habituelles, à la guerre, en garnison, ou en campagne, ou bien dans leurs domaines, absorbés par le soin de leurs cultures, de leurs procès, soucis dont ils se délassent par la chasse, le jeu et les « franches lippées et beuveries » ; paysans que nous voyons courbés sur leur travail, labourant, semant, fauchant, moissonnant, dont nous apprenons les procédés de culture, processifs eux aussi et impitoyables sur toutes les questions de bornage, d'irrigation, de droits de passage, mais se détendant les dimanches et jours de fête en joyeux passe-temps ; bourgeois et marchands des villes à leurs comptoirs ; gens de métier à leur ouvrage, drapiers, couteliers, cordonniers, chapeliers, teinturiers, bonnetiers, potiers, couturiers ; prêtres, curés et vicaires de campagne ; gens de guerre revenant de la frontière et de « delà les monts », ou regagnant leurs garnisons ; mariniers et gens de mer ; maîtres d'école ; étudiants des Universités ; aventuriers, braconniers, faux-sauniers, etc.
Mais ce ne sont pas seulement les types des différentes classes sociales du temps qui revivent ainsi sous nos yeux dans les lettres de rémission, c'est l'homme même du XVI e siècle qui est là ressuscité avec ses idées, ses passions, ses habitudes, son langage.
Mais ce ne sont pas seulement les types des différentes classes sociales du temps qui revivent ainsi sous nos yeux dans les lettres de rémission, c'est l'homme même du XVIe siècle qui est là ressuscité avec ses idées, ses passions, ses habitudes, son langage.
Avec ces lettres de rémission nous pénétrons dans les intérieurs, nous assistons aux querelles de famille que soulève telle question d'héritage, ou très souvent encore l'organisation communautaire qui demeure encore si fréquente à cette époque ; nous prenons part aux infortunes conjugales et entendons les lamentations de tant de maris trompés ; nous sommes les témoins et les auditeurs des querelles de ménage ou même d'alcôve qui surviennent entre époux mal assortis ; nous suivons toutes les péripéties d'un enlèvement, ou de la confection d'un faux testament ; nous prenons place dans les auberges à côté de gais compagnons, mais sommes par contre trop souvent les spectateurs des scènes de violence qui suivent ces ripailles ; nous pénétrons dans les lieux mal famés de la capitale et quand nous en sortons, c'est pour nous retrouver dans les rues de Paris et y être témoins de quelque attaque nocturne ; pour nous changer, nous voici invités à une fête de village ou aux noces de quelque « rustique » aisé, et prenant part aux jeux, « devis » et plaisanteries qui accompagnent ces réunions ; un peu plus tard nous partons en pèlerinage ou revenons d'une foire ; dans une église nous voyons se dérouler quelque « mystère », ou sur la place du village nous écoutons se jouer une « moralité » ; nous vivons en un mot complètement et intimement avec les contemporains du roi Louis XII, et cela grâce à des documents, dont la puissance d'évocation est incomparable.
......
#!/bin/bash
# Script pour renommer tout les *.txt en *.fr.txt
# change the path
for f in ./*.txt;
do mv -- "$f" "${f%.txt}.fr.txt";
done
echo '=== * Tous les fichiers sont renommés * ==='
\ No newline at end of file
# Présentation, installation et fonctionnement des ressources logicielles pour Ner4archives
-------------
Auteur : Lucas Terriel
Status : en cours de rédaction
Dernière version : 06/01/2020
------------
## Contenu
...A compléter...
- Le logiciel *Entity-fishing*
- Présentation
- Pré-requis à l'installation
- Procédure d'installation d'*Entity-fishing*
- Cas d'usage d'*Entity-fishing*
- Script `parser.py` d'indexation automatique des instruments de recherche EAD par le biai d'une instance locale d'*entity-fishing* et du client python *entity-fishing*
- Présentation
- Pré-requis
- Principes et fonctionnement
- Liens supplémentaires
## Le logiciel *Entity-fishing*
### Présentation
...A compléter...
### Pré-requis à l'installation
**Configuration matérielle requise:**
- Système d'exploitation : Linux-64bits ou MacOsX
- RAM : 8Go min voir 16Go (3Go peuvent suffirent pour traiter des textes et non des PDF)
- Stockage : SSD (fortement recommandé si la RAM <= 8Go) ou HDD de 500Go min pour stocker les données Wikidata et Wikipédia notamment
**Configuration logicielle requise :**
- Java : JDK 1.8 / JVM 8 (attention problème possible avec version supérieur à Java 8)
pour connaitre la version de Java utilisé lancer dans le terminal la commande :
```
java -version
```
### Procédure d'installation d'*Entity-fishing*
#### Étape 1 : Installation de GROBID et GROBID-NER
Le logiciel *Entity-fishing* dépend de deux autres logiciels Grobid et Grobid-ner :
- Grobid permet l'analyse (*parsing*), l'extraction de la mise en page (*layout*) et la re-structuration de documents bruts dans des formats structurés (exemple XML TEI).
- Grobid-NER (*Named Entity Recognition*) est un module de Grobid qui permet d'extraire et de classer les entités des documents en [27 classes](https://bit.ly/3nk89hB) dans le but de préparer la désembiguisation des entités en les confrontant à des bases de connaissance comme Wikipédia.
Nous allons donc procéder à l'installation préalable de ces deux logiciels.
**A. Installation de GROBID**
commencer par installer la dernière version la plus stable soit la version `0.6.1`.
Dans le terminal, choisir et se placer via `cd` dans le répertoire de travail où vous installarez *Entity-fishing* par la suite (très important). Assurer vous que le répertoire parent ne contient pas d'espace.
Télécharger l'archive via la commande :
```
$ wget https://github.com/kermitt2/grobid/archive/0.6.1.zip
```
Dézipper l'archive via :
```
$ unzip 0.6.1.zip
```
supprimer l'archive (optionnel) via :
```
$ rm -r 0.6.1.zip
```
renommer votre dossier `grobid-0.6.1/` en `grobid/` via :
```
$ mv 0.6.1/ grobid-0.6.1/
```
**B. Monter GROBID avec l'outil de build Gradle**
Se déplacer dans le répertoire `grobid/` comme suit :
```
$ cd grobid/
```
Puis lancer la commande :
```
$ ./gradlew clean install test
```
le terme "test" dans la commande est optionnel mais pratique pour visualiser les problèmes de build.
laisser s'executer le build, une fois terminé
le message suivant doit s'afficher `BUILD SUCCESSFUL` dans le terminal.
**C. Vérifier que GROBID fonctionne**
Dans le dossier `grobid/` lancer la commande suivante :
```
$ ./gradlew run
```
Le serveur devrai se lancer sur le port 8070 par défaut. Rentrer `http://localhost:8070/` dans votre navigateur pour vous en assurer. Le processus dans le terminal reste bloqué à 88% cela est normal.
**D. Installation de GROBID-NER**
Se placer dans le dossier `grobid/` via :
```
$ cd grobid/
```
Puis cloner le dépôt de grobid-ner dans `grobid/` via :
```
$ git clone https://github.com/kermitt2/grobid-ner.git
```
puis rentrer dans le dossier `grobid-ner/` via `cd`. Une fois dans le dossier, il faut s'assurer de copier 3 models (ner, nerfr, nersense) de grobid-ner sont copiés dans le chemin `grobid/grobid-home/models/`. Pour se faire lancer la commande :
```
$ ./gradlew copyModels
```
Se diriger vers le répertoire `grobid/grobid-home/models/` à l'aide de `cd`. Dans le cas où la copie des modèles n'a pas était effectué, copier-coller manuellement les modèles de grobid-ner (`grobid-ner/ressources/models/`) dans le dossier `grobid/grobid-home/models/`.
Dans le dossier `grobid-ner/` monter le sous-projet grobid-ner via la commande
```
$ ./gradlew clean install
```
jusqu'à obtenir l'affichage `BUILD SUCCESSFUL`
#### Étape 2 : Installation d'*Entity-fishing*
A partir de cette étape votre dossier contenant les sources *entity-fishing* devra se situer au même niveau que `grobid/` tel que par exemple :
```
D/
|
├── ressources/
| ├── entity-fishing/
│ ├── grobid/
| ├── grobid-ner/
```
Se placer dans le dossier ou se situe le dossier `grobid/`
Ensuite, cloner le dépôt via :
```
$ git clone https://github.com/kermitt2/entity-fishing
```
Puis lancer :
```
$ cd data/config/
```
et vérifier dans le fichier `mention.yaml` que le chemin vers grobidHome est correct (effectuer un `$ cat mention.yaml`), tel que :
```
# path to the GROBID home (for grobid-ner, grobid, etc.)
grobidHome: ../grobid/grobid-home/
```
Sinon modifier avec votre éditeur préféré `nano` ou `vim`.
#### Étape 3 : Téléchargement de la base de données pour *Entity-fishing* (démo pour Linux)
Il vous faut ensuite créer la base de données qui servira à la désambiguisation des entités. Le set-up minimal requiert **obligatoirement** les données Wikidata (Wikidata, db-kb.zip) et les données Wikipédia en anglais.
Pour se faire, se placer dans le dossier `entity-fishing/` et lancer la commande :
```
$ cd data/db/
```
Le dossier est vide pour l'instant, lancer ensuite successivement les commandes :
```
$ wget https://science-miner.s3.amazonaws.com/entity-fishing/0.0.4/linux/db-kb.zip
$ wget https://science-miner.s3.amazonaws.com/entity-fishing/0.0.4/linux/db-en.zip
```
Vous pouvez ensuite télécharger choisir de télécharger un set de données correspondant à votre environnement linguistique, par exemple le français, vous pouvez aussi télécharger toutes les langues (voir NOTE) pour se faire lancer :
```
$ wget https://science-miner.s3.amazonaws.com/entity-fishing/0.0.4/linux/db-fr.zip
```
Une fois vos archives téléchargées, lancer dans le dossier `db/` la commande pour dézipper vos archives en une fois, ce qui prend un certain temps :
```
$ unzip '*.zip'
```
Une fois décompresser, différents dossiers de type `db-code_langue` seront créés dans le dossier `db/`
Vous pouvez ensuite choisir de supprimer les archives avec la méthode vue plus haut.
:exclamation: NOTE : L'ensemble des données à télécharger proposés dans la [documentation d'entity-fishing](https://bit.ly/2XfmwsN) correspond à un *dump* de Wikidata et Wikipedia du 20/05/2020. L'ensemble des données dans toutes les langues représente un total de 36GB (compréssées) et de 119GB (décompréssées).
#### Étape 4 : Monter le projet *Entity-fishing*
sous le dossier `entity-fishing/` lancer la commande :
```
$ ./gradlew clean build
```
A la fin du processus le terminal doit afficher `BUILD SUCCESSFUL`
#### Étape 5 : Tester le lancement d'*Entity-fishing*
Pour lancer le service sur le port `8090`, se placer dans `entity-fishing/` et lancer la commande :
```
$ ./gradlew appRun
```
le processus reste bloqué à 87% ce qui est normal, ouvrez votre navigateur avec l'adresse `http://localhost:8090`.
Si le service s'affiche, l'installation est réussie. :tada:
## Cas d'usage d'*Entity-fishing* dans Ner4archives
...A compléter...
- Créer une nouvelle version de la base de connaissance pour *entity-fishing* à partir d'un dump de Wikidata et Wikipédia Cf. GRISP (à venir)
- Entrainer un modèle et évaluer avec Wikipédia dans *entity-fishing* (à venir)
- Entrainer et évaluer un modèle à partir d'un corpus annoté dans *entity-fishing* (à venir, voir utilité)
## Liens supplémentaires
utiliser des liens bit
- [Dépôt *Entity-fishing*](https://bit.ly/2LaRRdD)
- [Documentation *Entity-fishing*](https://bit.ly/3hOFOOZ)
- [Dépôt GROBID](https://bit.ly/3bfkwZB)
- [Documentation GROBID](https://bit.ly/3blncoy)
- [Dépôt GROBID-NER](https://bit.ly/2L0oivw)
- [Documentation GROBID-NER](https://bit.ly/2Xc9213)
# coding: utf-8
# import sys
from nerd.nerd_client import NerdClient
from nerd_client import NerdClient
import requests
......@@ -223,4 +223,4 @@ class HistoryFishing:
for dep in entity['dependencies']:
print("\t\tdependency: " + str(dep['form']) + " ==> " + str(dep['relation']))
return namedEntities
return namedEntities
\ No newline at end of file
......@@ -34,4 +34,4 @@ class ParserClient:
else:
response = str(statusCode) + ": " + message
return response
return response
\ No newline at end of file
""" Generic API Client """
from copy import deepcopy
import json
import requests
try:
from urlparse import urljoin
except ImportError:
from urllib.parse import urljoin
class ApiClient(object):
""" Client to interact with a generic Rest API.
Subclasses should implement functionality accordingly with the provided
service methods, i.e. ``get``, ``post``, ``put`` and ``delete``.
"""
accept_type = 'application/json'
api_base = None
def __init__(
self,
base_url,
username=None,
api_key=None,
status_endpoint=None,
timeout=60
):
""" Initialise client.
Args:
base_url (str): The base URL to the service being used.
username (str): The username to authenticate with.
api_key (str): The API key to authenticate with.
timeout (int): Maximum time before timing out.
"""
self.base_url = base_url
self.username = username
self.api_key = api_key
self.status_endpoint = urljoin(self.base_url, status_endpoint)
self.timeout = timeout
@staticmethod
def encode(request, data):
""" Add request content data to request body, set Content-type header.
Should be overridden by subclasses if not using JSON encoding.
Args:
request (HTTPRequest): The request object.
data (dict, None): Data to be encoded.
Returns:
HTTPRequest: The request object.
"""
if data is None:
return request
request.add_header('Content-Type', 'application/json')
request.data = json.dumps(data)
return request
@staticmethod
def decode(response):
""" Decode the returned data in the response.
Should be overridden by subclasses if something else than JSON is
expected.
Args:
response (HTTPResponse): The response object.
Returns:
dict or None.
"""
try:
return response.json()
except ValueError as e:
return e
def get_credentials(self):
""" Returns parameters to be added to authenticate the request.
This lives on its own to make it easier to re-implement it if needed.
Returns:
dict: A dictionary containing the credentials.
"""
return {"username": self.username, "api_key": self.api_key}
def call_api(
self,
method,
url,
headers=None,
params=None,
data=None,
files=None,
timeout=None,
):
""" Call API.
This returns object containing data, with error details if applicable.
Args:
method (str): The HTTP method to use.
url (str): Resource location relative to the base URL.
headers (dict or None): Extra request headers to set.
params (dict or None): Query-string parameters.
data (dict or None): Request body contents for POST or PUT requests.
files (dict or None: Files to be passed to the request.
timeout (int): Maximum time before timing out.
Returns:
ResultParser or ErrorParser.
"""
method = method.upper()
headers = deepcopy(headers) or {}
headers['Accept'] = self.accept_type
params = deepcopy(params) or {}
data = data or {}
files = files or {}
if self.username and self.api_key:
params.update(self.get_credentials())
url = urljoin(self.base_url, url)
r = requests.request(
method,
url,
headers=headers,
params=params,
files=files,
data=data,
timeout=timeout,
)
return r, r.status_code
def get(self, url, params=None, **kwargs):
""" Call the API with a GET request.
Args:
url (str): Resource location relative to the base URL.
params (dict or None): Query-string parameters.
Returns:
ResultParser or ErrorParser.
"""
return self.call_api(
"GET",
url,
params=params,
**kwargs
)
def delete(self, url, params=None, **kwargs):
""" Call the API with a DELETE request.
Args:
url (str): Resource location relative to the base URL.
params (dict or None): Query-string parameters.
Returns:
ResultParser or ErrorParser.
"""
return self.call_api(
"DELETE",
url,
params=params,
**kwargs
)
def put(self, url, params=None, data=None, files=None, **kwargs):
""" Call the API with a PUT request.
Args:
url (str): Resource location relative to the base URL.
params (dict or None): Query-string parameters.
data (dict or None): Request body contents.
files (dict or None: Files to be passed to the request.
Returns:
An instance of ResultParser or ErrorParser.
"""
return self.call_api(
"PUT",
url,
params=params,
data=data,
files=files,
**kwargs
)
def post(self, url, params=None, data=None, files=None, **kwargs):
""" Call the API with a POST request.
Args:
url (str): Resource location relative to the base URL.
params (dict or None): Query-string parameters.
data (dict or None): Request body contents.
files (dict or None: Files to be passed to the request.
Returns:
An instance of ResultParser or ErrorParser.
"""
return self.call_api(
"POST",
url,
params=params,
data=data,
files=files,
**kwargs
)
def service_status(self, **kwargs):
""" Call the API to get the status of the service.
Returns:
An instance of ResultParser or ErrorParser.
"""
return self.call_api(
'GET',
self.status_endpoint,
params={'format': 'json'},
**kwargs
)
\ No newline at end of file
import sys
try:
from urlparse import urljoin
except ImportError:
from urllib.parse import urljoin
from zenlog import logging
from client import ApiClient
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
stream = logging.StreamHandler(sys.stdout)
stream.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'\n\n %(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
stream.setFormatter(formatter)
logger.addHandler(stream)
class NerdClient(ApiClient):
api_base = "http://localhost:8090/service/"
max_text_length = 500 # Approximation.
sentences_per_group = 10 # Number of sentences per group
def __init__(self, apiBase=api_base):
super(NerdClient, self).__init__(base_url=apiBase)
if not apiBase.endswith('/'):
apiBase = apiBase + '/'
api_base = apiBase
self.disambiguate_service = urljoin(api_base, "disambiguate")
self.concept_service = urljoin(api_base, "kb/concept")