Catégories
Astuces et Design

Créer une application météo pour terminal à Deno

Si vous avez suivi nos articles d’introduction sur Deno, vous êtes probablement intéressé par la rédaction de votre premier programme. Dans cet article, nous allons passer en revue l'installation du runtime Deno et la création d'un programme météo en ligne de commande qui prendra un nom de ville comme argument et renverra les prévisions météo pour les prochaines 24 heures.

Pour écrire du code pour Deno, je recommande vivement Visual Studio Code avec le plugin officiel Deno. Pour rendre les choses un peu plus intéressantes, nous allons écrire l'application en TypeScript.

Installer Deno

Premièrement, installons Deno localement afin que nous puissions commencer à écrire notre script. Le processus est simple, car il existe des scripts d'installation pour les trois principaux systèmes d'exploitation.

les fenêtres

Sous Windows, vous pouvez installer Deno à partir de PowerShell:

iwr https://deno.land/x/install/install.ps1 -useb | iex

Linux

Depuis le terminal Linux, vous pouvez utiliser la commande suivante:

curl -fsSL https://deno.land/x/install/install.sh |  sh

macOS

Sur un Mac, Deno peut être installé avec Brew:

brew install deno

Après l'installation

Une fois le processus d'installation terminé, vous pouvez vérifier que Deno a été correctement installé en exécutant la commande suivante:

deno --version

Vous devriez maintenant voir quelque chose de similaire à ceci:

deno 1.2.0
v8 8.5.216
typescript 3.9.2

Créons un dossier pour notre nouveau projet (dans votre dossier de départ ou à l’endroit où vous souhaitez conserver vos projets de codage) et ajoutez un index.ts fichier:

mkdir weather-app
cd weather-app
code index.ts

Remarque: comme je l'ai mentionné ci-dessus, j'utilise VS Code pour ce didacticiel. Si vous utilisez un autre éditeur, remplacez la dernière ligne ci-dessus.

Obtenir une entrée utilisateur

Notre programme va récupérer les prévisions météorologiques pour une ville donnée, nous devrons donc accepter le nom de la ville comme argument lors de l'exécution du programme. Les arguments fournis à un script Deno sont disponibles sous la forme Deno.args. Déconnectons cette variable de la console pour voir comment cela fonctionne:

console.log(Deno.args);

Maintenant, exécutez le script, avec la commande suivante:

deno run index.ts --city London

Vous devriez voir la sortie suivante:

( "--city", "London" )

Bien que nous puissions analyser nous-mêmes ce tableau d’arguments, la bibliothèque standard de Deno comprend un module appelé flags qui se chargera de cela pour nous. Pour l'utiliser, il suffit d'ajouter une instruction d'importation en haut de notre fichier:

import { parse } from  "https://deno.land/std@0.61.0/flags/mod.ts";

Remarque: les exemples dans la documentation pour les modules de bibliothèque standard vous donneront une URL non versionnée (telle que https://deno.land/std/flags/mod.ts), qui pointera toujours vers la dernière version du code. Il est recommandé de spécifier une version dans vos importations afin de vous assurer que votre programme n’est pas interrompu par les futures mises à jour.*

Utilisons la fonction importée pour analyser le tableau d'arguments en quelque chose de plus utile:

const args = parse(Deno.args);

Nous allons également modifier le script pour déconnecter notre nouveau args variable, pour voir à quoi cela ressemble. Alors maintenant, votre code devrait ressembler à ceci:

import { parse } from  "https://deno.land/std@0.61.0/flags/mod.ts";

const args = parse(Deno.args);

console.log(args);

Maintenant, si vous exécutez le script avec le même argument que précédemment, vous devriez voir la sortie suivante:

Download https://deno.land/std@0.61.0/flags/mod.ts
Download https://deno.land/std@0.61.0/_util/assert.ts
Check file:///home/njacques/code/weather-app/index.ts
{ _: (), city: "London" }

Chaque fois que Deno exécute un script, il recherche de nouvelles instructions d'importation. Toutes les importations hébergées à distance sont téléchargées, compilées et mises en cache pour une utilisation future. le parse fonction nous a fourni un objet, qui a un city propriété contenant notre entrée.

Remarque: si vous devez télécharger à nouveau les importations pour un script pour une raison quelconque, vous pouvez exécuter deno cache --reload index.ts.

Nous devrions également ajouter un chèque pour le city argument, et quittez le programme avec un message d'erreur s'il n'est pas fourni:

if (args.city === undefined) {
    console.error("No city supplied");
    Deno.exit();
}

Parler à l'API météo

Nous allons obtenir nos données de prévision d'OpenWeatherMap. Vous devrez créer un compte gratuit pour obtenir une clé API. Nous utiliserons leur API de prévision sur 5 jours, en lui passant un nom de ville comme paramètre.

Ajoutons du code pour récupérer les prévisions et nous déconnecter de la console, pour voir ce que nous obtenons:

import { parse } from  "https://deno.land/std@0.61.0/flags/mod.ts";

const args = parse(Deno.args);

if (args.city === undefined) {
    console.error("No city supplied");
    Deno.exit();
}

const apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

const res = await fetch(`https://api.openweathermap.org/data/2.5/forecast?q=${args.city}&units=metric&appid=${apiKey}`);
const data = await res.json();

console.log(data);

Deno essaie de prendre en charge de nombreuses API de navigateur lorsque cela est possible, nous pouvons donc utiliser ici fetch sans avoir à importer de dépendances externes. Nous utilisons également la prise en charge des await: normalement, nous devons envelopper tout code qui utilise await dans un async fonction, mais TypeScript ne nous oblige pas à faire cela, ce qui rend le code un peu plus agréable.

Si vous essayez d'exécuter ce script maintenant, vous rencontrerez un message d'erreur:

Check file:///home/njacques/code/weather-app/index.ts
error: Uncaught PermissionDenied: network access to "https://api.openweathermap.org/data/2.5/forecast?q=London&units=metric&appid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", run again with the --allow-net flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:42:11)
    at Object.sendAsync ($deno$/ops/dispatch_json.ts:93:10)
    at async fetch ($deno$/web/fetch.ts:266:27)
    at async index.ts:12:13

