From 7476a74e2ecae702dfe08968c4540d4bad1b6756 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Thu, 25 Nov 2021 03:47:06 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=93=20More=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- thesis/source/docs/4_progetto/index.rst | 29 ++- .../source/docs/5_implementazione/index.rst | 175 +++++++++++------- 2 files changed, 132 insertions(+), 72 deletions(-) diff --git a/thesis/source/docs/4_progetto/index.rst b/thesis/source/docs/4_progetto/index.rst index 4b75ec5..f7d3e25 100644 --- a/thesis/source/docs/4_progetto/index.rst +++ b/thesis/source/docs/4_progetto/index.rst @@ -417,23 +417,23 @@ Fa ampio uso delle `CSS Custom Properties ` attorno a Sophon, si è deciso di pubblicare il progetto su `GitHub`, sotto la licenza `AGPL 3.0+`. +Inoltre, per favorire lo sviluppo di una community `open source ` attorno a Sophon, si è deciso di pubblicare il progetto su `GitHub`, sotto la `Affero General Public License 3.0+`. .. index:: @@ -688,6 +688,21 @@ GitHub Per ciascun repository sono messe a disposizione gratuitamente numerose funzionalità, quali un issue tracker, strumenti di code review e sistemi di automazione per lo sviluppo :cite:`github:features`. +Affero General Public License 3.0+ +---------------------------------- + +Sophon è rilasciato sotto la `GNU Affero General Public License 3`_ (o successiva). + +Il testo completo della licenza è disponibile all'interno del file `LICENSE.txt`_ allegato al codice sorgente del software. + +In breve, la licenza permette a chiunque di utilizzare, distribuire e modificare il software, a condizione che qualsiasi modifica venga ri-distribuita agli utenti del software modificato. + +Si specifica che la licenza copre tutti i file all'interno del repository ``Steffo99/sophon``, anche se essi non contengono un header che indica che sono protetti da copyright. + +.. _GNU Affero General Public License 3: https://www.gnu.org/licenses/agpl-3.0.html +.. _LICENSE.txt: https://github.com/Steffo99/sophon/blob/main/LICENSE.txt + + .. index:: pair: Sophon; entità @@ -778,7 +793,7 @@ Un *gruppo di ricerca* rappresenta un insieme di utenti che collaborano su uno o Membri e modalità di accesso ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Gli utenti dell'`istanza ` possono diventare *membri* dei gruppi di ricerca, con una delle seguenti modalità selezionate nelle impostazioni del gruppo: +Gli utenti dell'`istanza ` possono diventare *membri* dei gruppi di ricerca, con una delle seguenti modalità selezionate nelle impostazioni del gruppo: - se il gruppo è *aperto*, allora qualsiasi utente potrà diventarne membro semplicemente **facendo richiesta** attraverso l'interfaccia web; - se il gruppo è in *modalità manuale*, allora nessun utente potrà richiedere di unirsi, e i membri saranno **selezionati manualmente** dal creatore del gruppo. diff --git a/thesis/source/docs/5_implementazione/index.rst b/thesis/source/docs/5_implementazione/index.rst index 0fd80e0..15bf96a 100644 --- a/thesis/source/docs/5_implementazione/index.rst +++ b/thesis/source/docs/5_implementazione/index.rst @@ -59,7 +59,7 @@ Inoltre, il template predefinito viene sovrascritto da quello all'interno del fi .. attribute:: default_site = "sophon.admin.SophonAdminSite" - `.SophonAdminSite` è selezionata come classe predefinita per il sito di amministrazione. + :class:`.SophonAdminSite` è selezionata come classe predefinita per il sito di amministrazione. Caricamento dinamico delle impostazioni @@ -122,18 +122,18 @@ Aggiunta di un nuovo comando di gestione ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. module:: sophon.core.management.commands.initsuperuser -Per permettere l'integrazione la creazione automatica del primo :ref:`superutente` quando Sophon viene eseguito da Docker, viene introdotto dall'app il comando di gestione ``initsuperuser``. +Per permettere l'integrazione la creazione automatica del primo superutente quando Sophon viene eseguito da Docker, viene introdotto dall'app il comando di gestione ``initsuperuser``. .. class:: Command - Questo comando crea automaticamente un :ref:`superutente` con le credenziali specificate in :ref:`\`\`DJANGO_SU_USERNAME\`\``, :ref:`\`\`DJANGO_SU_EMAIL\`\`` e :ref:`\`\`DJANGO_SU_PASSWORD\`\``. + Questo comando crea automaticamente un superutente con le credenziali specificate in :env:`DJANGO_SU_USERNAME`, :env:`DJANGO_SU_EMAIL` e :env:`DJANGO_SU_PASSWORD`. Modello base astratto ^^^^^^^^^^^^^^^^^^^^^ .. module:: sophon.core.models -Viene estesa la classe astratta `django.db.models.Model` con funzioni per stabilire il `livello di accesso` di un `utente ` all'oggetto e per generare automaticamente i `rest_framework.serializers.ModelSerializer` in base ad esso. +Viene estesa la classe astratta `django.db.models.Model` con funzioni per stabilire il `livello di accesso ` di un `utente ` all'oggetto e per generare automaticamente i `rest_framework.serializers.ModelSerializer` in base ad esso. .. class:: SophonModel(django.db.models.Model) @@ -143,7 +143,7 @@ Viene estesa la classe astratta `django.db.models.Model` con funzioni per stabil Controlla se un utente può modificare l'oggetto attuale. :param user: L'utente da controllare. - :returns: `True` se l'utente deve poter modificare l'oggetto, altrimenti `False`. + :returns: :data:`True` se l'utente deve poter modificare l'oggetto, altrimenti :data:`False`. .. method:: can_admin(self, user: django.contrib.auth.models.User) -> bool :abstractmethod: @@ -151,23 +151,23 @@ Viene estesa la classe astratta `django.db.models.Model` con funzioni per stabil Controlla se un utente può amministrare l'oggetto attuale. :param user: L'utente da controllare. - :returns: `True` se l'utente deve poter amministrare l'oggetto, altrimenti `False`. + :returns: :data:`True` se l'utente deve poter amministrare l'oggetto, altrimenti :data:`False`. .. classmethod:: get_fields(cls) -> set[str] - :returns: il `set` di nomi di campi che devono essere mostrati quando viene richiesto l'oggetto attraverso l'API. + :returns: il :class:`set` di nomi di campi che devono essere mostrati quando viene richiesto l'oggetto attraverso l'API. .. classmethod:: get_editable_fields(cls) -> set[str] - :returns: il `set` di nomi di campi di cui deve essere permessa la modifica se l'utente può modificare (`.can_edit`) l'oggetto. + :returns: il :class:`set` di nomi di campi di cui deve essere permessa la modifica se l'utente può modificare (:meth:`.can_edit`) l'oggetto. .. classmethod:: get_administrable_fields(cls) -> set[str] - :returns: il `set` di nomi di campi di cui deve essere permessa la modifica se l'utente può amministrare (`.can_admin`) l'oggetto. + :returns: il :class:`set` di nomi di campi di cui deve essere permessa la modifica se l'utente può amministrare (:meth:`.can_admin`) l'oggetto. .. classmethod:: get_creation_fields(cls) -> set[str] - :returns: il `set` di nomi di campi che possono essere specificati dall'utente al momento della creazione dell'oggetto. + :returns: il :class:`set` di nomi di campi che possono essere specificati dall'utente al momento della creazione dell'oggetto. Modello di autorizzazione astratto @@ -223,7 +223,7 @@ Viene definito un nuovo modello astratto, basato su `SophonModel`, che permette Modello dei dettagli dell'istanza ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Viene creato il modello che rappresenta i dettagli dell'`istanza di Sophon `. +Viene creato il modello che rappresenta i dettagli dell'`istanza di Sophon `. .. class:: SophonInstanceDetails(SophonModel) @@ -254,7 +254,7 @@ Viene creato il modello che rappresenta i dettagli dell'`istanza di Sophon `. +Viene creato il modello che rappresenta un `gruppo di ricerca `. .. class:: ResearchGroup(SophonGroupModel) @@ -937,8 +937,11 @@ public Contiene i file statici da servire assieme all'app. -Comunicazione con il server ---------------------------- +Comunicazione con il backend +---------------------------- + +Sono state sviluppate alcune funzioni di utilità per facilitare la comunicazione con il `modulo backend `. + Axios ^^^^^ @@ -971,25 +974,53 @@ Utilizzo di viewset Viene implementato un hook che si integra con i viewset di Django, fornendo un API semplificato per effettuare azioni su di essi. -.. function:: useViewSet(baseRoute) +.. function:: useViewSet(baseRoute) → viewset Questo hook implementa tutte le azioni :py:mod:`rest_framework` di un viewset in lettura e scrittura. Richiede di essere chiamato all'interno di un :data:`AuthorizationContext`. - .. function:: async list(config = {}) - .. function:: async retrieve(pk, config = {}) - .. function:: async create(config) - .. function:: async update(pk, config) - .. function:: async destroy(pk, config) + .. function:: viewset.list(config = {}) + + Funzione **asincrona**, che restituisce una :class:`Promise`. + + Richiede la lista di tutte le risorse del viewset. + + .. function:: viewset.retrieve(pk, config = {}) + + Funzione **asincrona**, che restituisce una :class:`Promise`. + + Richiede i dettagli di una specifica risorsa del viewset. + + .. function:: viewset.create(config) + + Funzione **asincrona**, che restituisce una :class:`Promise`. + + Crea una nuova risorsa nel viewset. + + .. function:: viewset.update(pk, config) + + Funzione **asincrona**, che restituisce una :class:`Promise`. + + Aggiorna una specifica risorsa nel viewset. + + .. function:: viewset.destroy(pk, config) + + Funzione **asincrona**, che restituisce una :class:`Promise`. + + Elimina una specifica risorsa dal viewset. Viene inoltre fornito supporto per le azioni personalizzate. - .. function:: async command(config) + .. function:: viewset.command(config) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Permette azioni personalizzate su tutto il viewset. - .. function:: async action(config) + .. function:: viewset.action(config) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Permette azioni personalizzate su uno specifico oggetto del viewset. @@ -999,51 +1030,63 @@ Emulazione di viewset 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. -.. function:: useManagedViewSet(baseRoute, pkKey, refreshOnMount) +.. function:: useManagedViewSet(baseRoute, pkKey, refreshOnMount) → managed - .. attribute:: viewset + .. attribute:: managed.viewset Il viewset restituito da :func:`useViewSet`, utilizzato come interfaccia di basso livello per effettuare azioni. - .. attribute:: state + .. attribute:: managed.state Lo stato del viewset, che tiene traccia degli oggetti e delle azioni in corso su di essi. Gli oggetti all'interno di esso sono istanze di :class:`ManagedResource`, create usando wrapper di :func:`.update`, :func:`.destroy` e :func:`.action`, che permettono di modificare direttamente l'oggetto senza preoccuparsi dell'indice a cui si trova nell'array. - .. attribute:: dispatch + .. attribute:: managed.dispatch Riduttore che permette di alterare lo :attr:`.state`. - .. function:: async refresh() + .. function:: managed.refresh() + + Funzione **asincrona**, che restituisce una :class:`Promise`. Ricarica gli oggetti del viewset. Viene chiamata automaticamente al primo render se ``refreshOnMount`` è :data:`True`. - .. function:: async create(data) + .. function:: managed.create(data) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Crea un nuovo oggetto nel viewset con i dati specificati come argomento, e lo aggiunge allo stato se la richiesta va a buon fine. - .. function:: async command(method, cmd, data) + .. function:: managed.command(method, cmd, data) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Esegue l'azione personalizzata ``cmd`` su tutto il viewset, utilizzando il metodo ``method`` e con i dati specificati in ``data``. Se la richiesta va a buon fine, il valore restituito dal backend sostituisce nello stato le risorse dell'intero viewset. - .. function:: async update(index, data) + .. function:: managed.update(index, data) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Modifica l'oggetto alla posizione ``index`` dell'array :attr:`.state` con i dati specificati in ``data``. Se la richiesta va a buon fine, la modifica viene anche applicata all'interno di :attr:`.state` - .. function:: async destroy(index) + .. function:: managed.destroy(index) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Elimina l'oggetto alla posizione ``index`` dell'array :attr:`.state`. Se la richiesta va a buon fine, l'oggetto viene eliminato anche da :attr:`.state`. - .. function:: async action(index, method, act, data) + .. function:: managed.action(index, method, act, data) + + Funzione **asincrona**, che restituisce una :class:`Promise`. Esegue l'azione personalizzata ``act`` sull'oggetto alla posizione ``index`` dell'array :attr:`.state`, utilizzando il metodo ``method`` e con i dati specificati in ``data``. @@ -1064,11 +1107,11 @@ Viene definito un contesto per ogni tipo di risorsa selezionabile nell'interfacc Essi sono, in ordine dal più esterno al più interno: -#. :data:`InstanceContext` (:ref:`Istanza`) -#. :data:`AuthorizationContext` (:ref:`Utente`) -#. :data:`GroupContext` (:ref:`Gruppo di ricerca`) -#. :data:`ProjectContext` (:ref:`Progetto di ricerca`) -#. :data:`NotebookContext` (:ref:`Notebook`) +#. :data:`InstanceContext` (`Istanza `) +#. :data:`AuthorizationContext` (`Utente `) +#. :data:`GroupContext` (`Gruppo di ricerca `) +#. :data:`ProjectContext` (`Progetto di ricerca `) +#. :data:`NotebookContext` (`Notebook `) Contenuto dei contesti @@ -1113,10 +1156,10 @@ Viene definita una funzione in grado di comprendere gli URL contestuali: Un oggetto con le seguenti chiavi, dette "segmenti di percorso", le quali possono essere :data:`undefined` per indicare che non è stato selezionato un oggetto di quel tipo: - ``instance``: l'URL dell'istanza da utilizzare, con caratteri speciali sostituiti da ``:`` - - ``loggedIn``: :class:`Boolean`, se :data:`True` l'utente ha effettuato il login (come :ref:`Ospite` o :ref:`Utente`) - - ``researchGroup``: lo slug del :ref:`gruppo di ricerca` selezionato - - ``researchProject``: lo slug del :ref:`progetto di ricerca` selezionato - - ``notebook``: lo slug del :ref:`notebook` selezionato + - ``loggedIn``: :class:`Boolean`, se :data:`True` l'utente ha effettuato il login (anche come ospite) + - ``researchGroup``: lo slug del `gruppo di ricerca ` selezionato + - ``researchProject``: lo slug del `progetto di ricerca ` selezionato + - ``notebook``: lo slug del `notebook ` selezionato Ad esempio, l'URL precedente restituirebbe il seguente oggetto se processato: @@ -1181,7 +1224,7 @@ Esempio di utilizzo di ViewSetRouter Implementato come: - .. code-block:: tsx + .. code-block:: jsx ` in uno speciale contesto :data:`CacheContext` in modo da poter risolvere gli id degli utenti al loro username senza dover effettuare ulteriori richieste. Containerizzazione del modulo frontend @@ -1251,8 +1294,8 @@ Il modulo frontend è incapsulato in un'immagine `Docker` basata sull'immagine u L'immagine installa le dipendenze del modulo con `Yarn`, per poi eseguire il comando ``yarn run serve``, che avvia la procedura di preparazione della pagina e la rende disponibile su un webserver locale alla porta 3000. -Modulo proxy -============ +Realizzazione del modulo proxy +============================== Il modulo proxy consiste in un file di configurazione di `Apache HTTP Server`. @@ -1266,7 +1309,7 @@ Il file di configurazione abilita i moduli httpd `rewrite`_, `proxy`_, `proxy_ws Inoltre, nel file di configurazione viene abilitato il ``RewriteEngine``, che viene utilizzato per effettuare reverse proxying secondo le seguenti regole: -#. Tutte le richieste verso ``static.`` prefisso ad :ref:`\`\`APACHE_PROXY_BASE_DOMAIN\`\`` vengono processate direttamente dal webserver, utilizzando i file disponibili nella cartella ``/var/www/html/django-static`` che gli vengono forniti dal volume ``django-static`` del :ref:`modulo backend`. +#. Tutte le richieste verso ``static.`` prefisso ad :env:`APACHE_PROXY_BASE_DOMAIN` vengono processate direttamente dal webserver, utilizzando i file disponibili nella cartella ``/var/www/html/django-static`` che gli vengono forniti dal volume ``django-static`` del :ref:`modulo backend`. .. code-block:: apacheconf @@ -1275,7 +1318,7 @@ Inoltre, nel file di configurazione viene abilitato il ``RewriteEngine``, che vi # Process the request yourself RewriteRule ".?" - [L] -#. Tutte le richieste verso :ref:`\`\`APACHE_PROXY_BASE_DOMAIN\`\`` senza nessun sottodominio vengono inoltrate al container Docker del :ref:`modulo frontend` utilizzando la risoluzione dei nomi di dominio di Docker Compose. +#. Tutte le richieste verso :env:`APACHE_PROXY_BASE_DOMAIN` senza nessun sottodominio vengono inoltrate al container Docker del :ref:`modulo frontend` utilizzando la risoluzione dei nomi di dominio di Docker Compose. .. code-block:: apacheconf @@ -1286,7 +1329,7 @@ Inoltre, nel file di configurazione viene abilitato il ``RewriteEngine``, che vi # Forward to the frontend RewriteRule "/(.*)" "http://%1/$1" [P,L] -#. Tutte le richieste verso ``api.`` prefisso ad :ref:`\`\`APACHE_PROXY_BASE_DOMAIN\`\`` vengono inoltrate al container Docker del :ref:`modulo backend` utilizzando la risoluzione dei nomi di dominio di Docker Compose. +#. Tutte le richieste verso ``api.`` prefisso ad :env:`APACHE_PROXY_BASE_DOMAIN` vengono inoltrate al container Docker del :ref:`modulo backend` utilizzando la risoluzione dei nomi di dominio di Docker Compose. .. code-block:: apacheconf @@ -1334,8 +1377,8 @@ Dockerizzazione del modulo proxy Il modulo proxy è incapsulato in un'immagine `Docker` basata sull'immagine ufficiale `httpd:2.4 `_, che si limita ad applicare la configurazione personalizzata. -Modulo Jupyter -============== +Realizzazione del modulo Jupyter +================================ Il *modulo Jupyter* consiste in un ambiente `Jupyter `_ e `JupyterLab `_ modificato per una migliore integrazione con Sophon, in particolare con il :ref:`modulo frontend` e il :ref:`modulo backend`. @@ -1343,17 +1386,25 @@ Il *modulo Jupyter* consiste in un ambiente `Jupyter `_ e Sviluppo del tema per Jupyter -============================= +----------------------------- -.. todo:: Tema personalizzato di Jupyter +Per rendere l'interfaccia grafica più consistente ed user-friendly, è stato sviluppato un tema colori personalizzato per `JupyterLab`. + +È stato creato partendo dal template `jupyterlab/theme-cookiecutter `_, e in esso sono state modificati le variabili di stile (contenute nel file ``style/variables.css``) usando i colori del tema "The Sophonity" di `Bluelib`. + +È stato poi pubblicato sull':abbr:`PyPI (Python Package Index)` e su `npm`, permettendone l'uso a tutti gli utenti di JupyterLab. + +.. note:: + + Per facilitarne la distribuzione e il riutilizzo anche esternamente a Sophon, il tema è stato creato in un repository `Git` esterno a quello del progetto. -Funzionamento del modulo ------------------------- +Estensione del container Docker di Jupyter +------------------------------------------ -Il modulo è composto da un singolo ``Dockerfile`` che crea un immagine Docker in quattro fasi: +Il ``Dockerfile`` del modulo ne crea un immagine Docker in quattro fasi: -#. **Base**: Parte dall'immagine base ``jupyter/scipy-notebook`` ed altera i label dell'immagine; +#. **Base**: Parte dall'immagine base ``jupyter/scipy-notebook`` e ne altera i label. .. code-block:: docker @@ -1361,7 +1412,7 @@ Il modulo è composto da un singolo ``Dockerfile`` che crea un immagine Docker i # Set the maintainer label LABEL maintainer="Stefano Pigozzi " -#. **Env**: Configura le variabili di ambiente dell'immagine, attivando JupyterLab, configurando il riavvio automatico di Jupyter e permettendo all'utente non-privilegiato di acquisire i privilegi di root attraverso il comando ``sudo``; +#. **Env**: Configura le variabili di ambiente dell'immagine, attivando JupyterLab, configurando il riavvio automatico di Jupyter e permettendo all'utente non-privilegiato di acquisire i privilegi di root attraverso il comando ``sudo``. .. code-block:: docker @@ -1371,7 +1422,7 @@ Il modulo è composto da un singolo ``Dockerfile`` che crea un immagine Docker i ENV RESTARTABLE=yes ENV GRANT_SUDO=yes -#. **Extensions**: Installa, abilita e configura le estensioni necessarie all'integrazione con Sophon (attualmente, soltanto il tema JupyterLab Sophon); +#. **Extensions**: Installa, abilita e configura le estensioni necessarie all'integrazione con Sophon (attualmente, soltanto il tema JupyterLab Sophon). .. code-block:: docker @@ -1387,7 +1438,7 @@ Il modulo è composto da un singolo ``Dockerfile`` che crea un immagine Docker i RUN mkdir -p '.jupyter/lab/user-settings/@jupyterlab/apputils-extension/' RUN echo '{"theme": "JupyterLab Sophon"}' > ".jupyter/lab/user-settings/@jupyterlab/apputils-extension/themes.jupyterlab-settings" -#. **Healthcheck**: Installa ``curl``, e aggiunge all'immagine un controllo per verificarne lo stato di avvio, permettendo al :ref:`modulo backend` di comunicare una richiesta di avvio riuscita solo quando l'intera immagine è avviata +#. **Healthcheck**: Installa `curl`, uno strumento in grado di effettuare richieste :abbr:`HTTP (HyperText Transfer Protocol` da linea di comando, e configura la verifica dello `stato di salute ` dell'immagine, al fine di comunicare al `modulo backend ` il risultato di una richiesta di avvio. .. code-block:: docker @@ -1403,12 +1454,6 @@ Il modulo è composto da un singolo ``Dockerfile`` che crea un immagine Docker i # We probably should go back to the default user USER ${NB_UID} -.. note:: - - I blocchi di codice all'interno di questa sezione sono stati inseriti manualmente e potrebbero non essere interamente aggiornati alla versione più recente del file. - - Si consiglia di consultare il ``Dockerfile`` in caso si necessiti di informazioni aggiornate. - Strumenti di sviluppo =====================