Parcourir la source

Adding diagrams & twocolumns management

Pierre-Yves Barriat il y a 2 ans
Parent
commit
dc1aaef749

BIN
Report/Projet_Brevet_Barriat.pdf


+ 106 - 9
Report/Projet_brevet.md

@@ -4,20 +4,21 @@ author: [Pierre-Yves Barriat]
 date: "15 avril 2022"
 subject: "Examen d'avancement au grade d'informaticien-expert"
 keywords: [brevet, informaticien]
-lang: "en"
+#lang: "en"
 subtitle: "Examen d'avancement au grade d'informaticien-expert"
 option1: "**FGS :** 01108821"
 option2: "**Institut ELI - pôle ELIC**"
 option3: "**Dénomination de la fonction :** informaticien de recherche"
 option4: "**Fonction exercée depuis le :** 5 novembre 2007"
-option5: "**Grade et barème actuels :** informaticien  12/2"
-option6: "**Grade et barème sollicités :** informaticien-expert  13/3"
+option5: "**Grade et barème actuels :** informaticien - 12/2"
+option6: "**Grade et barème sollicités :** informaticien-expert - 13/3"
 titlepage: true
 titlepage-text-color: "1e355b"
-titlepage-background: "garde.pdf"
-page-background: "background.pdf"
+titlepage-background: "assets/garde.pdf"
+page-background: "assets/background.pdf"
 page-background-opacity: "0.8"
 footer-right: " "
+caption-justification: centering
 ...
 
 # Introduction
@@ -40,11 +41,10 @@ C'est un système de stockage hiérarchique qui fournit un accès partagé aux d
 
 [^1]: https://intranet.uclouvain.be/fr/myucl/services-informatiques/service-fichier-personnel-en-detail.html
 
-OneDrive et SharePoint sont des solutions de stockage intégrées à MS Office 365[^2]. Sharepoint est un espace très spécifique, fortement intégré aux outils de la suite bureautique. Son utilisation se fait en ligne via un navigateur. Il est possible de l'intégrer davantage (synchronisation, édition locale, etc) à l'environnement de travail via un client OneDrive (mais pas pour un environnement GNU/Linux).  
-Il s'agit d'un espace de travail collaboratif partagé, mais exclusivement disponible pour les utilisateurs de l'UCLouvain. Pour de plus grande quantité de données, l'utilisation de OneDrive est plus appropriée et plus performante. 
-Cette solution est un espace de stockage dans un cloud publique en modèle SaaS (Software as a Service ou Logiciel en tant que Service) : entièrement géré et hébergé par Microsoft. OneDrive (via un compte UCLouvain) permet de stocker et sauvegarder de grande quantité de données en toute sécurité dans l'UE (respectant les recommandations GDPR). Mais les données ne sont pérennes que pour un utilisateur de l'UCLouvain: si ce dernier quitte l'institution, les données disparaissent.  
+SharePoint est une solution de stockage dans un cloud publique en modèle SaaS (Software as a Service ou Logiciel en tant que Service) : entièrement géré et hébergé par Microsoft. Cette solution est parfaitement intégrée à la suite de logiciels bureautique MS Office 365. Il s'agit d'un espace de travail collaboratif partagé, mais exclusivement disponible pour les utilisateurs de l'UCLouvain. L'utilisation de Sharepoint se fait en ligne via un navigateur. Il est possible de l'intégrer davantage (synchronisation, édition locale, etc) à l'environnement de travail via un client OneDrive (mais pas pour un environnement GNU/Linux).  
+Pour une utilisation plus individuelle, OneDrive est plus approprié[^2]. OneDrive (via un compte UCLouvain) permet de stocker et sauvegarder de grande quantité de données en toute sécurité dans l'UE (respectant les recommandations GDPR). Mais les données ne sont pérennes que pour un utilisateur de l'UCLouvain: si ce dernier quitte l'institution, les données disparaissent.  
 