Par défaut, tous les scripts Deno sont exécutés dans un bac à sable sécurisé: ils n'ont pas accès au réseau, au système de fichiers ou à des éléments tels que les variables d'environnement. Les scripts doivent disposer d'une autorisation explicite pour les ressources système auxquelles ils doivent accéder. Dans ce cas, le message d'erreur nous permet de savoir de quelle autorisation nous avons besoin et comment l'activer.

Appelons à nouveau le script, avec l'indicateur approprié:

deno run --allow-net index.ts --city London

Cette fois, nous devrions récupérer une réponse JSON de l'API:

{
  cod: "200",
  message: 0,
  cnt: 40,
  list: (
    {
      dt: 1595527200,
      main: {
        temp: 22.6,
        feels_like: 18.7,
        temp_min: 21.04,
        temp_max: 22.6,
        pressure: 1013,
        sea_level: 1013,
        grnd_level: 1011,
        humidity: 39,
        temp_kf: 1.56
      },
      weather: ( (Object) ),
      clouds: { all: 88 },
      wind: { speed: 4.88, deg: 254 },
      visibility: 10000,
      pop: 0,
      sys: { pod: "d" },
      dt_txt: "2020-07-23 18:00:00"
    },
    ...
  ),
  city: {
    id: 2643743,
    name: "London",
    coord: { lat: 51.5085, lon: -0.1257 },
    country: "GB",
    population: 1000000,
    timezone: 3600,
    sunrise: 1595477494,
    sunset: 1595534525
  }
}

Vous pouvez consulter tous les détails de ce qui est renvoyé dans la réponse, mais ce qui nous intéresse principalement, c'est le tableau des données de prévision dans list. Chaque objet du tableau contient un horodatage (dt), une main objet avec des détails sur les conditions atmosphériques (température, humidité, pression, etc.), et un weather tableau contenant un objet avec une description de la météo prévue.

Nous allons parcourir le main tableau pour obtenir l'heure, la température et les conditions météorologiques prévues. Commençons par limiter le nombre d'enregistrements pour couvrir une période de 24 heures uniquement. Les données de prévision dont nous disposons dans le cadre du forfait gratuit ne sont disponibles que toutes les trois heures. Nous aurons donc besoin de huit enregistrements:

const  forecast = data.list.slice(0, 8)

Nous allons cartographier chacun des éléments de prévision et renvoyer un tableau des données qui nous intéressent:

const forecast = data.list.slice(0, 8).map(item => (
    item.dt,
    item.main.temp,
    item.weather(0).description,
));

Si nous essayons d'exécuter le script maintenant, nous obtiendrons une erreur de compilation (si vous utilisez un IDE comme VS Code, cette erreur s'affichera également lorsque vous tapez le code): Le paramètre «item» a implicitement un type «any».

TypeScript nous oblige à lui indiquer le type de variable qui item est, afin de savoir si nous en faisons quelque chose qui pourrait provoquer une erreur lors de l'exécution. Ajoutons une interface pour décrire la structure de item:

interface forecastItem {
    dt: string;
    main: { temp: number; };
    weather: { description: string; }();
}

Notez que nous ne décrivons pas tout les propriétés de l'objet ici, seulement celles auxquelles nous allons réellement accéder. Dans notre situation, nous savons quelles propriétés nous voulons.

Ajoutons notre nouveau type à notre map rappeler:

const forecast = data.list.slice(0, 8).map((item: forecastItem) => (
    item.dt,
    item.main.temp,
    item.weather(0).description,
));

Si vous utilisez un IDE avec prise en charge de TypeScript, il devrait être en mesure de compléter automatiquement les propriétés de item au fur et à mesure que vous tapez, grâce au type d'interface que nous avons fourni.

  • Créer une classe de service
  • Créer une interface pour la sortie

Formatage de la sortie

Maintenant que nous disposons de l'ensemble de données souhaité, examinons la mise en forme pour l'afficher à l'utilisateur.

Tout d'abord, transformons la valeur d'horodatage en une date lisible par l'homme. Si nous jetons un œil à la liste des modules tiers de Deno et recherchons "date", nous pouvons voir date-fns dans la liste. Nous pouvons utiliser le lien à partir d'ici pour importer les fonctions que nous allons utiliser dans notre application Deno:

import { fromUnixTime, format } from  "https://deno.land/x/date_fns@v2.15.0/index.js";

Nous pouvons maintenant passer l'horodatage à travers le fromUnixTime fonction, pour obtenir un objet Date, puis passer cet objet dans format afin d'obtenir une chaîne de date que nous voulons:

format(fromUnixTime(item.dt), "do LLL, k:mm", {})

La chaîne de formatage do LLL, k:mm nous donnera une date dans le format suivant: «24 juillet, 13h00».

Remarque: nous transmettons un objet vide comme troisième argument à format purement pour faire taire un avertissement IDE sur le nombre attendu d'arguments. Le code fonctionnera toujours correctement sans cela.

Pendant que nous y sommes, arrondissons la valeur de la température à une seule décimale et ajoutons un indicateur d'unités:

`${item.main.temp.toFixed(1)}C`

Maintenant que nos données de prévision sont formatées et prêtes à être affichées, présentons-les à l'utilisateur dans un petit tableau soigné, à l'aide du module ascii_table:

import  AsciiTable  from  'https://deno.land/x/ascii_table/mod.ts';

...

const table = AsciiTable.fromJSON({
  title: `${data.city.name} Forecast`,
  heading: ( 'Time', 'Temp', 'Weather'),
  rows: forecast
})

console.log(table.toString())

Enregistrez et exécutez le script, et maintenant nous devrions avoir bien formaté et présenté des prévisions pour la ville que nous avons choisie, pour les prochaines 24 heures:

.--------------------------------------------.
|              London Forecast               |
|--------------------------------------------|
|      Time       | Temp  |     Weather      |
|-----------------|-------|------------------|
| 23rd Jul, 19:00 | 17.8C | light rain       |
| 23rd Jul, 22:00 | 16.8C | light rain       |
| 24th Jul, 1:00  | 16.0C | broken clouds    |
| 24th Jul, 4:00  | 15.6C | light rain       |
| 24th Jul, 7:00  | 16.0C | broken clouds    |
| 24th Jul, 10:00 | 18.3C | scattered clouds |
| 24th Jul, 13:00 | 20.2C | light rain       |
| 24th Jul, 16:00 | 20.2C | light rain       |
'--------------------------------------------'

Liste complète des codes

C'est un script assez compact, mais voici la liste complète du code:

import { parse } from "https://deno.land/std@0.61.0/flags/mod.ts";
import {
  fromUnixTime,
  format,
} from "https://deno.land/x/date_fns@v2.15.0/index.js";
import AsciiTable from "https://deno.land/x/ascii_table/mod.ts";

const args = parse(Deno.args);

if (args.city === undefined) {
  console.error("No city supplied");
  Deno.exit();
}

const apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

const res = await fetch(
  `https://api.openweathermap.org/data/2.5/forecast?q=${args.city}&units=metric&appid=${apiKey}`,
);
const data = await res.json();

interface forecastItem {
  dt: string;
  main: { temp: number };
  weather: { description: string }();
}

const forecast = data.list.slice(0, 8).map((item: forecastItem) => (
  format(fromUnixTime(item.dt), "do LLL, k:mm", {}),
  `${item.main.temp.toFixed(1)}C`,
  item.weather(0).description,
));

const table = AsciiTable.fromJSON({
  title: `${data.city.name} Forecast`,
  heading: ("Time", "Temp", "Weather"),
  rows: forecast,
});

console.log(table.toString());

Résumé

Vous avez maintenant votre propre programme de ligne de commande Deno qui vous donnera les prévisions météorologiques pour les prochaines 24 heures. En suivant ce didacticiel, vous devez maintenant savoir comment démarrer un nouveau programme, importer des dépendances à partir de la bibliothèque standard et de tiers, et accorder des autorisations de script.

Alors, ayant le goût d'écrire des programmes pour Deno, où devriez-vous aller ensuite? Je recommande vivement de lire le manuel pour en savoir plus sur les différentes options de ligne de commande et les API intégrées, mais gardez également un œil sur SitePoint pour plus de contenu Deno!

Fondations Deno

Soyez au courant avec Deno. Notre collection Deno Foundations vous aide à faire vos premiers pas dans le monde Deno et au-delà, et nous y ajoutons constamment. Nous vous apporterons les tutoriels dont vous avez besoin pour devenir un pro. Vous pouvez toujours vous référer à notre index tel qu'il est mis à jour à la fin de notre introduction à Deno:

➤ Fondations Deno

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *