Compare commits

..

35 Commits

Author SHA1 Message Date
jgenero 1d8aa5e5d2 change map layout 2 years ago
jgenero 83d4bf5f43 add folium marker cluster to route /geolocalisation 2 years ago
jgenero 3867c61d8f remove full stop after analysis 2 years ago
jgenero 00eadece1e dates sorted 2 years ago
gwen 63f0cb7a1e add multiplace in geolocalisation 2 years ago
gwen 945be64de4 typo 2 years ago
gwen 7a6a911d30 add word extracts in the search results 2 years ago
gwen 07712ad544 add plaintext search in all the web pages 2 years ago
gwen 0bcc14afbb add image and add url in folium 2 years ago
gwen c1b57c13f7 add plain text form 2 years ago
gwen c156577470 texte de mise en production 2 years ago
gwen 3e911f15ec add bootstrap designed web form (suppress WTForms) 2 years ago
gwen e382c431c8 add flask wtforms on the head web page 2 years ago
gwen ba693793d1 all routes ok 2 years ago
gwen 8f2df04dbe trailing slash at the end of the uri 2 years ago
gwen b15df2d4c5 refactoring 2 years ago
gwen 8e5d857d09 prince_corpus route 2 years ago
gwen 0aadc12ee7 add docstrings 2 years ago
gwen a74dd31afb refactoring 2 years ago
gwen 9d16a610fb typos 2 years ago
gwen 11418fc863 add aggregation on the acte's storage collections 2 years ago
gwen 2caf6cf49d route /actes/<house> ok 2 years ago
gwen 7b1c31c9e5 geolocalisation (génération dynamique) 2 years ago
gwen b815571920 test if there is no map 2 years ago
gwen c5397217f7 add folium map 2 years ago
gwen d34cb275dc acte/house/prince/dateitem route working 2 years ago
gwen 3ddc9ad7d1 error in requirements.txt 2 years ago
gwen 13e4d17a7c Merge branch 'feature/DbReader' into develop 2 years ago
gwen 7537386a7d helper 2 years ago
gwen 0024ee3231 add helper 2 years ago
gwen cf28d5cdc5 configuration in params 2 years ago
gwen 1f577c04bd add config.py 2 years ago
gwen f40127b264 database query is working 2 years ago
gwen 1e82636d9f begin sluggify the routes 2 years ago
gwen cf5cf93763 index page 2 years ago

2
.gitignore vendored

