Merge branch 'flask_from_od2m' into 'main'

Flask from od2m

See merge request jgenero/actes-princiers!3
main
Jean-Damien Genero 3 years ago
commit 720d01f53a

4
.gitignore vendored

@ -379,4 +379,8 @@ TSWLatexianTemp*
# addenda
app/actes_princiers.db
app/static/xml/Bourbon/old-5-Charles-Ier/
CONTRIBUTING.md
INSTALL.md
LICENSE.md
actes_princiers.sqlite

@ -0,0 +1,21 @@
import typing as t
import werkzeug
from flask import render_template
from .app import app
from .cmd import db_cli
from .routes import main
app.register_blueprint(main)
app.cli.add_command(db_cli)
@app.errorhandler(404)
def page_not_found(e: werkzeug.exceptions.HTTPException) -> t.Tuple[t.Text, int]:
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]:
return render_template("500.html", title="Erreur interne du serveur"), 500

@ -1,19 +1,15 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
from flask import Flask
from playhouse.sqlite_ext import SqliteExtDatabase
from .debugger import initialize_flask_server_debugger_if_needed
APPPATH = os.path.dirname(os.path.abspath(__file__))
templates = os.path.join(APPPATH, "templates")
statics = os.path.join(APPPATH, "static")
app = Flask(
"Application",
template_folder=templates,
@ -21,11 +17,8 @@ app = Flask(
)
# DB configuration
app.config["DATABASE"] = os.path.join(APPPATH, ".", "actes_princiers.db")
app.config["DATABASE"] = os.path.join(APPPATH, "..", "actes_princiers.sqlite")
db = SqliteExtDatabase(app.config["DATABASE"], pragmas=[("journal_mode", "wal")])
# Enables debugging in VS Code if DEBUG env var is set
_debugging = initialize_flask_server_debugger_if_needed()
# Import de la route principale depuis le fichier routes.py
from .routes import home

@ -0,0 +1,3 @@
from .db import db_cli
__all__ = ["db_cli"]

@ -0,0 +1,223 @@
import csv
import os
import re
import typing as t
from bs4 import BeautifulSoup
from flask.cli import AppGroup
from lxml import etree
from tqdm import tqdm
from app.app import APPPATH, db
from app.data_actes import diplomatic_type, institution, state
from app.modeles import Institution, State, Production_place, Diplo_type, Document, Acte, Individual, Duke, Produced_by
# from app.data import institution
# from app.data import state
# from app.data import diplomatic_type
db_cli = AppGroup("db")
def _nonempty(s: str) -> t.Optional[str]:
"""Returns content only if non-empty; otherwise returns None"""
if s:
return s
else:
return None
def _capitalize_first(s: t.Optional[str]) -> t.Optional[str]:
return (s[0].upper() + s[1:]) if s else None
def make_soup(file):
"""open a xml file and return a BeautifulSoup object"""
with open(file, 'r', encoding="utf-8") as opening:
xml = BeautifulSoup(opening, 'xml')
return xml
def _create_institution(data_lst: list)-> None:
"""create institution table"""
for data in tqdm(data_lst, desc="Populating Institution..."):
Institution.create(**data)
def _create_state(data_lst: list)-> None:
"""create state table"""
for data in tqdm(data_lst, desc="Populating State..."):
State.create(**data)
def _create_diplo_type(data_lst: list)-> None:
"""create diplo type table"""
for data in tqdm(data_lst, desc="Populating Diplo_type..."):
Diplo_type.create(**data)
def _create_produc_place(xml_file: str, folder: str)-> None:
"""create production place table"""
places_xtract = []
production_places = []
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
for place in soup.find('placeName', {'type': 'production_place'}):
places_xtract.append(place)
production_places = [{"placename": xtraction} for xtraction in set(places_xtract)]
for data in tqdm(production_places, desc="Populating Place..."):
Production_place.create(**data)
def _create_doc(xml_file: str, folder: str)-> None:
"""create doc table"""
details_doc = []
infos_doc = []
# 1/ get repository (doc archives) + doc collection in a list
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
inst_doc = soup.repository.text
nb_doc_1 = soup.msIdentifier.find_all("idno", {"n": "1"})[0].text
details_doc.append(inst_doc + " == " + nb_doc_1)
# 2/ make a query on table Inst to get inst id
# then pretiffy data for the table Doc
for doc in set(details_doc):
doc_archives = re.sub('(.+) == .+', '\\1', doc)
doc_cote = re.sub('.+ == (.+)', '\\1', doc)
inst_query = [t.id_institution for t in Institution.select().where(
Institution.full_label == doc_archives)]
infos_doc.append({
"inst_doc": inst_query[0],
"collection_doc": doc_cote,
})
# 3/ create the table
for data in tqdm(infos_doc, desc="Populating Document..."):
Document.create(**data)
def _create_acte(xml_file: str, folder: str)-> None:
actes = []
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
numb = soup.TEI["xml:id"]
date_time = soup.msItem.docDate["when"]
date = soup.msItem.docDate.text
analyse = soup.abstract.p.text
ref = soup.msIdentifier.find_all("idno", {"n": "2"})
if len(ref) > 0:
ref_acte = ref[0].text
else:
ref_acte = "NS"
prod_place = soup.find_all("placeName", {"type": "production_place"})[0].text
doc = soup.msIdentifier.find_all("idno", {"n": "1"})[0]
type_diplo = soup.body.div["subtype"]
diplo_state = soup.body.div["type"]
place_query = [t.id_place for t in Production_place.select().where(
Production_place.placename == prod_place)]
doc_query = [t.id_document for t in Document.select().where(
Document.collection_doc == doc.text)]
diplo_query = [t.id_diplo_type for t in Diplo_type.select().where(
Diplo_type.diplo_label == type_diplo)]
state_query = [t.id_state for t in State.select().where(
State.state_label == diplo_state)]
actes.append({
"numb_acte": numb,
"date_time": date_time,
"date": date,
"prod_place_acte": place_query[0],
"analysis": analyse,
"doc_acte": doc_query[0],
"ref_acte": ref_acte,
"state_doc": state_query[0],
"diplo_type_acte": diplo_query[0]
})
for data in tqdm(actes, desc="Populating Actes..."):
Acte.create(**data)
def __find_indiv(xml_soup, role: str, indiv_lst: list)-> None:
princes = xml_soup.sourceDesc.find_all("listPerson", {"type": role})
for prince in princes:
dukes = prince.find_all("person")
for duke in dukes:
indiv_lst.append(duke.text.replace("\n", ""))
"""
def _create_individual(xml_file: str, folder: str)-> None:
indiv_prince = []
indiv_secret = []
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
__find_indiv(soup, "prince", indiv_prince)
__find_indiv(soup, "signatory", indiv_secret)
print(set(indiv_secret))
print(set(indiv_prince))
"""
def __indiv_infos(indiv_type):
with open(os.path.join(APPPATH, "static", "csv", "actors.csv"), 'r', encoding="utf-8") as opening:
actors_csv = csv.reader(opening, delimiter=";")
next(actors_csv, None)
lst_of_indiv = [row for row in actors_csv if row[1] == indiv_type]
return lst_of_indiv
def _create_indiv():
actors = [*__indiv_infos("secret"), *__indiv_infos("prince")]
individuals = [{"name_indiv": actor[0], "role_indiv": actor[1]}
for actor in actors]
for data in tqdm(individuals, desc="Populating Individual..."):
Individual.create(**data)
def _create_duke():
dukes = []
for info in __indiv_infos("prince"):
indiv_query = [t.id_indiv for t in Individual.select().where(
Individual.name_indiv == info[0])]
dukes.append({"house": info[2], "indiv_duke": indiv_query[0],
"birth": info[3], "reign": info[4], "death": info[4]})
for data in tqdm(dukes, desc="Populating Duke..."):
Duke.create(**data)
def _create_produced_by(xml_file: str, folder: str):
princes_actes = []
for acte in os.listdir(folder):
acte_q = [t.id_acte for t in Acte.select().where(
Acte.numb_acte == acte.replace(".xml", ""))]
# print(acte, "==", acte_q[0])
soup = make_soup(os.path.join(folder, acte))
princes = soup.sourceDesc.find_all("listPerson", {"type": "prince"})
for prince in princes:
dukes = prince.find_all("person")
for duke in dukes:
prince = duke.text.replace("\n", "")
prince_q = [t.id_indiv for t in Individual.select().where(
Individual.name_indiv == duke.text.replace("\n", ""))]
duke_q = [t.id_duke for t in Duke.select().where(
Duke.indiv_duke == prince_q[0])]
# print(prince, "==", prince_q[0], "==", duke_q[0])
princes_actes.append({"produced_by_acte": acte_q[0],
"produced_by_prince": duke_q[0]})
for data in tqdm(princes_actes, desc="Populating Produced_by..."):
Produced_by.create(**data)
@db_cli.command()
def init() -> None:
"""Initialization of the database"""
xml = os.path.join(APPPATH, "static", "xml",
"Bourbon", "Brb_5_Charles_Ier"), ".xml"
xml_folder = os.path.join(APPPATH, "static", "xml",
"Bourbon", "Brb_5_Charles_Ier")
print("Dropping existing DB...")
db.drop_tables([Institution, State, Production_place,
Diplo_type, Document, Acte, Individual, Duke,
Produced_by])
print("Re-creating schema...")
db.create_tables([Institution, State, Production_place,
Diplo_type, Document, Acte, Individual, Duke,
Produced_by])
_create_institution(institution)
_create_state(state)
_create_diplo_type(diplomatic_type)
_create_produc_place(xml, xml_folder)
_create_doc(xml, xml_folder)
_create_acte(xml, xml_folder)
_create_indiv()
_create_duke()
_create_produced_by(xml, xml_folder)

@ -0,0 +1,5 @@
from .diplo_type_data import diplomatic_type
from .institution_data import institution
from .state_data import state
__all__ = ["diplomatic_type", "institution", "state"]

@ -1,217 +0,0 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Authors : Jean-Damien Généro
Affiliation : French National Center for Scientific Research (CNRS)
Assigned at the Centre de recherches historiques (CRH, UMR 8558)
Date : 2022-10-11
Update : 2022-10-13
"""
import csv
import os
import re
from bs4 import BeautifulSoup
from peewee import *
from tqdm import tqdm
from modeles.princes_db_tables import db, Institution, State, Production_place, Diplo_type, Document, Acte, Individual, Duke, Produced_by
from data.institution_data import institution
from data.state_data import state
from data.diplo_type_data import diplomatic_type
def make_soup(file):
"""open a xml file and return a BeautifulSoup object"""
with open(file, 'r', encoding="utf-8") as opening:
xml = BeautifulSoup(opening, 'xml')
return xml
def _create_institution(data_lst: list)-> None:
"""create institution table"""
for data in tqdm(data_lst, desc="Populating Institution..."):
Institution.create(**data)
def _create_state(data_lst: list)-> None:
"""create state table"""
for data in tqdm(data_lst, desc="Populating State..."):
State.create(**data)
def _create_diplo_type(data_lst: list)-> None:
"""create diplo type table"""
for data in tqdm(data_lst, desc="Populating Diplo_type..."):
Diplo_type.create(**data)
def _create_produc_place(xml_file: str, folder: str)-> None:
"""create production place table"""
places_xtract = []
production_places = []
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
for place in soup.find('placeName', {'type': 'production_place'}):
places_xtract.append(place)
production_places = [{"placename": xtraction} for xtraction in set(places_xtract)]
for data in tqdm(production_places, desc="Populating Place..."):
Production_place.create(**data)
def _create_doc(xml_file: str, folder: str)-> None:
"""create doc table"""
details_doc = []
infos_doc = []
# 1/ get repository (doc archives) + doc collection in a list
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
inst_doc = soup.repository.text
nb_doc_1 = soup.msIdentifier.find_all("idno", {"n": "1"})[0].text
details_doc.append(inst_doc + " == " + nb_doc_1)
# 2/ make a query on table Inst to get inst id
# then pretiffy data for the table Doc
for doc in set(details_doc):
doc_archives = re.sub('(.+) == .+', '\\1', doc)
doc_cote = re.sub('.+ == (.+)', '\\1', doc)
inst_query = [t.id_institution for t in Institution.select().where(
Institution.full_label == doc_archives)]
infos_doc.append({
"inst_doc": inst_query[0],
"collection_doc": doc_cote,
})
# 3/ create the table
for data in tqdm(infos_doc, desc="Populating Document..."):
Document.create(**data)
def _create_acte(xml_file: str, folder: str)-> None:
actes = []
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
numb = soup.TEI["xml:id"]
date_time = soup.msItem.docDate["when"]
date = soup.msItem.docDate.text
analyse = soup.abstract.p.text
ref = soup.msIdentifier.find_all("idno", {"n": "2"})
if len(ref) > 0:
ref_acte = ref[0].text
else:
ref_acte = "NS"
prod_place = soup.find_all("placeName", {"type": "production_place"})[0].text
doc = soup.msIdentifier.find_all("idno", {"n": "1"})[0]
type_diplo = soup.body.div["subtype"]
diplo_state = soup.body.div["type"]
place_query = [t.id_place for t in Production_place.select().where(
Production_place.placename == prod_place)]
doc_query = [t.id_document for t in Document.select().where(
Document.collection_doc == doc.text)]
diplo_query = [t.id_diplo_type for t in Diplo_type.select().where(
Diplo_type.diplo_label == type_diplo)]
state_query = [t.id_state for t in State.select().where(
State.state_label == diplo_state)]
actes.append({
"numb_acte": numb,
"date_time": date_time,
"date": date,
"prod_place_acte": place_query[0],
"analysis": analyse,
"doc_acte": doc_query[0],
"ref_acte": ref_acte,
"state_doc": state_query[0],
"diplo_type_acte": diplo_query[0]
})
for data in tqdm(actes, desc="Populating Actes..."):
Acte.create(**data)
def __find_indiv(xml_soup, role: str, indiv_lst: list)-> None:
princes = xml_soup.sourceDesc.find_all("listPerson", {"type": role})
for prince in princes:
dukes = prince.find_all("person")
for duke in dukes:
indiv_lst.append(duke.text.replace("\n", ""))
"""
def _create_individual(xml_file: str, folder: str)-> None:
indiv_prince = []
indiv_secret = []
for acte in os.listdir(folder):
soup = make_soup(os.path.join(folder, acte))
__find_indiv(soup, "prince", indiv_prince)
__find_indiv(soup, "signatory", indiv_secret)
print(set(indiv_secret))
print(set(indiv_prince))
"""
def __indiv_infos(indiv_type):
with open("./static/csv/actors.csv", 'r', encoding="utf-8") as opening:
actors_csv = csv.reader(opening, delimiter=";")
next(actors_csv, None)
lst_of_indiv = [row for row in actors_csv if row[1] == indiv_type]
return lst_of_indiv
def _create_indiv():
actors = [*__indiv_infos("secret"), *__indiv_infos("prince")]
individuals = [{"name_indiv": actor[0], "role_indiv": actor[1]}
for actor in actors]
for data in tqdm(individuals, desc="Populating Individual..."):
Individual.create(**data)
def _create_duke():
dukes = []
for info in __indiv_infos("prince"):
indiv_query = [t.id_indiv for t in Individual.select().where(
Individual.name_indiv == info[0])]
dukes.append({"house": info[2], "indiv_duke": indiv_query[0],
"birth": info[3], "reign": info[4], "death": info[4]})
for data in tqdm(dukes, desc="Populating Duke..."):
Duke.create(**data)
def _create_produced_by(xml_file: str, folder: str):
princes_actes = []
for acte in os.listdir(folder):
acte_q = [t.id_acte for t in Acte.select().where(
Acte.numb_acte == acte.replace(".xml", ""))]
# print(acte, "==", acte_q[0])
soup = make_soup(os.path.join(folder, acte))
princes = soup.sourceDesc.find_all("listPerson", {"type": "prince"})
for prince in princes:
dukes = prince.find_all("person")
for duke in dukes:
prince = duke.text.replace("\n", "")
prince_q = [t.id_indiv for t in Individual.select().where(
Individual.name_indiv == duke.text.replace("\n", ""))]
duke_q = [t.id_duke for t in Duke.select().where(
Duke.indiv_duke == prince_q[0])]
# print(prince, "==", prince_q[0], "==", duke_q[0])
princes_actes.append({"produced_by_acte": acte_q[0],
"produced_by_prince": duke_q[0]})
for data in tqdm(princes_actes, desc="Populating Produced_by..."):
Produced_by.create(**data)
def init():
"""initializing db"""
db.connect()
print("Dropping existing DB...")
db.drop_tables([Institution, State, Production_place,
Diplo_type, Document, Acte, Individual, Duke,
Produced_by])
print("Re-creating schema...")
db.create_tables([Institution, State, Production_place,
Diplo_type, Document, Acte, Individual, Duke,
Produced_by])
_create_institution(institution)
_create_state(state)
_create_diplo_type(diplomatic_type)
_create_produc_place(xml, xml_folder)
_create_doc(xml, xml_folder)
_create_acte(xml, xml_folder)
_create_indiv()
_create_duke()
_create_produced_by(xml, xml_folder)
xml = "../bourbon-latex/charles-actes-latex.xml"
xml_folder = "./static/xml/Bourbon/Brb_5_Charles_Ier"
init()
# _create_individual(xml, xml_folder)
# _create_produced_by(xml, xml_folder)

@ -0,0 +1,3 @@
from .data import Institution, State, Production_place, Diplo_type, Document, Acte, Individual, Duke, Produced_by
__all__ = ["Institution", "State", "Production_place", "Diplo_type", "Document", "Acte", "Individual", "Duke", "Produced_by"]

@ -0,0 +1,126 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Authors : Jean-Damien Généro
Affiliation : French National Center for Scientific Research (CNRS)
Assigned at the Centre de recherches historiques (CRH, UMR 8558)
Date : 2022-10-11
Update :
"""
import re
import typing as t
import peewee
from flask import url_for
from playhouse.sqlite_ext import FTS5Model, RowIDField, SearchField
from app.app import db
class BaseModel(peewee.Model):
class Meta:
database = db
# db = SqliteDatabase('actes_princiers.db')
# from app.app import db
class Institution(BaseModel):
id_institution = peewee.AutoField()
full_label = peewee.TextField()
inst_label = peewee.TextField()
art_inst = peewee.TextField()
inst_place = peewee.TextField()
inst_rank = peewee.TextField()
inst_type = peewee.TextField()
class Meta:
database = db
db_table = 'Institution'
class State(BaseModel):
id_state = peewee.AutoField()
state_label = peewee.TextField()
class Meta:
database = db
db_table = 'State'
class Production_place(BaseModel):
id_place = peewee.AutoField()
placename = peewee.TextField()
class Meta:
database = db
db_table = 'Production_place'
class Diplo_type(BaseModel):
id_diplo_type = peewee.AutoField()
diplo_label = peewee.TextField()
class Meta:
database = db
db_table = 'Diplo_type'
class Document(BaseModel):
id_document = peewee.AutoField()
inst_doc = peewee.ForeignKeyField(Institution, backref='documents')
collection_doc = peewee.TextField()
class Meta:
database = db
db_table = 'Document'
class Acte(BaseModel):
id_acte = peewee.AutoField()
numb_acte = peewee.TextField()
date_time = peewee.TextField() # YYYY-MM-DD
date = peewee.TextField() # verbose
prod_place_acte = peewee.ForeignKeyField(Production_place, backref='actes')
analysis = peewee.TextField()
doc_acte = peewee.ForeignKeyField(Document, backref='actes')
ref_acte = peewee.TextField() # cote
state_doc = peewee.ForeignKeyField(State, backref='actes')
diplo_type_acte = peewee.ForeignKeyField(Diplo_type, backref='actes')
class Meta:
database = db
db_table = 'Acte'
class Individual(BaseModel):
id_indiv = peewee.AutoField()
name_indiv = peewee.TextField()
role_indiv = peewee.TextField()
class Meta:
database = db
db_table = 'Individual'
class Duke(BaseModel):
id_duke = peewee.AutoField()
house = peewee.TextField()
indiv_duke = peewee.ForeignKeyField(Individual, backref='dukes')
birth = peewee.TextField()
reign = peewee.TextField()
death = peewee.TextField()
class Meta:
database = db
db_table = 'Duke'
class Produced_by(BaseModel):
id_produced_by = peewee.AutoField()
produced_by_acte = peewee.ForeignKeyField(Acte, backref='produced_bys')
produced_by_prince = peewee.ForeignKeyField(Duke, backref='produced_bys')
class Meta:
database = db
db_table = 'Produced_by'

@ -1,114 +0,0 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Authors : Jean-Damien Généro
Affiliation : French National Center for Scientific Research (CNRS)
Assigned at the Centre de recherches historiques (CRH, UMR 8558)
Date : 2022-10-11
Update :
"""
from peewee import *
db = SqliteDatabase('actes_princiers.db')
# from app.app import db
class Institution(Model):
id_institution = IntegerField(primary_key=True)
full_label = TextField()
inst_label = TextField()
art_inst = TextField()
inst_place = TextField()
inst_rank = TextField()
inst_type = TextField()
class Meta:
database = db
db_table = 'Institution'
class State(Model):
id_state = IntegerField(primary_key=True)
state_label = TextField()
class Meta:
database = db
db_table = 'State'
class Production_place(Model):
id_place = IntegerField(primary_key=True)
placename = TextField()
class Meta:
database = db
db_table = 'Production_place'
class Diplo_type(Model):
id_diplo_type = IntegerField(primary_key=True)
diplo_label = TextField()
class Meta:
database = db
db_table = 'Diplo_type'
class Document(Model):
id_document = IntegerField(primary_key=True)
inst_doc = ForeignKeyField(Institution, backref='documents')
collection_doc = TextField()
class Meta:
database = db
db_table = 'Document'
class Acte(Model):
id_acte = IntegerField(primary_key=True)
numb_acte = TextField()
date_time = TextField() # YYYY-MM-DD
date = TextField() # verbose
prod_place_acte = ForeignKeyField(Production_place, backref='actes')
analysis = TextField()
doc_acte = ForeignKeyField(Document, backref='actes')
ref_acte = TextField() # cote
state_doc = ForeignKeyField(State, backref='actes')
diplo_type_acte = ForeignKeyField(Diplo_type, backref='actes')
class Meta:
database = db
db_table = 'Acte'
class Individual(Model):
id_indiv = IntegerField(primary_key=True)
name_indiv = TextField()
role_indiv = TextField()
class Meta:
database = db
db_table = 'Individual'
class Duke(Model):
id_duke = IntegerField(primary_key=True)
house = TextField()
indiv_duke = ForeignKeyField(Individual, backref='dukes')
birth = TextField()
reign = TextField()
death = TextField()
class Meta:
database = db
db_table = 'Duke'
class Produced_by(Model):
id_produced_by = IntegerField(primary_key=True)
produced_by_acte = ForeignKeyField(Acte, backref='produced_bys')
produced_by_prince = ForeignKeyField(Duke, backref='produced_bys')
class Meta:
database = db
db_table = 'Produced_by'

@ -1,42 +1,58 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import re
import typing as t
import peewee
from flask import Blueprint, abort, render_template, request, send_from_directory
from playhouse.flask_utils import PaginatedQuery
"""
author : Jean-Damien Généro
date : 2022-10-01
update :
"""
from .app import APPPATH
from .modeles import Institution, State, Production_place, Diplo_type, Document, Acte, Individual, Duke, Produced_by
RESULT_PAR_PAGES = 5
# import des librairie
from flask import Flask, render_template, request
from .app import app
main = Blueprint("main", __name__, url_prefix="/")
@app.route("/")
@main.route("/")
def home():
"""home route"""
return render_template("home.html")
"""home route"""
return render_template("home.html")
@app.route("/about/")
@main.route("/about/")
def about():
"""home route"""
return render_template("about.html")
@app.route("/actes/")
@main.route("/actes/")
def corpora_all():
"""copora all route"""
return render_template("corpora_all.html")
@app.route("/actes/<house>") # dont put a slash at the end
@main.route("/actes/<house>") # dont put a slash at the end
def actes(house):
"""actes route"""
return render_template("corpus.html", house=house)
@app.route("/actes/<house>/<prince>") # dont put a slash at the end
@main.route("/actes/<house>/<prince>") # dont put a slash at the end
def prince_corpus(house=None, prince=None):
"""copora prince route"""
return render_template("prince_corpus.html", house=house, prince=prince)
@main.route("/contact")
def contact() -> t.Text:
"""Displays the Contact page"""
return render_template("contact.html", title="Contact")
@main.route("/termsofservice")
def terms() -> t.Text:
"""Displaysthe T&C page."""
return render_template("terms.html", title="Mentions légales")
@main.route("/privacy")
def privacy() -> t.Text:
"""Displays the privacy policy page."""
return render_template("privacy.html", title="Politique de confidentialité")

File diff suppressed because one or more lines are too long

@ -0,0 +1,6 @@
{% extends "container.html" %}
{% block corps %}
<p class="lead">Impossible de trouver cette page.</p>
{% endblock %}

@ -0,0 +1,6 @@
{% extends "container.html" %}
{% block corps %}
<p class="lead">Une erreur interne s'est produite.</p>
{% endblock %}

@ -0,0 +1,10 @@
{% extends "container.html" %}
{% block corps %}
<p class="lead">Le travail sur le site et le corpus est toujours en cours. Pour toute remarque, suggestion ou demande d'informations complémentaires, vous pouvez contacter l'équipe scientifique et technique à cette adresse :</p>
<p class="lead" style='text-align: center; font-weight: bold;'>gestion.sourcesetdonnees [at] ehess.fr</p>
<p style="font-style: italic;">En contactant l'équipe du site Ouvriers des deux mondes, vous consentez au traitement des données que vous transmeterez dans votre mail. La Règlementation générale sur la protection des données (RGPD) vous permet d'exercer vos droits d'accès, de rectification, de modification ou de suppression de vos données personnelles par simple demande à dpo [at] ehess [.] fr. Pour plus d'information, <a href="{{ url_for('main.terms') }}" title="Mentions légales de ce site">consultez les mentions légales</a>.</p>
{% endblock %}

@ -15,7 +15,7 @@
<header>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark" style="background-color: #05386B !important">
<a class="navbar-brand" href="{{url_for('home')}}">Actes princiers</a>
<a class="navbar-brand" href="{{url_for('main.home')}}">Actes princiers</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
@ -25,10 +25,10 @@
<a class="nav-link" href="#">À propos <span class="sr-only">(current)</span></a>
</li>-->
<li class="nav-item">
<a class="nav-link" href="{{url_for('about')}}">À propos</a>
<a class="nav-link" href="{{url_for('main.about')}}">À propos</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('corpora_all')}}">Corpus</a>
<a class="nav-link" href="{{url_for('main.corpora_all')}}">Corpus</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Contact</a>

@ -4,8 +4,8 @@
<h1>Corpus</h1>
<p>Corpus disponibles :</p>
<ul>
<li><a href="{{url_for('actes', house='Bourbon')}}">Actes des ducs de Bourbon</a></li>
<li><a href="{{url_for('actes', house='Berry')}}">Actes des ducs de Berry</a></li>
<li><a href="{{url_for('main.actes', house='Bourbon')}}">Actes des ducs de Bourbon</a></li>
<li><a href="{{url_for('main.actes', house='Berry')}}">Actes des ducs de Berry</a></li>
</ul>
</div><!-- /.container -->
{% endblock %}

@ -4,7 +4,7 @@
<h1>Corpus des actes des ducs et duchesses de {{house}}</h1>
<h2>Les corpus disponibles</h2>
<ul>
<li><a href="{{url_for('prince_corpus', house='Bourbon', prince='Charles_')}}">Actes de Charles I<sup>er</sup> (1421-1456)</a></li>
<li><a href="{{url_for('main.prince_corpus', house='Bourbon', prince='Charles_')}}">Actes de Charles I<sup>er</sup> (1421-1456)</a></li>
<li><a href="#">Actes d'Agnès de Bourgogne (1423-1476)</a></li>
</ul>
</div><!-- /.container -->

@ -8,7 +8,7 @@
<div class="col-md-7">
<h2 class="featurette-heading">Actes princiers <br/> <span class="text-muted">XII<sup>e</sup> siècle &mdash; XVI<sup>e</sup> siècle</span></h2>
<p class="lead">Ce site présente des actes élaborés par les chancelleries de plusieurs princes et princessses de sang royal des XIVe et XVe siècles. Cet ensemble, en grande partie inédit, a pour but d'aider à mieux articuler les enjeux de pouvoir des princes de la fin du Moyen Âge avec les logiques décriture de leurs chancelleries.</p>
<p><a class="btn btn-outline-success" href="{{url_for('about')}}" role="button">En savoir plus &raquo;</a></p>
<p><a class="btn btn-outline-success" href="{{url_for('main.about')}}" role="button">En savoir plus &raquo;</a></p>
</div>
<div class="col-md-5">
<figure>
@ -61,7 +61,7 @@
<p>Corpus des actes de la maison de Bourbon.</p>
<p>Louis II et Anne Dauphine</p>
<p>Charles I<sup>er</sup> et Agnès de Bourgogne</p>
<p><a class="btn btn-outline-success" href="{{url_for('actes', house='Bourbon')}}" role="button">Accéder aux actes &raquo;</a></p>
<p><a class="btn btn-outline-success" href="{{url_for('main.actes', house='Bourbon')}}" role="button">Accéder aux actes &raquo;</a></p>
</div><!-- /.col-lg-4 -->
</div><!-- /.row -->

@ -0,0 +1,57 @@
{% extends "container.html" %}
{% block corps %}
<p><em>Dernière mise à jour de la page : 5 novembre 2021.</em></p>
<p>Notice d'informations dans le cadre de la collecte de données personnelles.</p>
<p>La présente Notice vise à vous fournir des informations détaillées sur la manière dont le projet de recherche en
sciences humaines et sociales « Ouvriers des deux mondes » (ouvriersdeuxmondes.huma-num.fr) traite vos données
personnelles, conformément au Règlement (UE) 2016/679 et à la Loi n° 78-17.</p>
<hr>
<h2>Traitement des données</h2>
<p>Vos données personnelles sont recueillies et analysées par le projet de
recherche sur le corpus des monographies de familles des <i>Ouvriers des deux mondes</i> menée par le Centre de recherches historiques lorsque vous utilisez le formulaire de contact.</p>
<ul>
<li>Responsable de traitement : Centre de recherches historiques (CRH), unité mixte de recherche 8558 du Centre
national de la recherche scientifique (CNRS) et de lÉcole des hautes études en sciences sociales (EHESS),
représenté par sa directrice Dinah Ribard et son directeur Raphaël Morera et situé à l'EHESS, 54,
boulevard Raspail, 75006 Paris.</li>
<li>Responsable scientifique du projet : Anne Lhuissier et Stéphane Baciocchi (gestion.sourcesetdonnees [at] ehess
[.] fr).</li>
<li>Finalité : La finalité du traitement est la gestion du projet de recherche.</li>
<li>Base légale : Le traitement de données personnelles repose sur le consentement de l'utilisateur.</li>
<li>
Catégorie de données traitées : Les données collectées et traitées sont le nom, prénom et adresse mail des utilisateurs utilisant le formulaire de contact.
</li>
<li>Personnes concernées : Les utilisateurs du formulaire de contact.
</li>
<li>Source des données : Les données sont issues mails envoyés par les utilisateurs.</li>
<li>Destinataires des données : Chercheurs et collaborateurs du projet Ouvriers des deux mondes.</li>
<li>Durée de conservation des données : Les données sont conservées pendant 6 mois après le traitement de la demande. À lissue de ce délai, les données seront supprimées.</li>
</ul>
<hr>
<h2>Vos droits</h2>
<p>Vous disposez, dans les limites et conditions autorisées par la réglementation en vigueur, dun droit daccès, de
rectification, deffacement et à la portabilité des données personnelles qui vous concernent. Vous pouvez à tout
moment retirer votre consentement.</p>
<p>Vous disposez également dun droit dopposition ou à la limitation du traitement qui vous concerne.</p>
<p>Enfin, vous pouvez définir le sort post-mortem que vous souhaitez donner à vos données personnelles.</p>
<p>Pour en savoir plus sur vos droits, vous pouvez consulter la <a
href="https://www.cnil.fr/fr/les-droits-pour-maitriser-vos-donnees-personnelles" target="_blank"
alt="lien CNIL">fiche pratique de la CNIL</a>.</p>
<p>Pour exercer vos droits, vous pouvez contacter le responsable scientifique du projet par email :
gestion.sourcesetdonnees [at] ehess [.] fr ; ou le délégué à la protection des données de lEHESS par courrier : «
DPO - École des hautes études en sciences sociales (EHESS) - 54, boulevard Raspail 75006 Paris » ou par email :
dpo [at] ehess [.] fr.</p>
<p>Pour lexercice de vos droits, vous devez impérativement justifier de votre identité en indiquant clairement vos nom
et prénoms, ladresse à laquelle vous souhaitez que la réponse vous soit envoyée, signer votre demande. Nous vous
conseillons de joindre une copie de votre pièce didentité afin de faciliter la prise en compte de votre demande.
</p>
<p>Si vous nêtes pas satisfait du traitement de votre demande concernant le traitement de vos données personnelles,
vous avez la possibilité de saisir la CNIL : 3, place de Fontenoy - TSA 80715 - 75334 PARIS CEDEX 07 - <a
href="www.cnil.fr" target="_blank" alt="lien CNIL">www.cnil.fr</a>.</p>
<p>Cette notice dinformation est susceptible dêtre modifiée.</p>
{% endblock %}

@ -0,0 +1,88 @@
{% extends "container.html" %}
{% block corps %}
<p><em>Dernière mise à jour de la page : 4 novembre 2021.</em></p>
<p>Le site « Ouvriers des deux mondes » (ouvriersdeuxmondes.huma-num.fr) est édité par le Centre national de la recherche scientifique (CNRS), 3, rue Michel-Ange 75794 Paris cedex 16 (tél. 01 44 96 40 00) et a pour directeur de publication Antoine Petit, président directeur général du CNRS. Il est hébergé par la Très grand infrastructure de recherche (TGIR) Huma-Num, Bâtiment de recherche Nord, 14, cours des humanités, 93322 Aubervilliers cedex.</p>
<p>Le responsable de la rédaction est le Centre de recherches historiques (CRH, tél. 01 49 54 24 42, gestion.sourcesetdonnees [at] ehess [.] fr), unité mixte de recherche (UMR) 8558 du CNRS et de l'École des hautes études en sciences sociales (EHESS).</p>
<hr>
<h2>Réalisation du site</h2>
<p>Le site est réalisé sous la direction scientifique d'Anne Lhuissier et de Stéphane Baciocchi, par Jean-Damien Généro et la société Oslandia.
</p>
<hr>
<h2>Propriété intellectuelle et statut juridique de la présente édition et de ses contenus</h2>
<p>Les contenus présentés sur ce site sont couverts par le code de la propriété intellectuelle (CPI).</p>
<p>Le corpus de monographies publié sur ce site est issu de volumes papier placés dans le domaine public.</p>
<p>Les personnes mentionnées sur le site ouvriersdeuxmondes.huma-num.fr peuvent exercer leurs droits auprès du délégué à la protection des données. Les modalités sont détaillées au paragraphe « données personnelles » des présentes mentions légales. Pour en savoir plus sur les traitements de données personnelles dans le cadre du projet de recherche, nous vous invitons à consulter la <a href="{{url_for('main.privacy')}}">notice dinformation</a>.</p>
<p>L'intégralité des textes transcrits et de l'appareil critique sont publiés sur le site sous <a
href="https://www.etalab.gouv.fr/licence-ouverte-open-licence"
title="Consulter la licence ouverte ETALAB 2">Licence Ouverte V 2.0</a>.</p>
<p>Pour toute autre demande : gestion.sourcesetdonnees [at] ehess [.] fr.</p>
<hr>
<h2>Crédits photographiques et autres mentions relatives aux droits dauteur</h2>
<p>Toutes les numérisations sont mises à disposition librement par Internet Archive (<a href="https://archive.org/"
target="_blank" title="lien Internet Archive">https://archive.org/</a>) ou le site Gallica (<a
href="https://gallica.bnf.fr/" target="_blank" title="lien Gallica">https://gallica.bnf.fr/</a>) de la
Bibliothèque nationale de France.</p>
<p>Les logos présents dans le footer sont mis à disposition par leurs auteurs sur leurs sites institutionnels.</p>
<p>Les crédits photographiques et autres mentions relatives aux droits dauteur, tels que spécifiés sur ce site, doivent être respectés.</p>
<hr>
<h2>Liens</h2>
<p>Vos données à caractère personnel peuvent être traitées par lÉditeur pour la gestion du formulaire de contact,
conformément au Règlement (UE) 2016/679 et à la Loi n° 78-17.</p>
<ul>
<li>Finalité : Instruction de la demande dinformation.</li>
<li>Responsable de traitement : Centre national de la recherche scientifique (CNRS), représentée par son président directeur général Monsieur Petit et sis au 3, rue Michel-Ange 75794 Paris cedex 16.</li>
<li>Base légale : Le traitement de données personnelles est basé sur le consentement des personnes concernées.
</li>
<li>Catégorie de données traitées : Nom, Prénom, Adresse email.</li>
<li>Source des données : Les données sont collectées directement auprès des personnes concernées.</li>
<li>Caractère obligatoire du recueil des données : Les données avec un astérisque sont obligatoires pour le traitement de la demande qui, à défaut, ne pourra pas être instruite.</li>
<li>Destinataires des données : Chercheurs et collaborateurs du projet Ouvriers des deux mondes.</li>
<li>Durée de conservation des données : Les données sont conservées pendant 6 mois après le traitement de la demande. À lissue de ce délai, les données seront supprimées.</li>
</ul>
<p>Vous disposez, dans les limites et conditions autorisées par la réglementation en vigueur, dun droit daccès, de rectification, deffacement et à la portabilité des données personnelles qui vous concernent. Vous pouvez à tout moment retirer votre consentement. Vous disposez également dun droit dopposition ou à la limitation du traitement qui vous concerne. Enfin, vous pouvez définir le sort post-mortem que vous souhaitez donner à vos données personnelles. Pour en savoir plus sur vos droits, vous pouvez consulter la <a
href="https://www.cnil.fr/fr/les-droits-pour-maitriser-vos-donnees-personnelles" target="_blank"
alt="lien CNIL">fiche pratique de la CNIL</a>.</p>
<p>Pour exercer vos droits, vous pouvez contacter le responsable scientifique du projet par email : gestion.sourcesetdonnees [at] ehess [.] fr ; ou le délégué à la protection des données de lEHESS par courrier : « DPO - École des hautes études en sciences sociales (EHESS) - 54, boulevard Raspail 75006 Paris » ou par email à l'adresse dpo [at] ehess [.] fr</p>
<p>Vous devez impérativement justifier de votre identité en indiquant clairement vos nom et prénoms, ladresse à laquelle vous souhaitez que la réponse vous soit envoyée, signer votre demande. Nous vous conseillons de joindre une copie de votre pièce didentité afin de faciliter la prise en compte de votre demande.</p>
<p>En outre, si vous nêtes pas satisfait du traitement de votre demande concernant le traitement de vos données personnelles, vous avez la possibilité de saisir la CNIL : 3, place de Fontenoy - TSA 80715 - 75334 PARIS CEDEX 07 - ou <a href="www.cnil.fr" target="_blank" title="lien CNIL">www.cnil.fr</a>.</p>
<p>Pour en savoir plus sur les traitements de données personnelles dans le cadre du projet de recherche, nous vous invitons à consulter la <a href="{{url_for('main.privacy')}}">notice dinformation</a>.</p>
<hr>
<h2>Témoins de connexion ou cookies</h2>
<p>Un cookie désigne un fichier texte quun serveur web (le site web) dépose sur votre terminal (sur le disque dur de votre ordinateur ou smartphone, ou dans la mémoire de votre navigateur) lorsque vous visitez et utilisez des sites web et des services en ligne. Il contient plusieurs données : le nom du serveur qui la déposé, un identifiant sous forme de numéro unique, éventuellement une date dexpiration. Il permet de reconnaître votre terminal lorsque vous revenez sur un site web. En effet, ce nest pas vous qui êtes reconnu mais le terminal depuis lequel vous visitez un site web. Vous disposez de différents moyens pour gérer les cookies. Vous pouvez à tout moment choisir de désactiver tout ou partie des cookies.</p>
<p>Le site « Ouvriers des deux mondes » dépose uniquement des cookies nécessaires au fonctionnement du site. Ces cookies permettent au site de fonctionner de manière optimale.</p>
<p>Vous pouvez vous y opposer et les supprimer en utilisant les paramètres de votre navigateur, cependant votre expérience utilisateur risque dêtre dégradée. Pour en savoir plus, vous pouvez consulter la <a
href="https://www.cnil.fr/fr/cookies-les-outils-pour-les-maitriser" target="_blank" title="lien CNIL">fiche
pratique sur le site de la CNIL</a>.</p>
<hr>
<h2>Avertissement</h2>
<p>Les informations figurant sur ce site ou accessibles par ce site proviennent de sources considérées comme étant
fiables. Toutefois, ces informations peuvent contenir des inexactitudes techniques et des erreurs typographiques.
</p>
<p>Les informations ou documents disponibles sur ce site sont susceptibles dêtre modifiés à tout moment, et peuvent
avoir fait lobjet de mises à jour. En particulier, ils peuvent avoir fait lobjet dune mise à jour, entre le
moment de leur téléchargement et celui où lutilisateur en prend connaissance.</p>
<p>Lutilisation des informations ou documents disponibles sur ce site se fait sous lentière et seule responsabilité de
lutilisateur, qui assume la totalité des conséquences pouvant en découler. Le CRH ne pourra en aucun cas être
tenu responsable de tout dommage de quelque nature quil soit résultant de linterprétation ou de lutilisation
des informations ou documents disponibles sur ce site.</p>
<hr>
<h2>Droit applicable</h2>
<p>Lutilisation de ce site est régie par le droit français, quel que soit le lieu dutilisation. </p>
<p>En cas de contestation éventuelle, et après léchec de toute tentative de recherche dune solution amiable, les
tribunaux français seront seuls compétents pour connaître de ce litige.</p>
<hr>
<h2>Droit de réponse</h2>
<p>Le droit de réponse peut être exercé auprès de l'École des hautes études en sciences sociales : service.juridique
[at] ehess [.] fr.</p>
{% endblock %}

@ -0,0 +1,4 @@
#!/bin/bash
source .venv/bin/activate
export FLASK_ENV=development
export FLASK_APP=index.py

@ -0,0 +1,45 @@
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerName ouvriersdeuxmondes.huma-num.fr
ServerAdmin jean-damien.genero@ehess.fr
# Document root doesn't really matter as it will be overriden
DocumentRoot /var/www/html
# WSGI configuration
# - name the process od2m
# - run it as dedicated www-od2m user
WSGIDaemonProcess od2m user=www-od2m group=www-od2m threads=5 python-home=/var/www/od2m/.venv/ locale=en_US.UTF-8 socket-timeout=600
WSGIScriptAlias / /var/www/od2m/wsgi.py
<Directory /var/www/od2m>
WSGIProcessGroup od2m
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
# Log files configuration
ErrorLog ${APACHE_LOG_DIR}/od2m-error.log
CustomLog ${APACHE_LOG_DIR}/od2m-access.log combined
# SSL configuration
SSLEngine on
SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/ouvriersdeuxmondes.huma-num.fr/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/ouvriersdeuxmondes.huma-num.fr/privkey.pem
</VirtualHost>
<VirtualHost *:80>
# Make the HTTP virtual host redirect to HTTPS
ServerName ouvriersdeuxmondes.huma-num.fr
Redirect / https://ouvriersdeuxmondes.huma-num.fr
</VirtualHost>
</IfModule>

@ -0,0 +1,2 @@
LoadModule wsgi_module "/var/www/od2m/.venv/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so"
WSGIPythonHome "/var/www/od2m/.venv"

@ -0,0 +1,3 @@
#!/bin/bash
export DEBUGGER=True
"$@"

@ -0,0 +1,14 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
author = Jean-Damien Généro
date = 2020-12-15
"""
from app import app
if __name__ == "__main__":
app.run(debug=True)

@ -0,0 +1,4 @@
[mypy]
ignore_missing_imports=True
follow_imports=silent
show_column_numbers=True

@ -2,7 +2,7 @@ beautifulsoup4==4.11.1
bs4==0.0.1
click==8.1.3
Flask==2.2.2
importlib-metadata==4.12.0
importlib-metadata==5.0.0
itsdangerous==2.1.2
Jinja2==3.1.2
lxml==4.9.1
@ -11,5 +11,6 @@ peewee==3.15.3
pkg_resources==0.0.0
soupsieve==2.3.2.post1
tqdm==4.64.1
typing==3.7.4.3
Werkzeug==2.2.2
zipp==3.8.1
zipp==3.9.0

@ -0,0 +1,17 @@
[flake8]
# E501: line too long -> managed by black, allow some lines (docstring, etc.) to be longer
# W503: Line break occurred before a binary operator -> preferred way for black
# E203: Whitespace before ':' -> managed by black, allow some cases (subscripting, etc.)
ignore = E501, W503, E203
max-line-length = 110
[isort]
multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
ensure_newline_before_comments = True
line_length = 110
[pytest]
norecursedirs = .venv .eggs

@ -0,0 +1,18 @@
import os
import sys
import typing as t
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
activate_this = os.path.abspath(os.path.dirname(__file__)) + "/.venv/bin/activate_this.py"
if os.path.exists(activate_this):
with open(activate_this) as file_:
exec(file_.read(), dict(__file__=activate_this))
# On importe l'app uniquement APRES avoir activé le virtualenv
from app import app
app.config["FLASK_ENV"] = "production"
def application(req_environ: dict, start_response: t.Callable) -> t.Any:
return app(req_environ, start_response)
Loading…
Cancel
Save