<liclass="toctree-l4"><aclass="reference internal"href="#module-sophon.core.management.commands.initsuperuser">Aggiunta di un nuovo comando di gestione</a></li>
<liclass="toctree-l4"><aclass="reference internal"href="#module-sophon.core.models">Modello base astratto</a></li>
<liclass="toctree-l4"><aclass="reference internal"href="#modello-di-autorizzazione-astratto">Modello di autorizzazione astratto</a></li>
<liclass="toctree-l4"><aclass="reference internal"href="#modello-dei-dettagli-dell-istanza">Modello dei dettagli dell'istanza</a></li>
<liclass="toctree-l4"><aclass="reference internal"href="#modello-del-gruppo-di-ricerca">Modello del gruppo di ricerca</a></li>
<liclass="toctree-l4"><aclass="reference internal"href="#module-sophon.core.permissions">Estensione ai permessi di Django</a></li>
<liclass="toctree-l3"><aclass="reference internal"href="#containerizzazione-del-modulo-frontend">Containerizzazione del modulo frontend</a></li>
</ul>
</li>
<liclass="toctree-l2"><aclass="reference internal"href="#realizzazione-del-modulo-proxy">5.3. Realizzazione del modulo proxy</a><ul>
<liclass="toctree-l3"><aclass="reference internal"href="#containerizzazione-del-modulo-proxy">Containerizzazione del modulo proxy</a></li>
</ul>
</li>
<liclass="toctree-l2"><aclass="reference internal"href="#realizzazione-del-modulo-jupyter">5.4. Realizzazione del modulo Jupyter</a><ul>
<liclass="toctree-l3"><aclass="reference internal"href="#sviluppo-del-tema-per-jupyter">Sviluppo del tema per Jupyter</a></li>
<liclass="toctree-l3"><aclass="reference internal"href="#estensione-del-container-docker-di-jupyter">Estensione del container Docker di Jupyter</a></li>
</ul>
</li>
<liclass="toctree-l2"><aclass="reference internal"href="#automazione-di-sviluppo">5.5. Automazione di sviluppo</a><ul>
<liclass="toctree-l3"><aclass="reference internal"href="#scansione-automatica-delle-dipendenze">Scansione automatica delle dipendenze</a></li>
<liclass="toctree-l3"><aclass="reference internal"href="#controllo-automatico-del-codice">Controllo automatico del codice</a></li>
<liclass="toctree-l3"><aclass="reference internal"href="#costruzione-automatica-delle-immagini-docker">Costruzione automatica delle immagini Docker</a></li>
<liclass="toctree-l3"><aclass="reference internal"href="#costruzione-automatica-della-documentazione">Costruzione automatica della documentazione</a></li>
<spanid="index-0"></span><h1><spanclass="section-number">5. </span>Realizzazione di Sophon<aclass="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>
<sectionid="realizzazione-del-modulo-backend">
<spanid="index-1"></span><h2><spanclass="section-number">5.1. </span>Realizzazione del modulo backend<aclass="headerlink"href="#realizzazione-del-modulo-backend"title="Link a questa intestazione"></a></h2>
<p>Il modulo backend è stato realizzato come un package <aclass="reference internal"href="../progetto/index.html#python"><spanclass="std std-ref">Python</span></a> denominato <codeclass="docutils literal notranslate"><spanclass="pre">sophon</span></code>, e poi <aclass="reference internal"href="#containerizzazione-del-modulo-backend"><spanclass="std std-ref">containerizzato</span></a>, creando un'immagine <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> standalone.</p>
<sectionid="module-sophon">
<spanid="il-project-django"></span><h3>Il project Django<aclass="headerlink"href="#module-sophon"title="Link a questa intestazione"></a></h3>
<p>Il package è stato creato utilizzando l'utility <codeclass="docutils literal notranslate"><spanclass="pre">startproject</span></code> di Django, la quale crea una cartella di script <aclass="reference internal"href="../progetto/index.html#python"><spanclass="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 <aclass="reference internal"href="#module-sophon.admin"><spanclass="std std-ref">app di amministrazione personalizzata</span></a>, il <aclass="reference internal"href="#module-sophon.settings"><spanclass="std std-ref">caricamento dinamico delle impostazioni</span></a> e vari <aclass="reference internal"href="#module-sophon.auth1"><spanclass="std std-ref">miglioramenti all'autenticazione</span></a></p>
<sectionid="module-sophon.admin">
<spanid="app-di-amministrazione-personalizzata"></span><h4>App di amministrazione personalizzata<aclass="headerlink"href="#module-sophon.admin"title="Link a questa intestazione"></a></h4>
<p>L'app di amministrazione di Django <aclass="reference external"href="http://docs.djangoproject.com/en/3.2/ref/contrib/admin/#module-django.contrib.admin"title="(in Django v3.2)"><codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">django.contrib.admin</span></code></a> viene personalizzata con la classe <aclass="reference internal"href="#sophon.admin.SophonAdminSite"title="sophon.admin.SophonAdminSite"><codeclass="xref any py py-class docutils literal notranslate"><spanclass="pre">SophonAdminSite</span></code></a>, che ne modifica alcuni parametri.</p>
<p>Inoltre, il template predefinito viene sovrascritto dal file <codeclass="docutils literal notranslate"><spanclass="pre">templates/admin/base.html</span></code>, che sostituisce il foglio di stile con uno personalizzato per Sophon.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonAdminSite</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">django.contrib.admin.AdminSite</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.admin.SophonAdminSite"title="Link a questa definizione"></a></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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonAdminConfig</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">django.contrib.admin.apps.AdminConfig</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.admin.SophonAdminConfig"title="Link a questa definizione"></a></dt>
<dd><p><aclass="reference internal"href="#sophon.admin.SophonAdminSite"title="sophon.admin.SophonAdminSite"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">SophonAdminSite</span></code></a> è selezionata come classe predefinita per il sito di amministrazione.</p>
<p><spanclass="caption-number">Figura 5.1.1 </span><spanclass="caption-text">Immagine della pagina principale dell'app di amministrazione.</span><aclass="headerlink"href="#id1"title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
<sectionid="module-sophon.settings">
<spanid="caricamento-dinamico-delle-impostazioni"></span><h4>Caricamento dinamico delle impostazioni<aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">settings.py</span></code>, rendendo la <aclass="reference internal"href="#containerizzazione-del-modulo-backend"><spanclass="std std-ref">containerizzazione</span></a> molto più semplice.</p>
<spanclass="n">log</span><spanclass="o">.</span><spanclass="n">warning</span><spanclass="p">(</span><spanclass="s2">"DJANGO_DATABASE_ENGINE was not set, defaulting to PostgreSQL"</span><spanclass="p">)</span>
<p>Inoltre, viene configurato il modulo <aclass="reference external"href="https://docs.python.org/3.8/library/logging.html#module-logging"title="(in Python v3.8)"><codeclass="xref any docutils literal notranslate"><spanclass="pre">logging</span></code></a> per emettere testo colorato di più facile comprensione usando il package <aclass="reference external"href="https://coloredlogs.readthedocs.io/en/latest/api.html#module-coloredlogs"title="(in coloredlogs v15.0)"><codeclass="xref any docutils literal notranslate"><spanclass="pre">coloredlogs</span></code></a>.</p>
<p>Una lista di tutte le variabili di ambiente di configurazione è riportata nel capitolo <aclass="reference internal"href="../installazione/index.html#installazione-di-sophon"><spanclass="std std-ref">Installazione di Sophon</span></a>.</p>
</section>
<sectionid="module-sophon.auth1">
<spanid="miglioramenti-all-autenticazione"></span><h4>Miglioramenti all'autenticazione<aclass="headerlink"href="#module-sophon.auth1"title="Link a questa intestazione"></a></h4>
<p>La classe <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.authentication.TokenAuthentication</span></code> viene modificata per ottenere un comportamento conforme allo standard della Bearer authentication.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">BearerTokenAuthentication</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">rest_framework.authentication.TokenAuthentication</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.auth1.BearerTokenAuthentication"title="Link a questa definizione"></a></dt>
<dd><p>Si configura <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code> per accettare header di autenticazione nella forma <codeclass="docutils literal notranslate"><spanclass="pre">Bearer</span><spanclass="pre"><token></span></code>, invece che il default di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code><codeclass="docutils literal notranslate"><spanclass="pre">Token</span><spanclass="pre"><token></span></code>.</p>
</dd></dl>
</dd></dl>
<spanclass="target"id="module-sophon.auth2"></span><p>La view <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.authtoken.views.ObtainAuthToken</span></code> viene estesa per aggiungere dati alla risposta di autenticazione riuscita.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">CustomObtainAuthToken</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">rest_framework.authtoken.views.ObtainAuthToken</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.auth2.CustomObtainAuthToken"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">post</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">*</span></span><spanclass="n"><spanclass="pre">args</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">**</span></span><spanclass="n"><spanclass="pre">kwargs</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.auth2.CustomObtainAuthToken.post"title="Link a questa definizione"></a></dt>
<dd><p>In particolare, viene aggiunta una chiave <codeclass="docutils literal notranslate"><spanclass="pre">user</span></code>, che contiene i dettagli sull'utente che ha effettuato il login.</p>
</dd></dl>
</dd></dl>
</section>
</section>
<sectionid="module-sophon.core">
<spanid="l-app-sophon-core"></span><h3>L'app Sophon Core<aclass="headerlink"href="#module-sophon.core"title="Link a questa intestazione"></a></h3>
<p>L'app <aclass="reference internal"href="#module-sophon.core"title="sophon.core"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="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>
<spanid="aggiunta-di-un-nuovo-comando-di-gestione"></span><h4>Aggiunta di un nuovo comando di gestione<aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">initsuperuser</span></code>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">Command</span></span><aclass="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 <spanclass="target"id="index-2"></span><aclass="reference internal"href="../installazione/4_configuring_compose.html#envvar-DJANGO_SU_USERNAME"><codeclass="xref std std-envvar docutils literal notranslate"><spanclass="pre">DJANGO_SU_USERNAME</span></code></a>, <spanclass="target"id="index-3"></span><aclass="reference internal"href="../installazione/4_configuring_compose.html#envvar-DJANGO_SU_EMAIL"><codeclass="xref std std-envvar docutils literal notranslate"><spanclass="pre">DJANGO_SU_EMAIL</span></code></a> e <spanclass="target"id="index-4"></span><aclass="reference internal"href="../installazione/4_configuring_compose.html#envvar-DJANGO_SU_PASSWORD"><codeclass="xref std std-envvar docutils literal notranslate"><spanclass="pre">DJANGO_SU_PASSWORD</span></code></a>.</p>
</dd></dl>
</section>
<sectionid="module-sophon.core.models">
<spanid="modello-base-astratto"></span><h4>Modello base astratto<aclass="headerlink"href="#module-sophon.core.models"title="Link a questa intestazione"></a></h4>
<p>Viene estesa la classe astratta <aclass="reference external"href="http://docs.djangoproject.com/en/3.2/ref/models/instances/#django.db.models.Model"title="(in Django v3.2)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">django.db.models.Model</span></code></a> con funzioni per stabilire il <aclass="reference internal"href="../progetto/index.html#livelli-di-accesso"><spanclass="std std-ref">livello di accesso</span></a> di un <aclass="reference internal"href="../progetto/index.html#utenti-in-sophon"><spanclass="std std-ref">utente</span></a> all'oggetto e per generare automaticamente i <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.serializers.ModelSerializer</span></code> in base ad esso.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonModel</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">django.db.models.Model</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.models.SophonModel"title="Link a questa definizione"></a></dt>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_fields</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><spanclass="pre">set</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.models.SophonModel.get_fields"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>il <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a> di nomi di campi che devono essere mostrati quando viene richiesto l'oggetto attraverso l'API.</p>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_editable_fields</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><spanclass="pre">set</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.models.SophonModel.get_editable_fields"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>il <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a> di nomi di campi di cui deve essere permessa la modifica se l'utente può modificare (<aclass="reference internal"href="#sophon.core.models.SophonModel.can_edit"title="sophon.core.models.SophonModel.can_edit"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">can_edit()</span></code></a>) l'oggetto.</p>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_administrable_fields</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><spanclass="pre">set</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.models.SophonModel.get_administrable_fields"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>il <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a> di nomi di campi di cui deve essere permessa la modifica se l'utente può amministrare (<aclass="reference internal"href="#sophon.core.models.SophonModel.can_admin"title="sophon.core.models.SophonModel.can_admin"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">can_admin()</span></code></a>) l'oggetto.</p>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_creation_fields</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><spanclass="pre">set</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.models.SophonModel.get_creation_fields"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>il <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#set"title="(in Python v3.8)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="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>
<sectionid="modello-di-autorizzazione-astratto">
<h4>Modello di autorizzazione astratto<aclass="headerlink"href="#modello-di-autorizzazione-astratto"title="Link a questa intestazione"></a></h4>
<p>Viene definito un nuovo modello astratto, basato su <aclass="reference internal"href="#sophon.core.models.SophonModel"title="sophon.core.models.SophonModel"><codeclass="xref any py py-class docutils literal notranslate"><spanclass="pre">SophonModel</span></code></a>, che permette di determinare i permessi dell'<aclass="reference internal"href="../progetto/index.html#utenti-in-sophon"><spanclass="std std-ref">utente</span></a> in base alla sua appartenenza al gruppo a cui è collegato l'oggetto implementatore.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonGroupModel</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonModel</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.models.SophonGroupModel"title="Link a questa definizione"></a></dt>
<emclass="property"><spanclass="pre">abstract</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_group</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference internal"href="#sophon.core.models.ResearchGroup"title="sophon.core.models.ResearchGroup"><spanclass="pre">ResearchGroup</span></a></span></span><aclass="headerlink"href="#sophon.core.models.SophonGroupModel.get_group"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>Il gruppo a cui appartiene l'oggetto.</p>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_access_to_edit</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference internal"href="#sophon.core.models.sophon.core.enums.SophonGroupAccess"title="sophon.core.models.sophon.core.enums.SophonGroupAccess"><spanclass="pre">sophon.core.enums.SophonGroupAccess</span></a></span></span><aclass="headerlink"href="#sophon.core.models.SophonGroupModel.get_access_to_edit"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>Il livello di autorità all'interno del gruppo necessario per modificare l'oggetto.</p>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_access_to_admin</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference internal"href="#sophon.core.models.sophon.core.enums.SophonGroupAccess"title="sophon.core.models.sophon.core.enums.SophonGroupAccess"><spanclass="pre">sophon.core.enums.SophonGroupAccess</span></a></span></span><aclass="headerlink"href="#sophon.core.models.SophonGroupModel.get_access_to_admin"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>Il livello di autorità all'interno del gruppo necessario per amministrare l'oggetto.</p>
<spanclass="sig-name descname"><spanclass="pre">get_access_serializer</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">user</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">User</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/typing.html#typing.Type"title="(in Python v3.8)"><spanclass="pre">typing.Type</span></a><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">rest_framework.serializers.ModelSerializer</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.models.SophonGroupModel.get_access_serializer"title="Link a questa definizione"></a></dt>
<dd><dlclass="field-list simple">
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>Restituisce il <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.serializers.ModelSerializer</span></code> adeguato al livello di autorità dell'utente.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-prename descclassname"><spanclass="pre">sophon.core.enums.</span></span><spanclass="sig-name descname"><spanclass="pre">SophonGroupAccess</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">enum.IntEnum</span></span></em><spanclass="sig-paren">)</span><aclass="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 <aclass="reference internal"href="../progetto/index.html#utenti-in-sophon"><spanclass="std std-ref">utente</span></a> può avere all'interno di un <aclass="reference internal"href="../progetto/index.html#gruppi-di-ricerca-in-sophon"><spanclass="std std-ref">gruppo di ricerca</span></a>.</p>
<h4>Modello dei dettagli dell'istanza<aclass="headerlink"href="#modello-dei-dettagli-dell-istanza"title="Link a questa intestazione"></a></h4>
<p>Viene creato il modello che rappresenta i dettagli dell'<aclass="reference internal"href="../progetto/index.html#istanza-in-sophon"><spanclass="std std-ref">istanza di Sophon</span></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonInstanceDetails</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonModel</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.models.SophonInstanceDetails"title="Link a questa definizione"></a></dt>
<dd><p>Impostando <codeclass="docutils literal notranslate"><spanclass="pre">1</span></code> come unica scelta per il campo della chiave primaria <codeclass="docutils literal notranslate"><spanclass="pre">id</span></code>, si crea un modello "singleton", ovvero un modello di cui può esistere un'istanza sola in tutto il database.</p>
<p>L'istanza unica viene creata dalla migrazione <codeclass="docutils literal notranslate"><spanclass="pre">0004_sophoninstancedetails.py</span></code>.</p>
<ddclass="field-odd"><p>La versione installata del pacchetto <aclass="reference internal"href="#module-sophon"title="sophon"><codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">sophon</span></code></a>.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<sectionid="modello-del-gruppo-di-ricerca">
<h4>Modello del gruppo di ricerca<aclass="headerlink"href="#modello-del-gruppo-di-ricerca"title="Link a questa intestazione"></a></h4>
<p>Viene creato il modello che rappresenta un <aclass="reference internal"href="../progetto/index.html#gruppi-di-ricerca-in-sophon"><spanclass="std std-ref">gruppo di ricerca</span></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchGroup</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonGroupModel</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.models.ResearchGroup"title="Link a questa definizione"></a></dt>
<dd><p>Elenco dei membri del gruppo. L'utente <codeclass="xref py py-attr docutils literal notranslate"><spanclass="pre">owner</span></code> è ignorato, in quanto è considerato sempre parte del gruppo.</p>
<dd><p>La <aclass="reference internal"href="../progetto/index.html#membri-e-modalita-di-accesso"><spanclass="std std-ref">modalità di accesso</span></a> del gruppo.</p>
</dd></dl>
</dd></dl>
</section>
<sectionid="module-sophon.core.permissions">
<spanid="estensione-ai-permessi-di-django"></span><h4>Estensione ai permessi di Django<aclass="headerlink"href="#module-sophon.core.permissions"title="Link a questa intestazione"></a></h4>
<p>I permessi di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code> vengono estesi con due nuove classi che utilizzano il <aclass="reference internal"href="#modello-di-autorizzazione-astratto"><spanclass="std std-ref">modello di autorizzazione astratto</span></a> precedentemente definito.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">Edit</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">rest_framework.permissions.BasePermission</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">Admin</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">rest_framework.permissions.BasePermission</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<sectionid="module-sophon.core.views">
<spanid="viewset-astratti"></span><h4>Viewset astratti<aclass="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 <aclass="reference internal"href="#sophon.core.models.SophonModel"title="sophon.core.models.SophonModel"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">models.SophonModel</span></code></a> e <aclass="reference internal"href="#sophon.core.models.SophonGroupModel"title="sophon.core.models.SophonGroupModel"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">models.SophonGroupModel</span></code></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ReadSophonViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">rest_framework.viewsets.ReadOnlyModelViewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass=abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.ReadSophonViewSet"title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende la classe base <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.viewsets.ReadOnlyModelViewSet</span></code> con metodi di utilità mancanti nell'implementazione originale, allacciandola inoltre a <aclass="reference internal"href="#sophon.core.models.SophonGroupModel"title="sophon.core.models.SophonGroupModel"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">models.SophonGroupModel</span></code></a>.</p>
<emclass="property"><spanclass="pre">abstract</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_queryset</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">QuerySet</span></span></span><aclass="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 <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">rest_framework.viewsets.ReadOnlyModelViewSet.get_queryset()</span></code>.</p>
<emclass="property"><spanclass="pre">property</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">permission_classes</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.ReadSophonViewSet.permission_classes"title="Link a questa definizione"></a></dt>
<dd><p>Sovrascrive il campo di classe <codeclass="xref py py-attr docutils literal notranslate"><spanclass="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 <aclass="reference internal"href="#sophon.core.views.ReadSophonViewSet.get_permission_classes"title="sophon.core.views.ReadSophonViewSet.get_permission_classes"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">get_permission_classes()</span></code></a>.</p>
<spanclass="sig-name descname"><spanclass="pre">get_serializer_class</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/typing.html#typing.Type"title="(in Python v3.8)"><spanclass="pre">typing.Type</span></a><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">Serializer</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.views.ReadSophonViewSet.get_serializer_class"title="Link a questa definizione"></a></dt>
<dd><p>Funzione che permette la selezione del <codeclass="xref py py-class docutils literal notranslate"><spanclass="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><ulclass="simple">
<li><p>il serializzatore <strong>in sola lettura</strong> per elencare gli oggetti (azione <codeclass="docutils literal notranslate"><spanclass="pre">list</span></code>);</p></li>
<li><p>il serializzatore <strong>di creazione</strong> per creare nuovi oggetti (azione <codeclass="docutils literal notranslate"><spanclass="pre">create</span></code>) e per generare i metadati del viewset (azione <codeclass="docutils literal notranslate"><spanclass="pre">metadata</span></code>);</p></li>
<li><p>il serializzatore ottenuto da <aclass="reference internal"href="#sophon.core.models.SophonGroupModel.get_access_serializer"title="sophon.core.models.SophonGroupModel.get_access_serializer"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">models.SophonGroupModel.get_access_serializer()</span></code></a> per la visualizzazione dettagliata (azione <codeclass="docutils literal notranslate"><spanclass="pre">retrieve</span></code>), la modifica (azioni <codeclass="docutils literal notranslate"><spanclass="pre">update</span></code> e <codeclass="docutils literal notranslate"><spanclass="pre">partial_update</span></code>) e l'eliminazione (azione <codeclass="docutils literal notranslate"><spanclass="pre">destroy</span></code>) di un singolo oggetto;</p></li>
<li><p>il serializzatore ottenuto da <aclass="reference internal"href="#sophon.core.views.ReadSophonViewSet.get_custom_serializer_classes"title="sophon.core.views.ReadSophonViewSet.get_custom_serializer_classes"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">get_custom_serializer_classes()</span></code></a> per le azioni personalizzate.</p></li>
<spanclass="sig-name descname"><spanclass="pre">get_custom_serializer_classes</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">t.Type</span><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">Serializer</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="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 <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.serializers.Serializer</span></code> utilizzare per le azioni personalizzate.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">WriteSophonViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">rest_framework.viewsets.ModelViewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">ReadSophonViewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass=abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.WriteSophonViewSet"title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende la classe base <aclass="reference internal"href="#sophon.core.views.ReadSophonViewSet"title="sophon.core.views.ReadSophonViewSet"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ReadSophonViewSet</span></code></a> aggiungendoci i metodi di <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">rest_framework.viewsets.ModelViewSet</span></code> che effettuano modifiche sugli oggetti.</p>
<p>Depreca i metodi <codeclass="docutils literal notranslate"><spanclass="pre">perform_*</span></code> di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code>, introducendone versioni estese con una signature diversa dal nome di <codeclass="docutils literal notranslate"><spanclass="pre">hook_*</span></code>.</p>
<spanclass="sig-name descname"><spanclass="pre">perform_create</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.WriteSophonViewSet.perform_create"title="Link a questa definizione"></a></dt>
<dd><divclass="deprecated">
<p><spanclass="versionmodified deprecated">Deprecato dalla versione 0.1.</span></p>
</div>
<p>Metodo di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code> rimosso da Sophon.</p>
<spanclass="sig-name descname"><spanclass="pre">perform_update</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.WriteSophonViewSet.perform_update"title="Link a questa definizione"></a></dt>
<dd><divclass="deprecated">
<p><spanclass="versionmodified deprecated">Deprecato dalla versione 0.1.</span></p>
</div>
<p>Metodo di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code> rimosso da Sophon.</p>
<spanclass="sig-name descname"><spanclass="pre">perform_destroy</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.WriteSophonViewSet.perform_destroy"title="Link a questa definizione"></a></dt>
<dd><divclass="deprecated">
<p><spanclass="versionmodified deprecated">Deprecato dalla versione 0.1.</span></p>
</div>
<p>Metodo di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code> rimosso da Sophon.</p>
<spanclass="sig-name descname"><spanclass="pre">hook_create</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><spanclass="pre">dict</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">,</span></span><spanclass="w"></span><spanclass="pre">typing.Any</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">create</span></code>.</p>
<dlclass="field-list simple">
<dtclass="field-odd">Parametri</dt>
<ddclass="field-odd"><p><strong>serializer</strong> -- Il <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Serializer</span></code> già "riempito" contenente i dati dell'oggetto che sta per essere creato.</p>
</dd>
<dtclass="field-even">Solleva</dt>
<ddclass="field-even"><p><aclass="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 <aclass="reference internal"href="#sophon.core.views.sophon.core.errors.HTTPException"title="sophon.core.views.sophon.core.errors.HTTPException"><codeclass="xref py py-exc docutils literal notranslate"><spanclass="pre">HTTPException</span></code></a> all'interno della funzione.</p>
</dd>
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>Un <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><codeclass="xref any docutils literal notranslate"><spanclass="pre">dict</span></code></a> da unire a quello del <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Serializer</span></code> per formare l'oggetto da creare.</p>
<spanclass="sig-name descname"><spanclass="pre">hook_update</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><spanclass="pre">dict</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">,</span></span><spanclass="w"></span><spanclass="pre">t.Any</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">update</span></code> e <codeclass="docutils literal notranslate"><spanclass="pre">partial_update</span></code>.</p>
<dlclass="field-list simple">
<dtclass="field-odd">Parametri</dt>
<ddclass="field-odd"><p><strong>serializer</strong> -- Il <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Serializer</span></code> già "riempito" contenente i dati dell'oggetto che sta per essere modificato.</p>
</dd>
<dtclass="field-even">Solleva</dt>
<ddclass="field-even"><p><aclass="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 <aclass="reference internal"href="#sophon.core.views.sophon.core.errors.HTTPException"title="sophon.core.views.sophon.core.errors.HTTPException"><codeclass="xref py py-exc docutils literal notranslate"><spanclass="pre">HTTPException</span></code></a> all'interno della funzione.</p>
</dd>
<dtclass="field-odd">Ritorna</dt>
<ddclass="field-odd"><p>Un <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">dict</span></code></a> da unire a quello del <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Serializer</span></code> per formare l'oggetto da modificare.</p>
<spanclass="sig-name descname"><spanclass="pre">hook_destroy</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><spanclass="pre">dict</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">,</span></span><spanclass="w"></span><spanclass="pre">typing.Any</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">destroy</span></code>.</p>
<dlclass="field-list simple">
<dtclass="field-odd">Solleva</dt>
<ddclass="field-odd"><p><aclass="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 <aclass="reference internal"href="#sophon.core.views.sophon.core.errors.HTTPException"title="sophon.core.views.sophon.core.errors.HTTPException"><codeclass="xref py py-exc docutils literal notranslate"><spanclass="pre">HTTPException</span></code></a> all'interno della funzione.</p>
<emclass="property"><spanclass="pre">exception</span><spanclass="w"></span></em><spanclass="sig-prename descclassname"><spanclass="pre">sophon.core.errors.</span></span><spanclass="sig-name descname"><spanclass="pre">HTTPException</span></span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">hook_*</span></code> di <aclass="reference internal"href="#sophon.core.views.WriteSophonViewSet"title="sophon.core.views.WriteSophonViewSet"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">WriteSophonViewSet</span></code></a> per interrompere l'azione in corso senza applicare le modifiche.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonGroupViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">WriteSophonViewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass</span></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="default_value"><spanclass="pre">abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.SophonGroupViewSet"title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende la classe base <aclass="reference internal"href="#sophon.core.views.WriteSophonViewSet"title="sophon.core.views.WriteSophonViewSet"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">WriteSophonViewSet</span></code></a> estendendo gli <codeclass="docutils literal notranslate"><spanclass="pre">hook_*</span></code> con verifiche dei permessi dell'utente che tenta di effettuare l'azione.</p>
<emclass="property"><spanclass="pre">abstract</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_group_from_serializer</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">serializer</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">models.ResearchGroup</span></span></span><aclass="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>
<dlclass="field-list simple">
<dtclass="field-odd">Parametri</dt>
<ddclass="field-odd"><p><strong>serializer</strong> -- Il <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Serializer</span></code> già "riempito" contenente i dati dell'oggetto.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<sectionid="viewset-concreti">
<h4>Viewset concreti<aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">UsersByIdViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ReadSophonViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">id</span></code>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">UsersByUsernameViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ReadSophonViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">username</span></code>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchGroupViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">WriteSophonViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<spanclass="sig-name descname"><spanclass="pre">join</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="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>
<spanclass="sig-name descname"><spanclass="pre">leave</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonInstanceDetailsView</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">APIView</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.views.SophonInstanceDetailsView"title="Link a questa definizione"></a></dt>
<dd><p>View che restituisce il valore attuale dell'unico oggetto <aclass="reference internal"href="#sophon.core.models.SophonInstanceDetails"title="sophon.core.models.SophonInstanceDetails"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">models.SophonInstanceDetails</span></code></a>.</p>
<spanid="pagina-di-amministrazione"></span><h4>Pagina di amministrazione<aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchGroupAdmin</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonAdmin</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonInstanceDetails</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonAdmin</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<sectionid="module-sophon.core.tests">
<spanid="testing-in-sophon-core"></span><h4>Testing in Sophon Core<aclass="headerlink"href="#module-sophon.core.tests"title="Link a questa intestazione"></a></h4>
<p>Per verificare che i <aclass="reference internal"href="#module-sophon.core.models"><spanclass="std std-ref">modelli</span></a> e <aclass="reference internal"href="#module-sophon.core.views"><spanclass="std std-ref">viewset</span></a> funzionassero correttamente e non avessero problemi di <aclass="reference internal"href="../progetto/index.html#sicurezza"><spanclass="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>
<sectionid="test-case-generici">
<h4>Test case generici<aclass="headerlink"href="#test-case-generici"title="Link a questa intestazione"></a></h4>
<p>Vengono definiti alcuni test case generici per facilitare le interazioni tra <codeclass="docutils literal notranslate"><spanclass="pre">APITestCase</span></code> e viewset.</p>
<divclass="admonition note">
<pclass="admonition-title">Nota</p>
<p>I nomi delle funzioni usano nomi con capitalizzazione inconsistente, in quanto lo stesso modulo <aclass="reference external"href="https://docs.python.org/3.8/library/unittest.html#module-unittest"title="(in Python v3.8)"><codeclass="xref any docutils literal notranslate"><spanclass="pre">unittest</span></code></a> non rispetta lo stile suggerito in <spanclass="target"id="index-5"></span><aclass="pep reference external"href="https://www.python.org/dev/peps/pep-0008"><strong>PEP 8</strong></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">BetterAPITestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">APITestCase</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.tests.BetterAPITestCase"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">as_user</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">username</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">password</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a></span><spanclass="w"></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="w"></span><spanclass="default_value"><spanclass="pre">None</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/typing.html#typing.ContextManager"title="(in Python v3.8)"><spanclass="pre">typing.ContextManager</span></a><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><spanclass="pre">None</span></a><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="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>
<spanclass="sig-name descname"><spanclass="pre">assertData</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">ReturnDict</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">expected</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><spanclass="pre">dict</span></a></span></em><spanclass="sig-paren">)</span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">expected</span></code>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ReadSophonTestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">BetterAPITestCase</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass</span></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="default_value"><spanclass="pre">abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="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 <aclass="reference internal"href="#sophon.core.views.ReadSophonViewSet"title="sophon.core.views.ReadSophonViewSet"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">views.ReadSophonViewSet</span></code></a>.</p>
<emclass="property"><spanclass="pre">classmethod</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">get_basename</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">cls</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a></span></span><aclass="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>
<spanclass="sig-name descname"><spanclass="pre">list</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">rest_framework.response.Response</span></span></span><aclass="headerlink"href="#sophon.core.tests.ReadSophonTestCase.list"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">retrieve</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">rest_framework.response.Response</span></span></span><aclass="headerlink"href="#sophon.core.tests.ReadSophonTestCase.retrieve"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">custom_detail</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">method</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">action</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><spanclass="pre">dict</span></a></span><spanclass="w"></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="w"></span><spanclass="default_value"><spanclass="pre">None</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">rest_framework.response.Response</span></span></span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">code</span></code>, e restituiscono i dati contenuti nella risposta se l'azione è riuscita (<codeclass="docutils literal notranslate"><spanclass="pre">200</span><spanclass="pre"><=</span><spanclass="pre">code</span><spanclass="pre"><</span><spanclass="pre">300</span></code>)</p>
<spanclass="sig-name descname"><spanclass="pre">assertActionList</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">code</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span><spanclass="w"></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="w"></span><spanclass="default_value"><spanclass="pre">200</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">typing.Optional</span><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">ReturnDict</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.tests.ReadSophonTestCase.assertActionList"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">assertActionRetrieve</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">code</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span><spanclass="w"></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="w"></span><spanclass="default_value"><spanclass="pre">200</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">typing.Optional</span><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">ReturnDict</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.tests.ReadSophonTestCase.assertActionRetrieve"title="Link a questa definizione"></a></dt>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">WriteSophonTestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ReadSophonTestCase</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass</span></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="default_value"><spanclass="pre">abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.tests.WriteSophonTestCase"title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che estende <aclass="reference internal"href="#sophon.core.tests.ReadSophonTestCase"title="sophon.core.tests.ReadSophonTestCase"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ReadSophonTestCase</span></code></a> con le azioni di un <aclass="reference internal"href="#sophon.core.views.WriteSophonViewSet"title="sophon.core.views.WriteSophonViewSet"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">views.WriteSophonViewSet</span></code></a>.</p>
<spanclass="sig-name descname"><spanclass="pre">create</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">rest_framework.response.Response</span></span></span><aclass="headerlink"href="#sophon.core.tests.WriteSophonTestCase.create"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">update</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">rest_framework.response.Response</span></span></span><aclass="headerlink"href="#sophon.core.tests.WriteSophonTestCase.update"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">destroy</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">rest_framework.response.Response</span></span></span><aclass="headerlink"href="#sophon.core.tests.WriteSophonTestCase.destroy"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">assertActionCreate</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">code</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span><spanclass="w"></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="w"></span><spanclass="default_value"><spanclass="pre">201</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">typing.Optional</span><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">ReturnDict</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.tests.WriteSophonTestCase.assertActionCreate"title="Link a questa definizione"></a></dt>
<spanclass="sig-name descname"><spanclass="pre">assertActionDestroy</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">code</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span><spanclass="w"></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="w"></span><spanclass="default_value"><spanclass="pre">200</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">typing.Optional</span><spanclass="p"><spanclass="pre">[</span></span><spanclass="pre">ReturnDict</span><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="headerlink"href="#sophon.core.tests.WriteSophonTestCase.assertActionDestroy"title="Link a questa definizione"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<sectionid="test-case-concreti">
<h4>Test case concreti<aclass="headerlink"href="#test-case-concreti"title="Link a questa intestazione"></a></h4>
<p>Vengono testate tutte le view dell'app tramite <aclass="reference internal"href="#sophon.core.tests.BetterAPITestCase"title="sophon.core.tests.BetterAPITestCase"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">BetterAPITestCase</span></code></a> e tutti i viewset dell'app tramite <aclass="reference internal"href="#sophon.core.tests.ReadSophonTestCase"title="sophon.core.tests.ReadSophonTestCase"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ReadSophonTestCase</span></code></a> e <aclass="reference internal"href="#sophon.core.tests.WriteSophonTestCase"title="sophon.core.tests.WriteSophonTestCase"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">WriteSophonTestCase</span></code></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">UsersByIdTestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ReadSophonTestCase</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.tests.UsersByIdTestCase"title="Link a questa definizione"></a></dt>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">UsersByUsernameTestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ReadSophonTestCase</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.tests.UsersByUsernameTestCase"title="Link a questa definizione"></a></dt>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchGroupTestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">WriteSophonTestCase</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.tests.ResearchGroupTestCase"title="Link a questa definizione"></a></dt>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">SophonInstanceDetailsTestCase</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">BetterAPITestCase</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.core.tests.SophonInstanceDetailsTestCase"title="Link a questa definizione"></a></dt>
<dd></dd></dl>
</section>
</section>
<sectionid="module-sophon.projects">
<spanid="l-app-sophon-projects"></span><h3>L'app Sophon Projects<aclass="headerlink"href="#module-sophon.projects"title="Link a questa intestazione"></a></h3>
<p>L'app <aclass="reference internal"href="#module-sophon.projects"title="sophon.projects"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.projects</span></code></a> è un app secondaria che dipende da <aclass="reference internal"href="#module-sophon.core"title="sophon.core"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.core</span></code></a> che introduce in Sophon il concetto di <aclass="reference internal"href="../progetto/index.html#progetti-di-ricerca-in-sophon"><spanclass="std std-ref">progetto di ricerca</span></a>.</p>
<divclass="admonition note">
<pclass="admonition-title">Nota</p>
<p>L'app <aclass="reference internal"href="#module-sophon.projects"title="sophon.projects"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.projects</span></code></a> teoricamente è opzionale, in quanto il modulo backend può funzionare senza di essa, e può essere rimossa dal modulo <aclass="reference internal"href="#module-sophon.settings"title="sophon.settings"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.settings</span></code></a>.</p>
<p>Non è però possibile rimuoverla nella versione finale distribuita, in quanto il modulo <aclass="reference internal"href="#module-sophon.settings"title="sophon.settings"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.settings</span></code></a> non è modificabile dall'esterno, e in quanto il <aclass="reference internal"href="../progetto/index.html#modulo-frontend"><spanclass="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 <aclass="reference internal"href="#module-sophon.projects"title="sophon.projects"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.projects</span></code></a> non sarà più possibile usare l'app <aclass="reference internal"href="#module-sophon.notebooks"title="sophon.notebooks"><codeclass="xref any py py-mod docutils literal notranslate"><spanclass="pre">sophon.notebooks</span></code></a>, in quanto dipende da essa.</p>
</div>
<sectionid="module-sophon.projects.models">
<spanid="modello-del-progetto-di-ricerca"></span><h4>Modello del progetto di ricerca<aclass="headerlink"href="#module-sophon.projects.models"title="Link a questa intestazione"></a></h4>
<p>Viene introdotto un modello concreto che rappresenta un <aclass="reference internal"href="../progetto/index.html#progetti-di-ricerca-in-sophon"><spanclass="std std-ref">progetto di ricerca</span></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchProject</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonGroupModel</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.projects.models.ResearchProject"title="Link a questa definizione"></a></dt>
<dd><p>Lo <codeclass="xref py py-attr docutils literal notranslate"><spanclass="pre">slug</span></code> del gruppo di ricerca al quale appartiene il progetto.</p>
<dd><p>La <aclass="reference internal"href="../progetto/index.html#visibilita-dei-progetti"><spanclass="std std-ref">visibilità del progetto</span></a>.</p>
</dd></dl>
</dd></dl>
</section>
<sectionid="module-sophon.projects.views">
<spanid="viewset-del-gruppo-di-ricerca"></span><h4>Viewset del gruppo di ricerca<aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchProjectViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonGroupViewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass</span></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="default_value"><spanclass="pre">abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.projects.views.ResearchProjectViewSet"title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che effettua l'override di <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">get_group_from_serializer()</span></code> per entrambi i viewset che seguono.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchProjectsBySlugViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ResearchProjectViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchProjectsByGroupViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">ResearchProjectViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<spanid="amministrazione-del-gruppo-di-ricerca"></span><h4>Amministrazione del gruppo di ricerca<aclass="headerlink"href="#module-sophon.projects.admin"title="Link a questa intestazione"></a></h4>
<p>Il modello <aclass="reference internal"href="#sophon.projects.models.ResearchProject"title="sophon.projects.models.ResearchProject"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">models.ResearchProject</span></code></a> viene registrato nella pagina di amministrazione attraverso la seguente classe:</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ResearchProjectAdmin</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">sophon.core.admin.SophonAdmin</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<sectionid="module-sophon.notebooks">
<spanid="l-app-sophon-notebooks"></span><h3>L'app Sophon Notebooks<aclass="headerlink"href="#module-sophon.notebooks"title="Link a questa intestazione"></a></h3>
<p>L'app <aclass="reference internal"href="#module-sophon.notebooks"title="sophon.notebooks"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">sophon.notebooks</span></code></a> è un app secondaria che dipende da <aclass="reference internal"href="#module-sophon.projects"title="sophon.projects"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">sophon.projects</span></code></a> che introduce in Sophon il concetto di <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">notebook</span></code>.</p>
<divclass="admonition note">
<pclass="admonition-title">Nota</p>
<p>L'app <aclass="reference internal"href="#module-sophon.notebooks"title="sophon.notebooks"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">sophon.notebooks</span></code></a> teoricamente è opzionale, in quanto il modulo backend può funzionare senza di essa, e può essere rimossa dal modulo <aclass="reference internal"href="#module-sophon.settings"title="sophon.settings"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">sophon.settings</span></code></a>.</p>
<p>Non è però possibile rimuoverla nella versione finale distribuita, in quanto il modulo <aclass="reference internal"href="#module-sophon.settings"title="sophon.settings"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">sophon.settings</span></code></a> non è modificabile dall'esterno, e in quanto il <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">frontend</span></code> non prevede questa funzionalità e si aspetta che i percorsi API relativi all'app siano disponibili.</p>
</div>
<sectionid="funzionamento-di-un-notebook">
<h4>Funzionamento di un notebook<aclass="headerlink"href="#funzionamento-di-un-notebook"title="Link a questa intestazione"></a></h4>
<p>Internamente, un notebook non è altro che un container <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> accessibile ad un determinato indirizzo il cui stato è sincronizzato con un oggetto del database del <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">backend</span></code>.</p>
<sectionid="modalita-sviluppo">
<h5>Modalità sviluppo<aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">proxy</span></code> non è in esecuzione, ed è possibile collegarsi direttamente ai container all'indirizzo IP locale <codeclass="docutils literal notranslate"><spanclass="pre">127.0.0.1</span></code>.</p>
<p>Il <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">proxy</span></code> è in esecuzione all'interno di un container Docker, e si collega ai <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">moduli</span><spanclass="pre">Jupyter</span></code> attraverso i relativi network Docker tramite una <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">rubrica</span></code>.</p></li>
</ul>
</section>
</section>
<sectionid="module-sophon.notebooks.apache">
<spanid="gestione-della-rubrica-del-proxy"></span><h4>Gestione della rubrica del proxy<aclass="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 <aclass="reference external"href="https://docs.python.org/3.8/library/dbm.html#module-dbm.gnu"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">dbm.gnu</span></code></a>, supportato da HTTPd.</p>
<p>La rubrica mappa gli URL pubblici dei notebook a URL privati relativi al <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">proxy</span></code>, in modo da effettuare reverse proxying <strong>dinamico</strong>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">ApacheDB</span></span><aclass="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 <aclass="reference external"href="https://docs.python.org/3.8/library/dbm.html#module-dbm.gnu"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">dbm.gnu</span></code></a> come se quest'ultimo fosse un <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#dict"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">dict</span></code></a> con supporto a chiavi e valori <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">str</span></code></a> e <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#bytes"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">bytes</span></code></a>.</p>
<emclass="property"><spanclass="pre">static</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">convert_to_bytes</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">item</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">typing.Union</span><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a><spanclass="p"><spanclass="pre">,</span></span><spanclass="w"></span><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#bytes"title="(in Python v3.8)"><spanclass="pre">bytes</span></a><spanclass="p"><spanclass="pre">]</span></span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#bytes"title="(in Python v3.8)"><spanclass="pre">bytes</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.apache.ApacheDB.convert_to_bytes"title="Link a questa definizione"></a></dt>
<dd><p>Tutte le <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">str</span></code></a> passate a questa classe vengono convertite in <aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#bytes"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="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>
<sectionid="assegnazione-porta-effimera">
<h4>Assegnazione porta effimera<aclass="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>
<spanclass="sig-name descname"><spanclass="pre">get_ephemeral_port</span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/functions.html#int"title="(in Python v3.8)"><spanclass="pre">int</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.apache.get_ephemeral_port"title="Link a questa definizione"></a></dt>
<dd><p>Questa funzione apre e chiude immediatamente un <aclass="reference external"href="https://docs.python.org/3.8/library/socket.html#socket.socket"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">socket.socket</span></code></a> all'indirizzo <codeclass="docutils literal notranslate"><spanclass="pre">localhost:0</span></code> in modo da ricevere dal sistema operativo un numero di porta sicuramente libero.</p>
</dd></dl>
</section>
<sectionid="module-sophon.notebooks.docker">
<spanid="connessione-al-daemon-docker"></span><h4>Connessione al daemon Docker<aclass="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 <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">docker</span></code>.</p>
<spanclass="sig-name descname"><spanclass="pre">get_docker_client</span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">docker.DockerClient</span></span></span><aclass="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><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>
<sectionid="controllo-dello-stato-di-salute">
<h4>Controllo dello stato di salute<aclass="headerlink"href="#controllo-dello-stato-di-salute"title="Link a questa intestazione"></a></h4>
<p>Il modulo <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">docker</span></code> viene esteso implementando supporto per l'istruzione <codeclass="docutils literal notranslate"><spanclass="pre">HEALTHCHECK</span></code> dei <codeclass="docutils literal notranslate"><spanclass="pre">Dockerfile</span></code>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">HealthState</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">enum.IntEnum</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<dd><p>Il <codeclass="docutils literal notranslate"><spanclass="pre">Dockerfile</span></code> non ha un <codeclass="docutils literal notranslate"><spanclass="pre">HEALTHCHECK</span></code> definito.</p>
<dd><p>Il container Docker non mai completato con successo un <codeclass="docutils literal notranslate"><spanclass="pre">HEALTHCHECK</span></code>.</p>
<dd><p>Il container Docker ha completato con successo l'ultimo <codeclass="docutils literal notranslate"><spanclass="pre">HEALTHCHECK</span></code> e quindi sta funzionando correttamente.</p>
<spanclass="sig-name descname"><spanclass="pre">get_health</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">container</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container"title="(in Docker SDK for Python v6.0)"><spanclass="pre">docker.models.containers.Container</span></a></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference internal"href="#sophon.notebooks.docker.HealthState"title="sophon.notebooks.docker.HealthState"><spanclass="pre">HealthState</span></a></span></span><aclass="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'<aclass="reference internal"href="#sophon.notebooks.docker.HealthState"title="sophon.notebooks.docker.HealthState"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">HealthState</span></code></a> dei container.</p>
<spanclass="sig-name descname"><spanclass="pre">sleep_until_container_has_started</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">container</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><aclass="reference external"href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container"title="(in Docker SDK for Python v6.0)"><spanclass="pre">docker.models.containers.Container</span></a></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference internal"href="#sophon.notebooks.docker.HealthState"title="sophon.notebooks.docker.HealthState"><spanclass="pre">HealthState</span></a></span></span><aclass="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 è <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">HealthState.STARTING</span></code>.</p>
<divclass="admonition danger">
<pclass="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 <aclass="reference external"href="https://docs.python.org/3.8/library/time.html#time.sleep"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">time.sleep</span></code></a> al suo interno.</p>
<p>Ciò è dovuto al mancato supporto alle funzioni asincrone nella versione attuale di <codeclass="xref py py-mod docutils literal notranslate"><spanclass="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>
<sectionid="generazione-di-token-sicuri">
<h4>Generazione di token sicuri<aclass="headerlink"href="#generazione-di-token-sicuri"title="Link a questa intestazione"></a></h4>
<p>Per rendere l'interfaccia grafica più <codeclass="xref py py-obj docutils literal notranslate"><spanclass="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>
<spanclass="sig-name descname"><spanclass="pre">generate_secure_token</span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/stdtypes.html#str"title="(in Python v3.8)"><spanclass="pre">str</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.docker.generate_secure_token"title="Link a questa definizione"></a></dt>
<dd><p>Funzione che utilizza <aclass="reference external"href="https://docs.python.org/3.8/library/secrets.html#secrets.token_urlsafe"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">secrets.token_urlsafe</span></code></a> per generare un token valido e crittograficamente sicuro.</p>
</dd></dl>
</section>
<sectionid="module-sophon.notebooks.models">
<spanid="modello-dei-notebook"></span><h4>Modello dei notebook<aclass="headerlink"href="#module-sophon.notebooks.models"title="Link a questa intestazione"></a></h4>
<p>Viene definito il modello rappresentante un <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">notebook</span></code>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">Notebook</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonGroupModel</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.notebooks.models.Notebook"title="Link a questa definizione"></a></dt>
<dd><p>Lo slug dei notebook prevede ulteriori restrizioni oltre a quelle previste dallo <aclass="reference external"href="http://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.SlugField"title="(in Django v3.2)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">django.db.models.SlugField</span></code></a>:</p>
<ulclass="simple">
<li><p>non può essere uno dei seguenti valori: <codeclass="docutils literal notranslate"><spanclass="pre">api</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">static</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">proxy</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">backend</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">frontend</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">src</span></code>;</p></li>
<li><p>non può iniziare o finire con un trattino <codeclass="docutils literal notranslate"><spanclass="pre">-</span></code>.</p></li>
<dd><p>L'<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">utente</span></code> che ha richiesto il blocco del notebook, o <aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">None</span></code></a> in caso il notebook non sia bloccato.</p>
<dd><p>Campo che specifica l'immagine che il client <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="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>
<divclass="admonition note">
<pclass="admonition-title">Nota</p>
<p>Al momento, Sophon si aspetta che tutte le immagini specificate espongano un server web sulla porta <codeclass="docutils literal notranslate"><spanclass="pre">8888</span></code>, e supportino il protocollo di autenticazione di Jupyter, ovvero che sia possibile raggiungere il container ai seguenti indirizzi: <codeclass="samp docutils literal notranslate"><em><spanclass="pre">PROTOCOLLO</span></em><spanclass="pre">://immagine:8888/lab?token=</span><em><spanclass="pre">TOKEN</span></em></code> e <codeclass="samp docutils literal notranslate"><em><spanclass="pre">PROTOCOLLO</span></em><spanclass="pre">://immagine:8888/tree?token=</span><em><spanclass="pre">TOKEN</span></em></code>.</p>
<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><p>L'id assegnato dal daemon Docker al container di questo oggetto.</p>
<p>Se il notebook non è avviato, questo attributo varrà <aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">None</span></code></a>.</p>
<dd><p>La porta TCP locale assegnata al container Docker dell'oggetto nel caso in cui Sophon sia avviato in <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modalità</span><spanclass="pre">sviluppo</span></code>.</p>
<dd><p>L'URL a cui è accessibile il container Docker dell'oggetto nel caso in cui Sophon non sia avviato in <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modalità</span><spanclass="pre">sviluppo</span></code>.</p>
<emclass="property"><spanclass="pre">property</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">log</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/logging.html#logging.Logger"title="(in Python v3.8)"><spanclass="pre">logging.Logger</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.models.Notebook.log"title="Link a questa definizione"></a></dt>
<dd><p>Viene creato un <aclass="reference external"href="https://docs.python.org/3.8/library/logging.html#logging.Logger"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="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 <codeclass="samp docutils literal notranslate"><spanclass="pre">sophon.notebooks.models.Notebook.</span><em><spanclass="pre">NOTEBOOK_SLUG</span></em></code>.</p>
<spanclass="sig-name descname"><spanclass="pre">enable_proxying</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><spanclass="pre">None</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.models.Notebook.enable_proxying"title="Link a questa definizione"></a></dt>
<dd><p>Aggiunge l'indirizzo del notebook alla <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">rubrica</span><spanclass="pre">del</span><spanclass="pre">proxy</span></code>.</p>
<spanclass="sig-name descname"><spanclass="pre">disable_proxying</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><spanclass="pre">None</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.models.Notebook.disable_proxying"title="Link a questa definizione"></a></dt>
<dd><p>Rimuove l'indirizzo del notebook dalla <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">rubrica</span><spanclass="pre">del</span><spanclass="pre">proxy</span></code>.</p>
<spanclass="sig-name descname"><spanclass="pre">sync_container</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">t.Optional</span><spanclass="p"><spanclass="pre">[</span></span><aclass="reference external"href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container"title="(in Docker SDK for Python v6.0)"><spanclass="pre">docker.models.containers.Container</span></a><spanclass="p"><spanclass="pre">]</span></span></span></span><aclass="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 <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> nel sistema.</p>
<spanclass="sig-name descname"><spanclass="pre">create_container</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container"title="(in Docker SDK for Python v6.0)"><spanclass="pre">docker.models.containers.Container</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.models.Notebook.create_container"title="Link a questa definizione"></a></dt>
<dd><p>Crea e configura un container <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> per l'oggetto, con l'immagine specificata in <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">container_image</span></code>.</p>
<spanclass="sig-name descname"><spanclass="pre">start</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><spanclass="pre">None</span></a></span></span><aclass="headerlink"href="#sophon.notebooks.models.Notebook.start"title="Link a questa definizione"></a></dt>
<dd><p>Tenta di creare e avviare un container <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> per l'oggetto, bloccando fino a quando esso non sarà avviato con <aclass="reference internal"href="#sophon.notebooks.docker.sleep_until_container_has_started"title="sophon.notebooks.docker.sleep_until_container_has_started"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">sleep_until_container_has_started</span></code></a>.</p>
<spanclass="sig-name descname"><spanclass="pre">stop</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><spanclass="pre">None</span></a></span></span><aclass="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>
<sectionid="module-sophon.notebooks.views">
<spanid="viewset-dei-notebook"></span><h4>Viewset dei notebook<aclass="headerlink"href="#module-sophon.notebooks.views"title="Link a questa intestazione"></a></h4>
<p>Come per il modulo <aclass="reference internal"href="#module-sophon.projects"title="sophon.projects"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">NotebooksViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">SophonGroupViewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">metaclass</span></span><spanclass="o"><spanclass="pre">=</span></span><spanclass="default_value"><spanclass="pre">abc.ABCMeta</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#sophon.notebooks.views.NotebooksViewSet"title="Link a questa definizione"></a></dt>
<dd><p>Classe <strong>astratta</strong> che effettua l'override di <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">get_group_from_serializer</span></code> e definisce cinque azioni personalizzate per l'interazione con il notebook.</p>
<spanclass="sig-name descname"><spanclass="pre">sync</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">**</span></span><spanclass="n"><spanclass="pre">kwargs</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="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>
<spanclass="sig-name descname"><spanclass="pre">start</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">**</span></span><spanclass="n"><spanclass="pre">kwargs</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="headerlink"href="#sophon.notebooks.views.NotebooksViewSet.start"title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che avvia il notebook con <aclass="reference internal"href="#sophon.notebooks.models.Notebook.start"title="sophon.notebooks.models.Notebook.start"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">models.Notebook.start</span></code></a>.</p>
<spanclass="sig-name descname"><spanclass="pre">stop</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">**</span></span><spanclass="n"><spanclass="pre">kwargs</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="headerlink"href="#sophon.notebooks.views.NotebooksViewSet.stop"title="Link a questa definizione"></a></dt>
<dd><p>Azione personalizzata che arresta il notebook con <aclass="reference internal"href="#sophon.notebooks.models.Notebook.stop"title="sophon.notebooks.models.Notebook.stop"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">models.Notebook.stop</span></code></a>.</p>
<spanclass="sig-name descname"><spanclass="pre">lock</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">**</span></span><spanclass="n"><spanclass="pre">kwargs</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">models.Notebook.locked_by</span></code> all'utente che ha effettuato la richiesta.</p>
<spanclass="sig-name descname"><spanclass="pre">unlock</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">self</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">request</span></span><spanclass="p"><spanclass="pre">:</span></span><spanclass="w"></span><spanclass="n"><spanclass="pre">Request</span></span></em>, <emclass="sig-param"><spanclass="o"><spanclass="pre">**</span></span><spanclass="n"><spanclass="pre">kwargs</span></span></em><spanclass="sig-paren">)</span><spanclass="sig-return"><spanclass="sig-return-icon">→</span><spanclass="sig-return-typehint"><spanclass="pre">Response</span></span></span><aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">models.Notebook.locked_by</span></code> a <aclass="reference external"href="https://docs.python.org/3.8/library/constants.html#None"title="(in Python v3.8)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">None</span></code></a>.</p>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">NotebooksBySlugViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">NotebooksViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<emclass="property"><spanclass="pre">class</span><spanclass="w"></span></em><spanclass="sig-name descname"><spanclass="pre">NotebooksByProjectViewSet</span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">NotebooksViewSet</span></span></em><spanclass="sig-paren">)</span><aclass="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>
<spanid="index-6"></span><h3>Containerizzazione del modulo backend<aclass="headerlink"href="#containerizzazione-del-modulo-backend"title="Link a questa intestazione"></a></h3>
<p>Il modulo backend è incapsulato in un'immagine <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> basata sull'immagine ufficiale <aclass="reference external"href="https://hub.docker.com/_/python">python:3.9.7-bullseye</a>.</p>
<p>L'immagine utilizza <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Poetry</span></code> per installare le dipendenze, poi esegue il file <codeclass="docutils literal notranslate"><spanclass="pre">docker_start.sh</span></code> riportato sotto che effettua le migrazioni, prepara i file statici di Django e <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">prova</span><spanclass="pre">a</span><spanclass="pre">creare</span><spanclass="pre">un</span><spanclass="pre">superutente</span></code>, per poi avviare il progetto Django attraverso <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">gunicorn</span></code> sulla porta 8000.</p>
<divclass="highlight-bash notranslate"><divclass="highlight"><pre><span></span>poetry run python -O ./manage.py migrate --no-input
poetry run python -O ./manage.py collectstatic --no-input
<spanid="index-7"></span><h2><spanclass="section-number">5.2. </span>Realizzazione del modulo frontend<aclass="headerlink"href="#realizzazione-del-modulo-frontend"title="Link a questa intestazione"></a></h2>
<p>Il modulo frontend è stato realizzato come un package <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Node.js</span></code> denominato <codeclass="docutils literal notranslate"><spanclass="pre">@steffo/sophon-frontend</span></code>, e poi <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">containerizzato</span></code>, creando un'immagine <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> standalone, esattamente come per il <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">backend</span></code>.</p>
<sectionid="struttura-delle-directory">
<h3>Struttura delle directory<aclass="headerlink"href="#struttura-delle-directory"title="Link a questa intestazione"></a></h3>
<p>Le directory di <codeclass="xref js js-mod docutils literal notranslate"><spanclass="pre">@steffo45/sophon-frontend</span></code> sono strutturate nella seguente maniera:</p>
<dlclass="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 <codeclass="xref js js-func docutils literal notranslate"><spanclass="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 <aclass="reference internal"href="../progetto/index.html#modulo-backend"><spanclass="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>
<sectionid="comunicazione-con-il-backend">
<h3>Comunicazione con il backend<aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">backend</span></code>.</p>
<sectionid="axios">
<h4>Axios<aclass="headerlink"href="#axios"title="Link a questa intestazione"></a></h4>
<p>Per effettuare richieste all'API web, si è deciso di utilizzare la libreria <codeclass="xref js js-mod docutils literal notranslate"><spanclass="pre">axios</span></code>, in quanto permette di creare dei "client" 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 <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">AbortController()</span></code>.</p>
</section>
<sectionid="client-personalizzati">
<h4>Client personalizzati<aclass="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 (<codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">InstanceContext</span></code>), viene creato un client dal seguente hook:</p>
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">useInstanceAxios</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">config</span><spanclass="pre">=</span><spanclass="pre">{}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#useInstanceAxios"title="Link a questa definizione"></a></dt>
<dd><p>Questo hook specifica il <codeclass="docutils literal notranslate"><spanclass="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 (<codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">AuthorizationContext</span></code>), viene creato invece un client dal seguente hook:</p>
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">useAuthorizedAxios</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">config</span><spanclass="pre">=</span><spanclass="pre">{}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#useAuthorizedAxios"title="Link a questa definizione"></a></dt>
<dd><p>Questo hook specifica il valore dell'header <codeclass="docutils literal notranslate"><spanclass="pre">Authorization</span></code> da inviare in tutte le richieste effettuate a <codeclass="samp docutils literal notranslate"><spanclass="pre">Bearer</span><em><spanclass="pre">TOKEN</span></em></code>, utilizzando il token ottenuto al momento dell'accesso.</p>
</dd></dl>
</section>
<sectionid="utilizzo-di-viewset">
<h4>Utilizzo di viewset<aclass="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>
<spanid="useViewSet(baseRoute) → viewset"></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">useViewSet(baseRoute)</span><spanclass="pre">→</span><spanclass="pre">viewset</span></span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><aclass="headerlink"href="#useViewSet-baseRoute-viewset"title="Link a questa definizione"></a></dt>
<dd><p>Questo hook implementa tutte le azioni <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">rest_framework</span></code> di un viewset in lettura e scrittura.</p>
<p>Richiede di essere chiamato all'interno di un <codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">AuthorizationContext</span></code>.</p>
<dlclass="js function">
<dtclass="sig sig-object js"id="viewset.list">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">list</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">config</span><spanclass="pre">=</span><spanclass="pre">{}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.list"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Richiede la lista di tutte le risorse del viewset.</p>
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">retrieve</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">config</span><spanclass="pre">=</span><spanclass="pre">{}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.retrieve"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Richiede i dettagli di una specifica risorsa del viewset.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="viewset.create">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">create</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">config</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.create"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Crea una nuova risorsa nel viewset.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="viewset.update">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">update</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">config</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.update"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Aggiorna una specifica risorsa nel viewset.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="viewset.destroy">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">destroy</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">pk</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">config</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.destroy"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="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>
<dlclass="js function">
<dtclass="sig sig-object js"id="viewset.command">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">command</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">config</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.command"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Permette azioni personalizzate su tutto il viewset.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="viewset.action">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">viewset</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">action</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">config</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#viewset.action"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Permette azioni personalizzate su uno specifico oggetto del viewset.</p>
</dd></dl>
</dd></dl>
</section>
<sectionid="emulazione-di-viewset">
<h4>Emulazione di viewset<aclass="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>
<spanid="useManagedViewSet(baseRoute, pkKey, refreshOnMount) → managed"></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">useManagedViewSet(baseRoute,</span><spanclass="pre">pkKey,</span><spanclass="pre">refreshOnMount)</span><spanclass="pre">→</span><spanclass="pre">managed</span></span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><aclass="headerlink"href="#useManagedViewSet-baseRoute-pkKey-refreshOnMount-managed"title="Link a questa definizione"></a></dt>
<dd><dlclass="js attribute">
<dtclass="sig sig-object js"id="managed.viewset">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">viewset</span></span></span><aclass="headerlink"href="#managed.viewset"title="Link a questa definizione"></a></dt>
<dd><p>Il viewset restituito da <codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">useViewSet()</span></code>, utilizzato come interfaccia di basso livello per effettuare azioni.</p>
</dd></dl>
<dlclass="js attribute">
<dtclass="sig sig-object js"id="managed.state">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">state</span></span></span><aclass="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 <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">ManagedResource()</span></code>, create usando wrapper di <aclass="reference internal"href="#managed.update"title="managed.update"><codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">update()</span></code></a>, <aclass="reference internal"href="#managed.destroy"title="managed.destroy"><codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">destroy()</span></code></a> e <aclass="reference internal"href="#managed.action"title="managed.action"><codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">action()</span></code></a>, che permettono di modificare direttamente l'oggetto senza preoccuparsi dell'indice a cui si trova nell'array.</p>
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">dispatch</span></span></span><aclass="headerlink"href="#managed.dispatch"title="Link a questa definizione"></a></dt>
<dd><p>Riduttore che permette di alterare lo <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a>.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="managed.refresh">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">refresh</span></span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><aclass="headerlink"href="#managed.refresh"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Ricarica gli oggetti del viewset.</p>
<p>Viene chiamata automaticamente al primo render se <codeclass="docutils literal notranslate"><spanclass="pre">refreshOnMount</span></code> è <codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">True</span></code>.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="managed.create">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">create</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#managed.create"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="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>
<dlclass="js function">
<dtclass="sig sig-object js"id="managed.command">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">command</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">method</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">cmd</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#managed.command"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Esegue l'azione personalizzata <codeclass="docutils literal notranslate"><spanclass="pre">cmd</span></code> su tutto il viewset, utilizzando il metodo <codeclass="docutils literal notranslate"><spanclass="pre">method</span></code> e con i dati specificati in <codeclass="docutils literal notranslate"><spanclass="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>
<dlclass="js function">
<dtclass="sig sig-object js"id="managed.update">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">update</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">index</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#managed.update"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Modifica l'oggetto alla posizione <codeclass="docutils literal notranslate"><spanclass="pre">index</span></code> dell'array <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a> con i dati specificati in <codeclass="docutils literal notranslate"><spanclass="pre">data</span></code>.</p>
<p>Se la richiesta va a buon fine, la modifica viene anche applicata all'interno di <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a></p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="managed.destroy">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">destroy</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">index</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#managed.destroy"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Elimina l'oggetto alla posizione <codeclass="docutils literal notranslate"><spanclass="pre">index</span></code> dell'array <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a>.</p>
<p>Se la richiesta va a buon fine, l'oggetto viene eliminato anche da <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a>.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="managed.action">
<spanclass="sig-prename descclassname"><spanclass="n"><spanclass="pre">managed</span></span><spanclass="p"><spanclass="pre">.</span></span></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">action</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">index</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">method</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">act</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">data</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#managed.action"title="Link a questa definizione"></a></dt>
<dd><p>Funzione <strong>asincrona</strong>, che restituisce una <codeclass="xref js js-class docutils literal notranslate"><spanclass="pre">Promise()</span></code>.</p>
<p>Esegue l'azione personalizzata <codeclass="docutils literal notranslate"><spanclass="pre">act</span></code> sull'oggetto alla posizione <codeclass="docutils literal notranslate"><spanclass="pre">index</span></code> dell'array <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a>, utilizzando il metodo <codeclass="docutils literal notranslate"><spanclass="pre">method</span></code> e con i dati specificati in <codeclass="docutils literal notranslate"><spanclass="pre">data</span></code>.</p>
<p>Se la richiesta va a buon fine, il valore restituito dal backend sostituisce l'oggetto utilizzato in <aclass="reference internal"href="#managed.state"title="managed.state"><codeclass="xref js js-attr docutils literal notranslate"><spanclass="pre">state</span></code></a>.</p>
</dd></dl>
</dd></dl>
</section>
</section>
<sectionid="contesti-innestati">
<spanid="index-8"></span><h3>Contesti innestati<aclass="headerlink"href="#contesti-innestati"title="Link a questa intestazione"></a></h3>
<p>Per minimizzare i re-render, l'applicazione è organizzata a "contesti innestati".</p>
<sectionid="i-contesti">
<spanid="index-9"></span><h4>I contesti<aclass="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>
<spanid="index-10"></span><h5>Contenuto dei contesti<aclass="headerlink"href="#contenuto-dei-contesti"title="Link a questa intestazione"></a></h5>
<p>Questi contesti possono avere tre tipi di valori: <codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">undefined</span></code> se ci si trova al di fuori del contesto, <codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">null</span></code> se non è stato selezionato alcun oggetto oppure <strong>l'oggetto selezionato</strong> se esso esiste.</p>
</section>
</section>
<sectionid="segmenti-di-url">
<spanid="index-11"></span><h4>Segmenti di URL<aclass="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>
<spanid="parsePathSegment({path, parsed, regex, key, next}) → ParsedPath"></span><spanclass="sig-name descname"><spanclass="n"><spanclass="pre">parsePathSegment({path,</span><spanclass="pre">parsed,</span><spanclass="pre">regex,</span><spanclass="pre">key,</span><spanclass="pre">next})</span><spanclass="pre">→</span><spanclass="pre">ParsedPath</span></span></span><spanclass="sig-paren">(</span><spanclass="sig-paren">)</span><aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">ParsedPath</span></code>.</p>
<dlclass="field-list simple">
<dtclass="field-odd">Parametri</dt>
<ddclass="field-odd"><ulclass="simple">
<li><p><strong>path</strong> -- La stringa del percorso ancora da parsare.</p></li>
<li><p><strong>parsed</strong> -- L'oggetto <codeclass="docutils literal notranslate"><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">ParsedPath</span></code>.</p></li>
<li><p><strong>key</strong> -- La chiave a cui inserire il valore catturato all'interno del <codeclass="docutils literal notranslate"><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">my-first-notebook</span></code> all'interno della istanza demo di Sophon potrebbe essere:</p>
<p><spanclass="caption-number">Figura 5.2.1 </span><spanclass="caption-text">La barra dei breadcrumbs. Ci si trova attualmente sulla pagina del gruppo <codeclass="docutils literal notranslate"><spanclass="pre">my-first-group</span></code>.</span><aclass="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>
<p><spanclass="caption-number">Figura 5.2.2 </span><spanclass="caption-text">La barra dei breadcrumbs in errore. È possibile riprendere la navigazione dalla pagina di selezione istanza o di login.</span><aclass="headerlink"href="#id3"title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
</section>
<sectionid="componenti-contestuali">
<spanid="index-13"></span><h4>Componenti contestuali<aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">ResourcePanel</span></code> e le <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">ListBox</span></code>.</p>
<dlclass="js function">
<dtclass="sig sig-object js"id="ResourcePanel">
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">ResourcePanel</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">{...}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#ResourcePanel"title="Link a questa definizione"></a></dt>
<dd><p>Panello che rappresenta un'<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">entità</span><spanclass="pre">di</span><spanclass="pre">Sophon</span></code>, diviso in quattro parti:</p>
<ulclass="simple">
<li><p>icona (a sinistra)</p></li>
<li><p>nome della risorsa (a destra dell'icona)</p></li>
<p><spanclass="caption-number">Figura 5.2.3 </span><spanclass="caption-text">Un <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">ResourcePanel</span></code> rappresentante un <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">gruppo</span><spanclass="pre">di</span><spanclass="pre">ricerca</span></code>.</span><aclass="headerlink"href="#id4"title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="ListBox">
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">ListBox</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">{...}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#ListBox"title="Link a questa definizione"></a></dt>
<dd><p>Riquadro che mostra le risorse di un <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">useManagedViewSet</span></code> raffigurandole come tanti <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">ResourcePanel</span></code>.</p>
<p><spanclass="caption-number">Figura 5.2.4 </span><spanclass="caption-text">Una <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">ListBox</span></code> che mostra l'elenco di notebook in un progetto.</span><aclass="headerlink"href="#id5"title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</dd></dl>
</section>
<sectionid="routing-basato-sui-contesti">
<spanid="index-14"></span><h4>Routing basato sui contesti<aclass="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>
<dlclass="js function">
<dtclass="sig sig-object js"id="ResourceRouter">
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">ResourceRouter</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">{selection</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">unselectedRoute</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">selectedRoute}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#ResourceRouter"title="Link a questa definizione"></a></dt>
<dd><p>Componente che sceglie se renderizzare <codeclass="docutils literal notranslate"><spanclass="pre">unselectedRoute</span></code> o <codeclass="docutils literal notranslate"><spanclass="pre">selectedRoute</span></code> in base alla <em>nullità</em> o <em>non-nullità</em> di <codeclass="docutils literal notranslate"><spanclass="pre">selection</span></code>.</p>
</dd></dl>
<dlclass="js function">
<dtclass="sig sig-object js"id="ViewSetRouter">
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">ViewSetRouter</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">{viewSet</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">unselectedRoute</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">selectedRoute</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pathSegment</span></span></em>, <emclass="sig-param"><spanclass="n"><spanclass="pre">pkKey}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#ViewSetRouter"title="Link a questa definizione"></a></dt>
<dd><p>Componente basato su <aclass="reference internal"href="#ResourceRouter"title="ResourceRouter"><codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">ResourceRouter()</span></code></a> che seleziona automaticamente l'elemento del viewset avente il valore del segmento di percorso <codeclass="docutils literal notranslate"><spanclass="pre">pathSegment</span></code> alla chiave <codeclass="docutils literal notranslate"><spanclass="pre">pkKey</span></code>.</p>
</dd></dl>
<p>Ad esempio, <aclass="reference internal"href="#ViewSetRouter"title="ViewSetRouter"><codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">ViewSetRouter()</span></code></a> viene esteso specificatamente per il contesto del gruppo, creando il seguente componente.</p>
<dlclass="js function">
<dtclass="sig sig-object js"id="GroupRouter">
<spanclass="sig-name descname"><spanclass="n"><spanclass="pre">GroupRouter</span></span></span><spanclass="sig-paren">(</span><emclass="sig-param"><spanclass="n"><spanclass="pre">{...props}</span></span></em><spanclass="sig-paren">)</span><aclass="headerlink"href="#GroupRouter"title="Link a questa definizione"></a></dt>
<h4>Albero completo dei contesti<aclass="headerlink"href="#albero-completo-dei-contesti"title="Link a questa intestazione"></a></h4>
<p>L'insieme di tutti i contesti è definito come componente <codeclass="xref js js-func docutils literal notranslate"><spanclass="pre">App()</span></code> nel modulo "principale"<codeclass="docutils literal notranslate"><spanclass="pre">App.tsx</span></code>.</p>
<p>Se ne riassume la struttura in pseudocodice:</p>
<h4>Altri contesti<aclass="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>
<sectionid="tema">
<spanid="index-15"></span><h5>Tema<aclass="headerlink"href="#tema"title="Link a questa intestazione"></a></h5>
<p>Il tema dell'istanza è implementato come uno speciale contesto globale <codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">ThemeContext</span></code> che riceve i dettagli dell'istanza a cui si è collegati dall'<codeclass="xref js js-data docutils literal notranslate"><spanclass="pre">InstanceContext</span></code>.</p>
<p>Ciò permette di sincronizzare il tema della webapp con quello dell'istanza di Sophon selezionata.</p>
<p><spanclass="caption-number">Figura 5.2.5 </span><spanclass="caption-text">La schermata di login dell'istanza dimostrativa di Sophon, che utilizza il tema "Royal Blue".</span><aclass="headerlink"href="#id6"title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
<sectionid="cache">
<spanid="index-16"></span><h5>Cache<aclass="headerlink"href="#cache"title="Link a questa intestazione"></a></h5>
<p>Viene salvato l'elenco di tutti i membri dell'<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">istanza</span></code> in uno speciale contesto <codeclass="xref js js-data docutils literal notranslate"><spanclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">backend</span></code> solamente gli ID numerici dei relativi utenti, pertanto è necessario risolverli attraverso il contesto cache.</p>
<p><spanclass="caption-number">Figura 5.2.6 </span><spanclass="caption-text">L'elenco dei membri appartenenti al gruppo "My First Group".</span><aclass="headerlink"href="#id7"title="Link a questa immagine"></a></p>
<p><spanclass="caption-number">Figura 5.2.7 </span><spanclass="caption-text">Il nome di un utente che ha bloccato un notebook. (In questo caso, "root".)</span><aclass="headerlink"href="#id8"title="Link a questa immagine"></a></p>
<spanid="index-17"></span><h3>Containerizzazione del modulo frontend<aclass="headerlink"href="#containerizzazione-del-modulo-frontend"title="Link a questa intestazione"></a></h3>
<p>Il modulo frontend è incapsulato in un'immagine <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> basata sull'immagine ufficiale <aclass="reference external"href="https://hub.docker.com/_/node">node:16.11.1-bullseye</a>.</p>
<p>L'immagine installa le dipendenze del modulo con <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Yarn</span></code>, per poi eseguire il comando <codeclass="docutils literal notranslate"><spanclass="pre">yarn</span><spanclass="pre">run</span><spanclass="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>
<sectionid="realizzazione-del-modulo-proxy">
<spanid="index-18"></span><h2><spanclass="section-number">5.3. </span>Realizzazione del modulo proxy<aclass="headerlink"href="#realizzazione-del-modulo-proxy"title="Link a questa intestazione"></a></h2>
<p>Il modulo proxy consiste in un file di configurazione di <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Apache</span><spanclass="pre">HTTP</span><spanclass="pre">Server</span></code>.</p>
<p>Il file di configurazione abilita i moduli httpd <aclass="reference external"href="https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html">rewrite</a>, <aclass="reference external"href="https://httpd.apache.org/docs/2.4/mod/mod_proxy.html">proxy</a>, <aclass="reference external"href="https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html">proxy_wstunnel</a> e <aclass="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 <aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">RewriteEngine</span></code>, che viene utilizzato per effettuare reverse proxying secondo le seguenti regole:</p>
<olclass="arabic">
<li><p>Tutte le richieste verso <codeclass="docutils literal notranslate"><spanclass="pre">static.</span></code> prefisso ad <spanclass="target"id="index-19"></span><aclass="reference internal"href="../installazione/4_configuring_compose.html#envvar-APACHE_PROXY_BASE_DOMAIN"><codeclass="xref std std-envvar docutils literal notranslate"><spanclass="pre">APACHE_PROXY_BASE_DOMAIN</span></code></a> vengono processate direttamente dal webserver, utilizzando i file disponibili nella cartella <codeclass="docutils literal notranslate"><spanclass="pre">/var/www/html/django-static</span></code> che gli vengono forniti dal volume <codeclass="docutils literal notranslate"><spanclass="pre">django-static</span></code> del <aclass="reference internal"href="../progetto/index.html#modulo-backend"><spanclass="std std-ref">Modulo backend</span></a>.</p>
<divclass="highlight-apacheconf notranslate"><divclass="highlight"><pre><span></span><spanclass="c"># If ENV:APACHE_PROXY_BASE_DOMAIN equals HTTP_HOST</span>
<li><p>Tutte le richieste verso <spanclass="target"id="index-20"></span><aclass="reference internal"href="../installazione/4_configuring_compose.html#envvar-APACHE_PROXY_BASE_DOMAIN"><codeclass="xref std std-envvar docutils literal notranslate"><spanclass="pre">APACHE_PROXY_BASE_DOMAIN</span></code></a> senza nessun sottodominio vengono inoltrate al container Docker del <aclass="reference internal"href="../progetto/index.html#modulo-frontend"><spanclass="std std-ref">Modulo frontend</span></a> utilizzando la risoluzione dei nomi di dominio di Docker Compose.</p>
<divclass="highlight-apacheconf notranslate"><divclass="highlight"><pre><span></span><spanclass="c"># If ENV:APACHE_PROXY_BASE_DOMAIN equals HTTP_HOST</span>
<li><p>Tutte le richieste verso <codeclass="docutils literal notranslate"><spanclass="pre">api.</span></code> prefisso ad <spanclass="target"id="index-21"></span><aclass="reference internal"href="../installazione/4_configuring_compose.html#envvar-APACHE_PROXY_BASE_DOMAIN"><codeclass="xref std std-envvar docutils literal notranslate"><spanclass="pre">APACHE_PROXY_BASE_DOMAIN</span></code></a> vengono inoltrate al container Docker del <aclass="reference internal"href="../progetto/index.html#modulo-backend"><spanclass="std std-ref">Modulo backend</span></a> utilizzando la risoluzione dei nomi di dominio di Docker Compose.</p>
<divclass="highlight-apacheconf notranslate"><divclass="highlight"><pre><span></span><spanclass="c"># If api. prefixed to ENV:APACHE_PROXY_BASE_DOMAIN equals HTTP_HOST</span>
<li><p>Carica in memoria la rubrica dei notebook generata dal <aclass="reference internal"href="../progetto/index.html#modulo-backend"><spanclass="std std-ref">Modulo backend</span></a> e disponibile in <codeclass="docutils literal notranslate"><spanclass="pre">/run/sophon/proxy/proxy.dbm</span></code> attraverso il volume <codeclass="docutils literal notranslate"><spanclass="pre">proxy-data</span></code>, assegnandogli il nome di <codeclass="docutils literal notranslate"><spanclass="pre">sophonproxy</span></code>.</p>
<divclass="highlight-apacheconf notranslate"><divclass="highlight"><pre><span></span><spanclass="c"># Create a map between the proxy file generated by Sophon and Apache</span>
<li><p>Effettua il proxying dei websocket verso i notebook mappati dalla rubrica <codeclass="docutils literal notranslate"><spanclass="pre">sophonproxy</span></code>.</p>
<divclass="highlight-apacheconf notranslate"><divclass="highlight"><pre><span></span><spanclass="c"># If this is any other subdomain of ENV:APACHE_PROXY_BASE_DOMAIN</span>
<li><p>Effettua il proxying delle richieste "normali" verso i notebook mappati dalla rubrica <codeclass="docutils literal notranslate"><spanclass="pre">sophonproxy</span></code>.</p>
<divclass="highlight-apacheconf notranslate"><divclass="highlight"><pre><span></span><spanclass="c"># If this is any other subdomain of ENV:APACHE_PROXY_BASE_DOMAIN</span>
<p>Tutte le regole usano il flag <codeclass="docutils literal notranslate"><spanclass="pre">L</span></code> di <codeclass="docutils literal notranslate"><spanclass="pre">RewriteRule</span></code>, che porta il motore di rewriting a ignorare tutte le regole successive, come il <codeclass="docutils literal notranslate"><spanclass="pre">return</span></code> di una funzione di un linguaggio di programmazione imperativo.</p>
<sectionid="containerizzazione-del-modulo-proxy">
<spanid="index-22"></span><h3>Containerizzazione del modulo proxy<aclass="headerlink"href="#containerizzazione-del-modulo-proxy"title="Link a questa intestazione"></a></h3>
<p>Il modulo proxy è incapsulato in un'immagine <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> basata sull'immagine ufficiale <aclass="reference external"href="https://hub.docker.com/_/httpd">httpd:2.4</a>, che si limita ad applicare la configurazione personalizzata.</p>
</section>
</section>
<sectionid="realizzazione-del-modulo-jupyter">
<spanid="index-23"></span><h2><spanclass="section-number">5.4. </span>Realizzazione del modulo Jupyter<aclass="headerlink"href="#realizzazione-del-modulo-jupyter"title="Link a questa intestazione"></a></h2>
<p>Il <em>modulo Jupyter</em> consiste in un ambiente <aclass="reference external"href="https://jupyter.org/">Jupyter</a> e <aclass="reference external"href="https://jupyterlab.readthedocs.io/en/stable/">JupyterLab</a> modificato per una migliore integrazione con Sophon, in particolare con il <aclass="reference internal"href="../progetto/index.html#modulo-frontend"><spanclass="std std-ref">Modulo frontend</span></a> e il <aclass="reference internal"href="../progetto/index.html#modulo-backend"><spanclass="std std-ref">Modulo backend</span></a>.</p>
<p>È collocato all'interno del repository in <codeclass="docutils literal notranslate"><spanclass="pre">/jupyter</span></code>.</p>
<sectionid="sviluppo-del-tema-per-jupyter">
<h3>Sviluppo del tema per Jupyter<aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">JupyterLab</span></code>.</p>
<p>È stato creato partendo dal template <aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">style/variables.css</span></code>) usando i colori del tema "The Sophonity" di <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Bluelib</span></code>.</p>
<p>È stato poi pubblicato sull'<abbrtitle="Python Package Index">PyPI</abbr> e su <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">npm</span></code>, permettendone l'uso a tutti gli utenti di JupyterLab.</p>
<divclass="admonition note">
<pclass="admonition-title">Nota</p>
<p>Per facilitarne la distribuzione e il riutilizzo anche esternamente a Sophon, il tema è stato creato in un repository <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Git</span></code> esterno a quello del progetto.</p>
<h3>Estensione del container Docker di Jupyter<aclass="headerlink"href="#estensione-del-container-docker-di-jupyter"title="Link a questa intestazione"></a></h3>
<p>Il <codeclass="docutils literal notranslate"><spanclass="pre">Dockerfile</span></code> del modulo ne crea un <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">immagine</span><spanclass="pre">Docker</span></code> in quattro fasi:</p>
<olclass="arabic">
<li><p><strong>Base</strong>: Parte dall'immagine base <codeclass="docutils literal notranslate"><spanclass="pre">jupyter/scipy-notebook</span></code> e ne altera i label.</p>
<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 <codeclass="docutils literal notranslate"><spanclass="pre">sudo</span></code>.</p>
<li><p><strong>Extensions</strong>: Installa, abilita e configura le estensioni necessarie all'integrazione con Sophon (attualmente, soltanto il tema JupyterLab Sophon).</p>
<li><p><strong>Healthcheck</strong>: Installa <aclass="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 <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">stato</span><spanclass="pre">di</span><spanclass="pre">salute</span></code> dell'immagine, al fine di comunicare al <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">backend</span></code> il risultato di una richiesta di avvio.</p>
<spanid="index-24"></span><h2><spanclass="section-number">5.5. </span>Automazione di sviluppo<aclass="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 <aclass="reference external"href="https://github.com/features/actions">GitHub Actions</a> per effettuare automaticamente alcuni compiti.</p>
<spanid="index-25"></span><h3>Scansione automatica delle dipendenze<aclass="headerlink"href="#scansione-automatica-delle-dipendenze"title="Link a questa intestazione"></a></h3>
<p>È stato abilitato su <aclass="reference internal"href="../progetto/index.html#github"><spanclass="std std-ref">GitHub</span></a> il supporto a <aclass="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>
<p><spanclass="caption-number">Figura 5.5.1 </span><spanclass="caption-text">Alcune vulnerabilità rilevate da Dependabot all'interno delle dipendenze di Sophon.</span><aclass="headerlink"href="#id9"title="Link a questa immagine"></a></p>
</figcaption>
</figure>
</section>
<sectionid="controllo-automatico-del-codice">
<spanid="index-26"></span><h3>Controllo automatico del codice<aclass="headerlink"href="#controllo-automatico-del-codice"title="Link a questa intestazione"></a></h3>
<p>Sono state configurate due azioni, <codeclass="docutils literal notranslate"><spanclass="pre">analyze-codeql-backend</span></code> e <codeclass="docutils literal notranslate"><spanclass="pre">analyze-codeql-frontend</span></code>, che usano <aclass="reference external"href="https://codeql.github.com/">CodeQL</a> per scansionare staticamente il codice e identificare problemi o vulnerabilità.</p>
<p>La prima, <codeclass="docutils literal notranslate"><spanclass="pre">analyze-codeql-backend</span></code>, viene eseguita solo quando viene inviato a GitHub nuovo codice relativo al <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">backend</span></code>, ed effettua analisi specifiche a <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Python</span></code>, mentre la seconda, <codeclass="docutils literal notranslate"><spanclass="pre">analyze-codeql-frontend</span></code>, viene eseguita solo quando viene inviato nuovo codice del <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">modulo</span><spanclass="pre">frontend</span></code>, ed effettua analisi specifiche a JavaScript.</p>
<p>Si riportano due estratti relativi all'azione <codeclass="docutils literal notranslate"><spanclass="pre">analyze-codeql-backend</span></code>.</p>
<h3>Costruzione automatica delle immagini Docker<aclass="headerlink"href="#costruzione-automatica-delle-immagini-docker"title="Link a questa intestazione"></a></h3>
<p>Sono state configurate quattro azioni, <codeclass="docutils literal notranslate"><spanclass="pre">build-docker-frontend</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">build-docker-backend</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">build-docker-jupyter</span></code> e <codeclass="docutils literal notranslate"><spanclass="pre">build-docker-proxy</span></code>, che costruiscono automaticamente l'immagine <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a> di ciascun modulo qualora il relativo codice venga modificato.</p>
<p>L'immagine creata viene poi caricata sul <aclass="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 <aclass="reference internal"href="../progetto/index.html#docker"><spanclass="std std-ref">Docker</span></a>.</p>
<p>Si riporta un estratto relativo all'azione <codeclass="docutils literal notranslate"><spanclass="pre">build-docker-proxy</span></code>.</p>
<spanid="index-27"></span><h3>Costruzione automatica della documentazione<aclass="headerlink"href="#costruzione-automatica-della-documentazione"title="Link a questa intestazione"></a></h3>
<p>Sono state configurate due azioni, <codeclass="docutils literal notranslate"><spanclass="pre">build-sphinx-report</span></code> e <codeclass="docutils literal notranslate"><spanclass="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 <aclass="reference external"href="https://www.sphinx-doc.org/en/master/">Sphinx</a>.</p>
<p>La documentazione per l'esame viene compilata solo da <aclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">build-sphinx-thesis</span></code>.</p>