[RUBY] Obtenez les prévisions météo de Open Weather Map with Rails

en premier

Ceci est le premier message de Qiita! Puisque je suis débutant, je pense qu'il y a de nombreux points que je ne peux pas atteindre, mais je vous serais reconnaissant de bien vouloir commenter!

Aperçu

Environnement de développement

ruby: 2.7.1 rails: 6.0.3.2

procédure

  1. Obtenez la clé API
  2. Obtenez le CITY ID de la ville que vous souhaitez obtenir
  3. Créer et enregistrer la table des villes
  4. Implémentation de la requête HTTP
  5. Créer et enregistrer la table WeatherForecast

1. Obtenez la clé API

Accédez à la page d'accueil d'OpenWeatherMap et accédez à Connexion et créez un compte avec Créer un compte. Si vous l'activez à partir de l'e-mail envoyé, vous recevrez une clé API.

Enregistrez-le en tant que variable d'environnement ou «informations d'identification». Cette fois, nous utiliserons les «informations d'identification». En ce qui concerne les informations d'identification, l '[article] de cette personne (https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39) est très utile.

EDITOR=vi bin/rails credentials:edit

credentials.yml.enc


open_weahter:
    appid: <API_KEY>
    uri: https://samples.openweathermap.org/data/2.5/forecast

De plus, puisque je vais avoir la météo toutes les 3 heures cette fois, je reçois l'URI pour envoyer la requête en me référant au document API.

Il semble qu'il existe de nombreux types de conditions météorologiques qui peuvent être obtenues même avec le niveau gratuit! Il est intéressant de rechercher divers documents API.

2. Obtenez un identifiant de ville

Téléchargez city.list.json depuis la documentation de l'API (https://openweathermap.org/forecast5). ![Capture d'écran 2020-08-02 15.54.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684915/9535a511-db9d-f4b3-f042- 028b93b1c498.png)

À partir de ce fichier, obtenez le CITY ID de la ville que vous souhaitez obtenir. Ceci est un extrait partiel du contenu. À propos, lon signifie longitude et lat signifie latitude.

city.list.json


  {
    "id": 1850147,
    "name": "Tokyo",
    "state": "",
    "country": "JP",
    "coord": {
      "lon": 139.691711,
      "lat": 35.689499
    }
  },

Je pleurais pour avoir cette identité ... Veuillez noter que certaines villes du même nom ont des latitudes et des longitudes différentes!

Si vous utilisez Excel ou mac, vous devez les lister en chiffres et les convertir en CSV! Au fait, le CSV que j'ai créé ressemble à ceci. Je racle quelques colonnes.

db/csv/cities.csv


Sapporo,2128295
Aomori,2130658
Morioka,2111834
Sendai,2111149
Akita,2113126
Yamagata,2110556
Fukushima,2112923
Mito,2111901
Utsunomiya,1849053
Maebashi,1857843
Saitama,6940394
Chiba,2113015
Tokyo,1850147
Yokohama,1848354
Niigata,1855431
Toyama,1849876
Kanazawa,1860243
Fukui,1863983
Yamanashi,1848649
Nagano,1856215
Gifu,1863640
Shizuoka,1851715
Nagoya,1856057
Tsu,1849796
Otsu,1853574
Kyoto,1857910
Osaka,1853909
Kobe,1859171
Nara,1855612
Wakayama,1926004
Tottori,1849890
Matsue,1857550
Okayama,1854383
Hiroshima,1862415
Yamaguchi,1848689
Tokushima,1850158
Takamatsu,1851100
Matsuyama,1926099
Kochi,1859146
Fukuoka,1863967
Saga,1853303
Nagasaki,1856177
Kumamoto,1858421
Oita,1854487
Miyazaki,1856717
Kagoshima,1860827
Naha,1856035

3. Créer et enregistrer la table des villes

Écrivez le code dans seeds.rb ou task et enregistrez-le dans la base de données. Cette fois, je l'ai implémenté sous lib / tasks. CITY ID stocke le nom de la colonne en tant que location_id.