@ -1,2 +1,2 @@
.venv/*
params.yaml

@ -0,0 +1,33 @@
Cérémonial de mise en production
===================================
Mise en production
-----------------------
Ajout d'un tag de release
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Vérifier que le tag n'existe pas : `git tag -n`
- Insérer le tag `git tag -a v0.1beta -m"first production release"`
- faire un `git push --tags` pour mettre le tag sur le dépôt gitlab
- renommer `params.yaml` en `params.dev.yaml`
::
cp params.yaml params.dev.yaml
- renommer `params.prod.yaml` en `params.yaml`
::
cp params.prod.yaml params.yaml
.. note:: Ce fichier `params.yaml` sera copié directement par le script de `deployment`
par la task "Deployment - copies the credentials file from the local
app working copy repository". Il n'est pas mis dans l'archive git
pour des raisons de sécurité.
Mise à jour de l'application
--------------------------------
Même procédure (ajout d'un tag de release et règlage des paramètres).

@ -16,14 +16,12 @@ _Nota_ : commands to execute through the terminal (Linux or macOS)
- Go to the following directory : `cd webapp`;
- Install the environment : `python3 -m venv .venv`.
- Install the packages and libraries :
- Activate the environment : `source bootstrap.sh`;
- Installation : `pip install -r requirements.txt`
- Exit the environment : `deactivate`;
## Launch
## Launch (development mode)
- Activate the environment : `source bootstrap.sh`;
- Launch : `flask run`;
- Go to http://127.0.0.1:5000/;
- Launch : `python index.py`;
- Go to http://127.0.0.1:8080/;
- Stop : `ctrl + c`;
- Exit the environment : `deactivate`.

@ -3,13 +3,14 @@ import os
from flask import Flask
from flask import render_template
import werkzeug
from .config import secret_key, wtf_csrf_secret_key
from .routes import main
APPPATH = os.path.dirname(os.path.abspath(__file__))
templates = os.path.join(APPPATH, "templates")
static = os.path.join(APPPATH, "static")
app = Flask(
"Princely-Acts",
template_folder=templates,
@ -17,19 +18,31 @@ app = Flask(
static_url_path=''
)
"in case we put a trailing slash at the end of the route's uri"
app.url_map.strict_slashes = False
app.config.update(dict(
SECRET_KEY= secret_key
# WTF_CSRF_SECRET_KEY= wtf_csrf_secret_key
))
@app.before_request
def clear_trailing():
from flask import redirect, request
rp = request.path
if rp != '/' and rp.endswith('/'):
return redirect(rp[:-1])
@app.errorhandler(404)
def page_not_found(e: werkzeug.exceptions.HTTPException) -> t.Tuple[t.Text, int]:
def page_not_found(e):
return render_template("404.html", title="Page non trouvée"), 404
@app.errorhandler(500)
def internal_server_error(e: werkzeug.exceptions.HTTPException) -> t.Tuple[t.Text, int]:
def internal_server_error(e):
return render_template("500.html", title="Erreur interne du serveur"), 500
app.register_blueprint(main)
### DEBUG
@app.route('/')
@app.route('/index')
def hello_world():
return "<p>Hello, World!</p>"

@ -0,0 +1,23 @@
"""Application's configuration file"""
from pathlib import Path
# path configuration
# let's guess that :file:`config.py` is located here : :file:`{rootpath}/app/`
_here = Path(__file__).resolve().parent
rootpath = _here.parent
"root project directory"
from yaml import safe_load
# loads database credentials in the globals config module
local_params_file = rootpath / "params.yaml"
with open(local_params_file, 'r') as file_handle:
params_content = safe_load(file_handle)
globals().update(params_content)
"""
# dbadmin = params_content['dbadmin']
# dbpassword = params_content['dbpassword']
# server_ip = params_content['server_ip']
# secret_key, wtf_csrf_secret_key
"""

@ -0,0 +1,128 @@
"""
Mongodb storage init
- collections queries initialization
TODO : maybe put the mongodb connector into the flask app object
with the pymongo library.
"""
import urllib.parse
from unidecode import unidecode # to remove accents in house and prince names in the urls
from pymongo import MongoClient
from .config import dbadmin, dbpassword, server_ip
# ______________________________________________________________________________
# database connexion
def database_connexion(username, password, server_ip):
username = urllib.parse.quote_plus(dbadmin)
password = urllib.parse.quote_plus(dbpassword)
dbclient = MongoClient(f'mongodb://{username}:{password}@{server_ip}:27017')
# database
actesdb = dbclient["actesdb"]
return actesdb
actesdb = database_connexion(dbadmin, dbpassword, server_ip)
# collections
housecol = actesdb["house"]
# the acte collection is the most important collection
actecol = actesdb["acte"]
helpers = actesdb["helpers"]
#folium_map = actesdb["folium_map"]
# ______________________________________________________________________________
# storage extractor utilities
def make_acteid_from_route(house=None, prince=None, date_and_item=None):
"""
/acte/Anjou/Isabelle_i/1441_08_05a -> anj_isa_i_1441_08_05a
"""
return "_".join([trigram_house(house), bigram_prince(prince), date_and_item])
def extract_princes_in_houses():
"""Extracts all princes from a house
by queries in the storage, (not by using csv metadatas)
sample:
>>> extract_princes_in_houses['anjou']
[{'prince_name': "Louis Ier d'Anjou", 'prince_code': 'lo_i'}, {'prince_name': "Louis III d'Anjou", 'prince_code': 'lo_iii'}, {'prince_name': 'Isabelle de Lorraine', 'prince_code': 'isa_i'}, {'prince_name': "Louis II d'Anjou", 'prince_code': 'lo_ii'}, {'prince_name': 'Marie de Blois', 'prince_code': 'mar_i'}, {'prince_name': "René d'Anjou", 'prince_code': 're_i'}, {'prince_name': "Yollande d'Aragon", 'prince_code': 'yol_i'}]
"""
princes_in_houses = dict()
for house in housecol.find():
housename = house['name'].lower()
query = list(actecol.aggregate([{
"$match": {"house": housename}},
{'$group': {'_id': {'prince_name': '$prince_name', 'prince_code': '$prince_code'}}
}]))
princes_in_houses[housename] = [pr['_id'] for pr in query]
return princes_in_houses
def extract_prince_in_house(house, prince_code):
"""
:param house: sample: 'bourbon'
:param prince_code: sample: 'lo_i'
:result: sample: {'prince_name': "Louis Ier d'Anjou", 'prince_code': 'lo_i'}
"""
princes_in_houses = extract_princes_in_houses()
princes_in_house = princes_in_houses[house]
for pr in princes_in_house:
if pr['prince_code'] == prince_code:
return pr
def normalize_trigrams(trigram):
"""normalizes names, usefull for the uris routes
sample: Alençon -> Alencon
Orléans -> Orleans
"""
return {unidecode(value):key for key, value in trigram.items()}
# ______________________________________________________________________________
# in memory storage extracted 'meta' informations upon the database
# TODO: if it takes too much time at startup, jsut put it in something like
# a `flask init` procedure
helpers_dicts = helpers.find_one()
house_trigram = helpers_dicts["house_trigram"]
prince_bigram = helpers_dicts["prince_bigram"]
princes_in_houses = extract_princes_in_houses()
"""
house_trigram sample:
{'Alencon': 'alb', 'Anjou': 'anj', 'Armagnac': 'arm', 'Bourbon': 'brb', 'Bretagne': 'bre', 'Bourgogne': 'brg', 'Berry': 'bry', 'Orleans': 'orl', 'Savoie': 'sav'}
"""
house_trigram = normalize_trigrams(house_trigram)
"""
prince_bigram sample:
{'Agnes': 'agn', 'Alain': 'al', 'Amedee': 'am', 'Anne': 'ann', 'Arthur': 'ar', 'Bernard': 'be', 'Beatrice': 'bea', 'Blanche': 'bla', 'Bonne': 'bon', 'Catherine': 'cat', 'Charles': 'ch', 'Edouard': 'ed', 'Francois': 'fr', 'Francoise': 'fra', 'Jean': 'je', 'Jeanne': 'jea', 'Louis': 'lo', 'Marguerite': 'mag', 'Marie': 'mar', 'Philippe': 'ph', 'Pierre': 'pi', 'Rene': 're', 'Isabelle': 'isa', 'Yolande': 'yol'}
"""
prince_bigram = normalize_trigrams(prince_bigram)
def inverted_prince_bigram(bigram):
"Translates ch -> Charles"
inverted_prince_bigram = {value: key for key, value in prince_bigram.items()}
return inverted_prince_bigram[bigram]
def bigram_prince(prince):
"Translates Charles_i -> ch_i"
name, number = prince.split("_")
return prince_bigram[name] + "_" + number
def trigram_house(house):
return house_trigram[house]

@ -0,0 +1,95 @@
"""helper functions
"""
from bs4 import BeautifulSoup
from .dbinit import inverted_prince_bigram
def make_timeitem_from_filename(filename):
"""
"anj_isa_i_1441_08_05a" -> "1441_08_05a"
"""
trs_fname = filename.split('_')
return "_".join(trs_fname[3:])
def plaintext_response(search, actecol, prince_bigram):
"""plain text search response
:search: the form's result
:return: database query
"""
query = [
{
"$match": {
"xmlcontent": {
"$regex": search,
"$options" :'i' # case-insensitive
}
}
},
{
'$group': {'_id': {'prince_name': '$prince_name',
'house': '$house',
'date': '$date',
'prince_code': '$prince_code',
'filename': '$filename',
"place": "$place.name",
"diplo_state": "$diplo_state",
"diplo_type": "$diplo_type",
"xmlcontent": "$xmlcontent"
}
}
}]
results = list(actecol.aggregate(query))
transformed_query = [pr['_id'] for pr in results]
invert_prince_bigram = {val: key for key, val in prince_bigram.items()}
for trs in transformed_query:
trs['house'] = trs['house'].capitalize()
trs['dateitem'] = make_timeitem_from_filename(trs['filename'])
bigram, number = trs['prince_code'].split('_')
long_prince_bigram = inverted_prince_bigram(bigram) + '_' + number
trs['prince_url'] = prince_name=long_prince_bigram.capitalize()
# find the word in the text
cleantext = BeautifulSoup(trs["xmlcontent"], "lxml").text
cleantext = cleantext.lower()
searchtext = search.lower()
begin = cleantext.find(searchtext)
#end = cleantext.find(searchtext, begin, len(cleantext))
strframe = 35
begin_mark = begin - strframe
if begin_mark < 0:
begin_mark = 0
end_mark = begin + len(searchtext) + strframe
trs['show_text'] = cleantext[begin_mark:end_mark]
return transformed_query
#from flask import abort
#
#def find_one_or_404(collection, **kwargs):
# """Find a single document or raise a 404.
#
# This is like :meth:`~pymongo.collection.Collection.find_one`, but
# rather than returning ``None``, cause a 404 Not Found HTTP status
# on the request.
#
# .. code-block:: python
#
# usercollection = mydb['usercollection']
#
# @app.route("/user/<username>")
# def user_profile(username):
# userfound = find_one_or_404(usercollection, {"_id": username})
# return render_template("user.html",
# user=userfound)
# """
# found = collection.find_one(**kwargs)
# if found is None:
# abort(404)
# return found

@ -0,0 +1,281 @@
"""main flask app routes handlers
There are two types of routes, first the 'static' routes :
- /
- /about
- /actes
- /contact
- /privacy
- /termsofservice
Then the 'dynamic' (calculated) routes :
- /actes
- /actes/<house>, sample: /actes/Anjou
- /actes/<house>/<prince>, sample: /actes/Anjou/isa_i
- /acte/<house>/<prince>/<dateitem>, sample: /acte/Anjou/Isabelle_i/1441_08_05a
- /geoloc
"""
import datetime
import typing as t
from collections import defaultdict
from flask import Blueprint, render_template, request, url_for, redirect
import folium
from folium.plugins import MarkerCluster, MiniMap
from pymongo import ASCENDING
from .dbinit import *
from .helper import make_timeitem_from_filename, plaintext_response
main = Blueprint("main", __name__, url_prefix="/")
# ______________________________________________________________________________
# routes
@main.route("/", methods=('GET', 'POST'))
def home():
"""home route"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
return render_template("home.html")
@main.route("/about/", methods=('GET', 'POST'))
def about():
"""about route"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
return render_template("about.html")
@main.route("/actes/", methods=('GET', 'POST'))
def corpora_all():
"""copora all
lists houses
sample_house_names = ["Bourbon", "Berry", "Anjou", ...]
"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
houses = list(housecol.find())
return render_template("corpora_all.html", houses=houses)
@main.route("/actes/<house>", methods=('GET', 'POST'))
def actes(house):
"""*actes* route, sample route: '/actes/Bourbon' or '/actes/Berry'
shows the princes in the selected house
:param: the house in the url is the house name
with a capital letter at the beginning
example: `house = "Berry"`
"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
# house in the store shall be in lower case, but let's force it, just in case
house = house.lower()
# sample result:
# [('Louis II de Bourbon', 'lo_ii'), ('Anne Dauphine', 'ann_i'), ('Agnès de Bourgogne', 'agn_i'), ('Charles Ier de Bourbon', 'ch_i')]
# [('Agnès de Bourgogne', 'agn_i'), ('Anne Dauphine', 'ann_i'), ('Charles Ier de Bourbon', 'ch_i'), ('Louis II de Bourbon', 'lo_ii')
princes = princes_in_houses[house]
# TODO : modify the html template's design in order to suppress this ugly line
princes = [(prc['prince_name'], prc['prince_code']) for prc in princes]
return render_template("corpus.html", house=house.capitalize(), princes=princes)
@main.route("/actes/<house>/<prince>", methods=('GET', 'POST'))
def prince_corpus(house=None, prince=None):
"""copora prince, *timeline* view
sample route: '/actes/Berry/je_i' or '/actes/Anjou/lo_i'
"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
prince_code = prince.lower()
house = house.lower()
prince_long_name = extract_prince_in_house(house, prince_code)['prince_name']
query = list(actecol.aggregate([
{
"$match": {"house": house, "prince_code": prince_code}
},
#{
# "$sort": {"date_time": ASCENDING}
#},
{
'$group': {'_id': {'prince_name': '$prince_name',
'prince_code': '$prince_code',
'filename': '$filename',
'date': '$date',
"date_time": "$date_time",
"analysis": '$analysis',
"place": "$place.name",
"diplo_state": "$diplo_state",
"diplo_type": "$diplo_type",
"image": "$image"
}
}
}
]))
transformed_query = [pr['_id'] for pr in query]
invert_prince_bigram = {val: key for key, val in prince_bigram.items()}
# constructing the dateitem
for trs in transformed_query:
trs['dateitem'] = make_timeitem_from_filename(trs['filename'])
bigram, number = trs['prince_code'].split('_')
long_prince_bigram = inverted_prince_bigram(bigram) + '_' + number
#
sorted_list = sorted(transformed_query, key=lambda x: x['date_time'])
return render_template("prince_corpus.html", house=house, duke_name=prince_long_name,
prince_name=long_prince_bigram.capitalize(), actes=sorted_list)
@main.route("/acte/<house>/<prince>/<dateitem>", methods=('GET', 'POST'))
def acte(house=None, prince=None, dateitem=None):
"""specific prince's acte view
:params: - house
- prince
- date + item (sample: 1441_08_05a)
:url location sample: /acte/Anjou/Isabelle_i/1441_08_05a
url transcription samples:
/acte/Anjou/Isabelle_i/1441_08_05a -> anj_isa_i_1441_08_05a
/acte/Bourbon/Anne_i/1388_09_15a -> brb_ann_i_1388_09_15a
"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
filestem = make_acteid_from_route(house, prince, dateitem)
result = actecol.find_one({'_id': filestem})
return render_template("acte.html", house=house, prince=result.get('prince_code'),
#infos=None, place=None, doc=None, arch=None,
#diplo=diplo_t[0].replace("_", " "), state=state[0],
output_doc=result.get('xmlcontent'), name_prince=result.get("prince_name"),
folium=result.get("folium"),
transcribers=result.get('transcribers'),
image=result.get('image'))
@main.route("/geolocalisation", methods=('GET', 'POST'))
def geolocalisation():
"global folium/leaflet map"
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
m = folium.Map(location=[46.603354, 1.888334], zoom_start=6)
marker_cluster = MarkerCluster().add_to(m)
minimap = MiniMap()
m.add_child(minimap)
markers = defaultdict(list)
for result in actecol.find():
place = result['place']
filename = result['filename']
name = place['name']
place_name = name.lower()
url = result['url']
latitude = place['latitude']
longitude = place['longitude']
name_prince = result['prince_name']
dateday = result['date']
markers[place_name].append([url, latitude, longitude, filename, name_prince, dateday])
for key, value in markers.items():
name = key.capitalize()
latitude = value[0][1]
longitude = value[0][2]
urls = [(url[0], url[-2], url[-1]) for url in value]
actes_place = [f"<a href='{item[0]}' target='_top'>{item[1]} ({item[2]})</a><br />" for item in urls]
popup_text =f"""
<div style='max-height: 200px; overflow-y: auto;'>
<h2 style="text-align: center;">{name}</h2>
<div style="font-size: 16px;">
{''.join(actes_place)}
</div>
</div>
"""
popup = folium.Popup(popup_text, min_width=100, max_width=400)
if latitude is not None:
folium.CircleMarker( # choice : marker (Circle, CircleMarker or Marker, optional)
location=[latitude, longitude],
radius = 15,
popup=popup,
color='darkblue',
opacity=0.4,
fill_color='blue',
fill_opacity=0.2
# icon=folium.Icon(color='darkblue', icon="cloud", prefix='fa')
).add_to(marker_cluster)
# to remove marker_cluster : .add_to(m)
# marker's colors : 'red', 'blue', 'gray', 'darkred', 'lightred',
# 'orange', 'beige', 'green', 'darkgreen', 'lightgreen', 'darkblue',
# 'lightblue', 'purple', 'darkpurple', 'pink', 'cadetblue',
# 'lightgray', 'black'
geolocalisation = m._repr_html_()
return render_template("map.html", geolocalisation=geolocalisation)
@main.route("/contact", methods=('GET', 'POST'))
def contact() -> t.Text:
"""Displays the Contact page"""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
return render_template("contact.html", title="Contact")
@main.route("/termsofservice", methods=('GET', 'POST'))
def terms() -> t.Text:
"""Displaysthe T&C page."""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
return render_template("terms.html", title="Mentions légales")
@main.route("/privacy", methods=('GET', 'POST'))
def privacy() -> t.Text:
"""Displays the privacy policy page."""
if request.method == 'POST':
search = request.form['search']
return render_template("plainsearch.html",
search=search,
actes=plaintext_response(search, actecol, prince_bigram))
return render_template("privacy.html", title="Politique de confidentialité")

@ -20,4 +20,4 @@
</div>
</div>
</div><!-- /.container -->
{% endblock %}
{% endblock %}

@ -1,5 +1,6 @@
{% extends "container.html" %}
{% block corps %}
<div class="container marketing" style="margin-top: 50px;">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
@ -16,11 +17,32 @@
<p>{{item.analysis}}.</p>
<p>{{state}}, {{diplo}}, {{arch}}, {{doc}}, {{item.ref_acte}}.</p>
{% endfor %}-->
{{output_doc|safe}}
{{ output_doc | safe }}
<p class="font-italic font-weight-light fs-6">
Transcription : {% for name in transcribers %}{% if name == transcribers[0] %}{{name}}{% elif name == transcribers[-1] %} et {{name}}{% else %}, {{name}}{% endif %}{% endfor %}.
Transcription :
{% for name in transcribers %}
{% if name == transcribers[0] %}{{name}}
{% elif name == transcribers[-1] %}
et {{name}}
{% else %}, {{name}}
{% endif %}
{% endfor %}.
</p>
<!--<p class="font-italic font-weight-light fs-6">Transcription : {{transcriber}}.</p>-->
{% if image != None %}
<center>
<image alt="Original de l'acte" src="{{ image }}" width="1000">
</center>
{% endif %}
{% if folium != None %}
<h1 class="text-center">Géolocalisation de l'acte </h1>
{{ folium | safe }}
{% endif %}
</div><!-- /.container -->
{% endblock %}
{% endblock %}

@ -27,13 +27,16 @@
<li class="nav-item">
<a class="nav-link" href="{{url_for('main.corpora_all')}}">Corpus</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('main.geolocalisation')}}">Géolocalisation</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('main.contact')}}">Contact</a>
</li>
</ul>
<form class="form-inline mt-2 mt-md-0">
<input class="form-control mr-sm-2" type="text" placeholder="Recherche" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Rechercher</button>
<form method="POST" class="form-inline mt-2 mt-md-0">
<input name="search" class="form-control mr-sm-2" type="text" placeholder="Recherche" aria-label="Search" value="{{ request.form['search'] }}">
<button name="submit" class="btn btn-outline-success my-2 my-sm-0" type="submit">Rechercher</button>
</form>
</div>
</nav>
@ -138,4 +141,4 @@
);
</script>
</body>
</html>
</html>

@ -4,9 +4,9 @@
<h1>Corpus</h1>
<p>Corpus disponibles :</p>
<ul>
<li><a href="{{url_for('main.actes', house='Bourbon')}}">Actes des ducs et duchesses de Bourbon</a></li>
<li><a href="{{url_for('main.actes', house='Berry')}}">Actes des ducs et duchesses de Berry</a></li>
<li><a href="{{url_for('main.actes', house='Anjou')}}">Actes des ducs et duchesses d'Anjou</a></li>
</ul>
</div><!-- /.container -->
{% endblock %}
{% for house in houses %}
<li><a href="{{url_for('main.actes', house=house.name)}}">Actes des ducs et duchesses {{ house.particle }}</a></li>
{% endfor %}
{% endblock %}

@ -15,4 +15,4 @@
{% endfor %}
</ul>
</div><!-- /.container -->
{% endblock %}
{% endblock %}

@ -58,4 +58,4 @@
</div><!-- /.row -->
</div><!-- /.container -->
{% endblock %}
{% endblock %}

@ -0,0 +1,5 @@
{% extends "container.html" %}
{% block corps %}
<h1 class="text-center">Géolocalisation des actes </h1>
{{ geolocalisation | safe }}
{% endblock %}

@ -0,0 +1,34 @@
{% extends "container.html" %}
{% block corps %}
<div class="container marketing" style="margin-top: 50px;">
<div>
<h1>Résulat de la recherche</h1>
<h2>{{ actes | count }} résulats pour la recherche sur "<b>{{ search }}</b>" :</h2>
{% for acte in actes %}
<div>
<div class="title-princier">
<h3></h3>
</div>
<div class="pboly">
<p class="chrono-p font-weight-bold"><a href="{{url_for('main.acte', house=acte['house'], prince=acte['prince_url'], dateitem=acte['dateitem'])}}">{{ acte['prince_name'] }}, {{ acte['date'] }}, extrait : "... {{ acte['show_text'] }} ..."</a></p>
<p style="margin-bottom: 5%">
{% if acte['place'] != 'NS' %}
<span class="badge badge-pill badge" style="background-color: #284AA7; color: white; font-size: small;">{{ acte['place'] }}</span>
{% endif %}
<span class="badge badge-pill badge" style="background-color: #A7288A; color: white; font-size: small;">{{ acte['diplo_state'] }}</span>
<span class="badge badge-pill badge" style="background-color: #A78528; color: white; font-size: small;">{{ acte['diplo_type'] }}</span>
</p>
</div>
</div>
{% endfor %}
<a class="btn btn-outline-success my-2 my-sm-0" onclick="history.back()" >&laquo; Retour</a>
</div>
</div><!-- /.container -->
{% endblock %}

@ -4,25 +4,25 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{url_for('main.corpora_all')}}">Corpus</a></li>
<li class="breadcrumb-item"><a href="{{url_for('main.actes', house=houseS)}}">{{houseS}}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{duke_name[0][0]}}</li>
<li class="breadcrumb-item"><a href="{{url_for('main.actes', house=house)}}">{{house | capitalize}}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{duke_name}}</li>
</ol>
</nav>
<h1 style="text-align: center; padding-bottom: 5%;">Actes de {{duke_name[0][0]}}</h1>
<h1 style="text-align: center; padding-bottom: 5%;">Actes de {{duke_name}}</h1>
<div class="timeline-princier">
{% for id in lst_id %}
{% for acte in actes %}
<div class="entry-princier">
<div class="title-princier">
<h3>{{id[1]}}</h3>
<h3>{{ acte['date'] }}</h3>
</div>
<div class="pboly">
<p class="chrono-p font-weight-bold"><a href="{{url_for('main.acte', prince=duke_name[0][1], acte_id=id[2])}}">{{id[3]}}.</a></p>
<p class="chrono-p font-weight-bold"><a href="{{url_for('main.acte', house=house.capitalize(), prince=prince_name, dateitem=acte['dateitem'])}}">{{ acte['analysis'] }}</a></p>
<p style="margin-bottom: 5%">
{% if id[4] != 'NS' %}
<span class="badge badge-pill badge" style="background-color: #284AA7; color: white; font-size: small;">{{id[4]}}</span>
{% if acte['place'] != 'NS' %}
<span class="badge badge-pill badge" style="background-color: #284AA7; color: white; font-size: small;">{{ acte['place'] }}</span>
{% endif %}
<span class="badge badge-pill badge" style="background-color: #A7288A; color: white; font-size: small;">{{id[6]}}</span>
<span class="badge badge-pill badge" style="background-color: #A78528; color: white; font-size: small;">{{id[5]}}</span>
<span class="badge badge-pill badge" style="background-color: #A7288A; color: white; font-size: small;">{{ acte['diplo_state'] }}</span>
<span class="badge badge-pill badge" style="background-color: #A78528; color: white; font-size: small;">{{ acte['diplo_type'] }}</span>
</p>
</div>
</div>
@ -30,4 +30,4 @@
{% endfor %}
</div>
</div><!-- /.container -->
{% endblock %}
{% endblock %}

@ -1,27 +1,26 @@
#beautifulsoup4==4.11.1
#bs4==0.0.1
click==8.1.3
dnspython==2.4.2
email-validator==2.0.0.post2
Flask==2.2.2
flask-mongoengine==1.0.0
Flask==2.3.3
Flask-WTF==1.1.1
idna==3.4
gunicorn==21.2.0
importlib-metadata==5.0.0
itsdangerous==2.1.2
Jinja2==3.1.2
# FIXME remove lxml later
# lxml==4.9.1
MarkupSafe==2.1.1
MarkupSafe==2.1.3
packaging==23.1
mongoengine==0.27.0
# FIXME remove pewee later
# peewee==3.15.3
#mongoengine==0.27.0
pymongo==4.5.0
#soupsieve==2.3.2.post1
#tqdm==4.64.1
typing==3.7.4.3
Werkzeug==2.2.2
Werkzeug==2.3.7
Flask-WTF==1.1.1
WTForms==3.0.1
zipp==3.9.0
pyyaml==6.0.1
Unidecode==1.3.6
folium==0.14.0
beautifulsoup4==4.12.2
lxml==4.9.3

Loading…
Cancel
Save