mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 14:54:22 +00:00
🎓 Create thesis structure
This commit is contained in:
parent
9a6d9619b3
commit
7b3195cc3f
119 changed files with 59 additions and 2936 deletions
|
@ -1,22 +0,0 @@
|
||||||
Il progetto in breve
|
|
||||||
********************
|
|
||||||
|
|
||||||
*Sophon* è una applicazione web realizzata nel 2021 da Stefano Pigozzi per conto del `dipartimento di Informatica`_ dell'`Università di Modena e Reggio Emilia`_.
|
|
||||||
|
|
||||||
Sophon permette ai suoi utenti di effettuare in sicurezza attività di ricerca collaborativa da remoto, sfruttando le macchine dell'istituzione di loro appartenenza per l'elaborazione dei dati.
|
|
||||||
|
|
||||||
.. _dipartimento di Informatica: https://www.fim.unimore.it/
|
|
||||||
.. _Università di Modena e Reggio Emilia: https://www.unimore.it/
|
|
||||||
|
|
||||||
|
|
||||||
Screenshots
|
|
||||||
===========
|
|
||||||
|
|
||||||
.. image:: ../2_concepts/1_instances/choose.png
|
|
||||||
.. image:: ../2_concepts/2_users/login.png
|
|
||||||
.. image:: ../2_concepts/3_researchgroups/list.png
|
|
||||||
.. image:: ../2_concepts/4_researchprojects/list.png
|
|
||||||
.. image:: ../2_concepts/5_notebooks/list.png
|
|
||||||
.. image:: ../2_concepts/5_notebooks/detail.png
|
|
||||||
.. image:: ../2_concepts/5_notebooks/inside_the_lab.png
|
|
||||||
.. image:: ../../2_admin/2_administration/admin_home.png
|
|
BIN
docs/source/1_user/2_concepts/1_instances/choose.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/1_instances/choose.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/1_instances/diagram.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/1_instances/diagram.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,26 +0,0 @@
|
||||||
Istanza
|
|
||||||
=======
|
|
||||||
|
|
||||||
Un'*istanza* rappresenta un'**installazione di Sophon** effettuata su un server di un'istituzione di ricerca, come ad esempio un'Università.
|
|
||||||
|
|
||||||
Ogni istanza è **fisicamente e logicamente separata** dalle altre; istanze diverse **non condividono alcun dato** tra loro.
|
|
||||||
|
|
||||||
.. image:: diagram.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
URL dell'istanza
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Ciascuna istanza è accessibile tramite **uno specifico URL**, deciso dall'amministratore di sistema al momento dell'installazione.
|
|
||||||
|
|
||||||
.. image:: urls.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
Istanze nell'interfaccia web
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
L'interfaccia web di Sophon permette di **selezionare l'istanza** che si desidera usare inserendo il corrispondente URL.
|
|
||||||
|
|
||||||
.. image:: choose.png
|
|
BIN
docs/source/1_user/2_concepts/1_instances/urls.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/1_instances/urls.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/2_users/creation.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/2_users/creation.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/2_users/diagram.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/2_users/diagram.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,58 +0,0 @@
|
||||||
Utente
|
|
||||||
======
|
|
||||||
|
|
||||||
Un *utente* è una entità che interagisce con una specifica istanza Sophon: ad esempio, un utente potrebbe essere una persona fisica, oppure potrebbe essere un software di automazione che si interfaccia con Sophon.
|
|
||||||
|
|
||||||
.. image:: diagram.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
Livelli di accesso
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Un utente può avere uno dei seguenti *livelli di accesso*:
|
|
||||||
|
|
||||||
|
|
||||||
Superutente
|
|
||||||
^^^^^^^^^^^
|
|
||||||
|
|
||||||
Utente con accesso completo a ogni singola risorsa sull'istanza Sophon, tipicamente riservato per l'amministratore di sistema.
|
|
||||||
|
|
||||||
|
|
||||||
Utente
|
|
||||||
^^^^^^
|
|
||||||
|
|
||||||
Utente con permessi limitati alle risorse che ha creato o a cui è stato fornito accesso.
|
|
||||||
|
|
||||||
|
|
||||||
Ospite
|
|
||||||
^^^^^^
|
|
||||||
|
|
||||||
Utente che può visualizzare alcuni contenuti dell'istanza Sophon ma non può interagirci.
|
|
||||||
|
|
||||||
|
|
||||||
Credenziali di accesso
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Gli utenti di tipo :ref:`Utente` e :ref:`Superutente` devono identificarsi sull'istanza con le loro credenziali.
|
|
||||||
|
|
||||||
Di default, le credenziali sono un **nome utente** e una **password**, ma è possibile che l'amministratore di sistema implementi un sistema diverso, ad esempio un sistema `Single Sign-On`_.
|
|
||||||
|
|
||||||
|
|
||||||
.. _Single Sign-On: https://it.wikipedia.org/wiki/Single_sign-on
|
|
||||||
|
|
||||||
|
|
||||||
Creazione di nuovi utenti
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
In un':ref:`istanza` Sophon, la registrazione autonoma **non è permessa**: nuovi utenti possono essere creati esclusivamente da un :ref:`superutente` all'interno del pannello di amministrazione.
|
|
||||||
|
|
||||||
.. image:: creation.png
|
|
||||||
|
|
||||||
|
|
||||||
Utenti nell'interfaccia web
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Dopo aver selezionato un':ref:`istanza`, l'interfaccia web di Sophon permette di **effettuare l'accesso** come la tipologia di utente con la quale si intende utilizzare il servizio.
|
|
||||||
|
|
||||||
.. image:: login.png
|
|
BIN
docs/source/1_user/2_concepts/2_users/login.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/2_users/login.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/3_researchgroups/choose.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/choose.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/3_researchgroups/creation.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/creation.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/3_researchgroups/diagram.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/diagram.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/3_researchgroups/icons.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/icons.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,69 +0,0 @@
|
||||||
Gruppo di ricerca
|
|
||||||
=================
|
|
||||||
|
|
||||||
Un *gruppo di ricerca* rappresenta un insieme di utenti che collaborano su uno o più progetti.
|
|
||||||
|
|
||||||
.. image:: diagram.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
Membri e modalità di accesso
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Gli utenti dell':ref:`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 può diventarne membro semplicemente **facendo richiesta** attraverso l'interfaccia web;
|
|
||||||
|
|
||||||
.. image:: join_open.png
|
|
||||||
|
|
||||||
- se il gruppo è in *modalità manuale*, allora nessun utente potrà richiedere di unirsi, e i membri saranno **selezionati manualmente** dal creatore del gruppo.
|
|
||||||
|
|
||||||
.. image:: join_manual.png
|
|
||||||
|
|
||||||
Nell'interfaccia web, i gruppi aperti sono marcati con l'icona di un **globo 🌐**, mentre i gruppi in modalità manuale sono marcati con l'icona di una **busta ✉️**.
|
|
||||||
|
|
||||||
.. image:: icons.png
|
|
||||||
|
|
||||||
In qualsiasi momento, i membri di un gruppo possono **lasciarlo** facendo richiesta attraverso l'interfaccia web.
|
|
||||||
|
|
||||||
|
|
||||||
Creazione di nuovi gruppi
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Qualsiasi :ref:`utente` può **creare** gruppi di ricerca dall'interfaccia web.
|
|
||||||
|
|
||||||
.. image:: creation.png
|
|
||||||
|
|
||||||
|
|
||||||
Modifica di gruppi
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Il creatore di un gruppo di ricerca è l'unico :ref:`utente` che può cambiarne **nome**, **descrizione**, **membri** e **modalità di accesso**.
|
|
||||||
|
|
||||||
Lo *slug*, l'identificatore univoco del gruppo, non è modificabile successivamente alla creazione, in quanto è utilizzato all'interno degli URL, che devono essere immutabili.
|
|
||||||
|
|
||||||
|
|
||||||
Eliminazione di gruppi
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Il creatore di un gruppo è l'unico utente in grado di **cancellare** il gruppo che ha creato.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
L'eliminazione di un gruppo è un'operazione distruttiva non reversibile!
|
|
||||||
|
|
||||||
.. hint::
|
|
||||||
|
|
||||||
Se si è i creatori di un gruppo, e si vuole trasferire il gruppo ad un altro utente, sarà necessario fare richiesta ad un :ref:`superutente` di cambiare il proprietario del gruppo all'interno del pannello di amministrazione.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
:ref:`Conferma di eliminazione`
|
|
||||||
|
|
||||||
|
|
||||||
Gruppi nell'interfaccia web
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Dopo aver effettuato l'accesso come :ref:`utente` o :ref:`ospite`, l'interfaccia utente di Sophon visualizza l'elenco di gruppi di ricerca disponibili nell':ref:`istanza`, permettendo agli utenti di unirsi ad essi, lasciarli, oppure eliminarli.
|
|
||||||
|
|
||||||
.. image:: list.png
|
|
BIN
docs/source/1_user/2_concepts/3_researchgroups/join_manual.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/join_manual.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/3_researchgroups/join_open.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/join_open.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/3_researchgroups/list.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/3_researchgroups/list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/4_researchprojects/creation.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/4_researchprojects/creation.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/4_researchprojects/diagram.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/4_researchprojects/diagram.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/4_researchprojects/icons.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/4_researchprojects/icons.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,68 +0,0 @@
|
||||||
Progetto di ricerca
|
|
||||||
===================
|
|
||||||
|
|
||||||
Un *progetto di ricerca* rappresenta una **collezione di oggetti** relativa a un singolo argomento mantenuta da un :ref:`gruppo di ricerca`.
|
|
||||||
|
|
||||||
.. image:: diagram.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
Visibilità dei progetti
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
I progetti hanno tre diverse impostazioni di visibilità che regolano chi può visualizzarne i contenuti:
|
|
||||||
|
|
||||||
.. glossary::
|
|
||||||
|
|
||||||
Progetto privato
|
|
||||||
Il progetto è visibile solo ai membri del gruppo a cui appartiene il progetto.
|
|
||||||
|
|
||||||
Progetto interno
|
|
||||||
Il progetto è visibile solo agli :term:`utenti` dell'istanza, e non agli :term:`ospiti`.
|
|
||||||
|
|
||||||
Progetto pubblico
|
|
||||||
Il progetto è visibile a tutti.
|
|
||||||
|
|
||||||
I progetti privati sono marcati con l'icona di un **lucchetto chiuso 🔒**, i progetti interni con l'icona di un **università 🏦** e i progetti pubblici con l'icona di un **globo 🌐**.
|
|
||||||
|
|
||||||
.. image:: icons.png
|
|
||||||
|
|
||||||
|
|
||||||
Creazione di nuovi progetti
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Qualsiasi *membro* di un :ref:`gruppo di ricerca` può creare nuovi progetti.
|
|
||||||
|
|
||||||
.. image:: creation.png
|
|
||||||
|
|
||||||
|
|
||||||
Modifica di progetti
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Qualsiasi *membro* di un :ref:`gruppo di ricerca` può modificare **nome**, **descrizione** dei progetti al suo interno.
|
|
||||||
|
|
||||||
Solo il *creatore del gruppo* può modificarne la **visibilità**, o **trasferire il progetto ad un altro gruppo**.
|
|
||||||
|
|
||||||
Lo *slug*, l'identificatore univoco del progetto, non è modificabile successivamente alla creazione, in quanto è utilizzato all'interno degli URL, che devono essere immutabili.
|
|
||||||
|
|
||||||
|
|
||||||
Eliminazione di progetti
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Qualsiasi *membro* di un :ref:`gruppo di ricerca` può eliminare i progetti al suo interno.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
L'eliminazione di un progetto è un'operazione distruttiva non reversibile!
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
:ref:`Conferma di eliminazione`
|
|
||||||
|
|
||||||
|
|
||||||
Progetti nell'interfaccia web
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
Dopo aver selezionato un :ref:`gruppo di ricerca`, l'interfaccia web mostra i progetti visibili all':term:`utente` attuale, e gli permette di selezionarne uno oppure di eliminarli.
|
|
||||||
|
|
||||||
.. image:: list.png
|
|
BIN
docs/source/1_user/2_concepts/4_researchprojects/list.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/4_researchprojects/list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/action_lock.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/action_lock.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/action_start.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/action_start.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/action_stop.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/action_stop.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/collaboration.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/collaboration.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/connection.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/connection.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/creation.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/creation.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/detail.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/detail.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/diagram.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/diagram.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/diagram_network.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/diagram_network.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,155 +0,0 @@
|
||||||
Notebook
|
|
||||||
========
|
|
||||||
|
|
||||||
Un *notebook* rappresenta una **postazione di lavoro** che può essere allegata ad un :ref:`progetto di ricerca`.
|
|
||||||
|
|
||||||
.. image:: diagram.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
Creazione di nuovi notebook
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Qualsiasi **membro** di un :ref:`gruppo di ricerca` può creare nuovi notebook all'interno di uno dei progetti del gruppo a cui appartiene.
|
|
||||||
|
|
||||||
.. image:: creation.png
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Per motivi tecnici, i notebook non possono avere come slug ``backend``, ``frontend``, ``proxy``, ``api``, ``static`` e ``src``, oppure iniziare o terminare con un trattino ``-``.
|
|
||||||
|
|
||||||
|
|
||||||
Stato del notebook
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Un notebook può essere *avviato* o *fermo* in base al suo stato di esecuzione sull':ref:`istanza` Sophon:
|
|
||||||
|
|
||||||
- è *avviato* se sta venendo eseguito ed è accessibile;
|
|
||||||
|
|
||||||
.. image:: status_stopped.png
|
|
||||||
:height: 40
|
|
||||||
|
|
||||||
- è *fermo* se non sta venendo eseguito o sta venendo preparato.
|
|
||||||
|
|
||||||
.. image:: status_running.png
|
|
||||||
:height: 40
|
|
||||||
|
|
||||||
Alla creazione, un notebook è *fermo*.
|
|
||||||
|
|
||||||
|
|
||||||
Avviare un notebook
|
|
||||||
^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Un **membro** del :ref:`gruppo di ricerca` a cui appartiene il notebook può richiedere al server l'avvio di quest'ultimo, in modo da poterlo utilizzare successivamente.
|
|
||||||
|
|
||||||
.. image:: action_start.png
|
|
||||||
|
|
||||||
|
|
||||||
Fermare un notebook
|
|
||||||
^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Un **membro** del :ref:`gruppo di ricerca` a cui appartiene il notebook può richiedere al server l'arresto di quest'ultimo, salvando i dati e interrompendo la sessione di lavoro attualmente in corso.
|
|
||||||
|
|
||||||
.. image:: action_stop.png
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Se un notebook viene fermato durante un upload o download di file, essi risulteranno corrotti e saranno da ritrasferire.
|
|
||||||
|
|
||||||
|
|
||||||
Immagine del notebook
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
In **fase di creazione** di un notebook, oppure mentre esso è **fermo**, è possibile selezionare un'*immagine*, ovvero il programma che sarà eseguito dal notebook all'avvio.
|
|
||||||
|
|
||||||
Attualmente, l'unica immagine configurata è **Jupyter (Sophon)**, che esegue un server `Jupyter`_ con un'interfaccia `JupyterLab`_.
|
|
||||||
|
|
||||||
|
|
||||||
.. _Jupyter: https://jupyter.org/
|
|
||||||
.. _JupyterLab: https://jupyterlab.readthedocs.io/en/stable/
|
|
||||||
|
|
||||||
|
|
||||||
Collegarsi a un notebook
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
I **membri** del :ref:`gruppo di ricerca` a cui appartiene il notebook possono connettersi ad un notebook **avviato** attraverso un URL segreto comunicatogli dall':ref:`istanza`.
|
|
||||||
|
|
||||||
.. image:: connection.png
|
|
||||||
|
|
||||||
|
|
||||||
Utilizzo di un notebook
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Una volta connessi ad un notebook, sarà visualizzato il programma eseguito dall'immagine selezionata.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
Per informazioni su come usare JupyterLab, è possibile consultare l'apposita `documentazione <https://jupyterlab.readthedocs.io/en/stable/>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Collaborazione
|
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
È possibile il collegamento **simultaneo** di più membri al notebook: l'immagine selezionata permetterà loro di collaborare in tempo reale sugli stessi file.
|
|
||||||
|
|
||||||
.. image:: collaboration.png
|
|
||||||
|
|
||||||
|
|
||||||
Blocco di un notebook
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Qualsiasi **membro** del :ref:`gruppo di ricerca` a cui appartiene il notebook può *bloccarlo* per segnalare agli altri utenti che vi hanno accesso di non utilizzare quello specifico notebook.
|
|
||||||
|
|
||||||
.. image:: action_lock.png
|
|
||||||
|
|
||||||
Bloccare un notebook **rimuove dall'interfaccia web** i bottoni per l'avvio, l'arresto, l'eliminazione al notebook bloccato, e, per tutti tranne l':ref:`utente` che ha effettuato la richiesta, anche il bottone per la connessione.
|
|
||||||
|
|
||||||
.. image:: locked.png
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Il blocco di un notebook è solo estetico, e non impedisce agli utenti di effettuare queste operazioni tramite strumenti esterni, come la Console per sviluppatori del browser web.
|
|
||||||
|
|
||||||
Un notebook bloccato potrà essere sbloccato da qualsiasi **membro** del :ref:`gruppo di ricerca`; il membro che ha richiesto il blocco potrà sbloccarlo **immediatamente**, mentre agli altri membri sarà richiesto di confermare l'azione come se stesse venendo effettuata un'eliminazione.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
:ref:`Conferma di eliminazione`
|
|
||||||
|
|
||||||
|
|
||||||
Isolamento dei notebook
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
I notebook risiedono tutti sullo **stesso elaboratore fisico** che esegue l':ref:`istanza` Sophon, pertanto ne condividono le risorse, come processore, scheda video e memoria.
|
|
||||||
|
|
||||||
Sono però **logicamente isolati**: i file contenuti in un notebook non sono accessibili agli altri, e i notebook non hanno modo di comunicare direttamente tra loro.
|
|
||||||
|
|
||||||
.. image:: diagram_network.png
|
|
||||||
:width: 400
|
|
||||||
|
|
||||||
|
|
||||||
Modifica di un notebook
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Qualsiasi *membro* di un :ref:`gruppo di ricerca` può modificare **nome** e **immagine** dei notebook *fermi* al suo interno.
|
|
||||||
|
|
||||||
I notebook *avviati* non possono essere modificati.
|
|
||||||
|
|
||||||
Lo *slug*, l'identificatore univoco del notebook, non è modificabile successivamente alla creazione, in quanto è utilizzato all'interno degli URL, che devono essere immutabili.
|
|
||||||
|
|
||||||
|
|
||||||
Eliminazione di un notebook
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Qualsiasi *membro* di un :ref:`gruppo di ricerca` può eliminare i notebook all'interno dei progetti del gruppo, a condizione che questi siano *fermi* e *non bloccati*.
|
|
||||||
|
|
||||||
|
|
||||||
Notebook nell'interfaccia web
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
Dopo aver selezionato un :ref:`progetto di ricerca`, l'interfaccia web mostra l'elenco dei notebook che gli appartengono, assieme alle azioni che è possibile effettuare su di essi.
|
|
||||||
|
|
||||||
.. image:: list.png
|
|
||||||
|
|
||||||
È possibile selezionare un notebook per visualizzarne i dettagli o connettercisi.
|
|
||||||
|
|
||||||
.. image:: detail.png
|
|
BIN
docs/source/1_user/2_concepts/5_notebooks/inside_the_lab.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/inside_the_lab.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/list.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/locked.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/locked.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/status_running.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/status_running.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/5_notebooks/status_stopped.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/5_notebooks/status_stopped.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/2_concepts/diagram_full.png
(Stored with Git LFS)
BIN
docs/source/1_user/2_concepts/diagram_full.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,15 +0,0 @@
|
||||||
I concetti
|
|
||||||
**********
|
|
||||||
|
|
||||||
Questa sezione illustra i concetti chiave di Sophon e il loro funzionamento.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
1_instances/index
|
|
||||||
2_users/index
|
|
||||||
3_researchgroups/index
|
|
||||||
4_researchprojects/index
|
|
||||||
5_notebooks/index
|
|
||||||
|
|
||||||
.. image:: diagram_full.png
|
|
BIN
docs/source/1_user/3_extras/confirm.png
(Stored with Git LFS)
BIN
docs/source/1_user/3_extras/confirm.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,72 +0,0 @@
|
||||||
Dettagli dell'interfaccia web
|
|
||||||
*****************************
|
|
||||||
|
|
||||||
Sono elencate in questo capitolo alcuni dettagli interessanti relativi all'intera interfaccia web.
|
|
||||||
|
|
||||||
|
|
||||||
Markdown nelle descrizioni
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Le descrizioni dell':ref:`istanza`, del :ref:`gruppo di ricerca` selezionato e del :ref:`progetto di ricerca` selezionato sono interpretate dall'interfaccia web come `Markdown`_, un semplice e comune linguaggio di marcatura del testo con varie funzionalità che possono essere utili per descrivere l'entità in questione o lasciare messaggi agli altri collaboratori.
|
|
||||||
|
|
||||||
Si fornisce un breve riassunto della sintassi di `Markdown`_.
|
|
||||||
|
|
||||||
.. code-block:: markdown
|
|
||||||
|
|
||||||
<!-- Commento, non viene visualizzato -->
|
|
||||||
|
|
||||||
<!-- Titoli -->
|
|
||||||
# Parte
|
|
||||||
## Capitolo
|
|
||||||
### Sezione
|
|
||||||
#### Sottosezione
|
|
||||||
##### Sottosottosezione
|
|
||||||
###### Paragrafo
|
|
||||||
|
|
||||||
<!-- Formattazione -->
|
|
||||||
**grassetto**
|
|
||||||
*corsivo*
|
|
||||||
__sottolineato__
|
|
||||||
`codice`
|
|
||||||
|
|
||||||
<!-- Collegamenti -->
|
|
||||||
[testo](url)
|
|
||||||
|
|
||||||
<!-- Immagini -->
|
|
||||||
![alt](url)
|
|
||||||
|
|
||||||
<!-- Tabelle -->
|
|
||||||
| Riga 1 | Riga 2 | Riga 3 |
|
|
||||||
|--------|--------|--------|
|
|
||||||
| Cella | Cella | Cella |
|
|
||||||
| Cella | Cella | Cella |
|
|
||||||
|
|
||||||
<!-- Codice -->
|
|
||||||
```linguaggio
|
|
||||||
def funzione():
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
.. _Markdown: https://daringfireball.net/projects/markdown/syntax
|
|
||||||
|
|
||||||
|
|
||||||
Elenco dei membri
|
|
||||||
=================
|
|
||||||
|
|
||||||
Quando viene selezionato un :ref:`gruppo di ricerca`, viene visualizzato l'elenco dei suoi membri.
|
|
||||||
|
|
||||||
Il creatore del :ref:`gruppo di ricerca` è evidenziato in blu, mentre l':ref:`utente` attuale è sottolineato.
|
|
||||||
|
|
||||||
.. image:: members_list.png
|
|
||||||
|
|
||||||
|
|
||||||
Conferma di eliminazione
|
|
||||||
========================
|
|
||||||
|
|
||||||
Per impedire eliminazioni accidentali di risorse, è presente un meccanismo di conferma che richiede all'utente di ripremere il tasto di eliminazione trascorsi 3 secondi dalla prima richiesta.
|
|
||||||
|
|
||||||
.. image:: confirm.png
|
|
||||||
|
|
||||||
.. raw:: html
|
|
||||||
|
|
||||||
<p><video width="460" height="232" controls src="../../_static/group_delete_confirm.mp4"></video></p>
|
|
BIN
docs/source/1_user/3_extras/members_list.png
(Stored with Git LFS)
BIN
docs/source/1_user/3_extras/members_list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/admin_login.png
(Stored with Git LFS)
BIN
docs/source/1_user/admin_login.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/classic_notebook.png
(Stored with Git LFS)
BIN
docs/source/1_user/classic_notebook.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/1_user/screenshots/admin_page.png
(Stored with Git LFS)
BIN
docs/source/1_user/screenshots/admin_page.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,21 +0,0 @@
|
||||||
Requisiti dell'host
|
|
||||||
===================
|
|
||||||
|
|
||||||
- Una connessione ad Internet (solo in fase di installazione)
|
|
||||||
- `GNU Wget`_ (solo in fase di installazione)
|
|
||||||
- Un nome di dominio
|
|
||||||
- Un webserver (ad esempio, `Apache HTTPd`_)
|
|
||||||
- Un certificato SSL valido *(opzionale, ma raccomandato)*
|
|
||||||
- `Docker Engine`_
|
|
||||||
- `Docker Compose`_
|
|
||||||
|
|
||||||
.. hint::
|
|
||||||
|
|
||||||
È possibile ottenere gratuitamente un certificato SSL utilizzando `Letsencrypt`_!
|
|
||||||
|
|
||||||
|
|
||||||
.. _GNU Wget: https://www.gnu.org/software/wget/
|
|
||||||
.. _Apache HTTPd: https://httpd.apache.org/
|
|
||||||
.. _Docker Engine: https://docs.docker.com/engine/
|
|
||||||
.. _Docker Compose: https://docs.docker.com/compose/
|
|
||||||
.. _Letsencrypt: https://letsencrypt.org/
|
|
|
@ -1,30 +0,0 @@
|
||||||
Preparazione di Docker Compose
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Come ``root``, si crei una nuova cartella sul proprio sistema operativo in cui archiviare le risorse relative a Sophon:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:~# mkdir -p /dock/sophon
|
|
||||||
|
|
||||||
Successivamente, si scarichi il file ``docker-compose.yml`` all'interno della cartella dal repository di Sophon:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:~# cd /dock/sophon
|
|
||||||
|
|
||||||
root:/dock/sophon# wget "https://raw.githubusercontent.com/Steffo99/sophon/main/docker-compose.yml"
|
|
||||||
--2021-11-02 18:03:05-- https://raw.githubusercontent.com/Steffo99/sophon/main/docker-compose.yml
|
|
||||||
SSL_INIT
|
|
||||||
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
|
|
||||||
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133,
|
|
||||||
185.199.109.133, 185.199.110.133, ...
|
|
||||||
Connecting to raw.githubusercontent.com (raw.githubusercontent.com) 185.199.108.133:443...
|
|
||||||
connected.
|
|
||||||
HTTP request sent, awaiting response... 200 OK
|
|
||||||
Length: 2957 (2.9K) [text/plain]
|
|
||||||
Saving to: ‘docker-compose.yml’
|
|
||||||
|
|
||||||
docker-compose.yml 100%[===================>] 2.89K --.-KB/s in 0s
|
|
||||||
|
|
||||||
2021-11-02 18:03:05 (48.0 MB/s) - ‘docker-compose.yml’ saved [2957/2957]
|
|
|
@ -1,17 +0,0 @@
|
||||||
Configurazione DNS
|
|
||||||
==================
|
|
||||||
|
|
||||||
Si scelga il dominio (o sottodominio) sul quale si vuole che Sophon sia accessibile e si aggiungano i seguenti record DNS, sostituendo il dominio ``ilmiosophon.it`` con il proprio nome di dominio, e gli indirizzi IPv4 e IPv6 del server al posto di `0.0.0.0` e `1234::1234`:
|
|
||||||
|
|
||||||
.. code-block:: dns
|
|
||||||
|
|
||||||
*.ilmiosophon.it 1800 IN A 0.0.0.0
|
|
||||||
*.ilmiosophon.it 1800 IN AAAA 1234::1234
|
|
||||||
ilmiosophon.it 1800 IN A 0.0.0.0
|
|
||||||
ilmiosophon.it 1800 IN AAAA 1234::1234
|
|
||||||
|
|
||||||
Sophon sarà quindi accessibile ai seguenti indirizzi:
|
|
||||||
|
|
||||||
- l'interfaccia web al dominio base (``https://ilmiosophon.it/``);
|
|
||||||
- l'API al dominio base prefisso con ``api.`` (``https://api.ilmiosophon.it/``);
|
|
||||||
- i notebook al dominio base prefissi con lo slug del notebook (``https://ilmionotebook.ilmiosophon.it/``).
|
|
|
@ -1,222 +0,0 @@
|
||||||
Configurazione ``docker-compose.yml``
|
|
||||||
=====================================
|
|
||||||
|
|
||||||
Si configuri con l'editor di testo preferito il file ``docker-compose.yml`` con le impostazioni desiderate.
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# open docker-compose.yml
|
|
||||||
|
|
||||||
In particolare, tutte le impostazioni precedute da ``# INSTALL`` vanno obbligatoriamente modificate.
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_SECRET_KEY``
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Specifica la chiave segreta da usare per i cookie di sessione.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_SECRET_KEY=do-not-use-this-key-in-production-or-you-will-get-hacked
|
|
||||||
|
|
||||||
.. tip::
|
|
||||||
|
|
||||||
Un modo facile per impostare la chiave è premere velocemente tasti a caso sulla tastiera!
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Cambiare la chiave segreta una volta installato Sophon invaliderà tutti gli accessi effettuati dagli utenti.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
La chiave segreta è un dato estremamente riservato: chiunque sia a conoscenza della chiave segreta potrà effettuare l'accesso come qualsiasi utente!
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`SECRET_KEY <https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-SECRET_KEY>`_ nella documentazione di Django.
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_PROXY_BASE_DOMAIN``
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Specifica il dominio che dovrà essere usato come radice per il proxy, ovvero il dominio per il quale si è configurato il DNS in precedenza.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_PROXY_BASE_DOMAIN=ilmiosophon.it
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Se non è specificato, Sophon verrà eseguito in modalità "sviluppo", e assegnerà porte libere del server ai :ref:`notebook` invece che sottodomini.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
L'opzione :ref:`\`\`APACHE_PROXY_BASE_DOMAIN\`\`` più avanti in questa guida, che deve coincidere con questo valore.
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_PROXY_PROTOCOL``
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Specifica il protocollo che dovrà essere usato nei mapping del proxy.
|
|
||||||
|
|
||||||
Si consiglia vivamente di utilizzare ``https``, ma è un valore valido anche ``http``.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_PROXY_PROTOCOL=https
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_ALLOWED_HOSTS``
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Specifica i domini da cui possono provenire le richieste alla pagina di amministrazione.
|
|
||||||
|
|
||||||
Per specificare più domini, è necessario separarli con dei pipe ``|`` .
|
|
||||||
|
|
||||||
Eccetto in configurazioni speciali, deve essere uguale al dominio prefisso da ``api.``.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_ALLOWED_HOSTS=api.ilmiosophon.it
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`ALLOWED_HOSTS <https://docs.djangoproject.com/en/3.2/ref/settings/#allowed-hosts>`_ nella documentazione di Django
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_ALLOWED_ORIGINS``
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Specifica i domini da cui possono provenire le richieste all'API.
|
|
||||||
|
|
||||||
Per specificare più domini, è necessario separarli con dei pipe ``|`` .
|
|
||||||
|
|
||||||
Eccetto in configurazioni speciali, deve contenere il proprio dominio prefisso dal protocollo, e in aggiunta il dominio speciale ``https://sophon.steffo.eu``, necessario per permettere l'accesso dall'interfaccia web "universale" di Sophon.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_ALLOWED_ORIGINS=https://ilmiosophon.it|https://sophon.steffo.eu
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`Access-Control-Allow-Origin <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin>`_ su MDN
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_STATIC_URL``
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Specifica l'URL a cui saranno accessibili i file statici di Sophon.
|
|
||||||
|
|
||||||
Eccetto in configurazioni speciali, deve essere uguale alla seguente stringa, con le parole in maiuscolo sostituite rispettivamente dal protocollo e dal dominio selezionato: ``PROTOCOLLO://static.DOMINIO/django-static/``.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_ALLOWED_ORIGINS=http://static.ilmiosophon.it/django-static/
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Ci si assicuri che sia presente uno slash al termine della stringa, oppure il pannello di amministrazione non sarà visualizzato correttamente!
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`STATIC_URL <https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-STATIC_URL>`_ nella documentazione di Django
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_LANGUAGE_CODE``
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Specifica la lingua che deve usare Sophon nei messaggi di errore.
|
|
||||||
|
|
||||||
Usa il formato `language code`_ di Django.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_LANGUAGE_CODE=en-us
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`LANGUAGE_CODE <https://docs.djangoproject.com/en/3.2/ref/settings/#language-code>`_ nella documentazione di Django
|
|
||||||
|
|
||||||
.. _language code: https://docs.djangoproject.com/en/3.2/topics/i18n/#term-language-code
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_TIME_ZONE``
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Specifica il fuso orario che deve usare Sophon nell'interfaccia di amministrazione.
|
|
||||||
|
|
||||||
Usa il formato `tzdata`_.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_TIME_ZONE=Europe/Paris
|
|
||||||
|
|
||||||
.. hint::
|
|
||||||
|
|
||||||
Il fuso orario italiano è ``Europe/Rome``.
|
|
||||||
|
|
||||||
.. _tzdata: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_SU_USERNAME``
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Specifica il nome del :ref:`superutente` che verrà automaticamente creato qualora il database non contenga altri utenti.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_SU_USERNAME=root
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_SU_EMAIL``
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Specifica l'email del :ref:`superutente` che verrà automaticamente creato qualora il database non contenga altri utenti.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_SU_USERNAME=bot@steffo.eu
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Attualmente, l'email non è utilizzata, ma è richiesta da Django per la creazione di un nuovo utente.
|
|
||||||
|
|
||||||
|
|
||||||
``DJANGO_SU_PASSWORD``
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Specifica la password del :ref:`superutente` che verrà automaticamente creato qualora il database non contenga altri utenti.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- DJANGO_SU_PASSWORD=square
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
La password è un dato estremamente riservato, in quanto chiunque ne venga a conoscenza potrà accedere a Sophon con pieni privilegi!
|
|
||||||
|
|
||||||
|
|
||||||
``REACT_APP_DEFAULT_INSTANCE``
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Specifica il valore con cui precompilare il campo "selezione istanza" dell'interfaccia web di Sophon.
|
|
||||||
|
|
||||||
Eccetto in configurazioni speciali, deve essere uguale al dominio prefisso dal protocollo e da ``api.``.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- REACT_APP_DEFAULT_INSTANCE=https://api.ilmiosophon.it
|
|
||||||
|
|
||||||
|
|
||||||
``APACHE_PROXY_BASE_DOMAIN``
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Specifica il dominio che dovrà essere usato come radice per il proxy, ovvero il ``DOMINIO`` per il quale si è configurato il DNS in precedenza.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
- APACHE_PROXY_BASE_DOMAIN=dev.sophon.steffo.eu
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
L'opzione :ref:`\`\`DJANGO_PROXY_BASE_DOMAIN\`\`` più indietro in questa guida, che deve coincidere con questo valore.
|
|
|
@ -1,48 +0,0 @@
|
||||||
Download delle immagini Docker
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Si utilizzi `Docker Compose`_ per scaricare le `immagini`_ Docker necessarie all'avvio di Sophon:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker compose pull
|
|
||||||
[+] Running 4/4
|
|
||||||
⠿ proxy Pulled 1.5s
|
|
||||||
⠿ frontend Pulled 1.4s
|
|
||||||
⠿ db Pulled 1.9s
|
|
||||||
⠿ backend Pulled 1.6s
|
|
||||||
|
|
||||||
Inoltre, si scarichi manualmente l':ref:`immagine del Notebook` che può essere avviata da Sophon:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker image pull "ghcr.io/steffo99/sophon-jupyter:latest"
|
|
||||||
latest: Pulling from steffo99/sophon-jupyter
|
|
||||||
7b1a6ab2e44d: Already exists
|
|
||||||
578d7ac380c6: Pull complete
|
|
||||||
37f1e0b584f6: Pull complete
|
|
||||||
3c7282703390: Pull complete
|
|
||||||
b38aa558f711: Pull complete
|
|
||||||
1412103d568f: Pull complete
|
|
||||||
67419a9a821e: Pull complete
|
|
||||||
37e6cc015184: Pull complete
|
|
||||||
7d9316e2b57c: Pull complete
|
|
||||||
a7f024508c72: Pull complete
|
|
||||||
f3eae3c301a1: Pull complete
|
|
||||||
d3e2107efade: Pull complete
|
|
||||||
d94bc6f8f069: Pull complete
|
|
||||||
1e1dc3e818ad: Pull complete
|
|
||||||
c975ee664182: Pull complete
|
|
||||||
101cfcc0e15b: Pull complete
|
|
||||||
bf991a0d7538: Pull complete
|
|
||||||
4c044af18c7e: Pull complete
|
|
||||||
605d8c6e8eba: Pull complete
|
|
||||||
ed06f2ae4a88: Pull complete
|
|
||||||
ed8b1c841d10: Pull complete
|
|
||||||
468fe9a390ae: Pull complete
|
|
||||||
Digest: sha256:5d42e5e40e406130c688914d6a58aa94769eab03620b53e0fd409a7fb2682a01
|
|
||||||
Status: Downloaded newer image for ghcr.io/steffo99/sophon-jupyter:latest
|
|
||||||
ghcr.io/steffo99/sophon-jupyter:latest
|
|
||||||
|
|
||||||
.. _Docker Compose: https://docs.docker.com/compose/
|
|
||||||
.. _immagini: https://docs.docker.com/engine/reference/commandline/images/
|
|
|
@ -1,22 +0,0 @@
|
||||||
Avvio di Sophon
|
|
||||||
===============
|
|
||||||
|
|
||||||
Si utilizzi `Docker Compose`_ per eseguire le `immagini`_ di Sophon precedentemente scaricate:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker compose up -d
|
|
||||||
[+] Running 4/4
|
|
||||||
⠿ Container sophon-db-1 Started 11.3s
|
|
||||||
⠿ Container sophon-frontend-1 Started 11.7s
|
|
||||||
⠿ Container sophon-backend-1 Started 10.1s
|
|
||||||
⠿ Container sophon-proxy-1 Started 11.5s
|
|
||||||
|
|
||||||
Si verifichi che i container si siano avviati correttamente con:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker compose logs
|
|
||||||
|
|
||||||
.. _Docker Compose: https://docs.docker.com/compose/
|
|
||||||
.. _immagini: https://docs.docker.com/engine/reference/commandline/images/
|
|
|
@ -1,62 +0,0 @@
|
||||||
Configurazione del webserver dell'host
|
|
||||||
======================================
|
|
||||||
|
|
||||||
Si configuri il webserver dell'host per inoltrare tutto il traffico dalla porta 443 (o 80, se si è selezionato ``http`` in :ref:`\`\`DJANGO_PROXY_PROTOCOL\`\``) alla porta locale 30033.
|
|
||||||
|
|
||||||
Sono allegate le istruzioni per il webserver `Apache HTTPd`_; possono essere però adattate se si vuole usare un webserver diverso, come `NGINX`_ o `caddy`_.
|
|
||||||
|
|
||||||
.. _Apache HTTPd: https://httpd.apache.org/
|
|
||||||
.. _nginx: https://www.nginx.com/
|
|
||||||
.. _caddy: https://caddyserver.com/
|
|
||||||
|
|
||||||
|
|
||||||
Con Apache HTTPd
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Ci si assicuri che `mod_rewrite`_, `mod_proxy`_, `mod_proxy_http`_ e `mod_proxy_wstunnel`_ siano attivati.
|
|
||||||
|
|
||||||
Si aggiungano i seguenti ``VirtualHost`` alla configurazione:
|
|
||||||
|
|
||||||
.. code-block:: apacheconf
|
|
||||||
|
|
||||||
<VirtualHost *:80>
|
|
||||||
ServerName "ilmiosophon.it"
|
|
||||||
ServerAlias "*.ilmiosophon.it"
|
|
||||||
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
.. code-block:: apacheconf
|
|
||||||
|
|
||||||
<VirtualHost *:443>
|
|
||||||
ServerName "ilmiosophon.it"
|
|
||||||
ServerAlias "*.ilmiosophon.it"
|
|
||||||
|
|
||||||
SSLEngine on
|
|
||||||
SSLCertificateFile "/SOSTITUISCIMI/CON/IL/PERCORSO/ALLA/FULL/CHAIN/SSL"
|
|
||||||
SSLCertificateKeyFile "/SOSTITUISCIMI/CON/IL/PERCORSO/ALLA/CHIAVE/PRIVATA/SSL"
|
|
||||||
|
|
||||||
ProxyPreserveHost On
|
|
||||||
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
|
|
||||||
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{HTTP:Upgrade} =websocket [NC]
|
|
||||||
RewriteRule /(.*) ws://127.0.0.1:30033/$1 [P,L]
|
|
||||||
RewriteRule /(.*) http://127.0.0.1:30033/$1 [P,L]
|
|
||||||
|
|
||||||
Protocols h2 http/1.1
|
|
||||||
Header always set Strict-Transport-Security "max-age=63072000"
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
Infine, si riavvii `Apache HTTPd`_:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# systemctl restart httpd
|
|
||||||
|
|
||||||
|
|
||||||
.. _mod_rewrite: https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
|
|
||||||
.. _mod_proxy: https://httpd.apache.org/docs/2.4/mod/mod_proxy.html
|
|
||||||
.. _mod_proxy_http: https://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html
|
|
||||||
.. _mod_proxy_wstunnel: https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
|
|
|
@ -1,4 +0,0 @@
|
||||||
Verificare il funzionamento
|
|
||||||
===========================
|
|
||||||
|
|
||||||
Se tutto è stato configurato correttamente, l'interfaccia web Sophon dovrebbe essere raggiungibile al dominio selezionato (``https://ilmiosophon.it``), e dovrebbe essere possibile effettuare il login con le credenziali configurate del primo :ref:`superutente`.
|
|
|
@ -1,18 +0,0 @@
|
||||||
Installazione
|
|
||||||
*************
|
|
||||||
|
|
||||||
Questo capitolo fornisce le informazioni per l'installazione di Sophon su un server Linux.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:numbered:
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
1_requirements
|
|
||||||
2_preparing_compose
|
|
||||||
3_configuring_dns
|
|
||||||
4_configuring_compose
|
|
||||||
5_pulling_images
|
|
||||||
6_starting_sophon
|
|
||||||
7_host_reverse_proxy
|
|
||||||
8_final_check
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
Pannello di amministrazione
|
|
||||||
===========================
|
|
||||||
|
|
||||||
Sophon include un pannello di amministrazione, accessibile premendo *Go to the admin page* sulla schermata di login, oppure visitando l'URL ``api.DOMINIO/admin``.
|
|
||||||
|
|
||||||
.. image:: admin_where.png
|
|
||||||
|
|
||||||
|
|
||||||
Effettuare l'accesso
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Per utilizzare il pannello di amministrazione è necessario inserire le credenziali di un :ref:`superutente`.
|
|
||||||
|
|
||||||
.. image:: admin_login.png
|
|
||||||
|
|
||||||
|
|
||||||
La schermata principale
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Nella schermata principale del pannello di amministrazione è visibile l'elenco di tutti i tipi di entità gestite da Django, assieme ad uno storico delle ultime operazioni effettuate dal pannello su di esse.
|
|
||||||
|
|
||||||
.. image:: admin_home.png
|
|
||||||
|
|
||||||
È possibile cliccare sul collegamento *Add* di fianco ad un tipo di entità per **crearne** una nuova di quel tipo, oppure il collegamento *Change* per **visualizzare**, **modificare** ed **eliminare** tutte le entità già esistenti di quel tipo.
|
|
||||||
|
|
||||||
|
|
||||||
La barra del titolo
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Tutte le pagine includono in cima la **barra del titolo**, che permette al :ref:`superutente` attualmente collegato di **cambiare la propria password** o effettuare la **disconnessione** dal pannello.
|
|
||||||
|
|
||||||
.. image:: topright.png
|
|
|
@ -1,112 +0,0 @@
|
||||||
Tipi di risorse
|
|
||||||
===============
|
|
||||||
|
|
||||||
Tokens
|
|
||||||
------
|
|
||||||
|
|
||||||
Contiene l'elenco di tutti i token di sessione usati per l'autenticazione tra l'API e l'interfaccia web di Sophon.
|
|
||||||
|
|
||||||
È possibile modificarli per **cambiare l'utente connesso** ad una certa sessione, oppure eliminarli per **forzare la disconnessione** di determinate sessioni.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
In seguito ad una disconnessione forzata, l'utente riscontrerà errori "non autorizzato" sull'interfaccia web fino ad un aggiornamento della pagina o logout manuale.
|
|
||||||
|
|
||||||
.. image:: token_list.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
.. image:: token_detail.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
|
|
||||||
Users
|
|
||||||
-----
|
|
||||||
|
|
||||||
Contiene l'elenco di tutti gli utenti registrati su Sophon.
|
|
||||||
|
|
||||||
In questa pagina è possibile la :ref:`creazione di nuovi utenti`, così come il **cambio di password**, l'**assegnazione di privilegi** di :ref:`superutente` e la **disattivazione degli utenti**.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
I superutenti devono avere sia *staff status* sia *superuser status* attivi per poter utilizzare il pannello di amministrazione.
|
|
||||||
|
|
||||||
.. image:: user_list.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
.. image:: user_detail.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
|
|
||||||
Research groups
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Contiene l'elenco di tutti i gruppi di ricerca creati su Sophon.
|
|
||||||
|
|
||||||
Dal pannello di amministrazione è possibile effettuare modifiche ed eliminazioni **ignorando i permessi normalmente richiesti** per farlo e **trasferire la proprietà** di un gruppo da un utente all'altro.
|
|
||||||
|
|
||||||
.. image:: researchgroup_list.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
.. image:: researchgroup_detail.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
|
|
||||||
Sophon instance details
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Contiene un'entità speciale che controlla l'**aspetto** dell':ref:`istanza`.
|
|
||||||
|
|
||||||
Modificandola, è possibile personalizzare:
|
|
||||||
|
|
||||||
- il **nome** dell':ref:`istanza`, che verrà visualizzato come titolo dell'interfaccia web;
|
|
||||||
|
|
||||||
.. image:: custom_title.png
|
|
||||||
|
|
||||||
- la **descrizione** dell':ref:`istanza`, visualizzata all'interno del riquadro "A proposito dell'istanza";
|
|
||||||
|
|
||||||
.. image:: custom_description.png
|
|
||||||
|
|
||||||
- il **tema colori** dell':ref:`istanza`, applicato all'interfaccia web una volta che un':ref:`istanza` è stata selezionata.
|
|
||||||
|
|
||||||
.. image:: theme_sophon.png
|
|
||||||
:width: 240
|
|
||||||
.. image:: theme_royalblue.png
|
|
||||||
:width: 240
|
|
||||||
.. image:: theme_amber.png
|
|
||||||
:width: 240
|
|
||||||
.. image:: theme_paper.png
|
|
||||||
:width: 240
|
|
||||||
.. image:: theme_hacker.png
|
|
||||||
:width: 240
|
|
||||||
|
|
||||||
|
|
||||||
Notebooks
|
|
||||||
---------
|
|
||||||
|
|
||||||
Contiene l'elenco di tutti i :ref:`notebook` creati su Sophon.
|
|
||||||
|
|
||||||
Oltre ad alterare le entità **ignorando i permessi**, è possibile vedere alcuni parametri tecnici, come l'ID del container Docker a cui è associato il notebook, oppure la porta o l'URL a cui è accessibile il notebook dal proxy.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Modificare *slug*, *container ID*, *local port number* o *internal URL* mentre il :ref:`notebook` è avviato renderà potenzialmente la connessione e l'arresto del notebook!
|
|
||||||
|
|
||||||
.. image:: notebook_list.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
.. image:: notebook_detail.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
|
|
||||||
Research projects
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Contiene l'elenco di tutti i progetti di ricerca creati su Sophon.
|
|
||||||
|
|
||||||
Oltre ad alterare le entità **ignorando i permessi**, è possibile **trasferire un progetto** da un gruppo a un altro.
|
|
||||||
|
|
||||||
.. image:: researchproject_list.png
|
|
||||||
:scale: 50%
|
|
||||||
|
|
||||||
.. image:: researchproject_detail.png
|
|
||||||
:scale: 50%
|
|
BIN
docs/source/2_admin/2_administration/admin_home.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/admin_home.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/admin_login.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/admin_login.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/admin_where.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/admin_where.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/custom_description.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/custom_description.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/custom_title.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/custom_title.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/customization.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/customization.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,9 +0,0 @@
|
||||||
Amministrazione
|
|
||||||
***************
|
|
||||||
|
|
||||||
Questo capitolo fornisce informazioni su come amministrare un':ref:`istanza` Sophon.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
|
|
||||||
1_admin_panel
|
|
||||||
2_entities
|
|
BIN
docs/source/2_admin/2_administration/notebook_detail.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/notebook_detail.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/notebook_list.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/notebook_list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/researchgroup_detail.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/researchgroup_detail.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/researchgroup_list.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/researchgroup_list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/researchproject_detail.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/researchproject_detail.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/researchproject_list.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/researchproject_list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/theme_amber.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/theme_amber.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/theme_hacker.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/theme_hacker.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/theme_paper.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/theme_paper.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/theme_royalblue.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/theme_royalblue.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/theme_sophon.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/theme_sophon.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/token_detail.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/token_detail.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/token_list.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/token_list.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/topright.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/topright.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/user_detail.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/user_detail.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/2_admin/2_administration/user_list.png
(Stored with Git LFS)
BIN
docs/source/2_admin/2_administration/user_list.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,21 +0,0 @@
|
||||||
Aggiornamento
|
|
||||||
*************
|
|
||||||
|
|
||||||
Per aggiornare Sophon, è sufficiente usare `Docker Compose`_ per scaricare le immagini aggiornate e riavviare i container del software.
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker compose down
|
|
||||||
root:/dock/sophon# docker compose pull
|
|
||||||
root:/dock/sophon# docker compose up -d
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
In seguito ad un aggiornamento o un riavvio, alcuni Notebook potrebbero essere **irraggiungibili dal proxy**.
|
|
||||||
|
|
||||||
In tal caso, sarà sufficiente **fermarli** e **riavviarli** dall'interfaccia web.
|
|
||||||
|
|
||||||
Non si verificherà alcuna perdita di dati!
|
|
||||||
|
|
||||||
|
|
||||||
.. _Docker Compose: https://docs.docker.com/compose/
|
|
|
@ -1,27 +0,0 @@
|
||||||
Disinstallazione
|
|
||||||
****************
|
|
||||||
|
|
||||||
Per rimuovere completamente Sophon, è necessario innanzitutto arrestare i container del software principale:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker compose down
|
|
||||||
|
|
||||||
In seguito, è necessario arrestare tutti i container dei notebook ancora avviati:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker container ls
|
|
||||||
c160ea085fe1 steffo45/jupyterlab-docker-sophon "tini -g -- start-no…" 23 hours ago Up 23 hours sophon-container-my-first-notebook
|
|
||||||
0892874ea0d5 ghcr.io/steffo99/sophon-jupyter "tini -g -- start-no…" 3 minutes ago Up 3 minutes (healthy) sophon-container-normal-slug
|
|
||||||
root:/dock/sophon# docker container rm --force c160ea085fe1 0892874ea0d5
|
|
||||||
|
|
||||||
Infine sarà possibile liberare lo spazio occupato dalle risorse Docker di Sophon:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
root:/dock/sophon# docker compose down -v
|
|
||||||
root:/dock/sophon# docker volume prune
|
|
||||||
root:/dock/sophon# docker container prune
|
|
||||||
root:/dock/sophon# docker network prune
|
|
||||||
root:/dock/sophon# docker image prune
|
|
|
@ -1,40 +0,0 @@
|
||||||
Strumenti usati per lo sviluppo
|
|
||||||
*******************************
|
|
||||||
|
|
||||||
Per sviluppare Sophon sono usati i seguenti strumenti:
|
|
||||||
|
|
||||||
- `IntelliJ IDEA Ultimate`_, un IDE multilinguaggio, con i seguenti plugin:
|
|
||||||
- Python
|
|
||||||
- Poetry
|
|
||||||
- ReStructuredText
|
|
||||||
- Node.JS
|
|
||||||
- JavaScript and TypeScript
|
|
||||||
- CSS
|
|
||||||
- Git
|
|
||||||
- Docker
|
|
||||||
|
|
||||||
- `Git`_, uno strumento di controllo versione;
|
|
||||||
- `GitHub`_, un host per repository `Git`_;
|
|
||||||
- `GitHub Issues`_, un issue tracker integrato in `GitHub`_;
|
|
||||||
- `GitHub Actions`_, un sistema di `Continuous Integration`_ e `Continuous Deployment`_ integrato in `GitHub`_;
|
|
||||||
- `GitHub Containers`_, un registro di container Docker integrato in `GitHub`_;
|
|
||||||
- `CodeQL`_, un tool di analisi statica integrato in `GitHub`_;
|
|
||||||
- `Dependabot`_, un tool di analisi delle dipendenze integrato in `GitHub`_;
|
|
||||||
|
|
||||||
- `Sphinx`_, uno strumento per la creazione di documentazione.
|
|
||||||
|
|
||||||
.. _IntelliJ IDEA Ultimate: https://www.jetbrains.com/idea/
|
|
||||||
.. _Git: https://git-scm.com/
|
|
||||||
.. _GitHub: https://github.com/
|
|
||||||
.. _GitHub Issues: https://github.com/features/issues/
|
|
||||||
.. _GitHub Actions: https://github.com/features/actions
|
|
||||||
.. _Continuous Integration: https://it.wikipedia.org/wiki/Integrazione_continua
|
|
||||||
.. _Continuous Deployment: https://en.wikipedia.org/wiki/Continuous_deployment
|
|
||||||
.. _CodeQL: https://codeql.github.com/
|
|
||||||
.. _GitHub Containers: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
|
|
||||||
.. _Sphinx: https://www.sphinx-doc.org/
|
|
||||||
.. _Dependabot: https://dependabot.com/
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
Nel capitolo successivo sono descritte le tecnologie utilizzate all'interno di Sophon.
|
|
|
@ -1,16 +0,0 @@
|
||||||
Librerie e tecnologie utilizzate
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Sono elencate solo le principali librerie utilizzate; dipendenze e librerie minori non sono specificate, ma sono visibili all'interno del file ``poetry.lock``.
|
|
||||||
|
|
||||||
- Il linguaggio di programmazione `Python <https://www.python.org/>`_
|
|
||||||
- Il gestore di dipendenze `Poetry <https://python-poetry.org/>`_
|
|
||||||
- Il framework web `Django <https://www.djangoproject.com/>`_
|
|
||||||
- L'estensione per Django `Django REST Framework <https://www.django-rest-framework.org/>`_
|
|
||||||
- L'estensione per Django `Django CORS Headers <https://github.com/adamchainz/django-cors-headers>`_
|
|
||||||
- L'adattatore database per PostgreSQL `Psycopg <https://pypi.org/project/psycopg2/>`_
|
|
||||||
- Il `Docker SDK for Python <https://docker-py.readthedocs.io/en/stable/>`_
|
|
||||||
- Il server web `Gunicorn <https://gunicorn.org/>`_
|
|
||||||
- L'utilità `lazy-object-proxy <https://github.com/ionelmc/python-lazy-object-proxy>`_
|
|
|
@ -1,89 +0,0 @@
|
||||||
Il progetto sophon
|
|
||||||
------------------
|
|
||||||
.. default-domain:: py
|
|
||||||
.. default-role:: obj
|
|
||||||
.. module:: sophon
|
|
||||||
|
|
||||||
Il progetto Django Sophon aggiunge varie funzionalità al template base dei progetti Django.
|
|
||||||
|
|
||||||
|
|
||||||
Pagina di amministrazione personalizzata
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.admin
|
|
||||||
|
|
||||||
La pagina di amministrazione viene personalizzata con la classe `SophonAdminSite`, che modifica alcuni parametri della classe base.
|
|
||||||
|
|
||||||
Inoltre, il template predefinito viene sovrascritto da quello all'interno del file ``templates/admin/base.html``, che sostituisce il foglio di stile con uno personalizzato per Sophon.
|
|
||||||
|
|
||||||
.. class:: SophonAdminSite(django.contrib.admin.AdminSite)
|
|
||||||
|
|
||||||
.. attribute:: site_header = "Sophon Server Administration"
|
|
||||||
|
|
||||||
Il nome della pagina nell'header viene modificato a *Sophon Server Administration*.
|
|
||||||
|
|
||||||
.. attribute:: site_title = "Sophon Server Administration"
|
|
||||||
|
|
||||||
Il titolo della pagina nell'header viene anch'esso modificato a *Sophon Server Administration*.
|
|
||||||
|
|
||||||
.. attribute:: site_url = None
|
|
||||||
|
|
||||||
Il collegamento *View Site* viene rimosso, in quanto è possibile accedere all'interfaccia web di Sophon da più domini contemporaneamente.
|
|
||||||
|
|
||||||
.. attribute:: index_title = "Resources Administration"
|
|
||||||
|
|
||||||
Il titolo dell'indice viene modificato a *Resources Administration*.
|
|
||||||
|
|
||||||
.. class:: SophonAdminConfig(django.contrib.admin.apps.AdminConfig)
|
|
||||||
|
|
||||||
.. attribute:: default_site = "sophon.admin.SophonAdminSite"
|
|
||||||
|
|
||||||
`.SophonAdminSite` è selezionata come classe predefinita per il sito di amministrazione.
|
|
||||||
|
|
||||||
|
|
||||||
Impostazioni dinamiche
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.settings
|
|
||||||
|
|
||||||
Il file di impostazioni viene modificato per **permettere la configurazione attraverso variabili di ambiente** invece che attraverso il file ``settings.py``, rendendo il deployment con Docker molto più semplice.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
try:
|
|
||||||
DATABASE_ENGINE = os.environ["DJANGO_DATABASE_ENGINE"]
|
|
||||||
except KeyError:
|
|
||||||
log.warning("DJANGO_DATABASE_ENGINE was not set, defaulting to PostgreSQL")
|
|
||||||
DATABASE_ENGINE = "django.db.backends.postgresql"
|
|
||||||
log.debug(f"{DATABASE_ENGINE = }")
|
|
||||||
|
|
||||||
Inoltre, viene configurato il modulo `logging` per emettere testo colorato di più facile comprensione usando il package `coloredlogs`.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
"detail": {
|
|
||||||
"()": coloredlogs.ColoredFormatter,
|
|
||||||
"format": "{asctime:>19} | {name:<24} | {levelname:>8} | {message}",
|
|
||||||
"style": "{",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Autenticazione migliorata
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.auth1
|
|
||||||
|
|
||||||
La classe `rest_framework.authentication.TokenAuthentication` viene modificata per ottenere un comportamento conforme agli standard del web.
|
|
||||||
|
|
||||||
.. class:: BearerTokenAuthentication(rest_framework.authentication.TokenAuthentication)
|
|
||||||
|
|
||||||
.. attribute:: keyword = "Bearer"
|
|
||||||
|
|
||||||
Si configura `rest_framework` per accettare header di autenticazione nella forma ``Bearer <token>``, invece che ``Token <token>``.
|
|
||||||
|
|
||||||
.. module:: sophon.auth2
|
|
||||||
|
|
||||||
La view `rest_framework.authtoken.views.ObtainAuthToken` viene estesa per aggiungere dati alla risposta di autenticazione riuscita.
|
|
||||||
|
|
||||||
.. class:: CustomObtainAuthToken(rest_framework.authtoken.views.ObtainAuthToken)
|
|
||||||
|
|
||||||
.. method:: post(self, request, *args, **kwargs)
|
|
||||||
|
|
||||||
In particolare, viene aggiunta una chiave ``user``, che contiene i dettagli sull'utente che ha effettuato il login.
|
|
|
@ -1,333 +0,0 @@
|
||||||
L'app sophon.core
|
|
||||||
-----------------
|
|
||||||
.. default-domain:: py
|
|
||||||
.. default-role:: obj
|
|
||||||
.. module:: sophon.core
|
|
||||||
|
|
||||||
L'app `sophon.core` è l'app principale del progetto, e non può essere disattivata, in quanto dipendenza obbligatoria di tutte le altre app.
|
|
||||||
|
|
||||||
|
|
||||||
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``.
|
|
||||||
|
|
||||||
.. 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\`\``.
|
|
||||||
|
|
||||||
|
|
||||||
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 al livello di accesso.
|
|
||||||
|
|
||||||
.. class:: SophonModel(django.db.models.Model)
|
|
||||||
|
|
||||||
.. method:: can_edit(self, user: django.contrib.auth.models.User) -> bool
|
|
||||||
:abstractmethod:
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
.. method:: can_admin(self, user: django.contrib.auth.models.User) -> bool
|
|
||||||
:abstractmethod:
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
|
|
||||||
Modello di autorizzazione astratto
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Viene definito un nuovo modello astratto, basato su `SophonModel`, che permette di determinare i permessi dell'utente in base alla sua appartenenza al gruppo a cui è collegato l'oggetto implementatore.
|
|
||||||
|
|
||||||
.. class:: SophonGroupModel(SophonModel)
|
|
||||||
|
|
||||||
.. method:: get_group(self) -> ResearchGroup
|
|
||||||
:abstractmethod:
|
|
||||||
|
|
||||||
:returns: Il gruppo a cui appartiene l'oggetto.
|
|
||||||
|
|
||||||
.. classmethod:: get_access_to_edit(cls) -> sophon.core.enums.SophonGroupAccess
|
|
||||||
|
|
||||||
:returns: Il livello di autorità all'interno del gruppo necessario per modificare l'oggetto.
|
|
||||||
|
|
||||||
.. classmethod:: get_access_to_admin(cls) -> sophon.core.enums.SophonGroupAccess
|
|
||||||
|
|
||||||
:returns: Il livello di autorità all'interno del gruppo necessario per amministrare l'oggetto.
|
|
||||||
|
|
||||||
.. method:: get_access_serializer(self, user: User) -> typing.Type[rest_framework.serializers.ModelSerializer]
|
|
||||||
|
|
||||||
:returns: Restituisce il `rest_framework.serializers.ModelSerializer` adeguato al livello di autorità dell'utente.
|
|
||||||
|
|
||||||
|
|
||||||
.. class:: sophon.core.enums.SophonGroupAccess(enum.IntEnum)
|
|
||||||
|
|
||||||
Enumerazione che stabilisce il livello di autorità che un utente può avere all'interno di un gruppo.
|
|
||||||
|
|
||||||
.. attribute:: NONE = 0
|
|
||||||
|
|
||||||
Utente :ref:`ospite`.
|
|
||||||
|
|
||||||
.. attribute:: REGISTERED = 10
|
|
||||||
|
|
||||||
:ref:`Utente` registrato.
|
|
||||||
|
|
||||||
.. attribute:: MEMBER = 50
|
|
||||||
|
|
||||||
Membro del :ref:`gruppo di ricerca`.
|
|
||||||
|
|
||||||
.. attribute:: OWNER = 100
|
|
||||||
|
|
||||||
Creatore del :ref:`gruppo di ricerca`.
|
|
||||||
|
|
||||||
.. attribute:: SUPERUSER = 200
|
|
||||||
|
|
||||||
:ref:`Superutente` con privilegi universali.
|
|
||||||
|
|
||||||
|
|
||||||
Modello dei dettagli dell'istanza
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Viene creato il modello che rappresenta i dettagli dell':ref:`istanza` Sophon.
|
|
||||||
|
|
||||||
.. class:: SophonInstanceDetails(SophonModel)
|
|
||||||
|
|
||||||
.. attribute:: id: IntegerField [1]
|
|
||||||
|
|
||||||
Impostando ``1`` come unica scelta per il campo della chiave primaria ``id``, si crea un modello "singleton", ovvero un modello di cui può esistere un'istanza sola in tutto il database.
|
|
||||||
|
|
||||||
L'istanza unica viene creata dalla migrazione ``0004_sophoninstancedetails.py``.
|
|
||||||
|
|
||||||
.. attribute:: name: CharField
|
|
||||||
.. attribute:: description: TextField
|
|
||||||
.. attribute:: theme: CharField ["sophon", "paper", "royalblue", "hacker", "amber"]
|
|
||||||
|
|
||||||
.. method:: version: str
|
|
||||||
:property:
|
|
||||||
|
|
||||||
:returns: La versione installata del pacchetto `sophon`.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
:ref:`Sophon instance details` nella guida per l'amministratore.
|
|
||||||
|
|
||||||
|
|
||||||
Modello del gruppo di ricerca
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Viene creato il modello che rappresenta un :ref:`gruppo di ricerca`.
|
|
||||||
|
|
||||||
.. class:: ResearchGroup(SophonGroupModel)
|
|
||||||
|
|
||||||
.. attribute:: slug: SlugField
|
|
||||||
.. attribute:: name: CharField
|
|
||||||
.. attribute:: description: TextField
|
|
||||||
.. attribute:: members: ManyToManyField → django.contrib.auth.models.User
|
|
||||||
.. attribute:: owner: ForeignKey → django.contrib.auth.models.User
|
|
||||||
.. attribute:: access: CharField ["MANUAL", "OPEN"]
|
|
||||||
|
|
||||||
|
|
||||||
Estensione ai permessi di Django
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.core.permissions
|
|
||||||
|
|
||||||
I permessi di `rest_framework` vengono estesi con due nuove classi che utilizzano il :ref:`modello di autorizzazione` precedentemente definito.
|
|
||||||
|
|
||||||
.. class:: Edit(rest_framework.permissions.BasePermission)
|
|
||||||
|
|
||||||
Consente l'interazione solo agli utenti che possono modificare (`.can_edit`) l'oggetto.
|
|
||||||
|
|
||||||
.. class:: Admin(rest_framework.permissions.BasePermission)
|
|
||||||
|
|
||||||
Consente l'interazione solo agli utenti che possono amministrare (`.can_admin`) l'oggetto.
|
|
||||||
|
|
||||||
|
|
||||||
Viewset astratti
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.core.views
|
|
||||||
|
|
||||||
Vengono definiti tre viewset in grado di utilizzare i metodi aggiunti dalle classi astratte `.models.SophonModel` e `.models.SophonGroupModel`.
|
|
||||||
|
|
||||||
.. class:: ReadSophonViewSet(rest_framework.viewsets.ReadOnlyModelViewSet, metaclass=abc.ABCMeta)
|
|
||||||
|
|
||||||
Classe **astratta** che estende la classe base `rest_framework.viewsets.ReadOnlyModelViewSet` con metodi di utilità mancanti nell'implementazione originale, allacciandola inoltre a `.models.SophonGroupModel`.
|
|
||||||
|
|
||||||
.. method:: get_queryset(self) -> QuerySet
|
|
||||||
:abstractmethod:
|
|
||||||
|
|
||||||
Imposta come astratto (e quindi obbligatorio) il metodo `rest_framework.viewsets.ReadOnlyModelViewSet.get_queryset`.
|
|
||||||
|
|
||||||
.. method:: permission_classes(self)
|
|
||||||
:property:
|
|
||||||
|
|
||||||
Sovrascrive il campo di classe `rest_framework.viewsets.ReadOnlyModelViewSet.permission_classes` 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).
|
|
||||||
|
|
||||||
Delega la selezione delle classi a `.get_permission_classes`.
|
|
||||||
|
|
||||||
.. method:: get_permission_classes(self) -> typing.Collection[typing.Type[permissions.BasePermission]]
|
|
||||||
|
|
||||||
Funzione che permette la selezione dei permessi necessari per effetuare una determinata richiesta al momento di ricezione di quest'ultima.
|
|
||||||
|
|
||||||
Utile per le classi che erediteranno da questa.
|
|
||||||
|
|
||||||
.. method:: get_serializer_class(self) -> typing.Type[Serializer]
|
|
||||||
|
|
||||||
Funzione che permette la selezione del `rest_framework.serializers.Serializer` da utilizzare per una determinata richiesta al momento di ricezione di quest'ultima.
|
|
||||||
|
|
||||||
Utilizza:
|
|
||||||
|
|
||||||
- il serializzatore **in sola lettura** per elencare gli oggetti (azione ``list``);
|
|
||||||
- il serializzatore **di creazione** per creare nuovi oggetti (azione ``create``) e per generare i metadati del viewset (azione ``metadata``);
|
|
||||||
- il serializzatore ottenuto da `.models.SophonGroupModel.get_access_serializer` per la visualizzazione dettagliata (azione ``retrieve``), la modifica (azioni ``update`` e ``partial_update``) e l'eliminazione (azione ``destroy``) di un singolo oggetto;
|
|
||||||
- il serializzatore ottenuto da `.get_custom_serializer_classes` per le azioni personalizzate.
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`.models.SophonGroupModel`
|
|
||||||
|
|
||||||
.. method:: get_custom_serializer_classes(self) -> t.Type[Serializer]
|
|
||||||
|
|
||||||
Permette alle classi che ereditano da questa di selezionare quale `rest_framework.serializers.Serializer` utilizzare per le azioni personalizzate.
|
|
||||||
|
|
||||||
.. class:: WriteSophonViewSet(rest_framework.viewsets.ModelViewSet, ReadSophonViewSet, metaclass=abc.ABCMeta)
|
|
||||||
|
|
||||||
Classe **astratta** che estende la classe base `ReadSophonViewSet` aggiungendoci i metodi di `rest_framework.viewsets.ModelViewSet` che effettuano modifiche sugli oggetti.
|
|
||||||
|
|
||||||
Depreca i metodi ``perform_*`` di `rest_framework`, introducendone versioni migliorate con una signature diversa dal nome di ``hook_*``.
|
|
||||||
|
|
||||||
.. method:: perform_create(self, serializer)
|
|
||||||
|
|
||||||
.. deprecated:: 0.1
|
|
||||||
|
|
||||||
.. method:: perform_update(self, serializer)
|
|
||||||
|
|
||||||
.. deprecated:: 0.1
|
|
||||||
|
|
||||||
.. method:: perform_destroy(self, serializer)
|
|
||||||
|
|
||||||
.. deprecated:: 0.1
|
|
||||||
|
|
||||||
.. method:: hook_create(self, serializer) -> dict[str, typing.Any]
|
|
||||||
|
|
||||||
Funzione chiamata durante l'esecuzione dell'azione di creazione oggetto ``create``.
|
|
||||||
|
|
||||||
:param serializer: Il `~rest_framework.serializers.Serializer` già "riempito" contenente i dati dell'oggetto che sta per essere creato.
|
|
||||||
:raises .HTTPException: È possibile interrompere la creazione dell'oggetto con uno specifico codice errore sollevando una `.HTTPException` all'interno della funzione.
|
|
||||||
:returns: Un `dict` da unire a quello del `~rest_framework.serializers.Serializer` per formare l'oggetto da creare.
|
|
||||||
|
|
||||||
.. method:: hook_update(self, serializer) -> dict[str, t.Any]
|
|
||||||
|
|
||||||
Funzione chiamata durante l'esecuzione delle azioni di modifica oggetto ``update`` e ``partial_update``.
|
|
||||||
|
|
||||||
:param serializer: Il `~rest_framework.serializers.Serializer` già "riempito" contenente i dati dell'oggetto che sta per essere modificato.
|
|
||||||
:raises .HTTPException: È possibile interrompere la creazione dell'oggetto con uno specifico codice errore sollevando una `.HTTPException` all'interno della funzione.
|
|
||||||
:returns: Un `dict` da unire a quello del `~rest_framework.serializers.Serializer` per formare l'oggetto da modificare.
|
|
||||||
|
|
||||||
.. method:: hook_destroy(self, serializer) -> dict[str, typing.Any]
|
|
||||||
|
|
||||||
Funzione chiamata durante l'esecuzione dell'azione di eliminazione oggetto ``destroy``.
|
|
||||||
|
|
||||||
:raises .HTTPException: È possibile interrompere la creazione dell'oggetto con uno specifico codice errore sollevando una `.HTTPException` all'interno della funzione.
|
|
||||||
|
|
||||||
.. exception:: sophon.core.errors.HTTPException
|
|
||||||
|
|
||||||
Tipo di eccezione che è possibile sollevare nei metodi ``hook_*`` di `.WriteSophonViewSet` per interrompere l'azione in corso senza applicare le modifiche.
|
|
||||||
|
|
||||||
.. attribute:: status: int
|
|
||||||
|
|
||||||
Permette di specificare il codice errore con cui rispondere alla richiesta interrotta.
|
|
||||||
|
|
||||||
|
|
||||||
.. class:: SophonGroupViewSet(WriteSophonViewSet, metaclass=abc.ABCMeta)
|
|
||||||
|
|
||||||
Classe **astratta** che estende la classe base `.WriteSophonViewSet` estendendo gli ``hook_*`` con verifiche dei permessi dell'utente che tenta di effettuare l'azione.
|
|
||||||
|
|
||||||
.. method:: get_group_from_serializer(self, serializer) -> models.ResearchGroup
|
|
||||||
:abstractmethod:
|
|
||||||
|
|
||||||
Metodo necessario a trovare il gruppo a cui apparterrà un oggetto prima che il suo serializzatore venga elaborato.
|
|
||||||
|
|
||||||
:param serializer: Il `~rest_framework.serializers.Serializer` già "riempito" contenente i dati dell'oggetto.
|
|
||||||
|
|
||||||
|
|
||||||
Viewset concreti
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Vengono poi definiti tre viewset e una view che permettono interazioni tra l'utente e i modelli definiti nell'app.
|
|
||||||
|
|
||||||
.. class:: UsersByIdViewSet(ReadSophonViewSet)
|
|
||||||
|
|
||||||
Viewset in sola lettura che permette di recuperare gli utenti dell'istanza partendo dal loro ``id``.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/core/users/by-id/{ID}/`.
|
|
||||||
|
|
||||||
.. class:: UsersByUsernameViewSet(ReadSophonViewSet)
|
|
||||||
|
|
||||||
Viewset in sola lettura che permette di recuperare gli utenti dell'istanza partendo dal loro ``username``.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/core/users/by-username/{USERNAME}/`.
|
|
||||||
|
|
||||||
.. class:: ResearchGroupViewSet(WriteSophonViewSet)
|
|
||||||
|
|
||||||
Viewset in lettura e scrittura che permette di interagire con i gruppi di ricerca.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/core/groups/{GROUP_SLUG}/`.
|
|
||||||
|
|
||||||
.. method:: join(self, request: Request, pk: int) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che permette ad un utente di unirsi ad un gruppo aperto.
|
|
||||||
|
|
||||||
Utilizza `.models.SophonGroupModel.get_access_serializer`.
|
|
||||||
|
|
||||||
.. method:: leave(self, request: Request, pk: int) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che permette ad un utente di abbandonare un gruppo di cui non è proprietario.
|
|
||||||
|
|
||||||
Utilizza `.models.SophonGroupModel.get_access_serializer`.
|
|
||||||
|
|
||||||
.. class:: SophonInstanceDetailsView(APIView)
|
|
||||||
|
|
||||||
View che restituisce il valore attuale dell'unico oggetto `.models.SophonInstanceDetails`.
|
|
||||||
|
|
||||||
Accessibile tramite richieste ``GET`` all'URL :samp:`/api/core/instance/`.
|
|
||||||
|
|
||||||
|
|
||||||
Pagina di amministrazione
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.core.admin
|
|
||||||
|
|
||||||
Vengono infine registrati nella pagina di amministrazione i modelli concreti definiti in questa app, effettuando alcune personalizzazioni elencate in seguito.
|
|
||||||
|
|
||||||
.. class:: ResearchGroupAdmin(SophonAdmin)
|
|
||||||
|
|
||||||
Per i gruppi di ricerca, viene specificato un ordinamento, permesso il filtraggio e selezionati i campi più importanti da visualizzare nella lista.
|
|
||||||
|
|
||||||
.. class:: SophonInstanceDetails(SophonAdmin)
|
|
||||||
|
|
||||||
Per i dettagli dell'istanza, vengono disattivate tutte le azioni, impedendo la creazione o eliminazione del singleton.
|
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
L'app sophon.projects
|
|
||||||
-----------------------
|
|
||||||
.. default-domain:: py
|
|
||||||
.. default-role:: obj
|
|
||||||
.. module:: sophon.projects
|
|
||||||
|
|
||||||
L'app `sophon.projects` è un app secondaria che dipende da `sophon.core` che introduce in Sophon il concetto di :ref:`progetto di ricerca`.
|
|
||||||
|
|
||||||
.. caution::
|
|
||||||
|
|
||||||
Anche se l'app `sophon.projects` è opzionale (il progetto può funzionare senza di essa), si sconsiglia di disattivarla, in quanto il :ref:`modulo frontend` si aspetta che l'app sia attiva e solleverà un errore nel caso che i viewset forniti da questa app non siano disponibile.
|
|
||||||
|
|
||||||
|
|
||||||
Modello del progetto di ricerca
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.projects.models
|
|
||||||
|
|
||||||
Viene introdotto un modello concreto che rappresenta un :ref:`progetto di ricerca`.
|
|
||||||
|
|
||||||
.. class:: ResearchProject(SophonGroupModel)
|
|
||||||
|
|
||||||
.. attribute:: slug: SlugField
|
|
||||||
.. attribute:: group: ForeignKey → sophon.core.models.ResearchGroup
|
|
||||||
.. attribute:: name: CharField
|
|
||||||
.. attribute:: description: TextField
|
|
||||||
.. attribute:: visibility: CharField ["PUBLIC", "INTERNAL", "PRIVATE"]
|
|
||||||
|
|
||||||
|
|
||||||
Viewset del gruppo di ricerca
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.projects.views
|
|
||||||
|
|
||||||
Da una base comune, vengono creati due viewset per interagire con i progetti di ricerca.
|
|
||||||
|
|
||||||
.. class:: ResearchProjectViewSet(SophonGroupViewSet, metaclass=abc.ABCMeta)
|
|
||||||
|
|
||||||
Classe **astratta** che effettua l'override di `~sophon.core.views.SophonGroupView.get_group_from_serializer` per entrambi i viewset che seguono.
|
|
||||||
|
|
||||||
.. class:: ResearchProjectsBySlugViewSet(ResearchProjectViewSet)
|
|
||||||
|
|
||||||
Viewset in lettura e scrittura che permette di interagire con tutti i progetti di ricerca a cui l'utente loggato ha accesso.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/projects/by-slug/{PROJECT_SLUG}/`.
|
|
||||||
|
|
||||||
.. class:: ResearchProjectsByGroupViewSet(ResearchProjectViewSet)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Il filtraggio viene effettuato limitando il queryset.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/projects/by-group/{GROUP_SLUG}/{PROJECT_SLUG}/`.
|
|
||||||
|
|
||||||
|
|
||||||
Amministrazione del gruppo di ricerca
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.projects.admin
|
|
||||||
|
|
||||||
Il modello `.models.ResearchProject` viene registrato nella pagina di amministrazione attraverso la seguente classe:
|
|
||||||
|
|
||||||
.. class:: ResearchProjectAdmin(sophon.core.admin.SophonAdmin)
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,241 +0,0 @@
|
||||||
L'app sophon.notebooks
|
|
||||||
----------------------
|
|
||||||
.. default-domain:: py
|
|
||||||
.. default-role:: obj
|
|
||||||
.. module:: sophon.notebooks
|
|
||||||
|
|
||||||
|
|
||||||
L'app `sophon.notebooks` è un app secondaria che dipende da `sophon.projects` che introduce in Sophon il concetto di :ref:`notebook`.
|
|
||||||
|
|
||||||
.. caution::
|
|
||||||
|
|
||||||
Anche se l'app `sophon.notebooks` è opzionale (il progetto può funzionare senza di essa), si sconsiglia di disattivarla, in quanto il :ref:`modulo frontend` si aspetta che l'app sia attiva e solleverà un errore nel caso che i viewset forniti da questa app non siano disponibile.
|
|
||||||
|
|
||||||
|
|
||||||
Funzionamento di un notebook
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Internamente, un notebook non è altro che un container Docker accessibile ad un determinato indirizzo il cui stato è sincronizzato con un oggetto del database del :ref:`modulo backend`.
|
|
||||||
|
|
||||||
|
|
||||||
Modalità sviluppo
|
|
||||||
"""""""""""""""""
|
|
||||||
|
|
||||||
Per facilitare lo sviluppo di Sophon, sono previste due modalità di operazione di quest'ultimo:
|
|
||||||
|
|
||||||
- nella prima, la **modalità sviluppo**, il :ref:`modulo proxy` non è in esecuzione, ed è possibile collegarsi direttamente ai container attraverso collegamenti a ``localhost``;
|
|
||||||
|
|
||||||
- nella seconda, la **modalità produzione**, il :ref:`modulo proxy` è in esecuzione all'interno di un container Docker, e si collega agli altri container attraverso i rispettivi network Docker agli indirizzi comunicatogli dal :ref:`modulo backend`.
|
|
||||||
|
|
||||||
.. image:: notebooks_diagram.png
|
|
||||||
|
|
||||||
|
|
||||||
Gestione della rubrica del proxy
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.notebooks.apache
|
|
||||||
|
|
||||||
Viene creata una classe per la gestione della rubrica del proxy, utilizzando il modulo `dbm.gnu`, supportato da HTTPd.
|
|
||||||
|
|
||||||
La rubrica mappa gli URL pubblici dei notebook a URL privati relativi al :ref:`modulo proxy`, in modo da effettuare reverse proxying **dinamico**.
|
|
||||||
|
|
||||||
.. class:: ApacheDB
|
|
||||||
|
|
||||||
Classe che permette il recupero, la creazione, la modifica e l'eliminazioni di chiavi di un database `dbm.gnu` come se quest'ultimo fosse un `dict` con supporto a chiavi e valori `str` e `bytes`.
|
|
||||||
|
|
||||||
.. staticmethod:: convert_to_bytes(item: typing.Union[str, bytes]) -> bytes
|
|
||||||
|
|
||||||
Tutte le `str` passate a questa classe vengono convertite in `bytes` attraverso questa funzione, che effettua un encoding in ASCII e solleva un errore se quest'ultimo fallisce.
|
|
||||||
|
|
||||||
|
|
||||||
Assegnazione porta effimera
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
In *modalità sviluppo*, è necessario trovare una porta libera a cui rendere accessibile i container Docker dei notebook.
|
|
||||||
|
|
||||||
.. function:: get_ephemeral_port() -> int
|
|
||||||
|
|
||||||
Questa funzione apre e chiude immediatamente un `socket.socket` all'indirizzo ``localhost:0`` in modo da ricevere dal sistema operativo un numero di porta sicuramente libero.
|
|
||||||
|
|
||||||
|
|
||||||
Connessione al daemon Docker
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.notebooks.docker
|
|
||||||
|
|
||||||
Per facilitare l'utilizzo del daemon Docker per la gestione dei container dei notebook, viene utilizzato il modulo `docker`.
|
|
||||||
|
|
||||||
.. function:: get_docker_client() -> docker.DockerClient
|
|
||||||
|
|
||||||
Funzione che crea un client Docker con le variabili di ambiente del modulo.
|
|
||||||
|
|
||||||
.. data:: client: docker.DockerClient = lazy_object_proxy.Proxy(get_docker_client)
|
|
||||||
|
|
||||||
Viene creato un client Docker globale con inizializzazione lazy al fine di non tentare connessioni (lente!) al daemon quando non sono necessarie.
|
|
||||||
|
|
||||||
|
|
||||||
Controllo dello stato di salute
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Il modulo `docker` viene esteso implementando supporto per l'istruzione ``HEALTHCHECK`` dei ``Dockerfile``.
|
|
||||||
|
|
||||||
.. class:: HealthState(enum.IntEnum)
|
|
||||||
|
|
||||||
Enumerazione che elenca gli stati possibili in cui può essere la salute di un container.
|
|
||||||
|
|
||||||
.. attribute:: UNDEFINED = -2
|
|
||||||
|
|
||||||
Il ``Dockerfile`` non ha un ``HEALTHCHECK`` definito.
|
|
||||||
|
|
||||||
.. attribute:: STARTING = -1
|
|
||||||
|
|
||||||
Il container Docker non mai completato con successo un ``HEALTHCHECK``.
|
|
||||||
|
|
||||||
.. attribute:: HEALTHY = 0
|
|
||||||
|
|
||||||
Il container Docker ha completato con successo l'ultimo ``HEALTHCHECK`` e quindi sta funzionando correttamente.
|
|
||||||
|
|
||||||
.. attribute:: UNHEALTHY = 1
|
|
||||||
|
|
||||||
Il container Docker ha fallito l'ultimo ``HEALTHCHECK``.
|
|
||||||
|
|
||||||
|
|
||||||
.. function:: get_health(container: docker.models.containers.Container) -> HealthState
|
|
||||||
|
|
||||||
Funzione che utilizza l'API a basso livello del client Docker per recuperare l'`HealthState` dei container.
|
|
||||||
|
|
||||||
.. function:: sleep_until_container_has_started(container: docker.models.containers.Container) -> HealthState
|
|
||||||
|
|
||||||
Funzione bloccante che restituisce solo quando lo stato del container specificato non è `HealthState.STARTING`.
|
|
||||||
|
|
||||||
|
|
||||||
Generazione di token sicuri
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Si è scelto di rendere completamente trasparente all'utente il meccanismo di autenticazione a JupyterLab.
|
|
||||||
|
|
||||||
Pertanto, si è verificata la necessità di generare token crittograficamente sicuri da richiedere per l'accesso a JupyterLab.
|
|
||||||
|
|
||||||
.. function:: generate_secure_token() -> str
|
|
||||||
|
|
||||||
Funzione che utilizza `secrets.token_urlsafe` per generare un token valido e crittograficamente sicuro.
|
|
||||||
|
|
||||||
|
|
||||||
Modello dei notebook
|
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.notebooks.models
|
|
||||||
|
|
||||||
Viene definito il modello rappresentante un :ref:`notebook`.
|
|
||||||
|
|
||||||
.. class:: Notebook(SophonGroupModel)
|
|
||||||
|
|
||||||
.. attribute:: slug: SlugField
|
|
||||||
|
|
||||||
Lo slug dei notebook prevede ulteriori restrizioni oltre a quelle previste dallo `django.db.models.SlugField`:
|
|
||||||
|
|
||||||
- non può essere uno dei seguenti valori: ``api``, ``static``, ``proxy``, ``backend``, ``frontend``, ``src``;
|
|
||||||
- non può iniziare o finire con un trattino ``-``.
|
|
||||||
|
|
||||||
.. attribute:: project: ForeignKey → sophon.projects.models.ResearchProject
|
|
||||||
.. attribute:: name: CharField
|
|
||||||
.. attribute:: locked_by: ForeignKey → django.contrib.auth.models.User
|
|
||||||
|
|
||||||
.. attribute:: container_image: CharField ["ghcr.io/steffo99/sophon-jupyter"]
|
|
||||||
|
|
||||||
Campo che specifica l'immagine che il client Docker dovrà avviare per questo notebook.
|
|
||||||
|
|
||||||
Al momento ne è configurata una sola per semplificare l'esperienza utente, ma altre possono essere specificate per permettere agli utenti più scelta.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Al momento, le immagini specificate devono esporre un server web sulla porta ``8888``, e supportare il protocollo di connessione di Jupyter, ovvero :samp:`{PROTOCOLLO}://immagine:8888/lab?token={TOKEN}` e :samp:`{PROTOCOLLO}://immagine:8888/tree?token={TOKEN}`.
|
|
||||||
|
|
||||||
.. attribute:: jupyter_token: CharField
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. attribute:: container_id: CharField
|
|
||||||
|
|
||||||
L'id assegnato dal daemon Docker al container di questo oggetto.
|
|
||||||
|
|
||||||
Se il notebook non è avviato, questo attributo varrà `None`.
|
|
||||||
|
|
||||||
.. attribute:: port: IntegerField
|
|
||||||
|
|
||||||
La porta assegnata al container Docker dell'oggetto nel caso in cui Sophon sia avviato in "modalità sviluppo", ovvero con il :ref:`modulo proxy` in esecuzione sul sistema host.
|
|
||||||
|
|
||||||
.. attribute:: internal_url: CharField
|
|
||||||
|
|
||||||
L'URL a cui è accessibile il container Docker dell'oggetto nel caso in cui Sophon non sia avviato in "modalità sviluppo", ovvero con il :ref:`modulo proxy` in esecuzione all'interno di un container.
|
|
||||||
|
|
||||||
.. method:: log(self) -> logging.Logger
|
|
||||||
:property:
|
|
||||||
|
|
||||||
Viene creato un `logging.Logger` per ogni oggetto della classe, in modo da facilitare il debug relativo ad uno specifico notebook.
|
|
||||||
|
|
||||||
Il nome del logger ha la forma :samp:`sophon.notebooks.models.Notebook.{NOTEBOOK_SLUG}`.
|
|
||||||
|
|
||||||
.. method:: enable_proxying(self) -> None
|
|
||||||
|
|
||||||
Aggiunge l'indirizzo del notebook alla rubrica del proxy.
|
|
||||||
|
|
||||||
.. method:: disable_proxying(self) -> None
|
|
||||||
|
|
||||||
Rimuove l'indirizzo del notebook dalla rubrica del proxy.
|
|
||||||
|
|
||||||
.. method:: sync_container(self) -> t.Optional[docker.models.containers.Container]
|
|
||||||
|
|
||||||
Sincronizza lo stato dell'oggetto nel database con lo stato del container Docker nel sistema.
|
|
||||||
|
|
||||||
.. method:: create_container(self) -> docker.models.containers.Container
|
|
||||||
|
|
||||||
Crea e configura un container Docker per l'oggetto, con l'immagine specificata in `.container_image`.
|
|
||||||
|
|
||||||
.. method:: start(self) -> None
|
|
||||||
|
|
||||||
Tenta di creare e avviare un container Docker per l'oggetto, bloccando fino a quando esso non sarà avviato con `~.docker.sleep_until_container_has_started`.
|
|
||||||
|
|
||||||
.. method:: stop(self) -> None
|
|
||||||
|
|
||||||
Arresta il container Docker dell'oggetto.
|
|
||||||
|
|
||||||
|
|
||||||
Viewset dei notebook
|
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. module:: sophon.notebooks.views
|
|
||||||
|
|
||||||
Come per il modulo `sophon.projects`, vengono creati due viewset per interagire con i progetti di ricerca, basati entrambi su un viewset astratto che ne definisce le proprietà comuni.
|
|
||||||
|
|
||||||
.. class:: NotebooksViewSet(SophonGroupViewSet, metaclass=abc.ABCMeta)
|
|
||||||
|
|
||||||
Classe **astratta** che effettua l'override di `~sophon.core.views.SophonGroupView.get_group_from_serializer` e definisce cinque azioni personalizzate per l'interazione con il notebook.
|
|
||||||
|
|
||||||
.. method:: sync(self, request: Request, **kwargs) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che sincronizza lo stato dell'oggetto dell'API con quello del daemon Docker.
|
|
||||||
|
|
||||||
.. method:: start(self, request: Request, **kwargs) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che avvia il notebook con `.models.Notebook.start`.
|
|
||||||
|
|
||||||
.. method:: stop(self, request: Request, **kwargs) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che arresta il notebook con `.models.Notebook.stop`.
|
|
||||||
|
|
||||||
.. method:: lock(self, request: Request, **kwargs) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che blocca il notebook impostando il campo `.models.Notebook.locked_by` all'utente che ha effettuato la richiesta.
|
|
||||||
|
|
||||||
.. method:: unlock(self, request: Request, **kwargs) -> Response
|
|
||||||
|
|
||||||
Azione personalizzata che sblocca il notebook impostando il campo `.models.Notebook.locked_by` a `None`.
|
|
||||||
|
|
||||||
.. class:: NotebooksBySlugViewSet(NotebooksViewSet)
|
|
||||||
|
|
||||||
Viewset in lettura e scrittura che permette di interagire con tutti i notebook a cui l'utente loggato ha accesso.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/notebooks/by-slug/{NOTEBOOK_SLUG}/`.
|
|
||||||
|
|
||||||
.. class:: NotebooksByProjectViewSet(NotebooksViewSet)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Accessibile all'URL :samp:`/api/notebooks/by-project/{PROJECT_SLUG}/{NOTEBOOK_SLUG/`.
|
|
|
@ -1,6 +0,0 @@
|
||||||
Continuous Integration
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Il codice sorgente del modulo viene automaticamente controllato attraverso GitHub Actions da **CodeQL** e **Dependabot** ad ogni modifica, segnalando gli eventuali errori o dipendenze non aggiornate nel pannello *Security* del repository.
|
|
||||||
|
|
||||||
.. image:: ci_example.png
|
|
|
@ -1,12 +0,0 @@
|
||||||
Continuous Deployment
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
L'immagine del modulo viene automaticamente ricompilata da GitHub Actions e pubblicata su GitHub Containers ogni volta che un file all'interno della cartella del modulo viene modificato.
|
|
||||||
|
|
||||||
Questo workflow è definito all'interno del file ``.github/workflows/build-docker-backend.yml``.
|
|
||||||
|
|
||||||
.. image:: cd_example.png
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`La pagina del container <https://github.com/Steffo99/sophon/pkgs/container/sophon-backend>`_ su GitHub Containers.
|
|
BIN
docs/source/3_dev/2_structure/1_backend/cd_example.png
(Stored with Git LFS)
BIN
docs/source/3_dev/2_structure/1_backend/cd_example.png
(Stored with Git LFS)
Binary file not shown.
BIN
docs/source/3_dev/2_structure/1_backend/ci_example.png
(Stored with Git LFS)
BIN
docs/source/3_dev/2_structure/1_backend/ci_example.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,28 +0,0 @@
|
||||||
Modulo backend
|
|
||||||
==============
|
|
||||||
.. default-domain:: py
|
|
||||||
.. default-role:: obj
|
|
||||||
.. py:currentmodule:: sophon
|
|
||||||
|
|
||||||
Il *modulo backend* consiste in un server web che espone un'API e un sito web per l'amministrazione.
|
|
||||||
|
|
||||||
È collocato all'interno del repository in ``/backend``.
|
|
||||||
|
|
||||||
È formato dal package Python `sophon`, che contiene al suo interno un progetto Django, che a sua volta contiene le tre app Django `sophon.core`, `sophon.projects` e `sophon.notebooks`.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
A causa della dipendenza di Django da variabili globali, è stato impossibile utilizzare lo strumento di documentazione automatica `sphinx.ext.autodoc`.
|
|
||||||
|
|
||||||
Pertanto, si è deciso di documentare soltanto le classi e metodi più rilevanti ai fini di questa documentazione.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
1_techstack
|
|
||||||
2_sophon
|
|
||||||
3_core
|
|
||||||
4_projects
|
|
||||||
5_notebooks
|
|
||||||
6_ci
|
|
||||||
7_cd
|
|
BIN
docs/source/3_dev/2_structure/1_backend/notebooks_diagram.png
(Stored with Git LFS)
BIN
docs/source/3_dev/2_structure/1_backend/notebooks_diagram.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,17 +0,0 @@
|
||||||
Librerie e tecnologie utilizzate
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Sono elencate solo le principali librerie utilizzate; dipendenze e librerie minori non sono specificate, ma sono visibili all'interno del file ``yarn.lock``.
|
|
||||||
|
|
||||||
- I linguaggi di programmazione `JavaScript <https://developer.mozilla.org/en-US/docs/Web/JavaScript/About_JavaScript>`_ e `TypeScript <https://www.typescriptlang.org/>`_
|
|
||||||
- Il gestore di dipendenze `Yarn <https://yarnpkg.com/>`_
|
|
||||||
- La libreria grafica `Bluelib <https://github.com/Steffo99/bluelib>`_ (sviluppata come progetto personale nell'estate 2021)
|
|
||||||
- Il framework per interfacce grafiche `React <https://reactjs.org>`_
|
|
||||||
- Il router `Reach Router <https://reach.tech/router/>`_
|
|
||||||
- L'integrazione con React di Bluelib `bluelib-react <https://github.com/Steffo99/bluelib-react>`_ (sviluppata durante il tirocinio)
|
|
||||||
- Il componente React `react-markdown <https://github.com/remarkjs/react-markdown>`_
|
|
||||||
- Il framework per testing `Jest <https://jestjs.io/>`_
|
|
||||||
- Un fork personalizzato del client XHR `axios <https://github.com/axios/axios>`_
|
|
||||||
- Il webserver statico `serve <https://www.npmjs.com/package/serve>`_
|
|
|
@ -1,23 +0,0 @@
|
||||||
Struttura delle directory
|
|
||||||
-------------------------
|
|
||||||
.. default-domain:: js
|
|
||||||
|
|
||||||
Le directory di :mod:`@steffo45/sophon-frontend` sono strutturate nella seguente maniera:
|
|
||||||
|
|
||||||
src/components
|
|
||||||
Contiene i componenti React sia con le classi sia funzionali.
|
|
||||||
|
|
||||||
src/contexts
|
|
||||||
Contiene i contesti React creati con :func:`React.createContext`.
|
|
||||||
|
|
||||||
src/hooks
|
|
||||||
Contiene gli hook React personalizzati utilizzati nei componenti funzionali.
|
|
||||||
|
|
||||||
src/types
|
|
||||||
Contiene estensioni ai tipi base TypeScript, come ad esempio i tipi restituiti dalla web API del :ref:`modulo backend`.
|
|
||||||
|
|
||||||
src/utils
|
|
||||||
Contiene varie funzioni di utility.
|
|
||||||
|
|
||||||
public
|
|
||||||
Contiene i file statici da servire assieme all'app.
|
|
|
@ -1,113 +0,0 @@
|
||||||
Comunicazione con il server
|
|
||||||
---------------------------
|
|
||||||
.. default-domain:: js
|
|
||||||
|
|
||||||
|
|
||||||
Axios
|
|
||||||
^^^^^
|
|
||||||
|
|
||||||
Per effettuare richieste all'API web, si è deciso di utilizzare la libreria :mod:`axios`, in quanto permette di creare dei "client" personalizzabili con varie proprietà.
|
|
||||||
|
|
||||||
In particolare, si è scelto di forkarla, integrando anticipatamente una proposta di funzionalità che permette alle richieste di essere interrotte attraverso degli :class:`AbortController`.
|
|
||||||
|
|
||||||
|
|
||||||
Client personalizzati
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
All'interno di un contesto in cui è stata selezionata un'istanza (:data:`InstanceContext`), viene creato un client dal seguente hook:
|
|
||||||
|
|
||||||
.. function:: useInstanceAxios(config = {})
|
|
||||||
|
|
||||||
Questo hook specifica il ``baseURL`` del client Axios, impostandolo all'URL dell'istanza selezionata.
|
|
||||||
|
|
||||||
All'interno di un contesto in cui è stato effettuato l'accesso come utente (:data:`AuthorizationContext`), viene creato invece un client dal seguente hook:
|
|
||||||
|
|
||||||
.. function:: useAuthorizedAxios(config = {})
|
|
||||||
|
|
||||||
Questo hook specifica il valore dell'header ``Authorization`` da inviare in tutte le richieste effettuate a :samp:`Bearer {TOKEN}`, utilizzando il token ottenuto al momento dell'accesso.
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
Viene inoltre fornito supporto per le azioni personalizzate.
|
|
||||||
|
|
||||||
.. function:: async command(config)
|
|
||||||
|
|
||||||
Permette azioni personalizzate su tutto il viewset.
|
|
||||||
|
|
||||||
.. function:: async action(config)
|
|
||||||
|
|
||||||
Permette azioni personalizzate su uno specifico oggetto del viewset.
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
.. attribute:: viewset
|
|
||||||
|
|
||||||
Il viewset restituito da :func:`useViewSet`, utilizzato come interfaccia di basso livello per effettuare azioni.
|
|
||||||
|
|
||||||
.. attribute:: 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
|
|
||||||
|
|
||||||
Riduttore che permette di alterare lo :attr:`.state`.
|
|
||||||
|
|
||||||
.. function:: async refresh()
|
|
||||||
|
|
||||||
Ricarica gli oggetti del viewset.
|
|
||||||
|
|
||||||
Viene chiamata automaticamente al primo render se ``refreshOnMount`` è :data:`True`.
|
|
||||||
|
|
||||||
.. function:: async create(data)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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``.
|
|
||||||
|
|
||||||
Se la richiesta va a buon fine, il valore restituito dal backend sostituisce l'oggetto utilizzato in :attr:`.state`.
|
|
|
@ -1,162 +0,0 @@
|
||||||
Contesti innestati
|
|
||||||
------------------
|
|
||||||
.. default-domain:: js
|
|
||||||
|
|
||||||
Per minimizzare i rerender, l'applicazione è organizzata a "contesti innestati".
|
|
||||||
|
|
||||||
|
|
||||||
I contesti
|
|
||||||
^^^^^^^^^^
|
|
||||||
|
|
||||||
Viene definito un contesto per ogni tipo di risorsa selezionabile nell'interfaccia.
|
|
||||||
|
|
||||||
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`)
|
|
||||||
|
|
||||||
|
|
||||||
Contenuto dei contesti
|
|
||||||
""""""""""""""""""""""
|
|
||||||
|
|
||||||
Questi contesti possono avere tre tipi di valori: :data:`undefined` se ci si trova al di fuori del contesto, :data:`null` se non è stato selezionato alcun oggetto oppure **l'oggetto selezionato** se esso esiste.
|
|
||||||
|
|
||||||
|
|
||||||
URL contestuale
|
|
||||||
^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
/i/{ISTANZA}
|
|
||||||
/l/logged-in
|
|
||||||
/g/{GROUP_SLUG}
|
|
||||||
/p/{PROJECT_SLUG}
|
|
||||||
/n/{NOTEBOOK_SLUG}/
|
|
||||||
|
|
||||||
Ad esempio, l'URL per il notebook ``my-first-notebook`` dell'istanza demo di Sophon sarebbe:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
/i/https:api.prod.sophon.steffo.eu:
|
|
||||||
/l/logged-in
|
|
||||||
/g/my-first-group
|
|
||||||
/p/my-first-project
|
|
||||||
/n/my-first-notebook/
|
|
||||||
|
|
||||||
|
|
||||||
Parsing degli URL contestuali
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Viene definita una funzione in grado di comprendere gli URL contestuali:
|
|
||||||
|
|
||||||
.. function:: parsePath(path)
|
|
||||||
|
|
||||||
:param path: Il "path" da leggere.
|
|
||||||
:returns:
|
|
||||||
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
|
|
||||||
|
|
||||||
Ad esempio, l'URL precedente restituirebbe il seguente oggetto se processato:
|
|
||||||
|
|
||||||
.. code-block:: js
|
|
||||||
|
|
||||||
{
|
|
||||||
"instance": "https:api.prod.sophon.steffo.eu:",
|
|
||||||
"loggedIn": True,
|
|
||||||
"researchGroup": "my-first-group",
|
|
||||||
"researchProject": "my-first-project",
|
|
||||||
"notebook": "my-first-notebook"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Routing basato sui contesti
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
I valori dei contesti vengono utilizzati per selezionare i componenti da mostrare all'utente nell'interfaccia grafica attraverso i seguenti componenti:
|
|
||||||
|
|
||||||
.. function:: ResourceRouter({selection, unselectedRoute, selectedRoute})
|
|
||||||
|
|
||||||
Componente che sceglie se renderizzare ``unselectedRoute`` o ``selectedRoute`` in base alla *nullità* o *non-nullità* di ``selection``.
|
|
||||||
|
|
||||||
.. function:: ViewSetRouter({viewSet, unselectedRoute, selectedRoute, pathSegment, pkKey})
|
|
||||||
|
|
||||||
Componente basato su :func:`ResourceRouter` che seleziona automaticamente l'elemento del viewset avente il valore del segmento di percorso ``pathSegment`` alla chiave ``pkKey``.
|
|
||||||
|
|
||||||
|
|
||||||
Esempio di utilizzo di ViewSetRouter
|
|
||||||
""""""""""""""""""""""""""""""""""""
|
|
||||||
|
|
||||||
.. function:: GroupRouter({...props})
|
|
||||||
|
|
||||||
Implementato come:
|
|
||||||
|
|
||||||
.. code-block:: tsx
|
|
||||||
|
|
||||||
<ViewSetRouter
|
|
||||||
{...props}
|
|
||||||
viewSet={useManagedViewSet<SophonResearchGroup>("/api/core/groups/", "slug")}
|
|
||||||
pathSegment={"researchGroup"}
|
|
||||||
pkKey={"slug"}
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
Albero completo dei contesti
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
L'insieme di tutti i contesti è definito come componente :func:`App` nel modulo "principale" ``App.tsx``.
|
|
||||||
|
|
||||||
Se ne riassume la struttura in pseudocodice:
|
|
||||||
|
|
||||||
.. code-block:: html
|
|
||||||
|
|
||||||
<InstanceContext>
|
|
||||||
<InstanceRouter>
|
|
||||||
unselected:
|
|
||||||
<InstanceSelect>
|
|
||||||
selected:
|
|
||||||
<AuthorizationContext>
|
|
||||||
<AuthorizationRouter>
|
|
||||||
unselected:
|
|
||||||
<UserLogin>
|
|
||||||
selected:
|
|
||||||
<GroupContext>
|
|
||||||
<GroupRouter>
|
|
||||||
unselected:
|
|
||||||
<GroupSelect>
|
|
||||||
selected:
|
|
||||||
<ProjectContext>
|
|
||||||
<ProjectRouter>
|
|
||||||
unselected:
|
|
||||||
<ProjectSelect>
|
|
||||||
selected:
|
|
||||||
<NotebookContext>
|
|
||||||
<NotebookRouter>
|
|
||||||
unselected:
|
|
||||||
<NotebookSelect>
|
|
||||||
selected:
|
|
||||||
<NotebookDetails>
|
|
||||||
|
|
||||||
|
|
||||||
Altri contesti
|
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Tema
|
|
||||||
""""
|
|
||||||
|
|
||||||
Il tema dell'istanza è implementato come uno speciale contesto globale :data:`ThemeContext` che riceve i dettagli dell'istanza a cui si è collegati dall':data:`InstanceContext`.
|
|
||||||
|
|
||||||
|
|
||||||
Cache
|
|
||||||
"""""
|
|
||||||
|
|
||||||
Viene salvato l'elenco di tutti i membri dell':ref:`istanza` in uno speciale contesto :data:`CacheContext` in modo da poter risolvere gli id degli utenti al loro username senza dover effettuare ulteriori richieste.
|
|
|
@ -1,6 +0,0 @@
|
||||||
Continuous Integration
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Il codice sorgente del modulo viene automaticamente controllato attraverso GitHub Actions da **CodeQL** e **Dependabot** ad ogni modifica, segnalando gli eventuali errori o dipendenze non aggiornate nel pannello *Security* del repository.
|
|
||||||
|
|
||||||
.. image:: ci_example.png
|
|
|
@ -1,12 +0,0 @@
|
||||||
Continuous Deployment
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
L'immagine del modulo viene automaticamente ricompilata da GitHub Actions e pubblicata su GitHub Containers ogni volta che un file all'interno della cartella del modulo viene modificato.
|
|
||||||
|
|
||||||
Questo workflow è definito all'interno del file ``.github/workflows/build-docker-frontend.yml``.
|
|
||||||
|
|
||||||
.. image:: cd_example.png
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
`La pagina del container <https://github.com/Steffo99/sophon/pkgs/container/sophon-frontend>`_ su GitHub Containers.
|
|
BIN
docs/source/3_dev/2_structure/2_frontend/cd_example.png
(Stored with Git LFS)
BIN
docs/source/3_dev/2_structure/2_frontend/cd_example.png
(Stored with Git LFS)
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue