1
Fork 0
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:
Steffo 2021-11-09 16:56:33 +01:00
parent 9a6d9619b3
commit 7b3195cc3f
Signed by: steffo
GPG key ID: 6965406171929D01
119 changed files with 59 additions and 2936 deletions

View file

@ -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

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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)

Binary file not shown.

View file

@ -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>

Binary file not shown.

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)

Binary file not shown.

Binary file not shown.

View file

@ -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/

View file

@ -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]

View file

@ -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/``).

View file

@ -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.

View file

@ -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/

View file

@ -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/

View file

@ -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

View file

@ -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`.

View file

@ -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

View file

@ -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

View file

@ -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%

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,9 +0,0 @@
Amministrazione
***************
Questo capitolo fornisce informazioni su come amministrare un':ref:`istanza` Sophon.
.. toctree::
1_admin_panel
2_entities

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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/

View file

@ -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

View file

@ -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.

View file

@ -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>`_

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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/`.

View file

@ -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

View file

@ -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.

Binary file not shown.

Binary file not shown.

View file

@ -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

Binary file not shown.

View file

@ -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>`_

View file

@ -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.

View file

@ -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`.

View file

@ -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.

View file

@ -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

View file

@ -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.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more