1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-22 14:54:22 +00:00
sophon/docs/realizzazione/index.html

1993 lines
252 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html>
<html class="writer-html5" lang="it" >
<head>
<meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>5. Realizzazione di Sophon &mdash; Progettazione e sviluppo di Sophon, applicativo cloud a supporto della ricerca</title>
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
<!--[if lt IE 9]>
<script src="../../_static/js/html5shiv.min.js"></script>
<![endif]-->
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/translations.js"></script>
<script src="../../_static/js/theme.js"></script>
<link rel="index" title="Indice analitico" href="../../genindex.html" />
<link rel="search" title="Cerca" href="../../search.html" />
<link rel="next" title="6. Risultati ottenuti" href="../risultato/index.html" />
<link rel="prev" title="4. Progettazione di Sophon" href="../progetto/index.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" style="background: #051836" >
<a href="../../index.html" class="icon icon-home"> Progettazione e sviluppo di Sophon, applicativo cloud a supporto della ricerca
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
<input type="text" name="q" placeholder="Cerca documenti" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Menu di navigazione">
<p class="caption" role="heading"><span class="caption-text">Contenuti</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../sinossi/index.html">1. Sinossi</a></li>
<li class="toctree-l1"><a class="reference internal" href="../introduzione/index.html">2. Introduzione alla tesi</a></li>
<li class="toctree-l1"><a class="reference internal" href="../ricercacollaborativa/index.html">3. Ricerca collaborativa</a></li>
<li class="toctree-l1"><a class="reference internal" href="../progetto/index.html">4. Progettazione di Sophon</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">5. Realizzazione di Sophon</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#realizzazione-del-modulo-backend">5.1. Realizzazione del modulo backend</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#module-sophon">Il project Django</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.admin">App di amministrazione personalizzata</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.settings">Caricamento dinamico delle impostazioni</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.auth1">Miglioramenti all'autenticazione</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#module-sophon.core">L'app Sophon Core</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.core.management.commands.initsuperuser">Aggiunta di un nuovo comando di gestione</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.core.models">Modello base astratto</a></li>
<li class="toctree-l4"><a class="reference internal" href="#modello-di-autorizzazione-astratto">Modello di autorizzazione astratto</a></li>
<li class="toctree-l4"><a class="reference internal" href="#modello-dei-dettagli-dell-istanza">Modello dei dettagli dell'istanza</a></li>
<li class="toctree-l4"><a class="reference internal" href="#modello-del-gruppo-di-ricerca">Modello del gruppo di ricerca</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.core.permissions">Estensione ai permessi di Django</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.core.views">Viewset astratti</a></li>
<li class="toctree-l4"><a class="reference internal" href="#viewset-concreti">Viewset concreti</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.core.admin">Pagina di amministrazione</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.core.tests">Testing in Sophon Core</a></li>
<li class="toctree-l4"><a class="reference internal" href="#test-case-generici">Test case generici</a></li>
<li class="toctree-l4"><a class="reference internal" href="#test-case-concreti">Test case concreti</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#module-sophon.projects">L'app Sophon Projects</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.projects.models">Modello del progetto di ricerca</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.projects.views">Viewset del gruppo di ricerca</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.projects.admin">Amministrazione del gruppo di ricerca</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#module-sophon.notebooks">L'app Sophon Notebooks</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#funzionamento-di-un-notebook">Funzionamento di un notebook</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.notebooks.apache">Gestione della rubrica del proxy</a></li>
<li class="toctree-l4"><a class="reference internal" href="#assegnazione-porta-effimera">Assegnazione porta effimera</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.notebooks.docker">Connessione al daemon Docker</a></li>
<li class="toctree-l4"><a class="reference internal" href="#controllo-dello-stato-di-salute">Controllo dello stato di salute</a></li>
<li class="toctree-l4"><a class="reference internal" href="#generazione-di-token-sicuri">Generazione di token sicuri</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.notebooks.models">Modello dei notebook</a></li>
<li class="toctree-l4"><a class="reference internal" href="#module-sophon.notebooks.views">Viewset dei notebook</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#containerizzazione-del-modulo-backend">Containerizzazione del modulo backend</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#realizzazione-del-modulo-frontend">5.2. Realizzazione del modulo frontend</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#struttura-delle-directory">Struttura delle directory</a></li>
<li class="toctree-l3"><a class="reference internal" href="#comunicazione-con-il-backend">Comunicazione con il backend</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#axios">Axios</a></li>
<li class="toctree-l4"><a class="reference internal" href="#client-personalizzati">Client personalizzati</a></li>
<li class="toctree-l4"><a class="reference internal" href="#utilizzo-di-viewset">Utilizzo di viewset</a></li>
<li class="toctree-l4"><a class="reference internal" href="#emulazione-di-viewset">Emulazione di viewset</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#contesti-innestati">Contesti innestati</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#i-contesti">I contesti</a></li>
<li class="toctree-l4"><a class="reference internal" href="#segmenti-di-url">Segmenti di URL</a></li>
<li class="toctree-l4"><a class="reference internal" href="#componenti-contestuali">Componenti contestuali</a></li>
<li class="toctree-l4"><a class="reference internal" href="#routing-basato-sui-contesti">Routing basato sui contesti</a></li>
<li class="toctree-l4"><a class="reference internal" href="#albero-completo-dei-contesti">Albero completo dei contesti</a></li>
<li class="toctree-l4"><a class="reference internal" href="#altri-contesti">Altri contesti</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="#containerizzazione-del-modulo-frontend">Containerizzazione del modulo frontend</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#realizzazione-del-modulo-proxy">5.3. Realizzazione del modulo proxy</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#containerizzazione-del-modulo-proxy">Containerizzazione del modulo proxy</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#realizzazione-del-modulo-jupyter">5.4. Realizzazione del modulo Jupyter</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#sviluppo-del-tema-per-jupyter">Sviluppo del tema per Jupyter</a></li>
<li class="toctree-l3"><a class="reference internal" href="#estensione-del-container-docker-di-jupyter">Estensione del container Docker di Jupyter</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#automazione-di-sviluppo">5.5. Automazione di sviluppo</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#scansione-automatica-delle-dipendenze">Scansione automatica delle dipendenze</a></li>
<li class="toctree-l3"><a class="reference internal" href="#controllo-automatico-del-codice">Controllo automatico del codice</a></li>
<li class="toctree-l3"><a class="reference internal" href="#costruzione-automatica-delle-immagini-docker">Costruzione automatica delle immagini Docker</a></li>
<li class="toctree-l3"><a class="reference internal" href="#costruzione-automatica-della-documentazione">Costruzione automatica della documentazione</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../risultato/index.html">6. Risultati ottenuti</a></li>
<li class="toctree-l1"><a class="reference internal" href="../conclusione/index.html">7. Il futuro di Sophon</a></li>
<li class="toctree-l1"><a class="reference internal" href="../bibliografia/index.html">8. Bibliografia</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Appendice</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../installazione/index.html">1. Installazione di Sophon</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Menu navigazione dispositivi mobili" style="background: #051836" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../../index.html">Progettazione e sviluppo di Sophon, applicativo cloud a supporto della ricerca</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Naviga tra le pagine">
<ul class="wy-breadcrumbs">
<li><a href="../../index.html" class="icon icon-home"></a> &raquo;</li>
<li><span class="section-number">5. </span>Realizzazione di Sophon</li>
<li class="wy-breadcrumbs-aside">
<!-- User defined GitHub URL -->
<a href="https://github.com/Steffo99/sophon/blob/main/thesis/source/5_implementazione/index.rst" class="fa fa-github"> Modifica su GitHub</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="realizzazione-di-sophon">
<span id="index-0"></span><h1><span class="section-number">5. </span>Realizzazione di Sophon<a class="headerlink" href="#realizzazione-di-sophon" title="Link a questa intestazione"></a></h1>
<p>Terminato il progetto, si è passati a realizzarne una versione funzionante su calcolatore.</p>
<section id="realizzazione-del-modulo-backend">
<span id="index-1"></span><h2><span class="section-number">5.1. </span>Realizzazione del modulo backend<a class="headerlink" href="#realizzazione-del-modulo-backend" title="Link a questa intestazione"></a></h2>
<p>Il modulo backend è stato realizzato come un package <a class="reference internal" href="../progetto/index.html#python"><span class="std std-ref">Python</span></a> denominato <code class="docutils literal notranslate"><span class="pre">sophon</span></code>, e poi <a class="reference internal" href="#containerizzazione-del-modulo-backend"><span class="std std-ref">containerizzato</span></a>, creando un'immagine <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> standalone.</p>
<section id="module-sophon">
<span id="il-project-django"></span><h3>Il project Django<a class="headerlink" href="#module-sophon" title="Link a questa intestazione"></a></h3>
<p>Il package è stato creato utilizzando l'utility <code class="docutils literal notranslate"><span class="pre">startproject</span></code> di Django, la quale crea una cartella di script <a class="reference internal" href="../progetto/index.html#python"><span class="std std-ref">Python</span></a> con i quali partire per lo sviluppo di una nuovo software web.</p>
<p>La cartella generata è stata modificata significativamente: ne si è modificata la struttura in modo tale da trasformarla da un insieme di script a un vero e proprio modulo Python eseguibile e distribuibile, e si sono aggiunte nuove funzionalità di utilità generale all'applicazione, quali una <a class="reference internal" href="#module-sophon.admin"><span class="std std-ref">app di amministrazione personalizzata</span></a>, il <a class="reference internal" href="#module-sophon.settings"><span class="std std-ref">caricamento dinamico delle impostazioni</span></a> e vari <a class="reference internal" href="#module-sophon.auth1"><span class="std std-ref">miglioramenti all'autenticazione</span></a></p>
<section id="module-sophon.admin">
<span id="app-di-amministrazione-personalizzata"></span><h4>App di amministrazione personalizzata<a class="headerlink" href="#module-sophon.admin" title="Link a questa intestazione"></a></h4>
<p>L'app di amministrazione di Django <a class="reference external" href="http://docs.djangoproject.com/en/3.2/ref/contrib/admin/#module-django.contrib.admin" title="(in Django v3.2)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">django.contrib.admin</span></code></a> viene personalizzata con la classe <a class="reference internal" href="#sophon.admin.SophonAdminSite" title="sophon.admin.SophonAdminSite"><code class="xref any py py-class docutils literal notranslate"><span class="pre">SophonAdminSite</span></code></a>, che ne modifica alcuni parametri.</p>
<p>Inoltre, il template predefinito viene sovrascritto dal file <code class="docutils literal notranslate"><span class="pre">templates/admin/base.html</span></code>, che sostituisce il foglio di stile con uno personalizzato per Sophon.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.admin.SophonAdminSite">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonAdminSite</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">django.contrib.admin.AdminSite</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.admin.SophonAdminSite" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">site_header</span> <span class="pre">=</span> <span class="pre">&quot;Sophon</span> <span class="pre">Server</span> <span class="pre">Administration&quot;</span></span></dt>
<dd><p>Il nome della pagina nell'header viene modificato a <em>Sophon Server Administration</em>.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">site_title</span> <span class="pre">=</span> <span class="pre">&quot;Sophon</span> <span class="pre">Server</span> <span class="pre">Administration&quot;</span></span></dt>
<dd><p>Il titolo della pagina nell'header viene anch'esso modificato a <em>Sophon Server Administration</em>.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">site_url</span> <span class="pre">=</span> <span class="pre">None</span></span></dt>
<dd><p>Il collegamento <em>View Site</em> viene rimosso, in quanto è possibile accedere all'interfaccia web di Sophon da più domini contemporaneamente.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">index_title</span> <span class="pre">=</span> <span class="pre">&quot;Resources</span> <span class="pre">Administration&quot;</span></span></dt>
<dd><p>Il titolo dell'indice viene modificato a <em>Resources Administration</em>.</p>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.admin.SophonAdminConfig">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonAdminConfig</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">django.contrib.admin.apps.AdminConfig</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.admin.SophonAdminConfig" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">default_site</span> <span class="pre">=</span> <span class="pre">&quot;sophon.admin.SophonAdminSite&quot;</span></span></dt>
<dd><p><a class="reference internal" href="#sophon.admin.SophonAdminSite" title="sophon.admin.SophonAdminSite"><code class="xref py py-class docutils literal notranslate"><span class="pre">SophonAdminSite</span></code></a> è selezionata come classe predefinita per il sito di amministrazione.</p>
</dd></dl>
</dd></dl>
<figure class="align-default" id="id1">
<img alt="../../_images/admin_site.png" src="../../_images/admin_site.png" />
<figcaption>
<p><span class="caption-number">Figura 5.1.1 </span><span class="caption-text">Immagine della pagina principale dell'app di amministrazione.</span><a class="headerlink" href="#id1" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
<section id="module-sophon.settings">
<span id="caricamento-dinamico-delle-impostazioni"></span><h4>Caricamento dinamico delle impostazioni<a class="headerlink" href="#module-sophon.settings" title="Link a questa intestazione"></a></h4>
<p>Il file di impostazioni viene modificato per <strong>permettere la configurazione attraverso variabili di ambiente</strong> invece che attraverso la modifica del file <code class="docutils literal notranslate"><span class="pre">settings.py</span></code>, rendendo la <a class="reference internal" href="#containerizzazione-del-modulo-backend"><span class="std std-ref">containerizzazione</span></a> molto più semplice.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="n">DATABASE_ENGINE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;DJANGO_DATABASE_ENGINE&quot;</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">log</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">&quot;DJANGO_DATABASE_ENGINE was not set, defaulting to PostgreSQL&quot;</span><span class="p">)</span>
<span class="n">DATABASE_ENGINE</span> <span class="o">=</span> <span class="s2">&quot;django.db.backends.postgresql&quot;</span>
<span class="n">log</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">DATABASE_ENGINE</span> <span class="si">= }</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Inoltre, viene configurato il modulo <a class="reference external" href="https://docs.python.org/3.8/library/logging.html#module-logging" title="(in Python v3.8)"><code class="xref any docutils literal notranslate"><span class="pre">logging</span></code></a> per emettere testo colorato di più facile comprensione usando il package <a class="reference external" href="https://coloredlogs.readthedocs.io/en/latest/api.html#module-coloredlogs" title="(in coloredlogs v15.0)"><code class="xref any docutils literal notranslate"><span class="pre">coloredlogs</span></code></a>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="s2">&quot;detail&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;()&quot;</span><span class="p">:</span> <span class="n">coloredlogs</span><span class="o">.</span><span class="n">ColoredFormatter</span><span class="p">,</span>
<span class="s2">&quot;format&quot;</span><span class="p">:</span> <span class="s2">&quot;</span><span class="si">{asctime:&gt;19}</span><span class="s2"> | </span><span class="si">{name:&lt;24}</span><span class="s2"> | </span><span class="si">{levelname:&gt;8}</span><span class="s2"> | </span><span class="si">{message}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="s2">&quot;style&quot;</span><span class="p">:</span> <span class="s2">&quot;{&quot;</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Una lista di tutte le variabili di ambiente di configurazione è riportata nel capitolo <a class="reference internal" href="../installazione/index.html#installazione-di-sophon"><span class="std std-ref">Installazione di Sophon</span></a>.</p>
</section>
<section id="module-sophon.auth1">
<span id="miglioramenti-all-autenticazione"></span><h4>Miglioramenti all'autenticazione<a class="headerlink" href="#module-sophon.auth1" title="Link a questa intestazione"></a></h4>
<p>La classe <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.authentication.TokenAuthentication</span></code> viene modificata per ottenere un comportamento conforme allo standard della Bearer authentication.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.auth1.BearerTokenAuthentication">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">BearerTokenAuthentication</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">rest_framework.authentication.TokenAuthentication</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.auth1.BearerTokenAuthentication" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">keyword</span> <span class="pre">=</span> <span class="pre">&quot;Bearer&quot;</span></span></dt>
<dd><p>Si configura <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> per accettare header di autenticazione nella forma <code class="docutils literal notranslate"><span class="pre">Bearer</span> <span class="pre">&lt;token&gt;</span></code>, invece che il default di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> <code class="docutils literal notranslate"><span class="pre">Token</span> <span class="pre">&lt;token&gt;</span></code>.</p>
</dd></dl>
</dd></dl>
<span class="target" id="module-sophon.auth2"></span><p>La view <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.authtoken.views.ObtainAuthToken</span></code> viene estesa per aggiungere dati alla risposta di autenticazione riuscita.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.auth2.CustomObtainAuthToken">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">CustomObtainAuthToken</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">rest_framework.authtoken.views.ObtainAuthToken</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.auth2.CustomObtainAuthToken" title="Link a questa definizione"></a></dt>
<dd><dl class="py method">
<dt class="sig sig-object py" id="sophon.auth2.CustomObtainAuthToken.post">
<span class="sig-name descname"><span class="pre">post</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.auth2.CustomObtainAuthToken.post" title="Link a questa definizione"></a></dt>
<dd><p>In particolare, viene aggiunta una chiave <code class="docutils literal notranslate"><span class="pre">user</span></code>, che contiene i dettagli sull'utente che ha effettuato il login.</p>
</dd></dl>
</dd></dl>
</section>
</section>
<section id="module-sophon.core">
<span id="l-app-sophon-core"></span><h3>L'app Sophon Core<a class="headerlink" href="#module-sophon.core" title="Link a questa intestazione"></a></h3>
<p>L'app <a class="reference internal" href="#module-sophon.core" title="sophon.core"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.core</span></code></a> è l'app principale del progetto, e non può essere disattivata, in quanto dipendenza obbligatoria di tutte le altre app.</p>
<section id="module-sophon.core.management.commands.initsuperuser">
<span id="aggiunta-di-un-nuovo-comando-di-gestione"></span><h4>Aggiunta di un nuovo comando di gestione<a class="headerlink" href="#module-sophon.core.management.commands.initsuperuser" title="Link a questa intestazione"></a></h4>
<p>Per permettere l'integrazione la creazione automatica del primo superutente quando Sophon viene eseguito da Docker, viene introdotto dall'app il comando di gestione <code class="docutils literal notranslate"><span class="pre">initsuperuser</span></code>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.management.commands.initsuperuser.Command">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">Command</span></span><a class="headerlink" href="#sophon.core.management.commands.initsuperuser.Command" title="Link a questa definizione"></a></dt>
<dd><p>Questo comando crea automaticamente un superutente con le credenziali specificate in <span class="target" id="index-2"></span><a class="reference internal" href="../installazione/4_configuring_compose.html#envvar-DJANGO_SU_USERNAME"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">DJANGO_SU_USERNAME</span></code></a>, <span class="target" id="index-3"></span><a class="reference internal" href="../installazione/4_configuring_compose.html#envvar-DJANGO_SU_EMAIL"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">DJANGO_SU_EMAIL</span></code></a> e <span class="target" id="index-4"></span><a class="reference internal" href="../installazione/4_configuring_compose.html#envvar-DJANGO_SU_PASSWORD"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">DJANGO_SU_PASSWORD</span></code></a>.</p>
</dd></dl>
</section>
<section id="module-sophon.core.models">
<span id="modello-base-astratto"></span><h4>Modello base astratto<a class="headerlink" href="#module-sophon.core.models" title="Link a questa intestazione"></a></h4>
<p>Viene estesa la classe astratta <a class="reference external" href="http://docs.djangoproject.com/en/3.2/ref/models/instances/#django.db.models.Model" title="(in Django v3.2)"><code class="xref py py-class docutils literal notranslate"><span class="pre">django.db.models.Model</span></code></a> con funzioni per stabilire il <a class="reference internal" href="../progetto/index.html#livelli-di-accesso"><span class="std std-ref">livello di accesso</span></a> di un <a class="reference internal" href="../progetto/index.html#utenti-in-sophon"><span class="std std-ref">utente</span></a> all'oggetto e per generare automaticamente i <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.serializers.ModelSerializer</span></code> in base ad esso.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonModel</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">django.db.models.Model</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.models.SophonModel" title="Link a questa definizione"></a></dt>
<dd><dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel.can_edit">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">can_edit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">user</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="http://docs.djangoproject.com/en/3.2/ref/contrib/auth/#django.contrib.auth.models.User" title="(in Django v3.2)"><span class="pre">django.contrib.auth.models.User</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#bool" title="(in Python v3.8)"><span class="pre">bool</span></a></span></span><a class="headerlink" href="#sophon.core.models.SophonModel.can_edit" title="Link a questa definizione"></a></dt>
<dd><p>Controlla se un utente può modificare l'oggetto attuale.</p>
<dl class="field-list simple">
<dt class="field-odd">Parametri</dt>
<dd class="field-odd"><p><strong>user</strong> -- L'utente da controllare.</p>
</dd>
<dt class="field-even">Ritorna</dt>
<dd class="field-even"><p><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#True" title="(in Python v3.8)"><code class="xref py py-data docutils literal notranslate"><span class="pre">True</span></code></a> se l'utente deve poter modificare l'oggetto, altrimenti <a class="reference external" href="https://docs.python.org/3.8/library/constants.html#False" title="(in Python v3.8)"><code class="xref py py-data docutils literal notranslate"><span class="pre">False</span></code></a>.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel.can_admin">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">can_admin</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">user</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="http://docs.djangoproject.com/en/3.2/ref/contrib/auth/#django.contrib.auth.models.User" title="(in Django v3.2)"><span class="pre">django.contrib.auth.models.User</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#bool" title="(in Python v3.8)"><span class="pre">bool</span></a></span></span><a class="headerlink" href="#sophon.core.models.SophonModel.can_admin" title="Link a questa definizione"></a></dt>
<dd><p>Controlla se un utente può amministrare l'oggetto attuale.</p>
<dl class="field-list simple">
<dt class="field-odd">Parametri</dt>
<dd class="field-odd"><p><strong>user</strong> -- L'utente da controllare.</p>
</dd>
<dt class="field-even">Ritorna</dt>
<dd class="field-even"><p><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#True" title="(in Python v3.8)"><code class="xref py py-data docutils literal notranslate"><span class="pre">True</span></code></a> se l'utente deve poter amministrare l'oggetto, altrimenti <a class="reference external" href="https://docs.python.org/3.8/library/constants.html#False" title="(in Python v3.8)"><code class="xref py py-data docutils literal notranslate"><span class="pre">False</span></code></a>.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel.get_fields">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_fields</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><span class="pre">set</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.models.SophonModel.get_fields" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>il <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">set</span></code></a> di nomi di campi che devono essere mostrati quando viene richiesto l'oggetto attraverso l'API.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel.get_editable_fields">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_editable_fields</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><span class="pre">set</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.models.SophonModel.get_editable_fields" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>il <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">set</span></code></a> di nomi di campi di cui deve essere permessa la modifica se l'utente può modificare (<a class="reference internal" href="#sophon.core.models.SophonModel.can_edit" title="sophon.core.models.SophonModel.can_edit"><code class="xref py py-meth docutils literal notranslate"><span class="pre">can_edit()</span></code></a>) l'oggetto.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel.get_administrable_fields">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_administrable_fields</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><span class="pre">set</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.models.SophonModel.get_administrable_fields" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>il <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">set</span></code></a> di nomi di campi di cui deve essere permessa la modifica se l'utente può amministrare (<a class="reference internal" href="#sophon.core.models.SophonModel.can_admin" title="sophon.core.models.SophonModel.can_admin"><code class="xref py py-meth docutils literal notranslate"><span class="pre">can_admin()</span></code></a>) l'oggetto.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonModel.get_creation_fields">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_creation_fields</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><span class="pre">set</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.models.SophonModel.get_creation_fields" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>il <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#set" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">set</span></code></a> di nomi di campi che possono essere specificati dall'utente al momento della creazione dell'oggetto.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<section id="modello-di-autorizzazione-astratto">
<h4>Modello di autorizzazione astratto<a class="headerlink" href="#modello-di-autorizzazione-astratto" title="Link a questa intestazione"></a></h4>
<p>Viene definito un nuovo modello astratto, basato su <a class="reference internal" href="#sophon.core.models.SophonModel" title="sophon.core.models.SophonModel"><code class="xref any py py-class docutils literal notranslate"><span class="pre">SophonModel</span></code></a>, che permette di determinare i permessi dell'<a class="reference internal" href="../progetto/index.html#utenti-in-sophon"><span class="std std-ref">utente</span></a> in base alla sua appartenenza al gruppo a cui è collegato l'oggetto implementatore.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.models.SophonGroupModel">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonGroupModel</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonModel</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.models.SophonGroupModel" title="Link a questa definizione"></a></dt>
<dd><dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonGroupModel.get_group">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_group</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#sophon.core.models.ResearchGroup" title="sophon.core.models.ResearchGroup"><span class="pre">ResearchGroup</span></a></span></span><a class="headerlink" href="#sophon.core.models.SophonGroupModel.get_group" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>Il gruppo a cui appartiene l'oggetto.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonGroupModel.get_access_to_edit">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_access_to_edit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#sophon.core.models.sophon.core.enums.SophonGroupAccess" title="sophon.core.models.sophon.core.enums.SophonGroupAccess"><span class="pre">sophon.core.enums.SophonGroupAccess</span></a></span></span><a class="headerlink" href="#sophon.core.models.SophonGroupModel.get_access_to_edit" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>Il livello di autorità all'interno del gruppo necessario per modificare l'oggetto.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonGroupModel.get_access_to_admin">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_access_to_admin</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#sophon.core.models.sophon.core.enums.SophonGroupAccess" title="sophon.core.models.sophon.core.enums.SophonGroupAccess"><span class="pre">sophon.core.enums.SophonGroupAccess</span></a></span></span><a class="headerlink" href="#sophon.core.models.SophonGroupModel.get_access_to_admin" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>Il livello di autorità all'interno del gruppo necessario per amministrare l'oggetto.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.models.SophonGroupModel.get_access_serializer">
<span class="sig-name descname"><span class="pre">get_access_serializer</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">user</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">User</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/typing.html#typing.Type" title="(in Python v3.8)"><span class="pre">typing.Type</span></a><span class="p"><span class="pre">[</span></span><span class="pre">rest_framework.serializers.ModelSerializer</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.models.SophonGroupModel.get_access_serializer" title="Link a questa definizione"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>Restituisce il <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.serializers.ModelSerializer</span></code> adeguato al livello di autorità dell'utente.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.models.sophon.core.enums.SophonGroupAccess">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">sophon.core.enums.</span></span><span class="sig-name descname"><span class="pre">SophonGroupAccess</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">enum.IntEnum</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.models.sophon.core.enums.SophonGroupAccess" title="Link a questa definizione"></a></dt>
<dd><p>Enumerazione che stabilisce il livello di autorità che un <a class="reference internal" href="../progetto/index.html#utenti-in-sophon"><span class="std std-ref">utente</span></a> può avere all'interno di un <a class="reference internal" href="../progetto/index.html#gruppi-di-ricerca-in-sophon"><span class="std std-ref">gruppo di ricerca</span></a>.</p>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">NONE</span> <span class="pre">=</span> <span class="pre">0</span></span></dt>
<dd><p>Ospite.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">REGISTERED</span> <span class="pre">=</span> <span class="pre">10</span></span></dt>
<dd><p>Utente registrato.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">MEMBER</span> <span class="pre">=</span> <span class="pre">50</span></span></dt>
<dd><p>Membro del gruppo al quale appartiene l'oggetto.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">OWNER</span> <span class="pre">=</span> <span class="pre">100</span></span></dt>
<dd><p>Creatore del gruppo al quale appartiene l'oggetto.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">SUPERUSER</span> <span class="pre">=</span> <span class="pre">200</span></span></dt>
<dd><p>Superutente con privilegi universali.</p>
</dd></dl>
</dd></dl>
</section>
<section id="modello-dei-dettagli-dell-istanza">
<h4>Modello dei dettagli dell'istanza<a class="headerlink" href="#modello-dei-dettagli-dell-istanza" title="Link a questa intestazione"></a></h4>
<p>Viene creato il modello che rappresenta i dettagli dell'<a class="reference internal" href="../progetto/index.html#istanza-in-sophon"><span class="std std-ref">istanza di Sophon</span></a>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.models.SophonInstanceDetails">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonInstanceDetails</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonModel</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.models.SophonInstanceDetails" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">id:</span> <span class="pre">IntegerField</span> <span class="pre">[1]</span></span></dt>
<dd><p>Impostando <code class="docutils literal notranslate"><span class="pre">1</span></code> come unica scelta per il campo della chiave primaria <code class="docutils literal notranslate"><span class="pre">id</span></code>, si crea un modello &quot;singleton&quot;, ovvero un modello di cui può esistere un'istanza sola in tutto il database.</p>
<p>L'istanza unica viene creata dalla migrazione <code class="docutils literal notranslate"><span class="pre">0004_sophoninstancedetails.py</span></code>.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">name:</span> <span class="pre">CharField</span></span></dt>
<dd><p>Il titolo dell'istanza Sophon.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">description:</span> <span class="pre">TextField</span></span></dt>
<dd><p>La descrizione dell'istanza Sophon, da visualizzare in un riquadro &quot;A proposito dell'istanza&quot;.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">theme:</span> <span class="pre">CharField</span> <span class="pre">[&quot;sophon&quot;,</span> <span class="pre">&quot;paper&quot;,</span> <span class="pre">&quot;royalblue&quot;,</span> <span class="pre">&quot;hacker&quot;,</span> <span class="pre">&quot;amber&quot;]</span></span></dt>
<dd><p>Il tema <a class="reference internal" href="../progetto/index.html#bluelib"><span class="std std-ref">Bluelib</span></a> dell'istanza.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">version:</span> <span class="pre">str</span></span></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>La versione installata del pacchetto <a class="reference internal" href="#module-sophon" title="sophon"><code class="xref py py-mod docutils literal notranslate"><span class="pre">sophon</span></code></a>.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<section id="modello-del-gruppo-di-ricerca">
<h4>Modello del gruppo di ricerca<a class="headerlink" href="#modello-del-gruppo-di-ricerca" title="Link a questa intestazione"></a></h4>
<p>Viene creato il modello che rappresenta un <a class="reference internal" href="../progetto/index.html#gruppi-di-ricerca-in-sophon"><span class="std std-ref">gruppo di ricerca</span></a>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.models.ResearchGroup">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchGroup</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonGroupModel</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.models.ResearchGroup" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">slug:</span> <span class="pre">SlugField</span></span></dt>
<dd><p>L'identificatore del gruppo di ricerca, usato nei percorsi dell'API.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">name:</span> <span class="pre">CharField</span></span></dt>
<dd><p>Il nome del gruppo di ricerca.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">description:</span> <span class="pre">TextField</span></span></dt>
<dd><p>La descrizione del gruppo di ricerca, da visualizzare in un riquadro &quot;A proposito del gruppo&quot;.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">members:</span> <span class="pre">ManyToManyField</span> <span class="pre"></span> <span class="pre">django.contrib.auth.models.User</span></span></dt>
<dd><p>Elenco dei membri del gruppo. L'utente <code class="xref py py-attr docutils literal notranslate"><span class="pre">owner</span></code> è ignorato, in quanto è considerato sempre parte del gruppo.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">owner:</span> <span class="pre">ForeignKey</span> <span class="pre"></span> <span class="pre">django.contrib.auth.models.User</span></span></dt>
<dd><p>Il creatore e proprietario del gruppo, con privilegi amministrativi.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">access:</span> <span class="pre">CharField</span> <span class="pre">[&quot;MANUAL&quot;,</span> <span class="pre">&quot;OPEN&quot;]</span></span></dt>
<dd><p>La <a class="reference internal" href="../progetto/index.html#membri-e-modalita-di-accesso"><span class="std std-ref">modalità di accesso</span></a> del gruppo.</p>
</dd></dl>
</dd></dl>
</section>
<section id="module-sophon.core.permissions">
<span id="estensione-ai-permessi-di-django"></span><h4>Estensione ai permessi di Django<a class="headerlink" href="#module-sophon.core.permissions" title="Link a questa intestazione"></a></h4>
<p>I permessi di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> vengono estesi con due nuove classi che utilizzano il <a class="reference internal" href="#modello-di-autorizzazione-astratto"><span class="std std-ref">modello di autorizzazione astratto</span></a> precedentemente definito.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.permissions.Edit">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">Edit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">rest_framework.permissions.BasePermission</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.permissions.Edit" title="Link a questa definizione"></a></dt>
<dd><p>Consente l'interazione solo agli utenti che possono modificare l'oggetto.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.permissions.Admin">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">Admin</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">rest_framework.permissions.BasePermission</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.permissions.Admin" title="Link a questa definizione"></a></dt>
<dd><p>Consente l'interazione solo agli utenti che possono amministrare l'oggetto.</p>
</dd></dl>
</section>
<section id="module-sophon.core.views">
<span id="viewset-astratti"></span><h4>Viewset astratti<a class="headerlink" href="#module-sophon.core.views" title="Link a questa intestazione"></a></h4>
<p>Vengono definiti tre viewset in grado di utilizzare i metodi aggiunti dalle classi astratte <a class="reference internal" href="#sophon.core.models.SophonModel" title="sophon.core.models.SophonModel"><code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonModel</span></code></a> e <a class="reference internal" href="#sophon.core.models.SophonGroupModel" title="sophon.core.models.SophonGroupModel"><code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonGroupModel</span></code></a>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.ReadSophonViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ReadSophonViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">rest_framework.viewsets.ReadOnlyModelViewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass=abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.ReadSophonViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende la classe base <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.viewsets.ReadOnlyModelViewSet</span></code> con metodi di utilità mancanti nell'implementazione originale, allacciandola inoltre a <a class="reference internal" href="#sophon.core.models.SophonGroupModel" title="sophon.core.models.SophonGroupModel"><code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonGroupModel</span></code></a>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ReadSophonViewSet.get_queryset">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_queryset</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">QuerySet</span></span></span><a class="headerlink" href="#sophon.core.views.ReadSophonViewSet.get_queryset" title="Link a questa definizione"></a></dt>
<dd><p>Imposta come astratto (e quindi obbligatorio) il metodo <code class="xref py py-meth docutils literal notranslate"><span class="pre">rest_framework.viewsets.ReadOnlyModelViewSet.get_queryset()</span></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ReadSophonViewSet.permission_classes">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">permission_classes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.ReadSophonViewSet.permission_classes" title="Link a questa definizione"></a></dt>
<dd><p>Sovrascrive il campo di classe <code class="xref py py-attr docutils literal notranslate"><span class="pre">rest_framework.viewsets.ReadOnlyModelViewSet.permission_classes</span></code> con una funzione, permettendone la selezione dei permessi richiesti al momento di ricezione di una richiesta HTTP (invece che al momento di definizione della classe).</p>
<p>Delega la selezione delle classi a <a class="reference internal" href="#sophon.core.views.ReadSophonViewSet.get_permission_classes" title="sophon.core.views.ReadSophonViewSet.get_permission_classes"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_permission_classes()</span></code></a>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ReadSophonViewSet.get_permission_classes">
<span class="sig-name descname"><span class="pre">get_permission_classes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/typing.html#typing.Collection" title="(in Python v3.8)"><span class="pre">typing.Collection</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/typing.html#typing.Type" title="(in Python v3.8)"><span class="pre">typing.Type</span></a><span class="p"><span class="pre">[</span></span><span class="pre">permissions.BasePermission</span><span class="p"><span class="pre">]</span></span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.views.ReadSophonViewSet.get_permission_classes" title="Link a questa definizione"></a></dt>
<dd><p>Funzione che permette la selezione dei permessi necessari per effetuare una determinata richiesta al momento di ricezione di quest'ultima.</p>
<p>Utile per le classi che erediteranno da questa.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ReadSophonViewSet.get_serializer_class">
<span class="sig-name descname"><span class="pre">get_serializer_class</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/typing.html#typing.Type" title="(in Python v3.8)"><span class="pre">typing.Type</span></a><span class="p"><span class="pre">[</span></span><span class="pre">Serializer</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.views.ReadSophonViewSet.get_serializer_class" title="Link a questa definizione"></a></dt>
<dd><p>Funzione che permette la selezione del <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.serializers.Serializer</span></code> da utilizzare per una determinata richiesta al momento di ricezione di quest'ultima.</p>
<p>Utilizza:</p>
<blockquote>
<div><ul class="simple">
<li><p>il serializzatore <strong>in sola lettura</strong> per elencare gli oggetti (azione <code class="docutils literal notranslate"><span class="pre">list</span></code>);</p></li>
<li><p>il serializzatore <strong>di creazione</strong> per creare nuovi oggetti (azione <code class="docutils literal notranslate"><span class="pre">create</span></code>) e per generare i metadati del viewset (azione <code class="docutils literal notranslate"><span class="pre">metadata</span></code>);</p></li>
<li><p>il serializzatore ottenuto da <a class="reference internal" href="#sophon.core.models.SophonGroupModel.get_access_serializer" title="sophon.core.models.SophonGroupModel.get_access_serializer"><code class="xref py py-meth docutils literal notranslate"><span class="pre">models.SophonGroupModel.get_access_serializer()</span></code></a> per la visualizzazione dettagliata (azione <code class="docutils literal notranslate"><span class="pre">retrieve</span></code>), la modifica (azioni <code class="docutils literal notranslate"><span class="pre">update</span></code> e <code class="docutils literal notranslate"><span class="pre">partial_update</span></code>) e l'eliminazione (azione <code class="docutils literal notranslate"><span class="pre">destroy</span></code>) di un singolo oggetto;</p></li>
<li><p>il serializzatore ottenuto da <a class="reference internal" href="#sophon.core.views.ReadSophonViewSet.get_custom_serializer_classes" title="sophon.core.views.ReadSophonViewSet.get_custom_serializer_classes"><code class="xref py py-meth docutils literal notranslate"><span class="pre">get_custom_serializer_classes()</span></code></a> per le azioni personalizzate.</p></li>
</ul>
</div></blockquote>
<div class="admonition seealso">
<p class="admonition-title">Vedi anche</p>
<p><a class="reference internal" href="#sophon.core.models.SophonGroupModel" title="sophon.core.models.SophonGroupModel"><code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonGroupModel</span></code></a></p>
</div>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ReadSophonViewSet.get_custom_serializer_classes">
<span class="sig-name descname"><span class="pre">get_custom_serializer_classes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">t.Type</span><span class="p"><span class="pre">[</span></span><span class="pre">Serializer</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.views.ReadSophonViewSet.get_custom_serializer_classes" title="Link a questa definizione"></a></dt>
<dd><p>Permette alle classi che ereditano da questa di selezionare quale <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.serializers.Serializer</span></code> utilizzare per le azioni personalizzate.</p>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">WriteSophonViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">rest_framework.viewsets.ModelViewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">ReadSophonViewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass=abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende la classe base <a class="reference internal" href="#sophon.core.views.ReadSophonViewSet" title="sophon.core.views.ReadSophonViewSet"><code class="xref py py-class docutils literal notranslate"><span class="pre">ReadSophonViewSet</span></code></a> aggiungendoci i metodi di <code class="xref py py-class docutils literal notranslate"><span class="pre">rest_framework.viewsets.ModelViewSet</span></code> che effettuano modifiche sugli oggetti.</p>
<p>Depreca i metodi <code class="docutils literal notranslate"><span class="pre">perform_*</span></code> di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code>, introducendone versioni estese con una signature diversa dal nome di <code class="docutils literal notranslate"><span class="pre">hook_*</span></code>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet.perform_create">
<span class="sig-name descname"><span class="pre">perform_create</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet.perform_create" title="Link a questa definizione"></a></dt>
<dd><div class="deprecated">
<p><span class="versionmodified deprecated">Deprecato dalla versione 0.1.</span></p>
</div>
<p>Metodo di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> rimosso da Sophon.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet.perform_update">
<span class="sig-name descname"><span class="pre">perform_update</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet.perform_update" title="Link a questa definizione"></a></dt>
<dd><div class="deprecated">
<p><span class="versionmodified deprecated">Deprecato dalla versione 0.1.</span></p>
</div>
<p>Metodo di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> rimosso da Sophon.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet.perform_destroy">
<span class="sig-name descname"><span class="pre">perform_destroy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet.perform_destroy" title="Link a questa definizione"></a></dt>
<dd><div class="deprecated">
<p><span class="versionmodified deprecated">Deprecato dalla versione 0.1.</span></p>
</div>
<p>Metodo di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> rimosso da Sophon.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet.hook_create">
<span class="sig-name descname"><span class="pre">hook_create</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">typing.Any</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet.hook_create" title="Link a questa definizione"></a></dt>
<dd><p>Funzione chiamata durante l'esecuzione dell'azione di creazione oggetto <code class="docutils literal notranslate"><span class="pre">create</span></code>.</p>
<dl class="field-list simple">
<dt class="field-odd">Parametri</dt>
<dd class="field-odd"><p><strong>serializer</strong> -- Il <code class="xref py py-class docutils literal notranslate"><span class="pre">Serializer</span></code> già &quot;riempito&quot; contenente i dati dell'oggetto che sta per essere creato.</p>
</dd>
<dt class="field-even">Solleva</dt>
<dd class="field-even"><p><a class="reference internal" href="#sophon.core.views.sophon.core.errors.HTTPException" title="sophon.core.views.sophon.core.errors.HTTPException"><strong>HTTPException</strong></a> -- È possibile interrompere la creazione dell'oggetto con uno specifico codice errore sollevando una <a class="reference internal" href="#sophon.core.views.sophon.core.errors.HTTPException" title="sophon.core.views.sophon.core.errors.HTTPException"><code class="xref py py-exc docutils literal notranslate"><span class="pre">HTTPException</span></code></a> all'interno della funzione.</p>
</dd>
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>Un <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><code class="xref any docutils literal notranslate"><span class="pre">dict</span></code></a> da unire a quello del <code class="xref py py-class docutils literal notranslate"><span class="pre">Serializer</span></code> per formare l'oggetto da creare.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet.hook_update">
<span class="sig-name descname"><span class="pre">hook_update</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">t.Any</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet.hook_update" title="Link a questa definizione"></a></dt>
<dd><p>Funzione chiamata durante l'esecuzione delle azioni di modifica oggetto <code class="docutils literal notranslate"><span class="pre">update</span></code> e <code class="docutils literal notranslate"><span class="pre">partial_update</span></code>.</p>
<dl class="field-list simple">
<dt class="field-odd">Parametri</dt>
<dd class="field-odd"><p><strong>serializer</strong> -- Il <code class="xref py py-class docutils literal notranslate"><span class="pre">Serializer</span></code> già &quot;riempito&quot; contenente i dati dell'oggetto che sta per essere modificato.</p>
</dd>
<dt class="field-even">Solleva</dt>
<dd class="field-even"><p><a class="reference internal" href="#sophon.core.views.sophon.core.errors.HTTPException" title="sophon.core.views.sophon.core.errors.HTTPException"><strong>HTTPException</strong></a> -- È possibile interrompere la creazione dell'oggetto con uno specifico codice errore sollevando una <a class="reference internal" href="#sophon.core.views.sophon.core.errors.HTTPException" title="sophon.core.views.sophon.core.errors.HTTPException"><code class="xref py py-exc docutils literal notranslate"><span class="pre">HTTPException</span></code></a> all'interno della funzione.</p>
</dd>
<dt class="field-odd">Ritorna</dt>
<dd class="field-odd"><p>Un <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">dict</span></code></a> da unire a quello del <code class="xref py py-class docutils literal notranslate"><span class="pre">Serializer</span></code> per formare l'oggetto da modificare.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.WriteSophonViewSet.hook_destroy">
<span class="sig-name descname"><span class="pre">hook_destroy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">typing.Any</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.views.WriteSophonViewSet.hook_destroy" title="Link a questa definizione"></a></dt>
<dd><p>Funzione chiamata durante l'esecuzione dell'azione di eliminazione oggetto <code class="docutils literal notranslate"><span class="pre">destroy</span></code>.</p>
<dl class="field-list simple">
<dt class="field-odd">Solleva</dt>
<dd class="field-odd"><p><a class="reference internal" href="#sophon.core.views.sophon.core.errors.HTTPException" title="sophon.core.views.sophon.core.errors.HTTPException"><strong>HTTPException</strong></a> -- È possibile interrompere la creazione dell'oggetto con uno specifico codice errore sollevando una <a class="reference internal" href="#sophon.core.views.sophon.core.errors.HTTPException" title="sophon.core.views.sophon.core.errors.HTTPException"><code class="xref py py-exc docutils literal notranslate"><span class="pre">HTTPException</span></code></a> all'interno della funzione.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
<dl class="py exception">
<dt class="sig sig-object py" id="sophon.core.views.sophon.core.errors.HTTPException">
<em class="property"><span class="pre">exception</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">sophon.core.errors.</span></span><span class="sig-name descname"><span class="pre">HTTPException</span></span><a class="headerlink" href="#sophon.core.views.sophon.core.errors.HTTPException" title="Link a questa definizione"></a></dt>
<dd><p>Tipo di eccezione che è possibile sollevare nei metodi <code class="docutils literal notranslate"><span class="pre">hook_*</span></code> di <a class="reference internal" href="#sophon.core.views.WriteSophonViewSet" title="sophon.core.views.WriteSophonViewSet"><code class="xref py py-class docutils literal notranslate"><span class="pre">WriteSophonViewSet</span></code></a> per interrompere l'azione in corso senza applicare le modifiche.</p>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">status:</span> <span class="pre">int</span></span></dt>
<dd><p>Permette di specificare il codice errore con cui rispondere alla richiesta interrotta.</p>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.SophonGroupViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonGroupViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">WriteSophonViewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.SophonGroupViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende la classe base <a class="reference internal" href="#sophon.core.views.WriteSophonViewSet" title="sophon.core.views.WriteSophonViewSet"><code class="xref py py-class docutils literal notranslate"><span class="pre">WriteSophonViewSet</span></code></a> estendendo gli <code class="docutils literal notranslate"><span class="pre">hook_*</span></code> con verifiche dei permessi dell'utente che tenta di effettuare l'azione.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.SophonGroupViewSet.get_group_from_serializer">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_group_from_serializer</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">serializer</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">models.ResearchGroup</span></span></span><a class="headerlink" href="#sophon.core.views.SophonGroupViewSet.get_group_from_serializer" title="Link a questa definizione"></a></dt>
<dd><p>Metodo necessario a trovare il gruppo a cui apparterrà un oggetto prima che il suo serializzatore venga elaborato.</p>
<dl class="field-list simple">
<dt class="field-odd">Parametri</dt>
<dd class="field-odd"><p><strong>serializer</strong> -- Il <code class="xref py py-class docutils literal notranslate"><span class="pre">Serializer</span></code> già &quot;riempito&quot; contenente i dati dell'oggetto.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<section id="viewset-concreti">
<h4>Viewset concreti<a class="headerlink" href="#viewset-concreti" title="Link a questa intestazione"></a></h4>
<p>Vengono poi definiti tre viewset e una view che permettono interazioni tra l'utente e i modelli definiti nell'app.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.UsersByIdViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">UsersByIdViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ReadSophonViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.UsersByIdViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in sola lettura che permette di recuperare gli utenti dell'istanza partendo dal loro <code class="docutils literal notranslate"><span class="pre">id</span></code>.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/core/users/by-id/</span><em><span class="pre">ID</span></em><span class="pre">/</span></code>.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.UsersByUsernameViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">UsersByUsernameViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ReadSophonViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.UsersByUsernameViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in sola lettura che permette di recuperare gli utenti dell'istanza partendo dal loro <code class="docutils literal notranslate"><span class="pre">username</span></code>.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/core/users/by-username/</span><em><span class="pre">USERNAME</span></em><span class="pre">/</span></code>.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.ResearchGroupViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchGroupViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">WriteSophonViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.ResearchGroupViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in lettura e scrittura che permette di interagire con i gruppi di ricerca.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/core/groups/</span><em><span class="pre">GROUP_SLUG</span></em><span class="pre">/</span></code>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ResearchGroupViewSet.join">
<span class="sig-name descname"><span class="pre">join</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.core.views.ResearchGroupViewSet.join" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che permette ad un utente di unirsi ad un gruppo aperto.</p>
<p>Utilizza <code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonGroupModel.get_access_serializer</span></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.views.ResearchGroupViewSet.leave">
<span class="sig-name descname"><span class="pre">leave</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.core.views.ResearchGroupViewSet.leave" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che permette ad un utente di abbandonare un gruppo di cui non è proprietario.</p>
<p>Utilizza <code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonGroupModel.get_access_serializer</span></code>.</p>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.views.SophonInstanceDetailsView">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonInstanceDetailsView</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">APIView</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.views.SophonInstanceDetailsView" title="Link a questa definizione"></a></dt>
<dd><p>View che restituisce il valore attuale dell'unico oggetto <a class="reference internal" href="#sophon.core.models.SophonInstanceDetails" title="sophon.core.models.SophonInstanceDetails"><code class="xref py py-class docutils literal notranslate"><span class="pre">models.SophonInstanceDetails</span></code></a>.</p>
<p>Accessibile tramite richieste <code class="docutils literal notranslate"><span class="pre">GET</span></code> all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/core/instance/</span></code>.</p>
</dd></dl>
</section>
<section id="module-sophon.core.admin">
<span id="pagina-di-amministrazione"></span><h4>Pagina di amministrazione<a class="headerlink" href="#module-sophon.core.admin" title="Link a questa intestazione"></a></h4>
<p>Vengono infine registrati nella pagina di amministrazione i modelli concreti definiti in questa app, effettuando alcune personalizzazioni elencate in seguito.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.admin.ResearchGroupAdmin">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchGroupAdmin</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonAdmin</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.admin.ResearchGroupAdmin" title="Link a questa definizione"></a></dt>
<dd><p>Per i gruppi di ricerca, viene specificato un ordinamento, permesso il filtraggio e selezionati i campi più importanti da visualizzare nella lista.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.admin.SophonInstanceDetails">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonInstanceDetails</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonAdmin</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.admin.SophonInstanceDetails" title="Link a questa definizione"></a></dt>
<dd><p>Per i dettagli dell'istanza, vengono disattivate tutte le azioni, impedendo la creazione o eliminazione del singleton.</p>
</dd></dl>
</section>
<section id="module-sophon.core.tests">
<span id="testing-in-sophon-core"></span><h4>Testing in Sophon Core<a class="headerlink" href="#module-sophon.core.tests" title="Link a questa intestazione"></a></h4>
<p>Per verificare che i <a class="reference internal" href="#module-sophon.core.models"><span class="std std-ref">modelli</span></a> e <a class="reference internal" href="#module-sophon.core.views"><span class="std std-ref">viewset</span></a> funzionassero correttamente e non avessero problemi di <a class="reference internal" href="../progetto/index.html#sicurezza"><span class="std std-ref">sicurezza</span></a>, sono stati realizzati degli unit test in grado di rilevare la presenza di errori all'interno dell'app.</p>
</section>
<section id="test-case-generici">
<h4>Test case generici<a class="headerlink" href="#test-case-generici" title="Link a questa intestazione"></a></h4>
<p>Vengono definiti alcuni test case generici per facilitare le interazioni tra <code class="docutils literal notranslate"><span class="pre">APITestCase</span></code> e viewset.</p>
<div class="admonition note">
<p class="admonition-title">Nota</p>
<p>I nomi delle funzioni usano nomi con capitalizzazione inconsistente, in quanto lo stesso modulo <a class="reference external" href="https://docs.python.org/3.8/library/unittest.html#module-unittest" title="(in Python v3.8)"><code class="xref any docutils literal notranslate"><span class="pre">unittest</span></code></a> non rispetta lo stile suggerito in <span class="target" id="index-5"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0008"><strong>PEP 8</strong></a>.</p>
</div>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.BetterAPITestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">BetterAPITestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">APITestCase</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.BetterAPITestCase" title="Link a questa definizione"></a></dt>
<dd><dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.BetterAPITestCase.as_user">
<span class="sig-name descname"><span class="pre">as_user</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">username</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">password</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/typing.html#typing.ContextManager" title="(in Python v3.8)"><span class="pre">typing.ContextManager</span></a><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><span class="pre">None</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.BetterAPITestCase.as_user" title="Link a questa definizione"></a></dt>
<dd><p>Context manager che permette di effettuare richieste all'API come uno specifico utente, effettuando il logout quando sono state effettuate le richieste necessarie.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.BetterAPITestCase.assertData">
<span class="sig-name descname"><span class="pre">assertData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ReturnDict</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">expected</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.BetterAPITestCase.assertData" title="Link a questa definizione"></a></dt>
<dd><p>Asserzione che permette di verificare che l'oggetto restituito da una richiesta all'API contenga almeno le chiavi e i valori contenuti nel dizionario <code class="docutils literal notranslate"><span class="pre">expected</span></code>.</p>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ReadSophonTestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">BetterAPITestCase</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che implementa metodi per testare rapidamente le azioni di un <a class="reference internal" href="#sophon.core.views.ReadSophonViewSet" title="sophon.core.views.ReadSophonViewSet"><code class="xref py py-class docutils literal notranslate"><span class="pre">views.ReadSophonViewSet</span></code></a>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.get_basename">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_basename</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.get_basename" title="Link a questa definizione"></a></dt>
<dd><p>Metodo <strong>astratto</strong> che deve restituire il basename del viewset da testare.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.get_url">
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">get_url</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cls</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">kind</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.get_url" title="Link a questa definizione"></a></dt>
<dd><p>Metodo utilizzato dal test case per trovare gli URL ai quali possono essere effettuate le varie azioni.</p>
</dd></dl>
<p>I seguenti metodi permettono di effettuare azioni sul viewset:</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.list">
<span class="sig-name descname"><span class="pre">list</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.list" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.retrieve">
<span class="sig-name descname"><span class="pre">retrieve</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.retrieve" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.custom_list">
<span class="sig-name descname"><span class="pre">custom_list</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.custom_list" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.custom_detail">
<span class="sig-name descname"><span class="pre">custom_detail</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.custom_detail" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<p>I seguenti metodi asseriscono che una determinata azione con determinati parametri risponderà con il codice di stato <code class="docutils literal notranslate"><span class="pre">code</span></code>, e restituiscono i dati contenuti nella risposta se l'azione è riuscita (<code class="docutils literal notranslate"><span class="pre">200</span> <span class="pre">&lt;=</span> <span class="pre">code</span> <span class="pre">&lt;</span> <span class="pre">300</span></code>)</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.assertActionList">
<span class="sig-name descname"><span class="pre">assertActionList</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">200</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.assertActionList" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.assertActionRetrieve">
<span class="sig-name descname"><span class="pre">assertActionRetrieve</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">200</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.assertActionRetrieve" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.assertActionCustomList">
<span class="sig-name descname"><span class="pre">assertActionCustomList</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">200</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.assertActionCustomList" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.ReadSophonTestCase.assertActionCustomDetail">
<span class="sig-name descname"><span class="pre">assertActionCustomDetail</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><span class="pre">dict</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">200</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.ReadSophonTestCase.assertActionCustomDetail" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">WriteSophonTestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ReadSophonTestCase</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende <a class="reference internal" href="#sophon.core.tests.ReadSophonTestCase" title="sophon.core.tests.ReadSophonTestCase"><code class="xref py py-class docutils literal notranslate"><span class="pre">ReadSophonTestCase</span></code></a> con le azioni di un <a class="reference internal" href="#sophon.core.views.WriteSophonViewSet" title="sophon.core.views.WriteSophonViewSet"><code class="xref py py-class docutils literal notranslate"><span class="pre">views.WriteSophonViewSet</span></code></a>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase.create">
<span class="sig-name descname"><span class="pre">create</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase.create" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase.update">
<span class="sig-name descname"><span class="pre">update</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase.update" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase.destroy">
<span class="sig-name descname"><span class="pre">destroy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">rest_framework.response.Response</span></span></span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase.destroy" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase.assertActionCreate">
<span class="sig-name descname"><span class="pre">assertActionCreate</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">201</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase.assertActionCreate" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase.assertActionUpdate">
<span class="sig-name descname"><span class="pre">assertActionUpdate</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">200</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase.assertActionUpdate" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.core.tests.WriteSophonTestCase.assertActionDestroy">
<span class="sig-name descname"><span class="pre">assertActionDestroy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">code</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">200</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">typing.Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">ReturnDict</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.core.tests.WriteSophonTestCase.assertActionDestroy" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="test-case-concreti">
<h4>Test case concreti<a class="headerlink" href="#test-case-concreti" title="Link a questa intestazione"></a></h4>
<p>Vengono testate tutte le view dell'app tramite <a class="reference internal" href="#sophon.core.tests.BetterAPITestCase" title="sophon.core.tests.BetterAPITestCase"><code class="xref py py-class docutils literal notranslate"><span class="pre">BetterAPITestCase</span></code></a> e tutti i viewset dell'app tramite <a class="reference internal" href="#sophon.core.tests.ReadSophonTestCase" title="sophon.core.tests.ReadSophonTestCase"><code class="xref py py-class docutils literal notranslate"><span class="pre">ReadSophonTestCase</span></code></a> e <a class="reference internal" href="#sophon.core.tests.WriteSophonTestCase" title="sophon.core.tests.WriteSophonTestCase"><code class="xref py py-class docutils literal notranslate"><span class="pre">WriteSophonTestCase</span></code></a>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.UsersByIdTestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">UsersByIdTestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ReadSophonTestCase</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.UsersByIdTestCase" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.UsersByUsernameTestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">UsersByUsernameTestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ReadSophonTestCase</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.UsersByUsernameTestCase" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.ResearchGroupTestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchGroupTestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">WriteSophonTestCase</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.ResearchGroupTestCase" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.core.tests.SophonInstanceDetailsTestCase">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">SophonInstanceDetailsTestCase</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">BetterAPITestCase</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.core.tests.SophonInstanceDetailsTestCase" title="Link a questa definizione"></a></dt>
<dd></dd></dl>
</section>
</section>
<section id="module-sophon.projects">
<span id="l-app-sophon-projects"></span><h3>L'app Sophon Projects<a class="headerlink" href="#module-sophon.projects" title="Link a questa intestazione"></a></h3>
<p>L'app <a class="reference internal" href="#module-sophon.projects" title="sophon.projects"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.projects</span></code></a> è un app secondaria che dipende da <a class="reference internal" href="#module-sophon.core" title="sophon.core"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.core</span></code></a> che introduce in Sophon il concetto di <a class="reference internal" href="../progetto/index.html#progetti-di-ricerca-in-sophon"><span class="std std-ref">progetto di ricerca</span></a>.</p>
<div class="admonition note">
<p class="admonition-title">Nota</p>
<p>L'app <a class="reference internal" href="#module-sophon.projects" title="sophon.projects"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.projects</span></code></a> teoricamente è opzionale, in quanto il modulo backend può funzionare senza di essa, e può essere rimossa dal modulo <a class="reference internal" href="#module-sophon.settings" title="sophon.settings"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.settings</span></code></a>.</p>
<p>Non è però possibile rimuoverla nella versione finale distribuita, in quanto il modulo <a class="reference internal" href="#module-sophon.settings" title="sophon.settings"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.settings</span></code></a> non è modificabile dall'esterno, e in quanto il <a class="reference internal" href="../progetto/index.html#modulo-frontend"><span class="std std-ref">modulo frontend</span></a> non prevede questa funzionalità e si aspetta che i percorsi API relativi all'app siano disponibili.</p>
<p>Inoltre, rimuovendo l'app <a class="reference internal" href="#module-sophon.projects" title="sophon.projects"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.projects</span></code></a> non sarà più possibile usare l'app <a class="reference internal" href="#module-sophon.notebooks" title="sophon.notebooks"><code class="xref any py py-mod docutils literal notranslate"><span class="pre">sophon.notebooks</span></code></a>, in quanto dipende da essa.</p>
</div>
<section id="module-sophon.projects.models">
<span id="modello-del-progetto-di-ricerca"></span><h4>Modello del progetto di ricerca<a class="headerlink" href="#module-sophon.projects.models" title="Link a questa intestazione"></a></h4>
<p>Viene introdotto un modello concreto che rappresenta un <a class="reference internal" href="../progetto/index.html#progetti-di-ricerca-in-sophon"><span class="std std-ref">progetto di ricerca</span></a>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.projects.models.ResearchProject">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchProject</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonGroupModel</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.projects.models.ResearchProject" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">slug:</span> <span class="pre">SlugField</span></span></dt>
<dd><p>L'identificatore del progetto di ricerca, usato nei percorsi dell'API.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">group:</span> <span class="pre">ForeignKey</span> <span class="pre"></span> <span class="pre">sophon.core.models.ResearchGroup</span></span></dt>
<dd><p>Lo <code class="xref py py-attr docutils literal notranslate"><span class="pre">slug</span></code> del gruppo di ricerca al quale appartiene il progetto.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">name:</span> <span class="pre">CharField</span></span></dt>
<dd><p>Il nome completo del progetto di ricerca.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">description:</span> <span class="pre">TextField</span></span></dt>
<dd><p>La descrizione del progetto di ricerca, da visualizzare in un riquadro &quot;A proposito del progetto&quot;.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">visibility:</span> <span class="pre">CharField</span> <span class="pre">[&quot;PUBLIC&quot;,</span> <span class="pre">&quot;INTERNAL&quot;,</span> <span class="pre">&quot;PRIVATE&quot;]</span></span></dt>
<dd><p>La <a class="reference internal" href="../progetto/index.html#visibilita-dei-progetti"><span class="std std-ref">visibilità del progetto</span></a>.</p>
</dd></dl>
</dd></dl>
</section>
<section id="module-sophon.projects.views">
<span id="viewset-del-gruppo-di-ricerca"></span><h4>Viewset del gruppo di ricerca<a class="headerlink" href="#module-sophon.projects.views" title="Link a questa intestazione"></a></h4>
<p>Da una base comune, vengono creati due viewset per interagire con i progetti di ricerca.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.projects.views.ResearchProjectViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchProjectViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonGroupViewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.projects.views.ResearchProjectViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che effettua l'override di <code class="xref py py-meth docutils literal notranslate"><span class="pre">get_group_from_serializer()</span></code> per entrambi i viewset che seguono.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.projects.views.ResearchProjectsBySlugViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchProjectsBySlugViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ResearchProjectViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.projects.views.ResearchProjectsBySlugViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in lettura e scrittura che permette di interagire con tutti i progetti di ricerca a cui l'utente loggato ha accesso.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/projects/by-slug/</span><em><span class="pre">PROJECT_SLUG</span></em><span class="pre">/</span></code>.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.projects.views.ResearchProjectsByGroupViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchProjectsByGroupViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ResearchProjectViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.projects.views.ResearchProjectsByGroupViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in lettura e scrittura che permette di interagire con i progetti di ricerca a cui l'utente loggato ha accesso, filtrati per il gruppo a cui appartengono.</p>
<p>Il filtraggio viene effettuato limitando il queryset.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/projects/by-group/</span><em><span class="pre">GROUP_SLUG</span></em><span class="pre">/</span><em><span class="pre">PROJECT_SLUG</span></em><span class="pre">/</span></code>.</p>
</dd></dl>
</section>
<section id="module-sophon.projects.admin">
<span id="amministrazione-del-gruppo-di-ricerca"></span><h4>Amministrazione del gruppo di ricerca<a class="headerlink" href="#module-sophon.projects.admin" title="Link a questa intestazione"></a></h4>
<p>Il modello <a class="reference internal" href="#sophon.projects.models.ResearchProject" title="sophon.projects.models.ResearchProject"><code class="xref py py-class docutils literal notranslate"><span class="pre">models.ResearchProject</span></code></a> viene registrato nella pagina di amministrazione attraverso la seguente classe:</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.projects.admin.ResearchProjectAdmin">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ResearchProjectAdmin</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">sophon.core.admin.SophonAdmin</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.projects.admin.ResearchProjectAdmin" title="Link a questa definizione"></a></dt>
<dd><p>Classe per la pagina di amministrazione che specifica un ordinamento, permette il filtraggio per gruppo di appartenenza e visibilità, e specifica i campi da visualizzare nell'elenco dei progetti.</p>
</dd></dl>
</section>
</section>
<section id="module-sophon.notebooks">
<span id="l-app-sophon-notebooks"></span><h3>L'app Sophon Notebooks<a class="headerlink" href="#module-sophon.notebooks" title="Link a questa intestazione"></a></h3>
<p>L'app <a class="reference internal" href="#module-sophon.notebooks" title="sophon.notebooks"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sophon.notebooks</span></code></a> è un app secondaria che dipende da <a class="reference internal" href="#module-sophon.projects" title="sophon.projects"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sophon.projects</span></code></a> che introduce in Sophon il concetto di <code class="xref py py-obj docutils literal notranslate"><span class="pre">notebook</span></code>.</p>
<div class="admonition note">
<p class="admonition-title">Nota</p>
<p>L'app <a class="reference internal" href="#module-sophon.notebooks" title="sophon.notebooks"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sophon.notebooks</span></code></a> teoricamente è opzionale, in quanto il modulo backend può funzionare senza di essa, e può essere rimossa dal modulo <a class="reference internal" href="#module-sophon.settings" title="sophon.settings"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sophon.settings</span></code></a>.</p>
<p>Non è però possibile rimuoverla nella versione finale distribuita, in quanto il modulo <a class="reference internal" href="#module-sophon.settings" title="sophon.settings"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sophon.settings</span></code></a> non è modificabile dall'esterno, e in quanto il <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">frontend</span></code> non prevede questa funzionalità e si aspetta che i percorsi API relativi all'app siano disponibili.</p>
</div>
<section id="funzionamento-di-un-notebook">
<h4>Funzionamento di un notebook<a class="headerlink" href="#funzionamento-di-un-notebook" title="Link a questa intestazione"></a></h4>
<p>Internamente, un notebook non è altro che un container <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> accessibile ad un determinato indirizzo il cui stato è sincronizzato con un oggetto del database del <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">backend</span></code>.</p>
<section id="modalita-sviluppo">
<h5>Modalità sviluppo<a class="headerlink" href="#modalita-sviluppo" title="Link a questa intestazione"></a></h5>
<p>Per facilitare lo sviluppo di Sophon, sono state realizzate due modalità di operazione di quest'ultimo.</p>
<ul>
<li><p>Nella prima, la <strong>modalità sviluppo</strong>, il <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">proxy</span></code> non è in esecuzione, ed è possibile collegarsi direttamente ai container all'indirizzo IP locale <code class="docutils literal notranslate"><span class="pre">127.0.0.1</span></code>.</p>
<p>Il <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">frontend</span></code> non supporta questa modalità, in quanto intesa solamente per lo sviluppo del modulo backend.</p>
</li>
<li><p>Nella seconda, la <strong>modalità produzione</strong>, il <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">proxy</span></code> è in esecuzione all'interno di un container Docker, e si collega ai <code class="xref py py-obj docutils literal notranslate"><span class="pre">moduli</span> <span class="pre">Jupyter</span></code> attraverso i relativi network Docker tramite una <code class="xref py py-obj docutils literal notranslate"><span class="pre">rubrica</span></code>.</p></li>
</ul>
</section>
</section>
<section id="module-sophon.notebooks.apache">
<span id="gestione-della-rubrica-del-proxy"></span><h4>Gestione della rubrica del proxy<a class="headerlink" href="#module-sophon.notebooks.apache" title="Link a questa intestazione"></a></h4>
<p>Viene creata una classe per la gestione della rubrica del proxy, utilizzando il modulo <a class="reference external" href="https://docs.python.org/3.8/library/dbm.html#module-dbm.gnu" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">dbm.gnu</span></code></a>, supportato da HTTPd.</p>
<p>La rubrica mappa gli URL pubblici dei notebook a URL privati relativi al <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">proxy</span></code>, in modo da effettuare reverse proxying <strong>dinamico</strong>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.notebooks.apache.ApacheDB">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">ApacheDB</span></span><a class="headerlink" href="#sophon.notebooks.apache.ApacheDB" title="Link a questa definizione"></a></dt>
<dd><p>Classe che permette il recupero, la creazione, la modifica e l'eliminazioni di chiavi di un database <a class="reference external" href="https://docs.python.org/3.8/library/dbm.html#module-dbm.gnu" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">dbm.gnu</span></code></a> come se quest'ultimo fosse un <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#dict" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">dict</span></code></a> con supporto a chiavi e valori <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">str</span></code></a> e <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#bytes" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">bytes</span></code></a>.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.apache.ApacheDB.convert_to_bytes">
<em class="property"><span class="pre">static</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">convert_to_bytes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">item</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">typing.Union</span><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a><span class="p"><span class="pre">,</span></span><span class="w"> </span><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#bytes" title="(in Python v3.8)"><span class="pre">bytes</span></a><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#bytes" title="(in Python v3.8)"><span class="pre">bytes</span></a></span></span><a class="headerlink" href="#sophon.notebooks.apache.ApacheDB.convert_to_bytes" title="Link a questa definizione"></a></dt>
<dd><p>Tutte le <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">str</span></code></a> passate a questa classe vengono convertite in <a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#bytes" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">bytes</span></code></a> attraverso questa funzione, che effettua un encoding in ASCII e solleva un errore se quest'ultimo fallisce.</p>
</dd></dl>
</dd></dl>
</section>
<section id="assegnazione-porta-effimera">
<h4>Assegnazione porta effimera<a class="headerlink" href="#assegnazione-porta-effimera" title="Link a questa intestazione"></a></h4>
<p>In <em>modalità sviluppo</em>, è necessario trovare una porta libera a cui rendere accessibile i container Docker dei notebook.</p>
<dl class="py function">
<dt class="sig sig-object py" id="sophon.notebooks.apache.get_ephemeral_port">
<span class="sig-name descname"><span class="pre">get_ephemeral_port</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/functions.html#int" title="(in Python v3.8)"><span class="pre">int</span></a></span></span><a class="headerlink" href="#sophon.notebooks.apache.get_ephemeral_port" title="Link a questa definizione"></a></dt>
<dd><p>Questa funzione apre e chiude immediatamente un <a class="reference external" href="https://docs.python.org/3.8/library/socket.html#socket.socket" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">socket.socket</span></code></a> all'indirizzo <code class="docutils literal notranslate"><span class="pre">localhost:0</span></code> in modo da ricevere dal sistema operativo un numero di porta sicuramente libero.</p>
</dd></dl>
</section>
<section id="module-sophon.notebooks.docker">
<span id="connessione-al-daemon-docker"></span><h4>Connessione al daemon Docker<a class="headerlink" href="#module-sophon.notebooks.docker" title="Link a questa intestazione"></a></h4>
<p>Per facilitare l'utilizzo del daemon Docker per la gestione dei container dei notebook, viene utilizzato il modulo <code class="xref py py-mod docutils literal notranslate"><span class="pre">docker</span></code>.</p>
<dl class="py function">
<dt class="sig sig-object py" id="sophon.notebooks.docker.get_docker_client">
<span class="sig-name descname"><span class="pre">get_docker_client</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">docker.DockerClient</span></span></span><a class="headerlink" href="#sophon.notebooks.docker.get_docker_client" title="Link a questa definizione"></a></dt>
<dd><p>Funzione che crea un client Docker con le variabili di ambiente del modulo.</p>
</dd></dl>
<dl class="py data">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">client:</span> <span class="pre">docker.DockerClient</span> <span class="pre">=</span> <span class="pre">lazy_object_proxy.Proxy(get_docker_client)</span></span></dt>
<dd><p>Viene creato un client Docker globale con inizializzazione lazy al fine di non tentare connessioni (lente!) al daemon quando non sono necessarie.</p>
</dd></dl>
</section>
<section id="controllo-dello-stato-di-salute">
<h4>Controllo dello stato di salute<a class="headerlink" href="#controllo-dello-stato-di-salute" title="Link a questa intestazione"></a></h4>
<p>Il modulo <code class="xref py py-mod docutils literal notranslate"><span class="pre">docker</span></code> viene esteso implementando supporto per l'istruzione <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code> dei <code class="docutils literal notranslate"><span class="pre">Dockerfile</span></code>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.notebooks.docker.HealthState">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">HealthState</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">enum.IntEnum</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.notebooks.docker.HealthState" title="Link a questa definizione"></a></dt>
<dd><p>Enumerazione che elenca gli stati possibili in cui può essere la salute di un container.</p>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">UNDEFINED</span> <span class="pre">=</span> <span class="pre">-2</span></span></dt>
<dd><p>Il <code class="docutils literal notranslate"><span class="pre">Dockerfile</span></code> non ha un <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code> definito.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">STARTING</span> <span class="pre">=</span> <span class="pre">-1</span></span></dt>
<dd><p>Il container Docker non mai completato con successo un <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code>.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">HEALTHY</span> <span class="pre">=</span> <span class="pre">0</span></span></dt>
<dd><p>Il container Docker ha completato con successo l'ultimo <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code> e quindi sta funzionando correttamente.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">UNHEALTHY</span> <span class="pre">=</span> <span class="pre">1</span></span></dt>
<dd><p>Il container Docker ha fallito l'ultimo <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code>.</p>
</dd></dl>
</dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="sophon.notebooks.docker.get_health">
<span class="sig-name descname"><span class="pre">get_health</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">container</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container" title="(in Docker SDK for Python v6.0)"><span class="pre">docker.models.containers.Container</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#sophon.notebooks.docker.HealthState" title="sophon.notebooks.docker.HealthState"><span class="pre">HealthState</span></a></span></span><a class="headerlink" href="#sophon.notebooks.docker.get_health" title="Link a questa definizione"></a></dt>
<dd><p>Funzione che utilizza l'API a basso livello del client Docker per recuperare l'<a class="reference internal" href="#sophon.notebooks.docker.HealthState" title="sophon.notebooks.docker.HealthState"><code class="xref py py-obj docutils literal notranslate"><span class="pre">HealthState</span></code></a> dei container.</p>
</dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="sophon.notebooks.docker.sleep_until_container_has_started">
<span class="sig-name descname"><span class="pre">sleep_until_container_has_started</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">container</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference external" href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container" title="(in Docker SDK for Python v6.0)"><span class="pre">docker.models.containers.Container</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#sophon.notebooks.docker.HealthState" title="sophon.notebooks.docker.HealthState"><span class="pre">HealthState</span></a></span></span><a class="headerlink" href="#sophon.notebooks.docker.sleep_until_container_has_started" title="Link a questa definizione"></a></dt>
<dd><p>Funzione bloccante che restituisce solo quando lo stato del container specificato non è <code class="xref py py-obj docutils literal notranslate"><span class="pre">HealthState.STARTING</span></code>.</p>
<div class="admonition danger">
<p class="admonition-title">Pericolo</p>
<p>L'implementazione di questa funzione potrebbe causare rallentamenti nella risposta alle pagine web per via di una chiamata al metodo <a class="reference external" href="https://docs.python.org/3.8/library/time.html#time.sleep" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">time.sleep</span></code></a> al suo interno.</p>
<p>Ciò è dovuto al mancato supporto alle funzioni asincrone nella versione attuale di <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code>.</p>
<p>Si è deciso di mantenere comunque la funzionalità a scopi dimostrativi e per compatibilità futura.</p>
</div>
</dd></dl>
</section>
<section id="generazione-di-token-sicuri">
<h4>Generazione di token sicuri<a class="headerlink" href="#generazione-di-token-sicuri" title="Link a questa intestazione"></a></h4>
<p>Per rendere l'interfaccia grafica più <code class="xref py py-obj docutils literal notranslate"><span class="pre">intuitiva</span></code>, si è scelto di rendere trasparente all'utente il meccanismo di autenticazione a JupyterLab.</p>
<p>Pertanto, si è verificata la necessità di generare token crittograficamente sicuri da richiedere per l'accesso a JupyterLab.</p>
<dl class="py function">
<dt class="sig sig-object py" id="sophon.notebooks.docker.generate_secure_token">
<span class="sig-name descname"><span class="pre">generate_secure_token</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/stdtypes.html#str" title="(in Python v3.8)"><span class="pre">str</span></a></span></span><a class="headerlink" href="#sophon.notebooks.docker.generate_secure_token" title="Link a questa definizione"></a></dt>
<dd><p>Funzione che utilizza <a class="reference external" href="https://docs.python.org/3.8/library/secrets.html#secrets.token_urlsafe" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">secrets.token_urlsafe</span></code></a> per generare un token valido e crittograficamente sicuro.</p>
</dd></dl>
</section>
<section id="module-sophon.notebooks.models">
<span id="modello-dei-notebook"></span><h4>Modello dei notebook<a class="headerlink" href="#module-sophon.notebooks.models" title="Link a questa intestazione"></a></h4>
<p>Viene definito il modello rappresentante un <code class="xref py py-obj docutils literal notranslate"><span class="pre">notebook</span></code>.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">Notebook</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonGroupModel</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.notebooks.models.Notebook" title="Link a questa definizione"></a></dt>
<dd><dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">slug:</span> <span class="pre">SlugField</span></span></dt>
<dd><p>Lo slug dei notebook prevede ulteriori restrizioni oltre a quelle previste dallo <a class="reference external" href="http://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.SlugField" title="(in Django v3.2)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">django.db.models.SlugField</span></code></a>:</p>
<ul class="simple">
<li><p>non può essere uno dei seguenti valori: <code class="docutils literal notranslate"><span class="pre">api</span></code>, <code class="docutils literal notranslate"><span class="pre">static</span></code>, <code class="docutils literal notranslate"><span class="pre">proxy</span></code>, <code class="docutils literal notranslate"><span class="pre">backend</span></code>, <code class="docutils literal notranslate"><span class="pre">frontend</span></code>, <code class="docutils literal notranslate"><span class="pre">src</span></code>;</p></li>
<li><p>non può iniziare o finire con un trattino <code class="docutils literal notranslate"><span class="pre">-</span></code>.</p></li>
</ul>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">project:</span> <span class="pre">ForeignKey</span> <span class="pre"></span> <span class="pre">sophon.projects.models.ResearchProject</span></span></dt>
<dd><p>Il <code class="xref py py-obj docutils literal notranslate"><span class="pre">progetto</span></code> che include questo notebook.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">name:</span> <span class="pre">CharField</span></span></dt>
<dd><p>Il nome del notebook.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">locked_by:</span> <span class="pre">ForeignKey</span> <span class="pre"></span> <span class="pre">django.contrib.auth.models.User</span></span></dt>
<dd><p>L'<code class="xref py py-obj docutils literal notranslate"><span class="pre">utente</span></code> che ha richiesto il blocco del notebook, o <a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">None</span></code></a> in caso il notebook non sia bloccato.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">container_image:</span> <span class="pre">CharField</span> <span class="pre">[&quot;ghcr.io/steffo99/sophon-jupyter&quot;]</span></span></dt>
<dd><p>Campo che specifica l'immagine che il client <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> dovrà avviare per questo notebook.</p>
<p>Al momento ne è supportata una sola per semplificare l'esperienza utente, ma altre possono essere aggiunte al file che definisce il modello per permettere agli utenti di scegliere tra più immagini.</p>
<div class="admonition note">
<p class="admonition-title">Nota</p>
<p>Al momento, Sophon si aspetta che tutte le immagini specificate espongano un server web sulla porta <code class="docutils literal notranslate"><span class="pre">8888</span></code>, e supportino il protocollo di autenticazione di Jupyter, ovvero che sia possibile raggiungere il container ai seguenti indirizzi: <code class="samp docutils literal notranslate"><em><span class="pre">PROTOCOLLO</span></em><span class="pre">://immagine:8888/lab?token=</span><em><span class="pre">TOKEN</span></em></code> e <code class="samp docutils literal notranslate"><em><span class="pre">PROTOCOLLO</span></em><span class="pre">://immagine:8888/tree?token=</span><em><span class="pre">TOKEN</span></em></code>.</p>
</div>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">jupyter_token:</span> <span class="pre">CharField</span></span></dt>
<dd><p>Il token segreto che verrà passato attraverso le variabili di ambiente al container Docker dell'oggetto per permettere solo agli utenti autorizzati di accedere a quest'ultimo.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">container_id:</span> <span class="pre">CharField</span></span></dt>
<dd><p>L'id assegnato dal daemon Docker al container di questo oggetto.</p>
<p>Se il notebook non è avviato, questo attributo varrà <a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">None</span></code></a>.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">port:</span> <span class="pre">IntegerField</span></span></dt>
<dd><p>La porta TCP locale assegnata al container Docker dell'oggetto nel caso in cui Sophon sia avviato in <code class="xref py py-obj docutils literal notranslate"><span class="pre">modalità</span> <span class="pre">sviluppo</span></code>.</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py">
<span class="sig-name descname"><span class="pre">internal_url:</span> <span class="pre">CharField</span></span></dt>
<dd><p>L'URL a cui è accessibile il container Docker dell'oggetto nel caso in cui Sophon non sia avviato in <code class="xref py py-obj docutils literal notranslate"><span class="pre">modalità</span> <span class="pre">sviluppo</span></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.log">
<em class="property"><span class="pre">property</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">log</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/logging.html#logging.Logger" title="(in Python v3.8)"><span class="pre">logging.Logger</span></a></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.log" title="Link a questa definizione"></a></dt>
<dd><p>Viene creato un <a class="reference external" href="https://docs.python.org/3.8/library/logging.html#logging.Logger" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">logging.Logger</span></code></a> per ogni oggetto della classe, in modo da facilitare il debug relativo ad uno specifico notebook.</p>
<p>Il nome del logger ha la forma <code class="samp docutils literal notranslate"><span class="pre">sophon.notebooks.models.Notebook.</span><em><span class="pre">NOTEBOOK_SLUG</span></em></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.enable_proxying">
<span class="sig-name descname"><span class="pre">enable_proxying</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><span class="pre">None</span></a></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.enable_proxying" title="Link a questa definizione"></a></dt>
<dd><p>Aggiunge l'indirizzo del notebook alla <code class="xref py py-obj docutils literal notranslate"><span class="pre">rubrica</span> <span class="pre">del</span> <span class="pre">proxy</span></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.disable_proxying">
<span class="sig-name descname"><span class="pre">disable_proxying</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><span class="pre">None</span></a></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.disable_proxying" title="Link a questa definizione"></a></dt>
<dd><p>Rimuove l'indirizzo del notebook dalla <code class="xref py py-obj docutils literal notranslate"><span class="pre">rubrica</span> <span class="pre">del</span> <span class="pre">proxy</span></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.sync_container">
<span class="sig-name descname"><span class="pre">sync_container</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">t.Optional</span><span class="p"><span class="pre">[</span></span><a class="reference external" href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container" title="(in Docker SDK for Python v6.0)"><span class="pre">docker.models.containers.Container</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.sync_container" title="Link a questa definizione"></a></dt>
<dd><p>Sincronizza lo stato dell'oggetto nel database con lo stato del container <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> nel sistema.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.create_container">
<span class="sig-name descname"><span class="pre">create_container</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container" title="(in Docker SDK for Python v6.0)"><span class="pre">docker.models.containers.Container</span></a></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.create_container" title="Link a questa definizione"></a></dt>
<dd><p>Crea e configura un container <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> per l'oggetto, con l'immagine specificata in <code class="xref py py-obj docutils literal notranslate"><span class="pre">container_image</span></code>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.start">
<span class="sig-name descname"><span class="pre">start</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><span class="pre">None</span></a></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.start" title="Link a questa definizione"></a></dt>
<dd><p>Tenta di creare e avviare un container <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> per l'oggetto, bloccando fino a quando esso non sarà avviato con <a class="reference internal" href="#sophon.notebooks.docker.sleep_until_container_has_started" title="sophon.notebooks.docker.sleep_until_container_has_started"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sleep_until_container_has_started</span></code></a>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.models.Notebook.stop">
<span class="sig-name descname"><span class="pre">stop</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><span class="pre">None</span></a></span></span><a class="headerlink" href="#sophon.notebooks.models.Notebook.stop" title="Link a questa definizione"></a></dt>
<dd><p>Arresta il container Docker dell'oggetto.</p>
</dd></dl>
</dd></dl>
</section>
<section id="module-sophon.notebooks.views">
<span id="viewset-dei-notebook"></span><h4>Viewset dei notebook<a class="headerlink" href="#module-sophon.notebooks.views" title="Link a questa intestazione"></a></h4>
<p>Come per il modulo <a class="reference internal" href="#module-sophon.projects" title="sophon.projects"><code class="xref py py-obj docutils literal notranslate"><span class="pre">sophon.projects</span></code></a>, vengono creati due viewset per interagire con i progetti di ricerca, basati entrambi su un viewset astratto che ne definisce le proprietà comuni.</p>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">NotebooksViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">SophonGroupViewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">metaclass</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">abc.ABCMeta</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.notebooks.views.NotebooksViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che effettua l'override di <code class="xref py py-obj docutils literal notranslate"><span class="pre">get_group_from_serializer</span></code> e definisce cinque azioni personalizzate per l'interazione con il notebook.</p>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksViewSet.sync">
<span class="sig-name descname"><span class="pre">sync</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.notebooks.views.NotebooksViewSet.sync" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che sincronizza lo stato dell'oggetto dell'API con quello del daemon Docker.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksViewSet.start">
<span class="sig-name descname"><span class="pre">start</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.notebooks.views.NotebooksViewSet.start" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che avvia il notebook con <a class="reference internal" href="#sophon.notebooks.models.Notebook.start" title="sophon.notebooks.models.Notebook.start"><code class="xref py py-obj docutils literal notranslate"><span class="pre">models.Notebook.start</span></code></a>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksViewSet.stop">
<span class="sig-name descname"><span class="pre">stop</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.notebooks.views.NotebooksViewSet.stop" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che arresta il notebook con <a class="reference internal" href="#sophon.notebooks.models.Notebook.stop" title="sophon.notebooks.models.Notebook.stop"><code class="xref py py-obj docutils literal notranslate"><span class="pre">models.Notebook.stop</span></code></a>.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksViewSet.lock">
<span class="sig-name descname"><span class="pre">lock</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.notebooks.views.NotebooksViewSet.lock" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che blocca il notebook impostando il campo <code class="xref py py-obj docutils literal notranslate"><span class="pre">models.Notebook.locked_by</span></code> all'utente che ha effettuato la richiesta.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksViewSet.unlock">
<span class="sig-name descname"><span class="pre">unlock</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">self</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Request</span></span></em>, <em class="sig-param"><span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwargs</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Response</span></span></span><a class="headerlink" href="#sophon.notebooks.views.NotebooksViewSet.unlock" title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che sblocca il notebook impostando il campo <code class="xref py py-obj docutils literal notranslate"><span class="pre">models.Notebook.locked_by</span></code> a <a class="reference external" href="https://docs.python.org/3.8/library/constants.html#None" title="(in Python v3.8)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">None</span></code></a>.</p>
</dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksBySlugViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">NotebooksBySlugViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">NotebooksViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.notebooks.views.NotebooksBySlugViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in lettura e scrittura che permette di interagire con tutti i notebook a cui l'utente loggato ha accesso.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/notebooks/by-slug/</span><em><span class="pre">NOTEBOOK_SLUG</span></em><span class="pre">/</span></code>.</p>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="sophon.notebooks.views.NotebooksByProjectViewSet">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">NotebooksByProjectViewSet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">NotebooksViewSet</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#sophon.notebooks.views.NotebooksByProjectViewSet" title="Link a questa definizione"></a></dt>
<dd><p>Viewset in lettura e scrittura che permette di interagire con i notebook a cui l'utente loggato ha accesso, filtrati per il progetto di appartenenza.</p>
<p>Accessibile all'URL <code class="samp docutils literal notranslate"><span class="pre">/api/notebooks/by-project/</span><em><span class="pre">PROJECT_SLUG</span></em><span class="pre">/</span><em><span class="pre">NOTEBOOK_SLUG</span></em><span class="pre">/</span></code>.</p>
</dd></dl>
</section>
</section>
<section id="containerizzazione-del-modulo-backend">
<span id="index-6"></span><h3>Containerizzazione del modulo backend<a class="headerlink" href="#containerizzazione-del-modulo-backend" title="Link a questa intestazione"></a></h3>
<p>Il modulo backend è incapsulato in un'immagine <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> basata sull'immagine ufficiale <a class="reference external" href="https://hub.docker.com/_/python">python:3.9.7-bullseye</a>.</p>
<p>L'immagine utilizza <code class="xref py py-obj docutils literal notranslate"><span class="pre">Poetry</span></code> per installare le dipendenze, poi esegue il file <code class="docutils literal notranslate"><span class="pre">docker_start.sh</span></code> riportato sotto che effettua le migrazioni, prepara i file statici di Django e <code class="xref py py-obj docutils literal notranslate"><span class="pre">prova</span> <span class="pre">a</span> <span class="pre">creare</span> <span class="pre">un</span> <span class="pre">superutente</span></code>, per poi avviare il progetto Django attraverso <code class="xref py py-mod docutils literal notranslate"><span class="pre">gunicorn</span></code> sulla porta 8000.</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>poetry run python -O ./manage.py migrate --no-input
poetry run python -O ./manage.py collectstatic --no-input
poetry run python -O ./manage.py initsuperuser
poetry run python -O -m gunicorn sophon.wsgi:application --workers<span class="o">=</span><span class="m">4</span> --bind<span class="o">=</span><span class="m">0</span>.0.0.0:8000 --timeout <span class="m">180</span>
</pre></div>
</div>
</section>
</section>
<section id="realizzazione-del-modulo-frontend">
<span id="index-7"></span><h2><span class="section-number">5.2. </span>Realizzazione del modulo frontend<a class="headerlink" href="#realizzazione-del-modulo-frontend" title="Link a questa intestazione"></a></h2>
<p>Il modulo frontend è stato realizzato come un package <code class="xref py py-obj docutils literal notranslate"><span class="pre">Node.js</span></code> denominato <code class="docutils literal notranslate"><span class="pre">&#64;steffo/sophon-frontend</span></code>, e poi <code class="xref py py-obj docutils literal notranslate"><span class="pre">containerizzato</span></code>, creando un'immagine <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> standalone, esattamente come per il <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">backend</span></code>.</p>
<section id="struttura-delle-directory">
<h3>Struttura delle directory<a class="headerlink" href="#struttura-delle-directory" title="Link a questa intestazione"></a></h3>
<p>Le directory di <code class="xref js js-mod docutils literal notranslate"><span class="pre">&#64;steffo45/sophon-frontend</span></code> sono strutturate nella seguente maniera:</p>
<dl class="simple">
<dt>src/components</dt><dd><p>Contiene i componenti React sia con le classi sia funzionali.</p>
</dd>
<dt>src/contexts</dt><dd><p>Contiene i contesti React creati con <code class="xref js js-func docutils literal notranslate"><span class="pre">React.createContext()</span></code>.</p>
</dd>
<dt>src/hooks</dt><dd><p>Contiene gli hook React personalizzati utilizzati nei componenti funzionali.</p>
</dd>
<dt>src/types</dt><dd><p>Contiene estensioni ai tipi base TypeScript, come ad esempio i tipi restituiti dalla web API del <a class="reference internal" href="../progetto/index.html#modulo-backend"><span class="std std-ref">Modulo backend</span></a>.</p>
</dd>
<dt>src/utils</dt><dd><p>Contiene varie funzioni di utility.</p>
</dd>
<dt>public</dt><dd><p>Contiene i file statici da servire assieme all'app.</p>
</dd>
</dl>
</section>
<section id="comunicazione-con-il-backend">
<h3>Comunicazione con il backend<a class="headerlink" href="#comunicazione-con-il-backend" title="Link a questa intestazione"></a></h3>
<p>Sono state sviluppate alcune funzioni di utilità per facilitare la comunicazione con il <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">backend</span></code>.</p>
<section id="axios">
<h4>Axios<a class="headerlink" href="#axios" title="Link a questa intestazione"></a></h4>
<p>Per effettuare richieste all'API web, si è deciso di utilizzare la libreria <code class="xref js js-mod docutils literal notranslate"><span class="pre">axios</span></code>, in quanto permette di creare dei &quot;client&quot; personalizzabili con varie proprietà.</p>
<p>In particolare, si è scelto di effettuare un fork della stessa, integrando anticipatamente una proposta di funzionalità che permette alle richieste di essere interrotte attraverso degli <code class="xref js js-class docutils literal notranslate"><span class="pre">AbortController()</span></code>.</p>
</section>
<section id="client-personalizzati">
<h4>Client personalizzati<a class="headerlink" href="#client-personalizzati" title="Link a questa intestazione"></a></h4>
<p>Per permettere all'utente di selezionare l'istanza da utilizzare e di comunicare con l'API con le proprie credenziali, si è scelto di creare client personalizzati partendo da due contesti.</p>
<p>All'interno di un contesto in cui è stata selezionata un'istanza (<code class="xref js js-data docutils literal notranslate"><span class="pre">InstanceContext</span></code>), viene creato un client dal seguente hook:</p>
<dl class="js function">
<dt class="sig sig-object js" id="useInstanceAxios">
<span class="sig-name descname"><span class="n"><span class="pre">useInstanceAxios</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config</span> <span class="pre">=</span> <span class="pre">{}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#useInstanceAxios" title="Link a questa definizione"></a></dt>
<dd><p>Questo hook specifica il <code class="docutils literal notranslate"><span class="pre">baseURL</span></code> del client Axios, impostandolo all'URL dell'istanza selezionata.</p>
</dd></dl>
<p>All'interno di un contesto in cui è stato effettuato l'accesso come utente (<code class="xref js js-data docutils literal notranslate"><span class="pre">AuthorizationContext</span></code>), viene creato invece un client dal seguente hook:</p>
<dl class="js function">
<dt class="sig sig-object js" id="useAuthorizedAxios">
<span class="sig-name descname"><span class="n"><span class="pre">useAuthorizedAxios</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config</span> <span class="pre">=</span> <span class="pre">{}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#useAuthorizedAxios" title="Link a questa definizione"></a></dt>
<dd><p>Questo hook specifica il valore dell'header <code class="docutils literal notranslate"><span class="pre">Authorization</span></code> da inviare in tutte le richieste effettuate a <code class="samp docutils literal notranslate"><span class="pre">Bearer</span> <em><span class="pre">TOKEN</span></em></code>, utilizzando il token ottenuto al momento dell'accesso.</p>
</dd></dl>
</section>
<section id="utilizzo-di-viewset">
<h4>Utilizzo di viewset<a class="headerlink" href="#utilizzo-di-viewset" title="Link a questa intestazione"></a></h4>
<p>Viene implementato un hook che si integra con i viewset di Django, fornendo un API semplificato per effettuare azioni su di essi.</p>
<dl class="js function">
<dt class="sig sig-object js" id="useViewSet-baseRoute-viewset">
<span id="useViewSet(baseRoute) → viewset"></span><span class="sig-name descname"><span class="n"><span class="pre">useViewSet(baseRoute)</span> <span class="pre"></span> <span class="pre">viewset</span></span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#useViewSet-baseRoute-viewset" title="Link a questa definizione"></a></dt>
<dd><p>Questo hook implementa tutte le azioni <code class="xref py py-mod docutils literal notranslate"><span class="pre">rest_framework</span></code> di un viewset in lettura e scrittura.</p>
<p>Richiede di essere chiamato all'interno di un <code class="xref js js-data docutils literal notranslate"><span class="pre">AuthorizationContext</span></code>.</p>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.list">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">list</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config</span> <span class="pre">=</span> <span class="pre">{}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.list" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Richiede la lista di tutte le risorse del viewset.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.retrieve">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">retrieve</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">config</span> <span class="pre">=</span> <span class="pre">{}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.retrieve" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Richiede i dettagli di una specifica risorsa del viewset.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.create">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">create</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.create" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Crea una nuova risorsa nel viewset.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.update">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">update</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">config</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.update" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Aggiorna una specifica risorsa nel viewset.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.destroy">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">destroy</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">pk</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">config</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.destroy" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Elimina una specifica risorsa dal viewset.</p>
</dd></dl>
<p>Viene inoltre fornito supporto per le azioni personalizzate.</p>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.command">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">command</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.command" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Permette azioni personalizzate su tutto il viewset.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="viewset.action">
<span class="sig-prename descclassname"><span class="n"><span class="pre">viewset</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">action</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">config</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#viewset.action" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Permette azioni personalizzate su uno specifico oggetto del viewset.</p>
</dd></dl>
</dd></dl>
</section>
<section id="emulazione-di-viewset">
<h4>Emulazione di viewset<a class="headerlink" href="#emulazione-di-viewset" title="Link a questa intestazione"></a></h4>
<p>Viene creato un hook che tiene traccia degli oggetti restituiti da un determinato viewset, ed emula i risultati delle azioni effettuate, minimizzando i rerender e ottenendo una ottima user experience.</p>
<dl class="js function">
<dt class="sig sig-object js" id="useManagedViewSet-baseRoute-pkKey-refreshOnMount-managed">
<span id="useManagedViewSet(baseRoute, pkKey, refreshOnMount) → managed"></span><span class="sig-name descname"><span class="n"><span class="pre">useManagedViewSet(baseRoute,</span> <span class="pre">pkKey,</span> <span class="pre">refreshOnMount)</span> <span class="pre"></span> <span class="pre">managed</span></span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#useManagedViewSet-baseRoute-pkKey-refreshOnMount-managed" title="Link a questa definizione"></a></dt>
<dd><dl class="js attribute">
<dt class="sig sig-object js" id="managed.viewset">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">viewset</span></span></span><a class="headerlink" href="#managed.viewset" title="Link a questa definizione"></a></dt>
<dd><p>Il viewset restituito da <code class="xref js js-func docutils literal notranslate"><span class="pre">useViewSet()</span></code>, utilizzato come interfaccia di basso livello per effettuare azioni.</p>
</dd></dl>
<dl class="js attribute">
<dt class="sig sig-object js" id="managed.state">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">state</span></span></span><a class="headerlink" href="#managed.state" title="Link a questa definizione"></a></dt>
<dd><p>Lo stato del viewset, che tiene traccia degli oggetti e delle azioni in corso su di essi.</p>
<p>Gli oggetti all'interno di esso sono istanze di <code class="xref js js-class docutils literal notranslate"><span class="pre">ManagedResource()</span></code>, create usando wrapper di <a class="reference internal" href="#managed.update" title="managed.update"><code class="xref js js-func docutils literal notranslate"><span class="pre">update()</span></code></a>, <a class="reference internal" href="#managed.destroy" title="managed.destroy"><code class="xref js js-func docutils literal notranslate"><span class="pre">destroy()</span></code></a> e <a class="reference internal" href="#managed.action" title="managed.action"><code class="xref js js-func docutils literal notranslate"><span class="pre">action()</span></code></a>, che permettono di modificare direttamente l'oggetto senza preoccuparsi dell'indice a cui si trova nell'array.</p>
</dd></dl>
<dl class="js attribute">
<dt class="sig sig-object js" id="managed.dispatch">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">dispatch</span></span></span><a class="headerlink" href="#managed.dispatch" title="Link a questa definizione"></a></dt>
<dd><p>Riduttore che permette di alterare lo <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a>.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="managed.refresh">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">refresh</span></span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#managed.refresh" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Ricarica gli oggetti del viewset.</p>
<p>Viene chiamata automaticamente al primo render se <code class="docutils literal notranslate"><span class="pre">refreshOnMount</span></code> è <code class="xref js js-data docutils literal notranslate"><span class="pre">True</span></code>.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="managed.create">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">create</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#managed.create" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Crea un nuovo oggetto nel viewset con i dati specificati come argomento, e lo aggiunge allo stato se la richiesta va a buon fine.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="managed.command">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">command</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">method</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">cmd</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#managed.command" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Esegue l'azione personalizzata <code class="docutils literal notranslate"><span class="pre">cmd</span></code> su tutto il viewset, utilizzando il metodo <code class="docutils literal notranslate"><span class="pre">method</span></code> e con i dati specificati in <code class="docutils literal notranslate"><span class="pre">data</span></code>.</p>
<p>Se la richiesta va a buon fine, il valore restituito dal backend sostituisce nello stato le risorse dell'intero viewset.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="managed.update">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">update</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">index</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#managed.update" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Modifica l'oggetto alla posizione <code class="docutils literal notranslate"><span class="pre">index</span></code> dell'array <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a> con i dati specificati in <code class="docutils literal notranslate"><span class="pre">data</span></code>.</p>
<p>Se la richiesta va a buon fine, la modifica viene anche applicata all'interno di <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a></p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="managed.destroy">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">destroy</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">index</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#managed.destroy" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Elimina l'oggetto alla posizione <code class="docutils literal notranslate"><span class="pre">index</span></code> dell'array <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a>.</p>
<p>Se la richiesta va a buon fine, l'oggetto viene eliminato anche da <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a>.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="managed.action">
<span class="sig-prename descclassname"><span class="n"><span class="pre">managed</span></span><span class="p"><span class="pre">.</span></span></span><span class="sig-name descname"><span class="n"><span class="pre">action</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">index</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">act</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#managed.action" title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <code class="xref js js-class docutils literal notranslate"><span class="pre">Promise()</span></code>.</p>
<p>Esegue l'azione personalizzata <code class="docutils literal notranslate"><span class="pre">act</span></code> sull'oggetto alla posizione <code class="docutils literal notranslate"><span class="pre">index</span></code> dell'array <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a>, utilizzando il metodo <code class="docutils literal notranslate"><span class="pre">method</span></code> e con i dati specificati in <code class="docutils literal notranslate"><span class="pre">data</span></code>.</p>
<p>Se la richiesta va a buon fine, il valore restituito dal backend sostituisce l'oggetto utilizzato in <a class="reference internal" href="#managed.state" title="managed.state"><code class="xref js js-attr docutils literal notranslate"><span class="pre">state</span></code></a>.</p>
</dd></dl>
</dd></dl>
</section>
</section>
<section id="contesti-innestati">
<span id="index-8"></span><h3>Contesti innestati<a class="headerlink" href="#contesti-innestati" title="Link a questa intestazione"></a></h3>
<p>Per minimizzare i re-render, l'applicazione è organizzata a &quot;contesti innestati&quot;.</p>
<section id="i-contesti">
<span id="index-9"></span><h4>I contesti<a class="headerlink" href="#i-contesti" title="Link a questa intestazione"></a></h4>
<p>Viene definito un contesto per ogni tipo di risorsa selezionabile nell'interfaccia.</p>
<p>Essi sono, in ordine dal più esterno al più interno:</p>
<ol class="arabic simple">
<li><p><code class="xref js js-data docutils literal notranslate"><span class="pre">InstanceContext</span></code> (<code class="xref py py-obj docutils literal notranslate"><span class="pre">Istanza</span></code>)</p></li>
<li><p><code class="xref js js-data docutils literal notranslate"><span class="pre">AuthorizationContext</span></code> (<code class="xref py py-obj docutils literal notranslate"><span class="pre">Utente</span></code>)</p></li>
<li><p><code class="xref js js-data docutils literal notranslate"><span class="pre">GroupContext</span></code> (<code class="xref py py-obj docutils literal notranslate"><span class="pre">Gruppo</span> <span class="pre">di</span> <span class="pre">ricerca</span></code>)</p></li>
<li><p><code class="xref js js-data docutils literal notranslate"><span class="pre">ProjectContext</span></code> (<code class="xref py py-obj docutils literal notranslate"><span class="pre">Progetto</span> <span class="pre">di</span> <span class="pre">ricerca</span></code>)</p></li>
<li><p><code class="xref js js-data docutils literal notranslate"><span class="pre">NotebookContext</span></code> (<code class="xref py py-obj docutils literal notranslate"><span class="pre">Notebook</span></code>)</p></li>
</ol>
<section id="contenuto-dei-contesti">
<span id="index-10"></span><h5>Contenuto dei contesti<a class="headerlink" href="#contenuto-dei-contesti" title="Link a questa intestazione"></a></h5>
<p>Questi contesti possono avere tre tipi di valori: <code class="xref js js-data docutils literal notranslate"><span class="pre">undefined</span></code> se ci si trova al di fuori del contesto, <code class="xref js js-data docutils literal notranslate"><span class="pre">null</span></code> se non è stato selezionato alcun oggetto oppure <strong>l'oggetto selezionato</strong> se esso esiste.</p>
</section>
</section>
<section id="segmenti-di-url">
<span id="index-11"></span><h4>Segmenti di URL<a class="headerlink" href="#segmenti-di-url" title="Link a questa intestazione"></a></h4>
<p>Si è definita la seguente struttura per gli URL del frontend di Sophon, in modo che essi identificassero universalmente una risorsa e che essi fossero human-readable.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>/i/{ISTANZA}
/l/logged-in
/g/{GROUP_SLUG}
/p/{PROJECT_SLUG}
/n/{NOTEBOOK_SLUG}
/
</pre></div>
</div>
<p>Ciascuna riga nel listato sopra rappresenta un <em>segmento di URL</em>.</p>
<section id="parsing-dei-segmenti-di-url">
<h5>Parsing dei segmenti di URL<a class="headerlink" href="#parsing-dei-segmenti-di-url" title="Link a questa intestazione"></a></h5>
<p>Il parsing di questi segmenti viene effettuato dalla seguente funzione:</p>
<dl class="js function">
<dt class="sig sig-object js" id="parsePathSegment-path-parsed-regex-key-next-ParsedPath">
<span id="parsePathSegment({path, parsed, regex, key, next}) → ParsedPath"></span><span class="sig-name descname"><span class="n"><span class="pre">parsePathSegment({path,</span> <span class="pre">parsed,</span> <span class="pre">regex,</span> <span class="pre">key,</span> <span class="pre">next})</span> <span class="pre"></span> <span class="pre">ParsedPath</span></span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#parsePathSegment-path-parsed-regex-key-next-ParsedPath" title="Link a questa definizione"></a></dt>
<dd><p>Funzione ricorsiva per la cattura di un segmento, che riempie ad ogni chiamata una chiave dell'oggetto <code class="docutils literal notranslate"><span class="pre">ParsedPath</span></code>.</p>
<dl class="field-list simple">
<dt class="field-odd">Parametri</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>path</strong> -- La stringa del percorso ancora da parsare.</p></li>
<li><p><strong>parsed</strong> -- L'oggetto <code class="docutils literal notranslate"><span class="pre">ParsedPath</span></code> riempito con i segmenti trovati fino ad ora.</p></li>
<li><p><strong>regex</strong> -- Una regular expression usata per catturare un segmento. Il <strong>primo gruppo di cattura</strong> sarà il valore che verrà mantenuto e inserito nel <code class="docutils literal notranslate"><span class="pre">ParsedPath</span></code>.</p></li>
<li><p><strong>key</strong> -- La chiave a cui inserire il valore catturato all'interno del <code class="docutils literal notranslate"><span class="pre">ParsedPath</span></code>.</p></li>
<li><p><strong>next</strong> -- Callback della prossima funzione da chiamare.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<p>Un esempio di URL per il notebook <code class="docutils literal notranslate"><span class="pre">my-first-notebook</span></code> all'interno della istanza demo di Sophon potrebbe essere:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>/i/https:api.prod.sophon.steffo.eu:
/l/logged-in
/g/my-first-group
/p/my-first-project
/n/my-first-notebook
/
</pre></div>
</div>
</section>
<section id="breadcrumbs">
<span id="index-12"></span><h5>Breadcrumbs<a class="headerlink" href="#breadcrumbs" title="Link a questa intestazione"></a></h5>
<p>È possibile vedere tutti i segmenti di URL dalla barra superiore dell'interfaccia grafica, detta <em>barra dei breadcrumbs</em>.</p>
<figure class="align-default" id="id2">
<img alt="../../_images/breadcrumbs.png" src="../../_images/breadcrumbs.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.1 </span><span class="caption-text">La barra dei breadcrumbs. Ci si trova attualmente sulla pagina del gruppo <code class="docutils literal notranslate"><span class="pre">my-first-group</span></code>.</span><a class="headerlink" href="#id2" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
<p>Nel caso il parsing dei segmenti fallisca, la barra dei breadcrumbs è in grado di mostrare un errore, e di permettere all'utente di riprendere la navigazione ad uno dei segmenti trovati.</p>
<figure class="align-default" id="id3">
<img alt="../../_images/breadcrumbs_error.png" src="../../_images/breadcrumbs_error.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.2 </span><span class="caption-text">La barra dei breadcrumbs in errore. È possibile riprendere la navigazione dalla pagina di selezione istanza o di login.</span><a class="headerlink" href="#id3" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
</section>
<section id="componenti-contestuali">
<span id="index-13"></span><h4>Componenti contestuali<a class="headerlink" href="#componenti-contestuali" title="Link a questa intestazione"></a></h4>
<p>Per ciascun contesto sono stati realizzati vari componenti.</p>
<p>I più significativi comuni a tutti i contesti sono i <code class="xref py py-obj docutils literal notranslate"><span class="pre">ResourcePanel</span></code> e le <code class="xref py py-obj docutils literal notranslate"><span class="pre">ListBox</span></code>.</p>
<dl class="js function">
<dt class="sig sig-object js" id="ResourcePanel">
<span class="sig-name descname"><span class="n"><span class="pre">ResourcePanel</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">{...}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#ResourcePanel" title="Link a questa definizione"></a></dt>
<dd><p>Panello che rappresenta un'<code class="xref py py-obj docutils literal notranslate"><span class="pre">entità</span> <span class="pre">di</span> <span class="pre">Sophon</span></code>, diviso in quattro parti:</p>
<ul class="simple">
<li><p>icona (a sinistra)</p></li>
<li><p>nome della risorsa (a destra dell'icona)</p></li>
<li><p>bottoni (a destra)</p></li>
<li><p>testo (a sinistra dei bottoni)</p></li>
</ul>
<figure class="align-default" id="id4">
<img alt="../../_images/resource_panel.png" src="../../_images/resource_panel.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.3 </span><span class="caption-text">Un <code class="xref py py-obj docutils literal notranslate"><span class="pre">ResourcePanel</span></code> rappresentante un <code class="xref py py-obj docutils literal notranslate"><span class="pre">gruppo</span> <span class="pre">di</span> <span class="pre">ricerca</span></code>.</span><a class="headerlink" href="#id4" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="ListBox">
<span class="sig-name descname"><span class="n"><span class="pre">ListBox</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">{...}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#ListBox" title="Link a questa definizione"></a></dt>
<dd><p>Riquadro che mostra le risorse di un <code class="xref py py-obj docutils literal notranslate"><span class="pre">useManagedViewSet</span></code> raffigurandole come tanti <code class="xref py py-obj docutils literal notranslate"><span class="pre">ResourcePanel</span></code>.</p>
<figure class="align-default" id="id5">
<img alt="../../_images/list_box.png" src="../../_images/list_box.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.4 </span><span class="caption-text">Una <code class="xref py py-obj docutils literal notranslate"><span class="pre">ListBox</span></code> che mostra l'elenco di notebook in un progetto.</span><a class="headerlink" href="#id5" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</dd></dl>
</section>
<section id="routing-basato-sui-contesti">
<span id="index-14"></span><h4>Routing basato sui contesti<a class="headerlink" href="#routing-basato-sui-contesti" title="Link a questa intestazione"></a></h4>
<p>I valori dei contesti vengono utilizzati per selezionare i componenti da mostrare all'utente nell'interfaccia grafica attraverso i seguenti componenti:</p>
<dl class="js function">
<dt class="sig sig-object js" id="ResourceRouter">
<span class="sig-name descname"><span class="n"><span class="pre">ResourceRouter</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">{selection</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">unselectedRoute</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">selectedRoute}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#ResourceRouter" title="Link a questa definizione"></a></dt>
<dd><p>Componente che sceglie se renderizzare <code class="docutils literal notranslate"><span class="pre">unselectedRoute</span></code> o <code class="docutils literal notranslate"><span class="pre">selectedRoute</span></code> in base alla <em>nullità</em> o <em>non-nullità</em> di <code class="docutils literal notranslate"><span class="pre">selection</span></code>.</p>
</dd></dl>
<dl class="js function">
<dt class="sig sig-object js" id="ViewSetRouter">
<span class="sig-name descname"><span class="n"><span class="pre">ViewSetRouter</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">{viewSet</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">unselectedRoute</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">selectedRoute</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pathSegment</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">pkKey}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#ViewSetRouter" title="Link a questa definizione"></a></dt>
<dd><p>Componente basato su <a class="reference internal" href="#ResourceRouter" title="ResourceRouter"><code class="xref js js-func docutils literal notranslate"><span class="pre">ResourceRouter()</span></code></a> che seleziona automaticamente l'elemento del viewset avente il valore del segmento di percorso <code class="docutils literal notranslate"><span class="pre">pathSegment</span></code> alla chiave <code class="docutils literal notranslate"><span class="pre">pkKey</span></code>.</p>
</dd></dl>
<p>Ad esempio, <a class="reference internal" href="#ViewSetRouter" title="ViewSetRouter"><code class="xref js js-func docutils literal notranslate"><span class="pre">ViewSetRouter()</span></code></a> viene esteso specificatamente per il contesto del gruppo, creando il seguente componente.</p>
<dl class="js function">
<dt class="sig sig-object js" id="GroupRouter">
<span class="sig-name descname"><span class="n"><span class="pre">GroupRouter</span></span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">{...props}</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#GroupRouter" title="Link a questa definizione"></a></dt>
<dd><p>Implementato come:</p>
<div class="highlight-jsx notranslate"><div class="highlight"><pre><span></span><span class="p">&lt;</span><span class="nt">ViewSetRouter</span>
<span class="p">{</span><span class="na">...props</span><span class="p">}</span>
<span class="na">viewSet</span><span class="o">=</span><span class="p">{</span><span class="nx">useManagedViewSet</span><span class="p">&lt;</span><span class="nt">SophonResearchGroup</span><span class="p">&gt;(</span><span class="s2">&quot;/api/core/groups/&quot;</span><span class="p">,</span> <span class="s2">&quot;slug&quot;</span><span class="p">)}</span>
<span class="na">pathSegment</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;researchGroup&quot;</span><span class="p">}</span>
<span class="na">pkKey</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;slug&quot;</span><span class="p">}</span>
<span class="p">/&gt;</span>
</pre></div>
</div>
</dd></dl>
</section>
<section id="albero-completo-dei-contesti">
<h4>Albero completo dei contesti<a class="headerlink" href="#albero-completo-dei-contesti" title="Link a questa intestazione"></a></h4>
<p>L'insieme di tutti i contesti è definito come componente <code class="xref js js-func docutils literal notranslate"><span class="pre">App()</span></code> nel modulo &quot;principale&quot; <code class="docutils literal notranslate"><span class="pre">App.tsx</span></code>.</p>
<p>Se ne riassume la struttura in pseudocodice:</p>
<div class="highlight-html notranslate"><div class="highlight"><pre><span></span><span class="p">&lt;</span><span class="nt">InstanceContext</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">InstanceRouter</span><span class="p">&gt;</span>
unselected:
<span class="p">&lt;</span><span class="nt">InstanceSelect</span><span class="p">&gt;</span>
selected:
<span class="p">&lt;</span><span class="nt">AuthorizationContext</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">AuthorizationRouter</span><span class="p">&gt;</span>
unselected:
<span class="p">&lt;</span><span class="nt">UserLogin</span><span class="p">&gt;</span>
selected:
<span class="p">&lt;</span><span class="nt">GroupContext</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">GroupRouter</span><span class="p">&gt;</span>
unselected:
<span class="p">&lt;</span><span class="nt">GroupSelect</span><span class="p">&gt;</span>
selected:
<span class="p">&lt;</span><span class="nt">ProjectContext</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">ProjectRouter</span><span class="p">&gt;</span>
unselected:
<span class="p">&lt;</span><span class="nt">ProjectSelect</span><span class="p">&gt;</span>
selected:
<span class="p">&lt;</span><span class="nt">NotebookContext</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">NotebookRouter</span><span class="p">&gt;</span>
unselected:
<span class="p">&lt;</span><span class="nt">NotebookSelect</span><span class="p">&gt;</span>
selected:
<span class="p">&lt;</span><span class="nt">NotebookDetails</span><span class="p">&gt;</span>
</pre></div>
</div>
</section>
<section id="altri-contesti">
<h4>Altri contesti<a class="headerlink" href="#altri-contesti" title="Link a questa intestazione"></a></h4>
<p>All'interno di Sophon sono presenti anche altri due contesti, utilizzati a scopo di semplificare e ottimizzare il codice.</p>
<section id="tema">
<span id="index-15"></span><h5>Tema<a class="headerlink" href="#tema" title="Link a questa intestazione"></a></h5>
<p>Il tema dell'istanza è implementato come uno speciale contesto globale <code class="xref js js-data docutils literal notranslate"><span class="pre">ThemeContext</span></code> che riceve i dettagli dell'istanza a cui si è collegati dall'<code class="xref js js-data docutils literal notranslate"><span class="pre">InstanceContext</span></code>.</p>
<p>Ciò permette di sincronizzare il tema della webapp con quello dell'istanza di Sophon selezionata.</p>
<figure class="align-default" id="id6">
<img alt="../../_images/login_royalblue.png" src="../../_images/login_royalblue.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.5 </span><span class="caption-text">La schermata di login dell'istanza dimostrativa di Sophon, che utilizza il tema &quot;Royal Blue&quot;.</span><a class="headerlink" href="#id6" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
<section id="cache">
<span id="index-16"></span><h5>Cache<a class="headerlink" href="#cache" title="Link a questa intestazione"></a></h5>
<p>Viene salvato l'elenco di tutti i membri dell'<code class="xref py py-obj docutils literal notranslate"><span class="pre">istanza</span></code> in uno speciale contesto <code class="xref js js-data docutils literal notranslate"><span class="pre">CacheContext</span></code> in modo da poter risolvere gli id degli utenti al loro username senza dover effettuare ulteriori richieste.</p>
<p>Questa funzionalità al momento viene usata per risolvere i nomi dei membri in un gruppo e il nome dell'utente che ha bloccato un notebook: in entrambi i casi, vengono restituiti dal <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">backend</span></code> solamente gli ID numerici dei relativi utenti, pertanto è necessario risolverli attraverso il contesto cache.</p>
<figure class="align-default" id="id7">
<img alt="../../_images/group_members.png" src="../../_images/group_members.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.6 </span><span class="caption-text">L'elenco dei membri appartenenti al gruppo &quot;My First Group&quot;.</span><a class="headerlink" href="#id7" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
<figure class="align-default" id="id8">
<img alt="../../_images/notebook_lock.png" src="../../_images/notebook_lock.png" />
<figcaption>
<p><span class="caption-number">Figura 5.2.7 </span><span class="caption-text">Il nome di un utente che ha bloccato un notebook. (In questo caso, &quot;root&quot;.)</span><a class="headerlink" href="#id8" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
</section>
</section>
<section id="containerizzazione-del-modulo-frontend">
<span id="index-17"></span><h3>Containerizzazione del modulo frontend<a class="headerlink" href="#containerizzazione-del-modulo-frontend" title="Link a questa intestazione"></a></h3>
<p>Il modulo frontend è incapsulato in un'immagine <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> basata sull'immagine ufficiale <a class="reference external" href="https://hub.docker.com/_/node">node:16.11.1-bullseye</a>.</p>
<p>L'immagine installa le dipendenze del modulo con <code class="xref py py-obj docutils literal notranslate"><span class="pre">Yarn</span></code>, per poi eseguire il comando <code class="docutils literal notranslate"><span class="pre">yarn</span> <span class="pre">run</span> <span class="pre">serve</span></code>, che avvia la procedura di preparazione della pagina e la rende disponibile su un webserver locale alla porta 3000.</p>
</section>
</section>
<section id="realizzazione-del-modulo-proxy">
<span id="index-18"></span><h2><span class="section-number">5.3. </span>Realizzazione del modulo proxy<a class="headerlink" href="#realizzazione-del-modulo-proxy" title="Link a questa intestazione"></a></h2>
<p>Il modulo proxy consiste in un file di configurazione di <code class="xref py py-obj docutils literal notranslate"><span class="pre">Apache</span> <span class="pre">HTTP</span> <span class="pre">Server</span></code>.</p>
<p>Il file di configurazione abilita i moduli httpd <a class="reference external" href="https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html">rewrite</a>, <a class="reference external" href="https://httpd.apache.org/docs/2.4/mod/mod_proxy.html">proxy</a>, <a class="reference external" href="https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html">proxy_wstunnel</a> e <a class="reference external" href="https://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html">proxy_http</a>, impostando quest'ultimo per inoltrare l'header <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host">Host</a> alle pagine verso cui viene effettuato reverse proxying.</p>
<p>Inoltre, nel file di configurazione viene abilitato il <code class="docutils literal notranslate"><span class="pre">RewriteEngine</span></code>, che viene utilizzato per effettuare reverse proxying secondo le seguenti regole:</p>
<ol class="arabic">
<li><p>Tutte le richieste verso <code class="docutils literal notranslate"><span class="pre">static.</span></code> prefisso ad <span class="target" id="index-19"></span><a class="reference internal" href="../installazione/4_configuring_compose.html#envvar-APACHE_PROXY_BASE_DOMAIN"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">APACHE_PROXY_BASE_DOMAIN</span></code></a> vengono processate direttamente dal webserver, utilizzando i file disponibili nella cartella <code class="docutils literal notranslate"><span class="pre">/var/www/html/django-static</span></code> che gli vengono forniti dal volume <code class="docutils literal notranslate"><span class="pre">django-static</span></code> del <a class="reference internal" href="../progetto/index.html#modulo-backend"><span class="std std-ref">Modulo backend</span></a>.</p>
<div class="highlight-apacheconf notranslate"><div class="highlight"><pre><span></span><span class="c"># If ENV:APACHE_PROXY_BASE_DOMAIN equals HTTP_HOST</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;static.%{ENV:APACHE_PROXY_BASE_DOMAIN} %{HTTP_HOST}&quot;</span> <span class="s2">&quot;^([^ ]+) \1$&quot;</span> [NC]
<span class="c"># Process the request yourself</span>
<span class="nb">RewriteRule</span> <span class="s2">&quot;.?&quot;</span> - [L]
</pre></div>
</div>
</li>
<li><p>Tutte le richieste verso <span class="target" id="index-20"></span><a class="reference internal" href="../installazione/4_configuring_compose.html#envvar-APACHE_PROXY_BASE_DOMAIN"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">APACHE_PROXY_BASE_DOMAIN</span></code></a> senza nessun sottodominio vengono inoltrate al container Docker del <a class="reference internal" href="../progetto/index.html#modulo-frontend"><span class="std std-ref">Modulo frontend</span></a> utilizzando la risoluzione dei nomi di dominio di Docker Compose.</p>
<div class="highlight-apacheconf notranslate"><div class="highlight"><pre><span></span><span class="c"># If ENV:APACHE_PROXY_BASE_DOMAIN equals HTTP_HOST</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;%{ENV:APACHE_PROXY_BASE_DOMAIN} %{HTTP_HOST}&quot;</span> <span class="s2">&quot;^([^ ]+) \1$&quot;</span> [NC]
<span class="c"># Capture ENV:SOPHON_FRONTEND_NAME for substitution in the rewriterule</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;%{ENV:SOPHON_FRONTEND_NAME}&quot;</span> <span class="s2">&quot;^(.+)$&quot;</span> [NC]
<span class="c"># Forward to the frontend</span>
<span class="nb">RewriteRule</span> <span class="s2">&quot;/(.*)&quot;</span> <span class="s2">&quot;http://%1/$1&quot;</span> [P,L]
</pre></div>
</div>
</li>
<li><p>Tutte le richieste verso <code class="docutils literal notranslate"><span class="pre">api.</span></code> prefisso ad <span class="target" id="index-21"></span><a class="reference internal" href="../installazione/4_configuring_compose.html#envvar-APACHE_PROXY_BASE_DOMAIN"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">APACHE_PROXY_BASE_DOMAIN</span></code></a> vengono inoltrate al container Docker del <a class="reference internal" href="../progetto/index.html#modulo-backend"><span class="std std-ref">Modulo backend</span></a> utilizzando la risoluzione dei nomi di dominio di Docker Compose.</p>
<div class="highlight-apacheconf notranslate"><div class="highlight"><pre><span></span><span class="c"># If api. prefixed to ENV:APACHE_PROXY_BASE_DOMAIN equals HTTP_HOST</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;api.%{ENV:APACHE_PROXY_BASE_DOMAIN} %{HTTP_HOST}&quot;</span> <span class="s2">&quot;^([^ ]+) \1$&quot;</span> [NC]
<span class="c"># Capture ENV:SOPHON_BACKEND_NAME for substitution in the rewriterule</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;%{ENV:SOPHON_BACKEND_NAME}&quot;</span> <span class="s2">&quot;^(.+)$&quot;</span> [NC]
<span class="c"># Forward to the backend</span>
<span class="nb">RewriteRule</span> <span class="s2">&quot;/(.*)&quot;</span> <span class="s2">&quot;http://%1/$1&quot;</span> [P,L]
</pre></div>
</div>
</li>
<li><p>Carica in memoria la rubrica dei notebook generata dal <a class="reference internal" href="../progetto/index.html#modulo-backend"><span class="std std-ref">Modulo backend</span></a> e disponibile in <code class="docutils literal notranslate"><span class="pre">/run/sophon/proxy/proxy.dbm</span></code> attraverso il volume <code class="docutils literal notranslate"><span class="pre">proxy-data</span></code>, assegnandogli il nome di <code class="docutils literal notranslate"><span class="pre">sophonproxy</span></code>.</p>
<div class="highlight-apacheconf notranslate"><div class="highlight"><pre><span></span><span class="c"># Create a map between the proxy file generated by Sophon and Apache</span>
<span class="nb">RewriteMap</span> <span class="s2">&quot;sophonproxy&quot;</span> <span class="s2">&quot;dbm=gdbm:/run/sophon/proxy/proxy.dbm&quot;</span>
</pre></div>
</div>
</li>
<li><p>Effettua il proxying dei websocket verso i notebook mappati dalla rubrica <code class="docutils literal notranslate"><span class="pre">sophonproxy</span></code>.</p>
<div class="highlight-apacheconf notranslate"><div class="highlight"><pre><span></span><span class="c"># If this is any other subdomain of ENV:APACHE_PROXY_BASE_DOMAIN</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;.%{ENV:APACHE_PROXY_BASE_DOMAIN} %{HTTP_HOST}&quot;</span> <span class="s2">&quot;^([^ ]+) [^ ]+\1$&quot;</span> [NC]
<span class="c"># If this is a websocket connection</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;%{HTTP:Connection}&quot;</span> <span class="s2">&quot;Upgrade&quot;</span> [NC]
<span class="nb">RewriteCond</span> <span class="s2">&quot;%{HTTP:Upgrade}&quot;</span> <span class="s2">&quot;websocket&quot;</span> [NC]
<span class="c"># Forward to the notebook</span>
<span class="nb">RewriteRule</span> <span class="s2">&quot;/(.*)&quot;</span> <span class="s2">&quot;ws://${sophonproxy:%{HTTP_HOST}}/$1&quot;</span> [P,L]
</pre></div>
</div>
</li>
<li><p>Effettua il proxying delle richieste &quot;normali&quot; verso i notebook mappati dalla rubrica <code class="docutils literal notranslate"><span class="pre">sophonproxy</span></code>.</p>
<div class="highlight-apacheconf notranslate"><div class="highlight"><pre><span></span><span class="c"># If this is any other subdomain of ENV:APACHE_PROXY_BASE_DOMAIN</span>
<span class="nb">RewriteCond</span> <span class="s2">&quot;.%{ENV:APACHE_PROXY_BASE_DOMAIN} %{HTTP_HOST}&quot;</span> <span class="s2">&quot;^([^ ]+) [^ ]+\1$&quot;</span> [NC]
<span class="c"># Forward to the notebook</span>
<span class="nb">RewriteRule</span> <span class="s2">&quot;/(.*)&quot;</span> <span class="s2">&quot;http://${sophonproxy:%{HTTP_HOST}}/$1&quot;</span> [P,L]
</pre></div>
</div>
</li>
</ol>
<p>Tutte le regole usano il flag <code class="docutils literal notranslate"><span class="pre">L</span></code> di <code class="docutils literal notranslate"><span class="pre">RewriteRule</span></code>, che porta il motore di rewriting a ignorare tutte le regole successive, come il <code class="docutils literal notranslate"><span class="pre">return</span></code> di una funzione di un linguaggio di programmazione imperativo.</p>
<section id="containerizzazione-del-modulo-proxy">
<span id="index-22"></span><h3>Containerizzazione del modulo proxy<a class="headerlink" href="#containerizzazione-del-modulo-proxy" title="Link a questa intestazione"></a></h3>
<p>Il modulo proxy è incapsulato in un'immagine <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> basata sull'immagine ufficiale <a class="reference external" href="https://hub.docker.com/_/httpd">httpd:2.4</a>, che si limita ad applicare la configurazione personalizzata.</p>
</section>
</section>
<section id="realizzazione-del-modulo-jupyter">
<span id="index-23"></span><h2><span class="section-number">5.4. </span>Realizzazione del modulo Jupyter<a class="headerlink" href="#realizzazione-del-modulo-jupyter" title="Link a questa intestazione"></a></h2>
<p>Il <em>modulo Jupyter</em> consiste in un ambiente <a class="reference external" href="https://jupyter.org/">Jupyter</a> e <a class="reference external" href="https://jupyterlab.readthedocs.io/en/stable/">JupyterLab</a> modificato per una migliore integrazione con Sophon, in particolare con il <a class="reference internal" href="../progetto/index.html#modulo-frontend"><span class="std std-ref">Modulo frontend</span></a> e il <a class="reference internal" href="../progetto/index.html#modulo-backend"><span class="std std-ref">Modulo backend</span></a>.</p>
<p>È collocato all'interno del repository in <code class="docutils literal notranslate"><span class="pre">/jupyter</span></code>.</p>
<section id="sviluppo-del-tema-per-jupyter">
<h3>Sviluppo del tema per Jupyter<a class="headerlink" href="#sviluppo-del-tema-per-jupyter" title="Link a questa intestazione"></a></h3>
<p>Per rendere l'interfaccia grafica più consistente ed user-friendly, è stato sviluppato un tema colori personalizzato per <code class="xref py py-obj docutils literal notranslate"><span class="pre">JupyterLab</span></code>.</p>
<p>È stato creato partendo dal template <a class="reference external" href="https://github.com/jupyterlab/theme-cookiecutter">jupyterlab/theme-cookiecutter</a>, e in esso sono state modificati le variabili di stile (contenute nel file <code class="docutils literal notranslate"><span class="pre">style/variables.css</span></code>) usando i colori del tema &quot;The Sophonity&quot; di <code class="xref py py-obj docutils literal notranslate"><span class="pre">Bluelib</span></code>.</p>
<p>È stato poi pubblicato sull'<abbr title="Python Package Index">PyPI</abbr> e su <code class="xref py py-obj docutils literal notranslate"><span class="pre">npm</span></code>, permettendone l'uso a tutti gli utenti di JupyterLab.</p>
<div class="admonition note">
<p class="admonition-title">Nota</p>
<p>Per facilitarne la distribuzione e il riutilizzo anche esternamente a Sophon, il tema è stato creato in un repository <code class="xref py py-obj docutils literal notranslate"><span class="pre">Git</span></code> esterno a quello del progetto.</p>
</div>
</section>
<section id="estensione-del-container-docker-di-jupyter">
<h3>Estensione del container Docker di Jupyter<a class="headerlink" href="#estensione-del-container-docker-di-jupyter" title="Link a questa intestazione"></a></h3>
<p>Il <code class="docutils literal notranslate"><span class="pre">Dockerfile</span></code> del modulo ne crea un <code class="xref py py-obj docutils literal notranslate"><span class="pre">immagine</span> <span class="pre">Docker</span></code> in quattro fasi:</p>
<ol class="arabic">
<li><p><strong>Base</strong>: Parte dall'immagine base <code class="docutils literal notranslate"><span class="pre">jupyter/scipy-notebook</span></code> e ne altera i label.</p>
<div class="highlight-docker notranslate"><div class="highlight"><pre><span></span><span class="k">FROM</span> <span class="s">jupyter/scipy-notebook</span> <span class="k">AS</span> <span class="s">base</span>
<span class="c"># Set the maintainer label</span>
<span class="k">LABEL</span> <span class="nv">maintainer</span><span class="o">=</span><span class="s2">&quot;Stefano Pigozzi &lt;me@steffo.eu&gt;&quot;</span>
</pre></div>
</div>
</li>
<li><p><strong>Env</strong>: Configura le variabili di ambiente dell'immagine, attivando JupyterLab, configurando il riavvio automatico di Jupyter, la collaborazione real time e permettendo all'utente non-privilegiato di acquisire i privilegi di root attraverso il comando <code class="docutils literal notranslate"><span class="pre">sudo</span></code>.</p>
<div class="highlight-docker notranslate"><div class="highlight"><pre><span></span><span class="k">FROM</span> <span class="s">base</span> <span class="k">AS</span> <span class="s">env</span>
<span class="c"># Set useful envvars for Sophon notebooks</span>
<span class="k">ENV</span> <span class="nv">JUPYTER_ENABLE_LAB</span><span class="o">=</span>yes
<span class="k">ENV</span> <span class="nv">RESTARTABLE</span><span class="o">=</span>yes
<span class="k">ENV</span> <span class="nv">GRANT_SUDO</span><span class="o">=</span>yes
<span class="c"># Enable real time collaboration</span>
<span class="k">CMD</span> <span class="p">[</span><span class="s2">&quot;start-notebook.sh&quot;</span><span class="p">,</span> <span class="s2">&quot;--collaborative&quot;</span><span class="p">]</span>
</pre></div>
</div>
</li>
<li><p><strong>Extensions</strong>: Installa, abilita e configura le estensioni necessarie all'integrazione con Sophon (attualmente, soltanto il tema JupyterLab Sophon).</p>
<div class="highlight-docker notranslate"><div class="highlight"><pre><span></span><span class="k">FROM</span> <span class="s">env</span> <span class="k">AS</span> <span class="s">extensions</span>
<span class="c"># As the default user...</span>
<span class="k">USER</span><span class="s"> ${NB_UID}</span>
<span class="k">WORKDIR</span><span class="s"> &quot;${HOME}&quot;</span>
<span class="c"># Install the JupyterLab Sophon theme</span>
<span class="k">RUN</span> jupyter labextension install <span class="s2">&quot;jupyterlab_theme_sophon&quot;</span>
<span class="c"># Enable the JupyterLab Sophon theme</span>
<span class="k">RUN</span> jupyter labextension <span class="nb">enable</span> <span class="s2">&quot;jupyterlab_theme_sophon&quot;</span>
<span class="c"># Set the JupyterLab Sophon theme as default</span>
<span class="k">RUN</span> mkdir -p <span class="s1">&#39;.jupyter/lab/user-settings/@jupyterlab/apputils-extension/&#39;</span>
<span class="k">RUN</span> <span class="nb">echo</span> <span class="s1">&#39;{&quot;theme&quot;: &quot;JupyterLab Sophon&quot;}&#39;</span> &gt; <span class="s2">&quot;.jupyter/lab/user-settings/@jupyterlab/apputils-extension/themes.jupyterlab-settings&quot;</span>
</pre></div>
</div>
</li>
<li><p><strong>Healthcheck</strong>: Installa <a class="reference external" href="https://curl.se/">curl</a>, uno strumento in grado di effettuare richieste <abbr>HTTP (HyperText Transfer Protocol</abbr> da linea di comando, e configura la verifica dello <code class="xref py py-obj docutils literal notranslate"><span class="pre">stato</span> <span class="pre">di</span> <span class="pre">salute</span></code> dell'immagine, al fine di comunicare al <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">backend</span></code> il risultato di una richiesta di avvio.</p>
<div class="highlight-docker notranslate"><div class="highlight"><pre><span></span><span class="k">FROM</span> <span class="s">extensions</span> <span class="k">AS</span> <span class="s">healthcheck</span>
<span class="c"># As root...</span>
<span class="k">USER</span><span class="s"> root</span>
<span class="c"># Install curl</span>
<span class="k">RUN</span> apt-get update
<span class="k">RUN</span> apt-get install -y curl
<span class="c"># Use curl to check the health status</span>
<span class="k">HEALTHCHECK</span> --start-period<span class="o">=</span>5s --timeout<span class="o">=</span>5s --interval<span class="o">=</span>10s CMD <span class="o">[</span><span class="s2">&quot;curl&quot;</span>, <span class="s2">&quot;--output&quot;</span>, <span class="s2">&quot;/dev/null&quot;</span>, <span class="s2">&quot;http://localhost:8888&quot;</span><span class="o">]</span>
<span class="c"># We probably should go back to the default user</span>
<span class="k">USER</span><span class="s"> ${NB_UID}</span>
</pre></div>
</div>
</li>
</ol>
</section>
</section>
<section id="automazione-di-sviluppo">
<span id="index-24"></span><h2><span class="section-number">5.5. </span>Automazione di sviluppo<a class="headerlink" href="#automazione-di-sviluppo" title="Link a questa intestazione"></a></h2>
<p>Al fine di snellire lo sviluppo del software, è stato configurato lo strumento di automazione <a class="reference external" href="https://github.com/features/actions">GitHub Actions</a> per effettuare automaticamente alcuni compiti.</p>
<section id="scansione-automatica-delle-dipendenze">
<span id="index-25"></span><h3>Scansione automatica delle dipendenze<a class="headerlink" href="#scansione-automatica-delle-dipendenze" title="Link a questa intestazione"></a></h3>
<p>È stato abilitato su <a class="reference internal" href="../progetto/index.html#github"><span class="std std-ref">GitHub</span></a> il supporto a <a class="reference external" href="https://docs.github.com/en/code-security/supply-chain-security/managing-vulnerabilities-in-your-projects-dependencies/configuring-dependabot-security-updates">Dependabot</a>, un software che scansiona le dipendenze dei vari moduli e notifica gli sviluppatori qualora una o più di esse siano vulnerabili ad exploit.</p>
<figure class="align-default" id="id9">
<img alt="../../_images/dependabot.png" src="../../_images/dependabot.png" />
<figcaption>
<p><span class="caption-number">Figura 5.5.1 </span><span class="caption-text">Alcune vulnerabilità rilevate da Dependabot all'interno delle dipendenze di Sophon.</span><a class="headerlink" href="#id9" title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
<section id="controllo-automatico-del-codice">
<span id="index-26"></span><h3>Controllo automatico del codice<a class="headerlink" href="#controllo-automatico-del-codice" title="Link a questa intestazione"></a></h3>
<p>Sono state configurate due azioni, <code class="docutils literal notranslate"><span class="pre">analyze-codeql-backend</span></code> e <code class="docutils literal notranslate"><span class="pre">analyze-codeql-frontend</span></code>, che usano <a class="reference external" href="https://codeql.github.com/">CodeQL</a> per scansionare staticamente il codice e identificare problemi o vulnerabilità.</p>
<p>La prima, <code class="docutils literal notranslate"><span class="pre">analyze-codeql-backend</span></code>, viene eseguita solo quando viene inviato a GitHub nuovo codice relativo al <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">backend</span></code>, ed effettua analisi specifiche a <code class="xref py py-obj docutils literal notranslate"><span class="pre">Python</span></code>, mentre la seconda, <code class="docutils literal notranslate"><span class="pre">analyze-codeql-frontend</span></code>, viene eseguita solo quando viene inviato nuovo codice del <code class="xref py py-obj docutils literal notranslate"><span class="pre">modulo</span> <span class="pre">frontend</span></code>, ed effettua analisi specifiche a JavaScript.</p>
<p>Si riportano due estratti relativi all'azione <code class="docutils literal notranslate"><span class="pre">analyze-codeql-backend</span></code>.</p>
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><span class="nt">on</span><span class="p">:</span>
<span class="nt">push</span><span class="p">:</span>
<span class="nt">branches</span><span class="p">:</span> <span class="p p-Indicator">[</span> <span class="nv">main</span> <span class="p p-Indicator">]</span>
<span class="nt">paths</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="s">&quot;backend/**&quot;</span>
</pre></div>
</div>
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><span class="nt">steps</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">Checkout repository</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">actions/checkout@v2</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">Initialize CodeQL</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">github/codeql-action/init@v1</span>
<span class="nt">with</span><span class="p">:</span>
<span class="nt">languages</span><span class="p">:</span> <span class="s">&quot;python&quot;</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">Perform CodeQL Analysis</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">github/codeql-action/analyze@v1</span>
</pre></div>
</div>
</section>
<section id="costruzione-automatica-delle-immagini-docker">
<h3>Costruzione automatica delle immagini Docker<a class="headerlink" href="#costruzione-automatica-delle-immagini-docker" title="Link a questa intestazione"></a></h3>
<p>Sono state configurate quattro azioni, <code class="docutils literal notranslate"><span class="pre">build-docker-frontend</span></code>, <code class="docutils literal notranslate"><span class="pre">build-docker-backend</span></code>, <code class="docutils literal notranslate"><span class="pre">build-docker-jupyter</span></code> e <code class="docutils literal notranslate"><span class="pre">build-docker-proxy</span></code>, che costruiscono automaticamente l'immagine <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a> di ciascun modulo qualora il relativo codice venga modificato.</p>
<p>L'immagine creata viene poi caricata sul <a class="reference external" href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry">GitHub Container Registry</a>, da cui può poi essere scaricata attraverso <a class="reference internal" href="../progetto/index.html#docker"><span class="std std-ref">Docker</span></a>.</p>
<p>Si riporta un estratto relativo all'azione <code class="docutils literal notranslate"><span class="pre">build-docker-proxy</span></code>.</p>
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><span class="nt">steps</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Checkout</span><span class="nv"> </span><span class="s">repository&quot;</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">actions/checkout@v2</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Login</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">GitHub</span><span class="nv"> </span><span class="s">Containers&quot;</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u Steffo99 --password-stdin</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Build</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">docker</span><span class="nv"> </span><span class="s">container</span><span class="nv"> </span><span class="s">`ghcr.io/steffo99/sophon-proxy:latest`&quot;</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">docker build ./proxy --tag ghcr.io/steffo99/sophon-proxy:latest</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Upload</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">container</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">GitHub</span><span class="nv"> </span><span class="s">Containers&quot;</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">docker push ghcr.io/steffo99/sophon-proxy:latest</span>
</pre></div>
</div>
</section>
<section id="costruzione-automatica-della-documentazione">
<span id="index-27"></span><h3>Costruzione automatica della documentazione<a class="headerlink" href="#costruzione-automatica-della-documentazione" title="Link a questa intestazione"></a></h3>
<p>Sono state configurate due azioni, <code class="docutils literal notranslate"><span class="pre">build-sphinx-report</span></code> e <code class="docutils literal notranslate"><span class="pre">build-sphinx-thesis</span></code>, che compilano rispettivamente la documentazione richiesta per l'esame di Tecnologie Web e questa stessa tesi usando lo strumento <a class="reference external" href="https://www.sphinx-doc.org/en/master/">Sphinx</a>.</p>
<p>La documentazione per l'esame viene compilata solo da <a class="reference external" href="https://docutils.sourceforge.io/rst.html">reStructuredText</a> ad HTML; la tesi, invece, viene compilata sia in HTML sia in PDF.</p>
<p>Si riporta un estratto relativo all'azione <code class="docutils literal notranslate"><span class="pre">build-sphinx-thesis</span></code>.</p>
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><span class="nt">latexpdf</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Build</span><span class="nv"> </span><span class="s">PDF</span><span class="nv"> </span><span class="s">document&quot;</span>
<span class="nt">runs-on</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">ubuntu-latest</span>
<span class="nt">steps</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Update</span><span class="nv"> </span><span class="s">apt</span><span class="nv"> </span><span class="s">repositories&quot;</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">sudo apt-get update -y</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Checkout</span><span class="nv"> </span><span class="s">repository&quot;</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">actions/checkout@v2</span>
<span class="nt">with</span><span class="p">:</span>
<span class="nt">lfs</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">true</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Checkout</span><span class="nv"> </span><span class="s">LFS</span><span class="nv"> </span><span class="s">objects&quot;</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">git lfs checkout</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Setup</span><span class="nv"> </span><span class="s">Python&quot;</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">actions/setup-python@v2</span>
<span class="nt">with</span><span class="p">:</span>
<span class="nt">python-version</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">3.9</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Setup</span><span class="nv"> </span><span class="s">Poetry&quot;</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">abatilo/actions-poetry@v2.0.0</span>
<span class="nt">with</span><span class="p">:</span>
<span class="nt">poetry-version</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">1.1.11</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Install</span><span class="nv"> </span><span class="s">LaTeX</span><span class="nv"> </span><span class="s">packages&quot;</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">sudo apt-get install -y latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-luatex fonts-ebgaramond fonts-ebgaramond-extra fonts-firacode xindy</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Install</span><span class="nv"> </span><span class="s">backend</span><span class="nv"> </span><span class="s">dependencies&quot;</span>
<span class="nt">working-directory</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">backend/</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">poetry install --no-interaction</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Find</span><span class="nv"> </span><span class="s">Poetry</span><span class="nv"> </span><span class="s">Python</span><span class="nv"> </span><span class="s">environment&quot;</span>
<span class="nt">working-directory</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">backend/</span>
<span class="nt">run</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">echo &quot;pythonLocation=$(poetry env list --full-path | cut -f1 -d&#39; &#39;)/bin&quot; &gt;&gt; $GITHUB_ENV</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Build</span><span class="nv"> </span><span class="s">LaTeX</span><span class="nv"> </span><span class="s">document</span><span class="nv"> </span><span class="s">with</span><span class="nv"> </span><span class="s">Sphinx&quot;</span>
<span class="nt">working-directory</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">thesis/</span>
<span class="nt">run</span><span class="p">:</span> <span class="p p-Indicator">|</span>
<span class="no">source $pythonLocation/activate</span>
<span class="no">make latexpdf</span>
<span class="p p-Indicator">-</span> <span class="nt">name</span><span class="p">:</span> <span class="s">&quot;Upload</span><span class="nv"> </span><span class="s">build</span><span class="nv"> </span><span class="s">artifact&quot;</span>
<span class="nt">uses</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">actions/upload-artifact@v2</span>
<span class="nt">with</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="s">&quot;thesis.pdf&quot;</span>
<span class="nt">path</span><span class="p">:</span> <span class="s">&quot;thesis/build/latex/progettazioneesviluppodisophonapplicativocloudasupportodellaricerca.pdf&quot;</span>
</pre></div>
</div>
</section>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Piè di pagina">
<a href="../progetto/index.html" class="btn btn-neutral float-left" title="4. Progettazione di Sophon" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Precedente</a>
<a href="../risultato/index.html" class="btn btn-neutral float-right" title="6. Risultati ottenuti" accesskey="n" rel="next">Prossimo <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright 2022, Stefano Pigozzi.</p>
</div>
Realizzato con <a href="https://www.sphinx-doc.org/">Sphinx</a> e il tema
<a href="https://github.com/readthedocs/sphinx_rtd_theme">tema</a>
fornito da <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>