import_csv.rake


  desc 'Import cities'
  task cities: [:environment] do
    list = []
    CSV.foreach('db/csv/cities.csv') do |row|
      list << {
        name: row[0],
        location_id: row[1],
      }
    end

    puts 'start creating cities'
    begin
      City.create!(list)
      puts 'completed!'
    rescue ActiveModel::UnknownAttributeError
      puts 'raised error: unknown attributes'
    end
  end

Chargez le fichier cities.csv créé précédemment avec la méthode CSV.foreach ligne par ligne. Vous pouvez obtenir le nom de la ville dans la première colonne avec row [0] et le CITY ID dans la deuxième colonne avec row [1], alors créez un tableau de hachages et enregistrez-le dans la base de données avec City.create! Je suis.

4. Implémentation de la requête HTTP

Analyse des réponses

Avant d'implémenter la requête HTTP, analysez d'abord le fichier JSON de la réponse.

Document API a une explication détaillée de chaque élément, alors reportez-vous-y pour obtenir la clé des données que vous souhaitez. Une demande pour une ville sera renvoyée au format JSON comme indiqué ci-dessous. (Si vous utilisez la commande curl ou VScode, vous devriez l'essayer avec REST Client.)

example_resopnse.json


{
  "cod": "200",
  "message": 0,
  "cnt": 40,
  "list": [
    {
      "dt": 1578409200,
      "main": {
        "temp": 284.92,
        "feels_like": 281.38,
        "temp_min": 283.58,
        "temp_max": 284.92,
        "pressure": 1020,
        "sea_level": 1020,
        "grnd_level": 1016,
        "humidity": 90,
        "temp_kf": 1.34
      },
      "weather": [
        {
          "id": 804,
          "main": "Clouds",
          "description": "overcast clouds",
          "icon": "04d"
        }
      ],
      "clouds": {
        "all": 100
      },
      "wind": {
        "speed": 5.19,
        "deg": 211
      },
      "sys": {
        "pod": "d"
      },
      "dt_txt": "2020-01-07 15:00:00"
    },
    

Cette fois, je crée un tableau en utilisant les éléments suivants.

«Pluie» est ajouté à la liste uniquement s'il y a des précipitations.

Veuillez vous référer à ici pour l'ID météo. OpenWeatherMap classe la météo par ID. Il est également possible d'obtenir la météo à partir de «description». Cependant, comme il existe de nombreux types, dans cette implémentation, l'ID météo est stocké dans la base de données et la météo est attribuée par la méthode.

Implémentation de la requête HTTP

Tout d'abord, ajoutez httpclient à votre Gemfile et à bundle install.

gem 'httpclient', '~> 2.8', '>= 2.8.3'

Créez ensuite lib / api / open_weather_map / request.rb. Je pensais qu'il n'était pas nécessaire de le rendre si profond, mais j'ai décidé d'organiser les fichiers comme celui-ci en tenant compte de l'implémentation d'autres classes pour cette api et de l'implémentation d'autres classes api.

Par défaut, seule la tâche est chargée sous lib, donc les paramètres suivants sont requis dans config / application.rb. Puisqu'il s'agit de ʻeager_load_paths`, l'environnement de production est également correct.

config/application.rb


config.paths.add 'lib', eager_load: true

Je serais très heureux si vous pouviez souligner des points tels que «Hé, c'est génial». Le placement des fichiers était le plus gros problème avec cette implémentation ...

Tableau WeatherForecast pour enregistrer les demandes ↓

WeatherForecast
temp_max float
temp_min float
temp_feel float
weather_id int
rainfall float
date datetime
aquired_at datetime

Voici la classe Request implémentée.

request.rb


module Api
  module OpenWeatherMap
    class Request
      attr_accessor :query

      def initialize(location_id)
        @query = {
          id: location_id,
          units: 'metric',
          appid: Rails.application.credentials.open_weather[:appid],
        }
      end

      def request
        client = HTTPClient.new
        request = client.get(Rails.application.credentials.open_weather[:uri], query) #La valeur de retour correspond aux données pendant 5 jours toutes les 3 heures
        JSON.parse(request.body)
      end

      def self.attributes_for(attrs)
        rainfall = attrs['rain']['3h'] if attrs['rain']
        date = attrs['dt_txt'].in_time_zone('UTC').in_time_zone

        {
          temp_max: attrs['main']['temp_max'],
          temp_min: attrs['main']['temp_min'],
          temp_feel: attrs['main']['feels_like'],
          weather_id: attrs['weather'][0]['id'],
          rainfall: rainfall,
          date: date,
          aquired_at: Time.current,
        }
      end
    end
  end
end

Je mets la chaîne de requête avec ʻinitialize. La chaîne de requête requise pour cette demande ajoute location_id et API KEY pour indiquer l'ID de VILLE, et ʻunits: 'metric' pour changer l'affichage de la température en degrés Celsius.

Afin de convertir la requête retournée en une forme qui peut être enregistrée dans la base de données, la méthode ʻattributes_for` est transformée en méthode de classe.

Ce à quoi vous devez faire attention est la quantité de précipitations et la date de prévision.

--Pour les précipitations, il n'y a pas d'élément s'il n'y a pas de précipitations. Par conséquent, il est conditionnel de ne l'obtenir que lorsque c'est le cas.

Pour la gestion des fuseaux horaires, reportez-vous à cet article.

5. Créer et enregistrer la table WeatherForecast

Créer une tâche de râteau

Je veux enregistrer / mettre à jour régulièrement la base de données, donc je l'écrirai dans rake task. Cela dit, j'ai écrit la plupart des méthodes de la classe Request plus tôt, donc tout ce que j'ai à faire est de les utiliser.

open_weather_api.rake


namespace :open_weather_api do
  desc 'Requests and save in database'
  task weather_forecasts: :environment do
    City.all.each do |city|
      open_weather = Api::OpenWeatherMap::Request.new(city.location_id)

      #Limite de demande: 60 fois/min
      response = open_weather.request

      #Économisez 2 jours de données toutes les 3 heures
      16.times do |i|
        params = Api::OpenWeatherMap::Request.attributes_for(response['list'][i])
        if weather_forecast = WeatherForecast.where(city: city, date: params[:date]).presence
          weather_forecast[0].update!(params)
        else
          city.weather_forecasts.create!(params)
        end
      end
    end
    puts 'completed!'
  end
end

Cette fois, nous avons fait une spécification pour sauvegarder et mettre à jour les données toutes les 3 heures dans la base de données pendant 2 jours. Le point est la limite de demande et s'il s'agit de création ou de mise à jour de données.

finalement

Si vous préparez une icône météo correspondant au weather_id sauvegardé, cela ressemblera à une prévision météo! Je pense que c'est une bonne idée de frapper régulièrement api avec cron ou heroku schedular pour heroku!

C'était affiché comme ça! スクリーンショット 2020-08-02 18.26.45.png

Merci d'avoir lu le long texte!

référence

https://openweathermap.org/api https://qiita.com/yoshito410kam/items/26c3c6e519d4990ed739

Recommended Posts

Obtenez les prévisions météo de Open Weather Map with Rails
[Rails] Obtenir UserAgent sur le contrôleur
[Rails] Comment afficher les prévisions météo de l'adresse enregistrée en japonais en utilisant OpenWeatherMap
Obtenez les prévisions météorologiques de Watson Weather Company Data avec Java simple
Obtenez "2-4, 7, 9" de [4, 7, 9, 2, 3]
Obtenir l'historique du serveur Zabbix en Java
Comment obtenir une classe depuis Element en Java
Enregistreur de rails Obtenez une idée approximative en 1 minute
Obtenez unixtime (secondes) de ZonedDateTime dans Scala / Java
Group_by dans Rails
[Java] Obtenir KFunction à partir de la méthode / du constructeur en Java [Kotlin]
Obtenez des informations de localisation avec Rails et triez par ordre croissant
Association de modèles dans Rails
Ajout de colonnes dans les rails
Désactiver les turbolinks dans les rails
^, $ dans l'expression régulière Rails
Utiliser des images avec des rails
Comprendre la migration dans les rails
Obtenez des cookies au printemps
Diviser routes.rb dans Rails6
Cloud9 (Rails) de Github
Markdown implémenté dans Rails
Obtenez facilement des entiers à partir des propriétés système en Java
Obtenir des attributs et des valeurs à partir d'un fichier XML en Java
Obtenir une collection non vide à partir d'un flux facultatif avec java