Mi sono imbattuto in un comportamento piuttosto curioso utilizzando DBeaver con un server MySQL locale. All’avvio di DBeaver, il tentativo di connessione falliva con il messaggio: La cosa sorprendente era che bastava aprire un terminale …
Per anni avevo sentito ripetere che Docker è una virtualizzazione leggera. È una frase comoda per iniziare, ma in realtà è fuorviante. L’ho capito nel modo migliore: cercando di capire perché MySQL si comportasse in …
Aprendo Thunderbird mi è comparso un avviso che invitava a intervenire entro il 21 luglio 2026 per continuare a ricevere gli aggiornamenti ESR (Extended Support Release). Partiamo con le istruzioni operative per chi sa già …
Negli ultimi tempi termini come LLM, embedding e database vettoriali sono usciti rapidamente dall’ambito della ricerca e sono diventati argomenti quasi quotidiani. Sono mattoncini coi quali possiamo costruire sistemi capaci di interrogare grandi collezioni di …
Ho condotto un esperimento di scrittura di software per analizzare i risultati di una esportazione delle mie conversazioni con ChatGPT. Ma ho deciso di aiutarmi nell’analisi con un Large Language Model che sicuramente sa fare molto bene questo lavoro e in più con un LLM in locale, per vedere come funziona.
Un utilizzo un po’ meno banale di quello illustrato nel primo post sulla clausola WITH è quello che alla fine ho utilizzato per riuscire in un task del genere:
Come faccio a produrre un recordset contente le date di una settimana da domenica a sabato dato che sia un giorno qualsiasi della settimana che si vuole estrarre?
Utilizzando la clausola WITH possiamo costruirci una tabella temporanea arr_dates con un’unica colonna arr_date:
Oracle: Esempio di uso della clausola WITH
Ora questo costrutto ha una sua complessità. Partiamo individuando i blocchi:
la clausola WITH definisce un recordset temporaneo arr_dates
contente il campo arr_date
il recordset è definito come segue: seleziona (riga 2) il sabato successivo (+5) al parametro data (:1)
union all
seleziono (riga 4) il giorno precedente (-1) dal recordset definito sopra con la condizione che la data deve essere maggiore della data di inizio della settimana della data parametro (:1); questa condizione è soddisfatta da tutti i giorni dall’inizio della settimana di partenza alla domenica successiva.
alla fine (riga 9) c’è una semplice selezione sull’oggetto appena costruito
Quindi l’effetto è quello di selezionare 7 “record” centrati attorno al giorno :1. Ad esempio se prendiamo come parametro il 2 luglio 2019 ho
Stesso recordset se come parametro scegliamo il 5 luglio 2019. Se invece scegliamo come “centro” il 10 luglio 2019:
Alla fine possiamo calcolare questi recordset inglobando il tutto in una function ma lo vediamo nel prossimo post.
Oracle with: questa clausola, introdotta a partire dalla versione Oracle 9.2, consente di eseguire una query in una tabella utilizzando come parametro il risultato di un’altra query.
Come esempio immaginiamo di avere una tabella di rilievi di temperature e voler selezionare i record in cui i valori sono superiori alla media.
create table temperature(
id number(10),
valore number(10,2),
data_rilievo timestamp
);
Aggiungiamo un po’ di dati:
ID
VALORE
TIMESTAMP
1
24,3
01-LUG-19 10:26:15,820000000
2
28,2
02-LUG-19 10:26:43,310000000
3
34,8
03-LUG-19 10:26:57,810000000
4
28,7
04-LUG-19 10:27:15,819000000
5
32,5
05-LUG-19 10:27:30,714000000
Una possibile soluzione per questo problema è la query seguente:
select valore from temperature
where valore > (
select avg(valore) from temperature
)
che da’ il seguente recordset
VALORE
34,8
32,5
Con la clausola WITH costruiamo una tabella temporanea con una sola colonna e una sola riga che contiene il valore medio. Potremo successivamente riferirci al valore contenuto in questo recordset per ritornare il recordset ottenuto con la query standard di prima:
WITH tmpTemperature (tmpMedia) as
(SELECT avg(valore) FROM Temperature)
SELECT valore
FROM Temperature, tmpTemperature
WHERE Temperature.valore > tmpTemperature.tmpMedia;
In sostanza si tratta di definire l’oggetto temporaneo che è un recordset con una riga e una colonna; poi si esegue la query sulla tabella delle temperature utilizzando la tabella temporanea definita in virtù della clausola WITH.
Il recordset ritornato è identico a quello di prima:
VALORE
34,8
32,5
Quindi fondamentalmente la sintassi della clausola WITH è
WITH tabellaTemporanea (parametro) AS
(select di calcolo del parametro)
SELECT *
FROM tabella1, tabella2, ..., tabellaTemporanea
WHERE
... condizione su tabellaTemporanea.parametro...
;
Questo è quello che si osserva una volta che si preme il pulsante Invia del modulino, dopo aver selezionato l’immagine. In particolare, di tutto il messaggio HTTP che viene compilato dal browser ho qui riportato l’inizio della parte contenente l’immagine, che è l’informazione binaria che vogliamo capire come viaggia in rete.
In particolare possiamo notare, leggendo la colonna centrale del dump riportato da Wireshark, che in HTTP non passa solo testo ma anche informazioni puramente binarie. I singoli bytes sono a volte superiori a 7F (=011111112) – e i caratteri codificati in ASCII standard sono a 7 bit – per cui si tratta di bytes a 8 bit (con il bit più significativo a 1) e non 7 (cioè con il bit più significativo a 0). Non c’è inoltre alcuna applicazione di una codifica base64 ai bytes che compongono il messaggio.
4.3 Encoding
While the HTTP protocol can transport arbitrary binary data, the
default for mail transport is the 7BIT encoding. The value supplied
for a part may need to be encoded and the "content-transfer-encoding"
header supplied if the value does not conform to the default
encoding.
Quindi, a imitazione del protocollo SMTP, si potrebbe codificare il file (che nella mail è l’allegato) utilizzando la codifica base64, ma in questo caso la codifica adottata è 8 bit. La scelta della codifica è ampia, come si desume dalla grammatica di encoding riportata nella rfc-1521:
In sintesi: il browser costruisce un file che contiene tutte le parti (multipart) contenute nei campi della form (multipart/form-data in questo caso solo due, l’immagine ed il pulsante di invio); in questo file tutto ciò che è testo rimane testo e ciò che è binario rimane binario; ogni elemento del form è separato dagli altri da un delimitatore (lo potete notare all’inizio della colonna di destra nell’immagine della cattura Wireshark):
——WebKitFormBoundaryXxWGBqDWd6OsDPE2
(per questo il tipo di encoding, o enctype, utilizzato dal browser è il multipart/form-data)
Le informazioni di intestazione che precedono il messaggio HTTP sono
Un fastidioso errore 404 mi risultava dalla visita di uno dei miei siti (che utilizzano come http server nginx su sistema operativo CentOS), e mi sembrava che l’attribuzione dei permessi fosse corretta.
L’errore HTTP 404 viene emesso dal server web quando non trova nel file system la risorsa (file o cartella) corrispondente all’URI che gli viene passato dal client (ad esempio Google Chrome o Firefox): il famoso errore di Pagina non trovata.
Questo errore si verifica anche se il file c’è ma si verifica una di queste condizioni:
l’utente sotto il quale sta girando il server non ha permessi di lettura sul file desiderato né come utente né come gruppo
non ha la possibilità di raggiungere quella risorsa percorrendo il grafo della directory da / al file desiderato
In realtà basta infatti che una directory della catena di “cd command” (change directory) che il servizio web deve fare non sia leggibile e si verifica l’errore. Il modo più efficiente di vedere lo stato di tutta la gerarchia di directory è utilizzare il comando namei:
namei - follow a pathname until a terminal point is found
Qui è evidente il problema: tutta la catena di directory ha l’opzione x (eXecute) tranne la directory contenente la DocRoot del web server (la directory html) ragion per cui è sufficiente aggiungere questo permesso:
Devo testare delle API con Unit Test di una classe Category. Come primo passaggio genero la classe di Test:
php artisan make:test CategoryTest
Questo comando mi genera un file sotto tests/Feature/CategoryTest.php che tosto personalizzo come segue, un metodo per ogni operazione del CRUD:
<?php
namespace Tests\Feature;
use App\Category;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class CategoryTest extends TestCase
{
public function testsCategoriesAreCreatedCorrectly()
{
$headers = ['Authorization' => "ajs8ebddau"];
$payload = [
'name' => 'Libri e Riviste INS',
'short' => 'libri INS',
];
$this->json('POST', '/api/categories', $payload, $headers)
->assertStatus(302)
->assertJson([
'name' => 'Libri e Riviste INS',
'short' => 'libri INS'
]);
}
public function testsCategoriesAreUpdatedCorrectly()
{
$headers = ['Authorization' => "ajs8ebddau"];
$category = new Category;
$category->name = 'Libri e Riviste TEST';
$category->short = 'libri TEST';
$category->save();
$id = $category->id;
$payload = [
'name' => 'Libri e Riviste UPDATE',
'short' => 'libri UPDATE',
];
$response = $this->json('PUT', '/api/categories/' . $id, $payload, $headers)
->assertStatus(200)
->assertJson([
'id' => $id,
'name' => 'Libri e Riviste UPDATE',
'short' => 'libri UPDATE'
]);
}
public function testsCategoriesAreDeletedCorrectly()
{
$headers = ['Authorization' => "ajs8ebddau"];
$id = 30;
$payload = [
'name' => 'Lorem',
'short' => 'Ipsum',
];
$category = new Category;
$category->name = 'Libri e Riviste DEL';
$category->short = 'libri DEL';
$category->save();
$id = $category->id;
$response = $this->json('DELETE', '/api/categories/' . $id, $payload, $headers)
->assertStatus(200);
}
public function testCategoriesAreListedCorrectly()
{
$category = new Category;
$category->name = 'Libri e Riviste first';
$category->short = 'libri first';
$category->save();
$category = new Category;
$category->name = 'Libri e Riviste second';
$category->short = 'libri second';
$category->save();
$headers = ['Authorization' => "ajs8ebddau"];
$response = $this->json('GET', '/api/categories', [], $headers)
->assertStatus(200)
->assertJson([
[ 'name' => 'Libri e Riviste first', 'short' => 'libri first' ],
[ 'name' => 'Libri e Riviste second', 'short' => 'libri second' ]
])
->assertJsonStructure([
'*' => ['id', 'name', 'short', 'created_at', 'updated_at'],
]);
}
}
In realtà la variabile $headers qui è superflua, ma mi viene comoda dopo, quando aggiungerò lo strato di autenticazione.
Eseguo i test
marcob@jsbach:shopping3$ composer test
> vendor/bin/phpunit
PHPUnit 7.4.0 by Sebastian Bergmann and contributors.
..... 5 / 5 (100%)
Time: 248 ms, Memory: 16.00MB
OK (5 tests, 7 assertions)
marcob@jsbach:shopping3$
Il comando di test qui è dato utilizzando composer, in quanto ho definito uno script “test” all’interno del file di configurazione composer.json: alla sezione script ho aggiunto:
Interferometria VLBI: Katie Bouman e la prima immagine di un buco nero
Interferometria
L’interferometria è una tecnica utilizzata in fisica per dedurre proprietà della materia o di campi attraverso l’interferenza delle onde. L’interferometria è utilizzata per esempio negli ologrammi per identificare variazioni anche piccolissime intervenute nel tempo della forma di un oggetto e in astrofisica per rilevare le forme di oggetti molto lontani.
L’interferomeria è anche alla abse dell’importanissimo esperimento di Michelson e Morley che nel 1887 dimostrarono che non esisteva alcun vento d’etere: detto in altro modo, la luce si propaga anche nel vuoto senza bisogno di un supporto meccanico e la sua velocità è costante e non dipende dalla velocità del sistema di riferimento in cui la si misura.
Questo articolo rappresenta un “ground breaking work” perché per la prima volta si è visto davvero la forma di un buco nero lontanissimo attraverso il suo disco di accrescimento così come era stato previsto dalla teoria.
Ecco un’istantanea dell’articolo che descrive l’algoritmo sviluppato da Katie Bouman e altri (due del MIT, due di Harvard e uno di Google) che è stato utilizzato per ricomporre l’immagine del buco nero.
L’articolo risale al 2016, era stato presentato alla IEEE Conference on Computer Vision and Pattern Recognition ed è stato reso di dominio pubblico grazie alla Computer Vision Foundation. In calce il link al pdf dell’articolo e anche il link a Github con i sorgenti del software di analisi utilizzato per produrre l’immagine del buco nero. Tra i contributi della Bouman ad esempio il commit #73. Ma come tutti i progetti importanti, ci hanno lavorato in tanti 😊
Con la direttiva @property in Python è possibile pubblicare i getter e i setter per le proprietà della classe senza doverli invocare esplicitamente, bensì utilizzando la variabile stessa. Nell’ambito Python il nome di queste direttive è decoratori. In realtà, come vedremo, si tratta solo di una pseudo variabile.
Esempio: una classe che prende un valore di temperatura intesa in gradi Celsius e lo trasforma in Fahrenheit:
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
Ora rilasciamo la versione nel nostro software. Subito dopo ci vengono a dire che il modulo accetta temperature sotto lo 0 assoluto (e questo è francamente imbarazzante), per cui rilasciamo subito questa patch che utilizza una proprietà privata (quella contrassegnata col prefisso _) e i get/setters per consentire il controllo del valore di ingresso:
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
# getter & setters
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature sotto -273 non sono ammesse")
self._temperature = value
Ora però il nostro codice non è più retro compatibile e se qualcuno per caso ha usato la nostra classe, quando userà una istruzione del tipo
>>> from celsius import Celsius >>> c = Celsius(100) >>> c.temperature
Traceback (most recent call last): File "", line 1, in AttributeError: 'Celsius' object has no attribute 'temperature'
Ohi ohi. In effetti adesso per permettere il controllo abbiamo trasferito il valore alla variabile privata _temperature, perdendo per strada la variabile temperature. Ma Python ci permette di risolvere il problema dichiarando una pseudo-proprietàtemperature che sostituisce i getter e i setter e fa in modo che dall’esterno si osservi effettivamente una proprietà (nel senso della variabile della classe) che si chiama nel vecchio modo, ossia della prima versione dl software (temperature); questo senza rinunciare alla proprietà privata che ci consente il controllo che il valore non sia sotto lo zero assoluto. Per questa ultima versione utilizziamo anche un file di constanti fisiche per portare il software al miglior grado di modularità:
# celsius.py
# A silly class to show the @property decorator
# allowing to implicitly define get/setters
#
############################################################
# Celsius object
############################################################
import constants
K0 = constants.KELVIN0
class Celsius:
def __init__(self, temperature = 0):
try:
if temperature < K0:
raise ValueError
self._temperature = temperature
except ValueError:
print("Temperature sotto " + str(K0) + " non permesse")
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Acquisizione valore")
return self._temperature
@temperature.setter
def temperature(self, value):
try:
if value < K0:
raise ValueError
self._temperature = value
except ValueError:
print("Temperature sotto " + str(K0) + " non permesse")
Qualche commento:
Per evitare fastidiosi messaggi di traceback del runtime environment di Python utilizziamo il costrutto try/except che ci consente una più robusta gestione delle eccezioni
utilizziamo le costanti importate dal file constants.py in cui ho definito una serie di costanti fisiche
definiamo la sezione @property che ci consente di invocare il getter semplicemente utilizzando la pseudo variabile
definiamo la sezione @temperature.setter che ci consente di sostituire il setter
Proviamo la nuova versione del nostro software
marcob@jsbach:python$ python3 Python 3.5.2 (default, Nov 12 2018, 13:43:14) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from celsius import Celsius >>> c=Celsius(-2000) Temperature sotto -273.15 non permesse >>> c=Celsius(-20) >>> c.temperature <-- getter! Acquisizione valore -20 >>> c.temperature = 130 <-- setter! >>> c.temperature Acquisizione valore 130
Quindi @property è un modo di sostituire getter e setter in modo tale da utilizzare nominalmente la stessa variabile come se fosse una vera proprietà della classe (in effetti non esiste, esiste soltanto l’attributo privato _temperature).
Possiamo anche trovare l’utilizzo del decoratore @property sbirciando nel codice eht-imaging, il software Python scritto per ricostruire l’immagine del buco nero nell’ambito della collaborazione Event Horizon Telescope
Ad esempio nel file movie.py c’è la dichiarazione di questa proprietà frames:
@property
def frames(self):
frames = self._movdict[self.pol_prim]
return frames
@frames.setter
def frames(self, frames):
if len(frames[0]) != self.xdim*self.ydim:
raise Exception("imvec size is not consistent with xdim*ydim!")
#TODO -- more checks on consistency with the existing pol data???
self._movdict[self.pol_prim] = frames
Il getter: quando viene menzionata la pseudo variabile frames, ritorna l’attributo privato dell’oggetto self._movdict[self.pol_prim].
Il setter: dopo un controllo di consistenza, valorizza l’attributo self._movdict[self.pol_prim] con il valore assegnato alla pseudo variabile frames.
Event Horizon Telescope puntato verso il buco nero M87
M87 Black Hole from Event Horizon telescope
Gli scienziati hanno ottenuto la prima immagine di un buco nero, utilizzando le osservazioni della rete di telescopi Event Horizon (EHT) puntato verso il centro della galassia M87, distante 55 milioni di anni luce, nel cluster galattico in corrispondenza alla costellazione della Vergine. L’immagine mostra un anello luminoso formato dalla luce che proviene da dietro poiché si incurva per l’intensa gravità che si manifesta attorno al buco nero che ha una massa 6,5 miliardi di volte quella del Sole. Questa immagine tanto attesa fornisce la più forte evidenza ad oggi dell’esistenza di buchi neri super massicci e apre una finestra sullo studio degli stessi buchi neri, sul loro orizzonte degli eventi e sulla gravità.
Una delle (molte) pregevoli caratteristiche di Laravel è la possibilità di allestire un database alimentandolo con dati di test (fake) che in Laravel è facilissima grazie ad una classe Faker scritta da François Zaninotto.
Dopo aver creato il file per la migrazione di una tabella Articles, Artisan ci mette a disposizione la funzionalità seeder cioè alimentatore:
$ php artisan make:seeder ArticlesTableSeeder
Questo comando costruisce la classe che servirà ad alimentare la tabella: sotto la directory /database/seeds troviamo la nuova classe dell’alimentatore del db:
use Illuminate\Database\Seeder;
use App\Article;
class ArticlesTableSeeder extends Seeder {
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Article::truncate();
$faker = \Faker\Factory::create();
// And now, let's create a few articles in our database:
for ($i = 0; $i < 50; $i++) {
Article::create([
'title' => $faker->sentence,
'body' => $faker->paragraph,
]);
}
}
}
quindi facciamo girare Faker che alimenta con dati più o meno casuali la tabella
$ php artisan db:seed --class=ArticlesTableSeeder
Possiamo automatizzare il tutto al momento della creazione del database inserendo la chiamata ai metodi in fase di migrazione:
class DatabaseSeeder extends Seeder {
public function run()
{
$this->call(ArticlesTableSeeder::class);
$this->call(UsersTableSeeder::class);
...
}
}
HTML è un linguaggio di mark-up in cui sono stati definiti molti tag per conferire una semantica al testo. Quindi ci sono i marcatori di titolo h1, …, h6 per definire la gerarchia delle titolazioni nelle sezioni del documento, come i tag per le liste, i paragrafi, le immagini e così via.
Ogni marcatore (tag) è corredato di una serie di attributi standard che vengono utilizzati sia da browser che dai programmi Javascript per interagire con il DOM (Document Object Model).
Nulla vieta però all’occorrenza di definire degli attributi personalizzati (custom) se l’applicazione ne ha bisogno. Il browser ignorerà tali attributi per quanto riguarda la visualizzazione ma li metterà comunque a disposizione nel DOM per poter essere utilizzati dai programmi Javascript. Come esempio di un caso d’uso possiamo aver bisogno di identificare l’icona di un utente del social network che stiamo sviluppando etichettandolo con l’id univoco che rappresenta l’utente nello spazio dati:
Potrebbe esserci un problema di conflitto di nomi, se abbiamo definito un nome attributo che viene utilizzato in modo diverso da più librerie Javascript entrambe agenti sulla stessa pagina HTML.
HTML5 [1] da’ la possibilità di definire una convenzione per cui se i nomi degli attributi personalizzati iniziano con data-
allora i valori sono reperibili direttamente con un accesso all’oggetto img.dataset
console.log(img.dataset.userid);
Oppure con AngularJS è possibile la stessa cosa utilizzando il prefisso ng-* (le cosiddette direttive con le quali AngularJS estende gli attributi HTML):
Un esempio più illuminante è il seguente in AngularJS nel quale definisco una direttiva completamente custom:
<div ng-app="coolApp" pippo-directive>
<script>
var app = angular.module("coolApp", []); app.directive("pippoDirective", function() { return { template : "Testo definito nel callback del costruttore della direttiva!" }; }); </script>
Il tag coolApp (che definisce l’app AngularJS) ha una direttiva completamente custom pippo-directive che viene agganciata da Javascript tramite la directive app.directive, il cui nome è la versione camel-case del nome dell’attibuto HTML.
Utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Lo facciamo per migliorare l'esperienza di navigazione e per mostrare annunci personalizzati. Il consenso a queste tecnologie ci consentirà di elaborare dati quali il comportamento di navigazione o gli ID univoci su questo sito. Il mancato consenso o la revoca del consenso possono influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Commenti recenti