-Les solutions de stockage de Microsoft ne sont en revanche pas ou peu adaptées pour des données sous environnement GNU/Linux. En outre, il n'est pas possible de collaborer (SharePoint) ou de partager des données (SharePoint, OneDrive) avec des utilisateurs extérieurs.
+Les solutions de stockage de Microsoft ne sont en revanche pas ou peu adaptées pour des données sous environnement GNU/Linux. En outre, collaborer (SharePoint) ou partager des données (SharePoint, OneDrive) avec des utilisateurs extérieurs n'est pas sytématique: il est nécessaire d'être authentifié avec un compte Microsoft (UClouvain, personnel ou d'une autre organisation).
 
 [^2]: https://intranet.uclouvain.be/fr/myucl/services-informatiques/applications-disponibles.html
 
@@ -221,3 +221,100 @@ Quelques points encore à détailler :
   - fonctionnels, de performance et de qualité
   - de montée en charge du réseau et des applications, d'ergonomie
   - des fonctions de sauvegarde et de reprise
+
+# Analyse et conception du projet
+
+## Choix techniques
+
+L'objectif est la création d'une interface de gestion de données sous forme d'une application web service.
+
+![Simple Nextcloud achitecture](./assets/dia_nc_simple.png)
+
+### Framework
+
+Un framework est un cadre qui permet de structurer le travail de développement grâce à un ensemble d'outils, une structure et des modèles prêts-à-l'emploi.
+Étant donné l'étendue des développements à effectuer pour concevoir une application web moderne, un framework est indispensable.
+
+Django est un framework Backend Open Source développé en Python. Il a été spécialement créé pour réaliser des sites web puissants et de haut niveau. Il embarque tous les composants utiles, que ce soit la gestion de vues, l'authentification, le mapping objet-relationnel, une documentation détaillée, etc.
+Python est un avantage car c'est le langage le plus utilisé par les chercheurs en ELIC. En outre, les services IT de l'UCL utilisent également ce framework pour les nouveaux développements web.
+Une alternative solide serait Ruby on Rails (RoR). Il est le framework libre le plus populaire ces 5 dernières années, et a été conçu pour développer des applications web plus rapidement. Il permet aux développeurs de créer des fonctionnalités avec moins de code. Mais si RoR nécessite peu de configuration, il exige aussi plus de conventions. En outre, le niveau d'expertise pour se lancer est une barrière à l'entrée pour les débutants. Enfin Ruby nécessite des ressources serveur plus importantes que Django et sa technologie comme son utilisation sont en déclin.
+
+![Improved Nextcloud achitecture](./assets/dia_nc_pelican.png){width=80%}
+
+Python/Django sera préféré pour la conception du projet.
+C'est un framework Full-Stack - il est très facile de combiner Django et Angular par exemple - et tout clé en main : modèles, côté serveur, panneau d'administration pour configurer un site sans coder, etc. Il utilise, comme souhaité, le patron de conception modèle-vue-contrôleur (MVC), c'est à dire que la structure du framework sépare les données (models) qui sont séparées des traitements (controller) qui sont eux-mêmes séparés de la vue (view/template). C'est également un outil idéal pour un projet collaboratif.
+Django étant très populaire auprès des développeurs web, de nombreux projets sont apparus autour du framework. Par exemple dans notre cas, Ncdjango est un ensemble d'outils de gestion de données et de géotraitement écrits en Python qui fonctionnent sur des données NetCDF.
+
+### Environnement
+
+Cette application sera conteneurisée. La conteneurisation logicielle permet une gestion simplifiée des dépendances: une application et toutes ses dépendances sont placées dans une seule unité. Le système hôte ne doit pas se soucier de ces dépendances.
+L'application conteneurisée est donc indépendante de l'architecture ou des ressources de l'hôte. Elle est donc plus flexible et plus facilement distribuable.
+Si cette conteneurisation apporte son lot d'avantages en développement et pour les tests de validation, son utilisation reste plus discutable dans le contexte d'une mise en production. Nous en rediscuterons plus en avant dans ce projet.
+
+Docker est la solution de conteneurisation la plus utilisée aujourd’hui. C'est un logiciel libre qui utilise une interface de programmation « Libcontainer » pour démarrer, gérer et arrêter les conteneurs. Il est basée sur le fonctionnement de LXC et y ajoute des capacités de niveau supérieur. Les conteneurs Docker peuvent servir d’images à d'autres conteneurs et le partage de conteneurs en public est possible via un service en ligne appelé Docker Hub. Il contient des images de conteneurs, ce qui permet aux utilisateurs de faire des échanges. Cela rend l’installation d’un conteneur extrêmement facile. 
+
+### Outil de développement
+
+PyCharm est un environnement de développement intégré utilisé pour développer en Python ainsi qu'avec Django. Il propose la possibilité de débugger en direct dans un conteneur Docker.
+Vagrant est un logiciel libre et open-source pour la création et la configuration des environnements de développement virtuel. Il peut être considéré comme un wrapper autour de logiciels de virtualisation comme VirtualBox.
+
+## Méthodologie
+
+L'application sera donc standardisée MVC, c'est-à-dire selon une architecture classique à trois couches.
+La couche vue sera développée très simplement sur base de templates existants à l'UCL.
+Les couches traitement et modèle présenteront les cas de figure suivants:
+- données locales: traitement "on the fly" sur DB(s) locales 130.104
+- données distantes
+  - à posteriori (DB & protocoles connus)
+  - à priori (infos de structures à soumettre)
+- données distantes
+  - indexées: traitement "on the fly" (batch process possible sur DB distante)
+  - non-indexées: traitement différé (DB distante accessible en interactif uniquement)
+
+Scénarios pour les données à posteriori et non-indexées :
+- téléchargement tiers + demande d'intégration aux DB
+- téléchargement à travers l'appli + intégration automatique aux DB locales
+
+![Large and efficient Nextcloud achitecture](./assets/dia_nc_improved.png)
+
+![Database cluster](./assets/dia_db_cluster.png)
+
+Comme nous souhaitons mettre à disposition des données pour quelles soient utilisées sur d’autres plateformes et qu'elles puissent intéragir avec d’autres données, une architecture REST ("REpresentational State Transfer") semble appropriée ici.
+L'architecture REST est plus axée sur un modèle orienté ressources (les données, dans notre cas) que sur un modèle orienté fonctions. Elle imite la façon dont le web lui-même fonctionne dans les échanges entre un client et un serveur.
+REST constitue donc une méthode d'intégration efficace puisque le service à développer ici concerne surtout la récupération de données. Aussi, plutôt que de définir toute une API (interfaces de programmation d'application) personnalisée mieux vaut utiliser un standard de manipulation des données CRUD (Create, Read, Update, Delete : créer, lire, mettre à jour, supprimer), qui "correspond" aux opérations HTTP (HyperText Transfert Protocol) GET, PUT, POST et DELETE. Ce fonctionnement ne repose pas sur la seule utilisation de ces opérateurs, mais sur une combinaison avec des URI.
+
+Le "Django REST framework" va nous permettre de créer plus facilement une API REST sur notre application Django.
+
+Un interfacage avec Amazon S3 serait un atout supplémentaire.
+
+![caption KO](./assets/openstack.png){width=30%}\ ![test backslash](./assets/openstack.png){width=30%}
+
+::: twocolumns
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a ante in mi ornare volutpat sed sit amet diam. Nullam interdum erat a augue faucibus, nec tempus tortor sagittis. Aenean imperdiet imperdiet dignissim. Nam aliquam blandit ex, sed molestie nibh feugiat ac. Morbi feugiat convallis semper. Ut et consequat purus. Fusce convallis vehicula enim in vulputate. Curabitur a augue arcu. Mauris laoreet lectus arcu, sed elementum turpis scelerisque id. Etiam porta turpis quis ipsum dictum vulputate. In ut convallis urna, at imperdiet nunc. Cras laoreet, massa lobortis gravida egestas, lacus est pellentesque arcu, imperdiet efficitur nibh dolor vel sapien. Sed accumsan condimentum diam non pellentesque.
+
+Vestibulum cursus nisi risus, sit amet consectetur massa suscipit nec. Sed condimentum, est id iaculis ornare, purus risus finibus felis, posuere congue est nibh eget dui. Maecenas orci erat, commodo auctor justo quis, vestibulum mollis ex. Vivamus sed bibendum turpis.
+
+:::
+
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
+
+\Begin{multicols}{2}
+
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+
+![caption y](./assets/openstack.png){#fig:y width=30%}
+
+\End{multicols}
+
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+
+<!-- needs: --filter pandoc-crossref -->
+
+<div id="fig:coolFig">
+![caption a](./assets/openstack.png){#fig:cfa width=30%}
+![caption b](./assets/openstack.png){#fig:cfb width=30%}
+![caption c](./assets/openstack.png){#fig:cfc width=30%}
+
+Cool figure!
+</div>

BIN
Report/Projet_brevet.pdf


+ 0 - 0
Report/background.pdf → Report/assets/background.pdf


+ 1036 - 0
Report/assets/columns.lua

@@ -0,0 +1,1036 @@
+--[[-- # Columns - multiple column support in Pandoc's markdown.
+
+This Lua filter provides support for multiple columns in
+latex and html outputs. For details, see README.md.
+
+@author Julien Dutant <julien.dutant@kcl.ac.uk>
+@copyright 2021 Julien Dutant
+@license MIT - see LICENSE file for details.
+@release 1.1
+]]
+
+-- # Internal settings
+
+-- target_formats  filter is triggered when those format are targeted
+local target_formats = {
+  "html.*",
+  "latex",
+}
+
+-- # Helper functions
+
+--- type: pandoc-friendly type function
+-- panbdoc.utils.type is only defined in Pandoc >= 2.17
+-- if it isn't, we extend Lua's type function to give the same values
+-- as pandoc.utils.type on Meta objects: Inlines, Inline, Blocks, Block,
+-- string and booleans
+-- Caution: not to be used on non-Meta Pandoc elements, the
+-- results will differ (only 'Block', 'Blocks', 'Inline', 'Inlines' in
+-- >=2.17, the .t string in <2.17).
+local utils = require('pandoc.utils')
+local type = utils.type or function (obj)
+        local tag = type(obj) == 'table' and obj.t and obj.t:gsub('^Meta', '')
+        return tag and tag ~= 'Map' and tag or type(obj)
+    end
+
+--- Test whether the target format is in a given list.
+-- @param formats list of formats to be matched
+-- @return true if match, false otherwise
+local function format_matches(formats)
+  for _,format in pairs(formats) do
+    if FORMAT:match(format) then
+      return true
+    end
+  end
+  return false
+end
+
+
+--- Add a block to the document's header-includes meta-data field.
+-- @param meta the document's metadata block
+-- @param block Pandoc block element (e.g. RawBlock or Para) to be added to header-includes
+-- @return meta the modified metadata block
+local function add_header_includes(meta, block)
+
+    local header_includes = pandoc.List:new()
+
+    -- use meta['header-includes']
+
+    if meta['header-includes'] then
+      if type(meta['header-includes']) ==  'List' then
+        header_includes:extend(meta['header-includes'])
+      else
+        header_includes:insert(meta['header-includes'])
+      end
+    end
+
+    -- insert `block` in header-includes
+
+    header_includes:insert(pandoc.MetaBlocks({block}))
+
+    -- save header-includes in the document's meta
+
+    meta['header-includes'] = header_includes
+
+    return meta
+end
+
+--- Add a class to an element.
+-- @param element Pandoc AST element
+-- @param class name of the class to be added (string)
+-- @return the modified element, or the unmodified element if the element has no classes
+local function add_class(element, class)
+
+  -- act only if the element has classes
+  if element.attr and element.attr.classes then
+
+    -- if the class is absent, add it
+    if not element.attr.classes:includes(class) then
+      element.attr.classes:insert(class)
+    end
+
+  end
+
+  return element
+end
+
+--- Removes a class from an element.
+-- @param element Pandoc AST element
+-- @param class name of the class to be removed (string)
+-- @return the modified element, or the unmodified element if the element has no classes
+local function remove_class(element, class)
+
+  -- act only if the element has classes
+  if element.attr and element.attr.classes then
+
+    -- if the class is present, remove it
+    if element.attr.classes:includes(class) then
+      element.attr.classes = element.attr.classes:filter(
+        function(x)
+          return not (x == class)
+        end
+        )
+    end
+
+  end
+
+  return element
+end
+
+--- Set the value of an element's attribute.
+-- @param element Pandoc AST element to be modified
+-- @param key name of the attribute to be set (string)
+-- @param value value to be set. If nil, the attribute is removed.
+-- @return the modified element, or the element if it's not an element with attributes.
+local function set_attribute(element,key,value)
+
+  -- act only if the element has attributes
+  if element.attr and element.attr.attributes then
+
+    -- if `value` is `nil`, remove the attribute
+    if value == nil then
+      if element.attr.attributes[key] then
+       element.attr.attributes[key] = nil
+     end
+
+    -- otherwise set its value
+    else
+      element.attr.attributes[key] = value
+    end
+
+  end
+
+  return element
+end
+
+--- Add html style markup to an element's attributes.
+-- @param element the Pandoc AST element to be modified
+-- @param style the style markup to add (string in CSS)
+-- @return the modified element, or the unmodified element if it's an element without attributes
+local function add_to_html_style(element, style)
+
+  -- act only if the element has attributes
+  if element.attr and element.attr.attributes then
+
+    -- if the element has style markup, append
+    if element.attr.attributes['style'] then
+
+      element.attr.attributes['style'] =
+        element.attr.attributes['style'] .. '; ' .. style .. ' ;'
+
+    -- otherwise create
+    else
+
+      element.attr.attributes['style'] = style .. ' ;'
+
+    end
+
+  end
+
+  return element
+
+end
+
+--- Translate an English number name into a number.
+-- Converts cardinals ("one") and numerals ("first").
+-- Returns nil if the name isn't understood.
+-- @param name an English number name (string)
+-- @return number or nil
+local function number_by_name(name)
+
+  local names = {
+    one = 1,
+    two = 2,
+    three = 3,
+    four = 4,
+    five = 5,
+    six = 6,
+    seven = 7,
+    eight = 8,
+    nine = 9,
+    ten = 10,
+    first = 1,
+    second = 2,
+    third = 3,
+    fourth = 4,
+    fifth = 5,
+    sixth = 6,
+    seventh = 7,
+    eighth = 8,
+    ninth = 9,
+    tenth = 10,
+  }
+
+  result = nil
+
+  if name and names[name] then
+      return names[name]
+  end
+
+end
+
+--- Convert some CSS values (lengths, colous) to LaTeX equivalents.
+-- Example usage: `css_values_to_latex("1px solid black")` returns
+-- `{ length = "1pt", color = "black", colour = "black"}`.
+-- @param css_str a CSS string specifying a value
+-- @return table with keys `length`, `color` (alias `colour`) if found
+local function css_values_to_latex(css_str)
+
+  -- color conversion table
+  --  keys are CSS values, values are LaTeX equivalents
+
+  latex_colors = {
+    -- xcolor always available
+    black = 'black',
+    blue = 'blue',
+    brown = 'brown',
+    cyan = 'cyan',
+    darkgray = 'darkgray',
+    gray = 'gray',
+    green = 'green',
+    lightgray = 'lightgray',
+    lime = 'lime',
+    magenta = 'magenta',
+    olive = 'olive',
+    orange = 'orange',
+    pink = 'pink',
+    purple = 'purple',
+    red = 'red',
+    teal = 'teal',
+    violet = 'violet',
+    white = 'white',
+    yellow = 'yellow',
+    -- css1 colors
+    silver = 'lightgray',
+    fuschia = 'magenta',
+    aqua = 'cyan',
+  }
+
+  local result = {}
+
+  -- look for color values
+  --  by color name
+  --  rgb, etc.: to be added
+
+  local color = ''
+
+  -- space in front simplifies pattern matching
+  css_str = ' ' .. css_str
+
+  -- look for colour names
+  for text in string.gmatch(css_str, '[%s](%a+)') do
+
+    -- if we have LaTeX equivalent of `text`, store it
+    if latex_colors[text] then
+      result['color'] = latex_colors[text]
+    end
+
+  end
+
+  -- provide British spelling
+
+  if result['color'] then
+    result['colour'] = result['color']
+  end
+
+  -- look for lengths
+
+  --  0 : converted to 0em
+  if string.find(css_str, '%s0%s') then
+   result['length'] = '0em'
+  end
+
+  --  px : converted to pt
+  for text in string.gmatch(css_str, '(%s%d+)px') do
+   result['length'] = text .. 'pt'
+  end
+
+  -- lengths units to be kept as is
+  --  nb, % must be escaped
+  --  nb, if several found, the latest type is preserved
+  keep_units = { '%%', 'pt', 'mm', 'cm', 'in', 'ex', 'em' }
+
+  for _,unit in pairs(keep_units) do
+
+    -- .11em format
+    for text in string.gmatch(css_str, '%s%.%d+'.. unit) do
+      result['length'] = text
+    end
+
+    -- 2em and 1.2em format
+    for text in string.gmatch(css_str, '%s%d+%.?%d*'.. unit) do
+      result['length'] = text
+    end
+
+  end
+
+  return result
+
+end
+
+--- Ensures that a string specifies a LaTeX length
+-- @param text text to be checked
+-- @return text if it is a LaTeX length, `nil` otherwise
+local function ensures_latex_length(text)
+
+  -- LaTeX lengths units
+  --  nb, % must be escaped in lua patterns
+  units = { '%%', 'pt', 'mm', 'cm', 'in', 'ex', 'em' }
+
+  local result = nil
+
+  -- ignore spaces, controls and punctuation other than
+  -- dot, plus, minus
+  text = string.gsub(text, "[%s%c,;%(%)%[%]%*%?%%%^%$]+", "")
+
+  for _,unit in pairs(units) do
+
+    -- match .11em format and 1.2em format
+    if string.match(text, '^%.%d+'.. unit .. '$') or
+      string.match(text, '^%d+%.?%d*'.. unit .. '$') then
+
+      result = text
+
+    end
+
+  end
+
+  return result
+end
+
+
+-- # Filter-specific functions
+
+--- Process the metadata block.
+-- Adds any needed material to the document's metadata block.
+-- @param meta the document's metadata element
+local function process_meta(meta)
+
+  -- in LaTeX, require the `multicols` package
+  if FORMAT:match('latex') then
+
+    return add_header_includes(meta,
+      pandoc.RawBlock('latex', '\\usepackage{multicol}\n'))
+
+  end
+
+  -- in html, ensure that the first element of `columns` div
+  -- has a top margin of zero (otherwise we get white space
+  -- on the top of the first column)
+  -- idem for the first element after a `column-span` element
+  if FORMAT:match('html.*') then
+
+    html_header = [[
+<style>
+/* Styles added by the columns.lua pandoc filter */
+  .columns :first-child {margin-top: 0;}
+  .column-span + * {margin-top: 0;}
+</style>
+]]
+
+    return add_header_includes(meta, pandoc.RawBlock('html', html_header))
+
+  end
+
+  return meta
+
+end
+
+--- Convert explicit columnbreaks.
+-- This function converts any explict columnbreak markup in an element
+-- into a single syntax: a Div with class `columnbreak`.
+-- Note: if there are `column` Divs in the element we keep them
+-- in case they harbour further formatting (e.g. html classes). However
+-- we remove their `column` class to avoid double-processing when
+-- column fields are nested.
+-- @param elem Pandoc native Div element
+-- @return elem modified as needed
+local function convert_explicit_columbreaks(elem)
+
+  -- if `elem` ends with a `column` Div, this last Div should
+  -- not generate a columnbreak. We tag it to make sure we don't convert it.
+
+  if elem.content[#elem.content] and elem.content[#elem.content].classes
+    and elem.content[#elem.content].classes:includes('column') then
+
+    elem.content[#elem.content] =
+      add_class(elem.content[#elem.content], 'column-div-in-last-position')
+
+  end
+
+  -- processes `column` Divs and `\columnbreak` LaTeX RawBlocks
+  filter = {
+
+    Div = function (el)
+
+      -- syntactic sugar: `column-break` converted to `columnbreak`
+      if el.classes:includes("column-break") then
+
+        el = add_class(el,"columnbreak")
+        el = remove_class(el,"column-break")
+
+      end
+
+      if el.classes:includes("column") then
+
+        -- with `column` Div, add a break if it's not in last position
+        if not el.classes:includes('column-div-in-last-position') then
+
+          local breaking_div = pandoc.Div({})
+          breaking_div = add_class(breaking_div, "columnbreak")
+
+          el.content:insert(breaking_div)
+
+        -- if it's in the last position, remove the custom tag
+        else
+
+          el = remove_class(el, 'column-div-in-last-position')
+
+        end
+
+        -- remove `column` classes, but leave the div and other
+        -- attributes the user might have added
+        el = remove_class(el, 'column')
+
+      end
+
+      return el
+    end,
+
+    RawBlock = function (el)
+      if el.format == "tex" and el.text == '\\columnbreak' then
+
+        local breaking_div = pandoc.Div({})
+        breaking_div = add_class(breaking_div, "columnbreak")
+
+        return breaking_div
+
+      else
+
+        return el
+
+      end
+
+    end
+
+  }
+
+  return pandoc.walk_block(elem, filter)
+
+end
+
+--- Tag an element with the number of explicit columnbreaks it contains.
+-- Counts the number of epxlicit columnbreaks contained in an element and
+-- tags the element with a `number_explicit_columnbreaks` attribute.
+-- In the process columnbreaks are tagged with the class `columnbreak_already_counted`
+-- in order to avoid double-counting when multi-columns are nested.
+-- @param elem Pandoc element (native Div element of class `columns`)
+-- @return elem with the attribute `number_explicit_columnbreaks` set.
+local function tag_with_number_of_explicit_columnbreaks(elem)
+
+  local number_columnbreaks = 0
+
+  local filter = {
+
+    Div = function(el)
+
+      if el.classes:includes('columnbreak') and
+        not el.classes:includes('columnbreak_already_counted')  then
+
+          number_columnbreaks = number_columnbreaks + 1
+          el = add_class(el, 'columnbreak_already_counted')
+
+      end
+
+      return el
+
+    end
+  }
+
+  elem = pandoc.walk_block(elem, filter)
+
+  elem = set_attribute(elem, 'number_explicit_columnbreaks',
+      number_columnbreaks)
+
+  return elem
+
+end
+
+--- Consolidate aliases for column attributes.
+-- Provides syntacic sugar: unifies various ways of
+-- specifying attributes of a multi-column environment.
+-- When several specifications conflit, favours `column-gap` and
+-- `column-rule` specifications.
+-- @param elem Pandoc element (Div of class `columns`) with column attributes.
+-- @return elem modified as needed.
+local function consolidate_colattrib_aliases(elem)
+
+  if elem.attr and elem.attr.attributes then
+
+    -- `column-gap` if the preferred syntax is set, erase others
+    if elem.attr.attributes["column-gap"] then
+
+      elem = set_attribute(elem, "columngap", nil)
+      elem = set_attribute(elem, "column-sep", nil)
+      elem = set_attribute(elem, "columnsep", nil)
+
+    -- otherwise fetch and unset any alias
+    else
+
+      if elem.attr.attributes["columnsep"] then
+
+        elem = set_attribute(elem, "column-gap",
+            elem.attr.attributes["columnsep"])
+        elem = set_attribute(elem, "columnsep", nil)
+
+      end
+
+      if elem.attr.attributes["column-sep"] then
+
+        elem = set_attribute(elem, "column-gap",
+            elem.attr.attributes["column-sep"])
+        elem = set_attribute(elem, "column-sep", nil)
+
+      end
+
+      if elem.attr.attributes["columngap"] then
+
+        elem = set_attribute(elem, "column-gap",
+            elem.attr.attributes["columngap"])
+        elem = set_attribute(elem, "columngap", nil)
+
+      end
+
+    end
+
+    -- `column-rule` if the preferred syntax is set, erase others
+    if elem.attr.attributes["column-rule"] then
+
+      elem = set_attribute(elem, "columnrule", nil)
+
+    -- otherwise fetch and unset any alias
+    else
+
+      if elem.attr.attributes["columnrule"] then
+
+        elem = set_attribute(elem, "column-rule",
+            elem.attr.attributes["columnrule"])
+        elem = set_attribute(elem, "columnrule", nil)
+
+      end
+
+    end
+
+  end
+
+  return elem
+
+end
+
+--- Pre-process a Div of class `columns`.
+-- Converts explicit column breaks into a unified syntax
+-- and count the Div's number of columns.
+-- When several columns are nested Pandoc will apply
+-- this filter to the innermost `columns` Div first;
+-- we use that feature to prevent double-counting.
+-- @param elem Pandoc element to be processes (Div of class `columns`)
+-- @return elem modified as needed
+local function preprocess_columns(elem)
+
+  -- convert any explicit column syntax in a single format:
+  -- native Divs with class `columnbreak`
+
+  elem = convert_explicit_columbreaks(elem)
+
+  -- count explicit columnbreaks
+
+  elem = tag_with_number_of_explicit_columnbreaks(elem)
+
+  return elem
+end
+
+--- Determine the number of column in a `columns` Div.
+-- Looks up two attributes in the Div: the user-specified
+-- `columns-count` and the filter-generated `number_explicit_columnbreaks`
+-- which is based on the number of explicit breaks specified.
+-- The final number of columns will be 2 or whichever of `column-count` and
+-- `number_explicit_columnbreaks` is the highest. This ensures there are
+-- enough columns for all explicit columnbreaks.
+-- This provides a single-column when the user specifies `column-count = 1` and
+-- there are no explicit columnbreaks.
+-- @param elem Pandoc element (Div of class `columns`) whose number of columns is to be determined.
+-- @return number of columns (number, default 2).
+local function determine_column_count(elem)
+
+    -- is there a specified column count?
+  local specified_column_count = 0
+  if elem.attr.attributes and elem.attr.attributes['column-count'] then
+      specified_column_count = tonumber(
+        elem.attr.attributes["column-count"])
+  end
+
+  -- is there an count of explicit columnbreaks?
+  local number_explicit_columnbreaks = 0
+  if elem.attr.attributes and elem.attr.attributes['number_explicit_columnbreaks'] then
+
+      number_explicit_columnbreaks = tonumber(
+        elem.attr.attributes['number_explicit_columnbreaks']
+        )
+
+      set_attribute(elem, 'number_explicit_columnbreaks', nil)
+
+  end
+
+  -- determines the number of columns
+  -- default 2
+  -- recall that number of columns = nb columnbreaks + 1
+
+  local number_columns = 2
+
+  if specified_column_count > 0 or number_explicit_columnbreaks > 0 then
+
+      if (number_explicit_columnbreaks + 1) > specified_column_count then
+        number_columns = number_explicit_columnbreaks + 1
+      else
+        number_columns = specified_column_count
+      end
+
+  end
+
+  return number_columns
+
+end
+
+--- Convert a pandoc Header to a list of inlines for latex output.
+-- @param header Pandoc Header element
+-- @return list of Inline elements
+local function header_to_latex_and_inlines(header)
+
+-- @todo check if level interpretation has been shifted, e.g. section is level 2
+-- @todo we could check the Pandoc state to check whether hypertargets are required?
+
+  local latex_header = {
+    'section',
+    'subsection',
+    'subsubsection',
+    'paragraph',
+    'subparagraph',
+  }
+
+  -- create a list if the header's inlines
+  local inlines = pandoc.List:new(header.content)
+
+  -- wrap in a latex_header if available
+
+  if header.level and latex_header[header.level] then
+
+    inlines:insert(1, pandoc.RawInline('latex',
+        '\\' .. latex_header[header.level] .. '{'))
+    inlines:insert(pandoc.RawInline('latex', '}'))
+
+  end
+
+  -- wrap in a link if available
+  if header.identifier then
+
+    inlines:insert(1, pandoc.RawInline('latex',
+        '\\hypertarget{' .. header.identifier .. '}{%\n'))
+    inlines:insert(pandoc.RawInline('latex',
+        '\\label{' .. header.identifier .. '}}'))
+
+  end
+
+  return inlines
+
+end
+
+--- Format column span in LaTeX.
+-- Formats a bit of text spanning across all columns for LaTeX output.
+-- If the colspan is only one block, it is turned into an option
+-- of a new `multicol` environment. Otherwise insert it is
+-- inserted between the two `multicol` environments.
+-- @param elem Pandoc element that is supposed to span across all
+--    columns.
+-- @param number_columns number of columns in the present environment.
+-- @return a pandoc RawBlock element in LaTeX format
+local function format_colspan_latex(elem, number_columns)
+
+    local result = pandoc.List:new()
+
+    -- does the content consists of a single header?
+
+    if #elem.content == 1 and elem.content[1].t == 'Header' then
+
+      -- create a list of inlines
+      inlines = pandoc.List:new()
+      inlines:insert(pandoc.RawInline('latex',
+        "\\end{multicols}\n"))
+      inlines:insert(pandoc.RawInline('latex',
+        "\\begin{multicols}{".. number_columns .."}["))
+      inlines:extend(header_to_latex_and_inlines(elem.content[1]))
+      inlines:insert(pandoc.RawInline('latex',"]\n"))
+
+      -- insert as a Plain block
+      result:insert(pandoc.Plain(inlines))
+
+      return result
+
+    else
+
+      result:insert(pandoc.RawBlock('latex',
+        "\\end{multicols}\n"))
+      result:extend(elem.content)
+      result:insert(pandoc.RawBlock('latex',
+        "\\begin{multicols}{".. number_columns .."}"))
+      return result
+
+    end
+
+end
+
+--- Format columns for LaTeX output
+-- @param elem Pandoc element (Div of "columns" class) containing the
+--    columns to be formatted.
+-- @return elem with suitable RawBlocks in LaTeX added
+local function format_columns_latex(elem)
+
+  -- make content into a List object
+  pandoc.List:new(elem.content)
+
+  -- how many columns?
+  number_columns = determine_column_count(elem)
+
+  -- set properties and insert LaTeX environment
+  --  we wrap the entire environment in `{...}` to
+  --  ensure properties (gap, rule) don't carry
+  --  over to following columns
+
+  local latex_begin = '{'
+  local latex_end = '}'
+
+  if elem.attr.attributes then
+
+    if elem.attr.attributes["column-gap"] then
+
+      local latex_value = ensures_latex_length(
+        elem.attr.attributes["column-gap"])
+
+      if latex_value then
+
+        latex_begin = latex_begin ..
+          "\\setlength{\\columnsep}{" .. latex_value .. "}\n"
+
+      end
+
+      -- remove the `column-gap` attribute
+      elem = set_attribute(elem, "column-gap", nil)
+
+    end
+
+    if elem.attr.attributes["column-rule"] then
+
+      -- converts CSS value string to LaTeX values
+      local latex_values = css_values_to_latex(
+        elem.attr.attributes["column-rule"])
+
+      if latex_values["length"] then
+
+        latex_begin = latex_begin ..
+          "\\setlength{\\columnseprule}{" ..
+          latex_values["length"] .. "}\n"
+
+      end
+
+      if latex_values["color"] then
+
+        latex_begin = latex_begin ..
+          "\\renewcommand{\\columnseprulecolor}{\\color{" ..
+          latex_values["color"] .. "}}\n"
+
+      end
+
+
+      -- remove the `column-rule` attribute
+      elem = set_attribute(elem, "column-rule", nil)
+
+    end
+
+  end
+
+  latex_begin = latex_begin ..
+    "\\begin{multicols}{" .. number_columns .. "}\n"
+  latex_end = "\\end{multicols}\n" .. latex_end
+
+  elem.content:insert(1, pandoc.RawBlock('latex', latex_begin))
+  elem.content:insert(pandoc.RawBlock('latex', latex_end))
+
+  -- process blocks contained in `elem`
+  --  turn any explicit columnbreaks into LaTeX markup
+  --  turn `column-span` Divs into LaTeX markup
+
+  filter = {
+
+    Div = function(el)
+
+      if el.classes:includes("columnbreak") then
+        return pandoc.RawBlock('latex', "\\columnbreak\n")
+      end
+
+      if el.classes:includes("column-span-to-be-processed") then
+        return format_colspan_latex(el, number_columns)
+      end
+
+    end
+
+  }
+
+  elem = pandoc.walk_block(elem, filter)
+
+  return elem
+
+end
+
+
+--- Formats columns for html output.
+-- Uses CSS3 style added to the elements themselves.
+-- @param elem Pandoc element (Div of `columns` style)
+-- @return elem with suitable html attributes
+local function format_columns_html(elem)
+
+  -- how many columns?
+  number_columns = determine_column_count(elem)
+
+  -- add properties to the `columns` Div
+
+  elem = add_to_html_style(elem, 'column-count: ' .. number_columns)
+  elem = set_attribute(elem, 'column-count', nil)
+
+  if elem.attr.attributes then
+
+    if elem.attr.attributes["column-gap"] then
+
+      elem = add_to_html_style(elem, 'column-gap: ' ..
+        elem.attr.attributes["column-gap"])
+
+      -- remove the `column-gap` attribute
+      elem = set_attribute(elem, "column-gap")
+
+    end
+
+    if elem.attr.attributes["column-rule"] then
+
+      elem = add_to_html_style(elem, 'column-rule: ' ..
+        elem.attr.attributes["column-rule"])
+
+      -- remove the `column-rule` attribute
+      elem = set_attribute(elem, "column-rule", nil)
+
+    end
+
+  end
+
+  -- convert any explicit columnbreaks in CSS markup
+
+  filter = {
+
+    Div = function(el)
+
+      -- format column-breaks
+      if el.classes:includes("columnbreak") then
+
+        el = add_to_html_style(el, 'break-after: column')
+
+        -- remove columbreaks class to avoid double processing
+        -- when nested
+        -- clean up already-counted tag
+        el = remove_class(el, "columnbreak")
+        el = remove_class(el, "columnbreak_already_counted")
+
+      -- format column-spans
+      elseif el.classes:includes("column-span-to-be-processed") then
+
+        el = add_to_html_style(el, 'column-span: all')
+
+        -- remove column-span-to-be-processed class to avoid double processing
+        -- add column-span class to allow for styling
+        el = add_class(el, "column-span")
+        el = remove_class(el, "column-span-to-be-processed")
+
+      end
+
+      return el
+
+    end
+
+  }
+
+  elem = pandoc.walk_block(elem, filter)
+
+  return elem
+
+end
+
+
+-- # Main filters
+
+--- Formating filter.
+-- Applied last, converts prepared columns in target output formats
+-- @field Div looks for `columns` class
+format_filter = {
+
+  Div = function (element)
+
+    -- pick up `columns` Divs for formatting
+    if element.classes:includes ("columns") then
+
+      if FORMAT:match('latex') then
+        element = format_columns_latex(element)
+      elseif FORMAT:match('html.*') then
+        element = format_columns_html(element)
+      end
+
+      return element
+
+    end
+
+  end
+}
+
+--- Preprocessing filter.
+-- Processes meta-data fields and walks the document to pre-process
+-- columns blocks. Determine how many columns they contain, tags the
+-- last column Div, etc. Avoids double-counting when columns environments
+-- are nested.
+-- @field Div looks for `columns` class
+-- @field Meta processes the metadata block
+preprocess_filter = {
+
+  Div = function (element)
+
+      -- send `columns` Divs to pre-processing
+      if element.classes:includes("columns") then
+        return preprocess_columns(element)
+      end
+
+    end,
+
+  Meta = function (meta)
+
+    return process_meta(meta)
+
+  end
+}
+
+--- Syntactic sugar filter.
+-- Provides alternative ways of specifying columns properties.
+-- Kept separate from the pre-processing filter for clarity.
+-- @field Div looks for Div of classes `columns` (and related) and `column-span`
+syntactic_sugar_filter = {
+
+  Div = function(element)
+
+      -- convert "two-columns" into `columns` Divs
+      for _,class in pairs(element.classes) do
+
+        -- match xxxcolumns, xxx_columns, xxx-columns
+        -- if xxx is the name of a number, make
+        -- a `columns` div and set its `column-count` attribute
+        local number = number_by_name(
+          string.match(class,'(%a+)[_%-]?columns$')
+          )
+
+        if number then
+
+          element = set_attribute(element,
+              "column-count", tostring(number))
+          element = remove_class(element, class)
+          element = add_class(element, "columns")
+
+        end
+
+      end
+
+      -- allows different ways of specifying `columns` attributes
+      if element.classes:includes('columns') then
+
+        element = consolidate_colattrib_aliases(element)
+
+      end
+
+      -- `column-span` syntax
+      -- mark up as "to-be-processed" to avoid
+      --  double processing when nested
+      if element.classes:includes('column-span') or
+        element.classes:includes('columnspan') then
+
+        element = add_class(element, 'column-span-to-be-processed')
+        element = remove_class(element, 'column-span')
+        element = remove_class(element, 'columnspan')
+
+      end
+
+    return element
+
+  end
+
+}
+
+-- Main statement returns filters only if the
+-- target format matches our list. The filters
+-- returned are applied in the following order:
+-- 1. `syntatic_sugar_filter` deals with multiple syntax
+-- 2. `preprocessing_filter` converts all explicit
+--    columnbreaks into a common syntax and tags
+--    those that are already counted. We must do
+--    that for all `columns` environments before
+--    turning any break back into LaTeX `\columnbreak` blocks
+--    otherwise we mess up the count in nested `columns` Divs.
+-- 3. `format_filter` formats the columns after the counting
+--    has been done
+if format_matches(target_formats) then
+  return {syntactic_sugar_filter,
+    preprocess_filter,
+    format_filter}
+else
+  return
+end

BIN
Report/assets/dia_db_cluster.png


+ 30 - 0
Report/assets/dia_db_cluster.py

@@ -0,0 +1,30 @@
+from diagrams import Cluster, Diagram, Edge
+
+from diagrams.onprem.database import Mariadb
+from diagrams.azure.database import DatabaseForMariadbServers
+from diagrams.onprem.inmemory import Redis
+from diagrams.onprem.groupware import Nextcloud
+
+from diagrams.custom import Custom
+
+with Diagram(filename="assets/dia_db_cluster", show=False):
+    nc = Nextcloud()
+
+    with Cluster("Database cluster"):
+
+        with Cluster("Load balancer"):
+            paSQL = Custom("ProxySQL Active", "./proxysql.png")
+            ppSQL = Custom("ProxySQL Passive", "./proxysql.png")
+        
+        with Cluster("Galera cluster"):
+            bckp1 = DatabaseForMariadbServers("Backup writer")
+            primary = Mariadb("Primary writer")
+            bckp2 = DatabaseForMariadbServers("Backup writer")            
+    
+        paSQL - Edge(color="firebrick") - primary
+        bckp1 - Edge(style="dashed") - paSQL - Edge(style="dashed") - bckp2
+        
+        ppSQL - Edge(color="firebrick") - primary
+        bckp1 - Edge(style="dashed") - ppSQL - Edge(style="dashed") - bckp2
+
+    paSQL << nc >> ppSQL

BIN
Report/assets/dia_nc_improved.png


+ 54 - 0
Report/assets/dia_nc_improved.py

@@ -0,0 +1,54 @@
+from diagrams import Cluster, Diagram
+
+from diagrams.azure.database import SQLDatabases
+from diagrams.onprem.inmemory import Redis
+from diagrams.onprem.network import Nginx, Internet
+from diagrams.onprem.groupware import Nextcloud
+
+from diagrams.digitalocean.network import LoadBalancer
+
+import diagrams.generic.storage as storage_1
+
+from diagrams.azure.identity import Groups
+from diagrams.custom import Custom
+
+with Diagram(filename="assets/dia_nc_improved", show=False, direction="TB"):
+    out = Internet("Internet")
+
+    with Cluster("Load Balancing"):
+        openstack_lb = Custom("", "./openstack.png")
+        lb = [Nginx("SSL proxy 1"), Nginx("SSL proxy 2")]
+
+    with Cluster("Web servers"):
+        openstack_web = Custom("", "./openstack.png")
+        nc_frontend = [ Groups("local LDAP slave") - Nextcloud(),
+                        Groups("local LDAP slave") - Nextcloud(),
+                        Groups("local LDAP slave") - Nextcloud(),
+                        Groups("local LDAP slave") - Nextcloud()]        
+
+    with Cluster("Caching"):
+        openstack_cach = Custom("", "./openstack.png")
+        redis = [Redis("Redis 1"), Redis("Redis 2")]
+
+    with Cluster("LDAP"):
+        openstack_ldap = Custom("", "./openstack.png")
+        ldap = Groups()
+
+    with Cluster("Database cluster"):
+
+        with Cluster("Load Balancer"):
+            openstack_dblb = Custom("", "./openstack.png")
+            nc_dblb = LoadBalancer()    
+
+        with Cluster("Databases"):
+            openstack_db = Custom("", "./openstack.png")
+            SQLDatabases("Master") >> [SQLDatabases("Slave 1"),
+                      SQLDatabases("Slave 2")]
+
+    nc_frontend >> storage_1.Storage("NFS")
+    openstack_cach << nc_frontend
+    openstack_web >> openstack_dblb
+    openstack_lb >> openstack_web
+    openstack_web << ldap
+    nc_dblb >> openstack_db
+    out >> lb

BIN
Report/assets/dia_nc_pelican.png


+ 42 - 0
Report/assets/dia_nc_pelican.py

@@ -0,0 +1,42 @@
+from diagrams import Cluster, Diagram
+
+from diagrams.azure.database import DatabaseForMariadbServers
+from diagrams.onprem.inmemory import Redis
+from diagrams.onprem.container import Docker
+from diagrams.onprem.network import Nginx, Internet
+from diagrams.onprem.groupware import Nextcloud
+import diagrams.generic.storage as storage_1
+import diagrams.outscale.storage as storage_2
+from diagrams.azure.identity import Groups
+
+with Diagram(filename="assets/dia_nc_pelican", show=False):
+    out = Internet("Internet")
+    ldap = Groups("LDAP")
+    nfs = storage_1.Storage("NFS")
+
+    with Cluster("Interactive workstation"):
+        local_storage = storage_2.Storage("local storage")
+
+        with Cluster("Containers"):
+            docker = Docker("Docker")
+
+            with Cluster("SSL Proxy"):
+                proxy = Nginx("Nginx")
+
+            with Cluster("Web server"):
+                nc_frontend = Nextcloud()
+                ldap >> nc_frontend << nfs            
+
+            with Cluster("Database"):
+                nc_db = DatabaseForMariadbServers("MariaDB")
+                nc_frontend << nc_db
+
+            with Cluster("Caching"):
+                nc_redis = Redis("Redis")
+                nc_frontend << nc_redis
+
+        nc_frontend << local_storage
+      
+    out >> proxy >> nc_frontend
+    out << proxy << nc_frontend
+    

BIN
Report/assets/dia_nc_simple.png


+ 25 - 0
Report/assets/dia_nc_simple.py

@@ -0,0 +1,25 @@
+from diagrams import Cluster, Diagram
+
+from diagrams.onprem.database import Mysql
+from diagrams.onprem.network import Apache, Internet
+from diagrams.onprem.groupware import Nextcloud
+from diagrams.generic.os import LinuxGeneral
+from diagrams.programming.language import Php
+
+import diagrams.outscale.storage as storage_2
+
+with Diagram(filename="assets/dia_nc_simple", show=False):
+    out = Internet("Internet")
+
+    with Cluster("LAMP"):
+
+        with Cluster("Linux = L"):
+            linux = LinuxGeneral()
+            storage = storage_2.Storage()
+                
+            with Cluster("Nextcloud Web server = AMP"):
+                Apache() - Mysql() - Php() 
+                nc = Nextcloud()             
+
+    linux >> out >> linux >> storage
+    linux >> nc >> storage

+ 9 - 0
Report/eisvogel.latex → Report/assets/eisvogel.latex

@@ -388,6 +388,9 @@ $else$
 $endif$
 $if(graphics)$
 \usepackage{graphicx}
+\usepackage{multicol}
+\newcommand{\hideFromPandoc}[1]{#1}
+\hideFromPandoc{\let\Begin\begin\let\End\end}
 \makeatletter
 \def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
 \def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
@@ -436,6 +439,12 @@ $endif$
 $if(pagestyle)$
 \pagestyle{$pagestyle$}
 $endif$
+
+% Make use of float-package and set default placement for figures to H.
+% The option H means 'PUT IT HERE' (as  opposed to the standard h option which means 'You may put it here if you like').
+\usepackage{float}
+\floatplacement{figure}{$if(float-placement-figure)$$float-placement-figure$$else$H$endif$}
+
 $if(csl-refs)$
 \newlength{\cslhangindent}
 \setlength{\cslhangindent}{1.5em}

+ 0 - 0
Report/garde.pdf → Report/assets/garde.pdf


BIN
Report/assets/openstack.png


BIN
Report/assets/proxysql.png


+ 42 - 1
Report/compile.sh

@@ -1,2 +1,43 @@
 #!/bin/bash
-pandoc Projet_brevet.md -o Projet_brevet.pdf --from markdown --template eisvogel --listings
+#
+# PY Barriat, May 2022
+#
+# Install pandoc & pandac-crossref
+# sudo apt install ghc-9.0.1
+# https://github.com/jgm/pandoc/releases/tag/2.17.1.1
+# https://github.com/lierdakil/pandoc-crossref/releases/tag/v0.3.12.2a
+# https://github.com/jgm/pandoc-types/releases/tag/1.22.1
+#
+# Download and install diagrams:
+# sudo apt install graphviz python3-pydotplus
+# pip install diagrams
+#
+# Install npm (needed for mermaid: nice extension to make diagramm)
+# npm i
+#
+
+# with diagrams
+
+python assets/dia_nc_simple.py
+python assets/dia_nc_pelican.py
+python assets/dia_nc_improved.py
+
+# without mermaid
+
+#pandoc -s Projet_brevet.md -o Projet_brevet.pdf \
+#    --from markdown \
+#    --template assets/eisvogel \
+#    --lua-filter assets/columns.lua \
+#    --listings
+
+pandoc -s Projet_brevet.md -o Projet_brevet.pdf \
+    --from markdown \
+    --template assets/eisvogel \
+    --filter pandoc-crossref \
+    --lua-filter assets/columns.lua \
+    --listings
+
+#/opt/quarto/bin/quarto render tufte.qmd --to pdf
+
+# with mermaid
+