Télécharger en tant que données ouvertes à l'aide de l'API CKAN en Python et lier automatiquement avec les actions Github

table des matières

  1. Résumé
  2. [Prérequis](# Prérequis)
  3. [Mécanisme de téléchargement de données](# mécanisme de téléchargement de données)
  4. [Automatisation de la liaison de données](#Automation de la liaison de données)
  5. Résumé

Aperçu

Récemment, un site portail africain de données ouvertes appelé openAFRICA géré par une organisation appelée Code for Africa et son propre approvisionnement en eau au Rwanda La fonction de liaison automatique des données de tuiles vectorielles de l'eau, gérée conjointement avec la société publique WASAC, a été implémentée en Python.

Puisqu'il utilise une API appelée CKAN, qui semble être largement utilisée dans les sites de données ouvertes des gouvernements locaux japonais, je pense qu'elle peut être utilisée lorsque vous souhaitez lier automatiquement des données ouvertes telles que des fichiers appartenant à votre organisation via l'API. Alors je veux le partager.

Conditions préalables

-Vous avez votre propre compte sur une plateforme de données ouvertes utilisant CKAN API --Gestion des données ouvertes sur Github

Tout au long de cet article, lorsque les données ouvertes sur Github sont mises à jour, Github Action sera utilisé pour lier automatiquement les données sur la plateforme via CKAN.

À propos, la page openAFRICA des données ouvertes de la tuile vecteur eau de la Rwanda Water Authority peut être trouvée sur le lien suivant. https://open.africa/dataset/rw-water-vectortiles

image.png

De plus, le référentiel Github des tuiles vectorielles de l'eau se trouve sur le lien ci-dessous, et est automatiquement mis à jour vers Github à partir du serveur du bureau public de l'eau chaque semaine. https://github.com/WASAC/vt

image.png

Mécanisme de téléchargement de données

Téléchargez et installez le référentiel

Si pipenv n'est pas installé, veuillez d'abord le configurer.

git clone https://github.com/watergis/open-africa-uploader
cd open-africa-uploader
pipenv install
pipenv shell

Mécanisme de téléchargement de fichiers à l'aide de l'API CKAN

Tout d'abord, je publierai le code source complet de OpenAfricaUploader.py dans le référentiel.

import os
import ckanapi
import requests


class OpanAfricaUploader(object):
  def __init__(self, api_key):
    """Constructor

    Args:
        api_key (string): CKAN api key
    """
    self.data_portal = 'https://africaopendata.org'
    self.APIKEY = api_key
    self.ckan = ckanapi.RemoteCKAN(self.data_portal, apikey=self.APIKEY)

  def create_package(self, url, title):
    """create new package if it does not exist yet.

    Args:
        url (str): the url of package eg. https://open.africa/dataset/{package url}
        title (str): the title of package
    """
    package_name = url
    package_title = title
    try:
        print ('Creating "{package_title}" package'.format(**locals()))
        self.package = self.ckan.action.package_create(name=package_name,
                                            title=package_title,
                                            owner_org = 'water-and-sanitation-corporation-ltd-wasac')
    except (ckanapi.ValidationError) as e:
        if (e.error_dict['__type'] == 'Validation Error' and
          e.error_dict['name'] == ['That URL is already in use.']):
            print ('"{package_title}" package already exists'.format(**locals()))
            self.package = self.ckan.action.package_show(id=package_name)
        else:
            raise

  def resource_create(self, data, path, api="/api/action/resource_create"):
    """create new resource, or update existing resource

    Args:
        data (object): data for creating resource. data must contain package_id, name, format, description. If you overwrite existing resource, id also must be included.
        path (str): file path for uploading
        api (str, optional): API url for creating or updating. Defaults to "/api/action/resource_create". If you want to update, please specify url for "/api/action/resource_update"
    """
    self.api_url = self.data_portal + api
    print ('Creating "{}"'.format(data['name']))
    r = requests.post(self.api_url,
                      data=data,
                      headers={'Authorization': self.APIKEY},
                      files=[('upload', open(path, 'rb'))])

    if r.status_code != 200:
        print ('Error while creating resource: {0}'.format(r.content))
    else:
      print ('Uploaded "{}" successfully'.format(data['name']))

  def resource_update(self, data, path):
    """update existing resource

    Args:
        data (object): data for creating resource. data must contain id, package_id, name, format, description.
        path (str): file path for uploading
    """
    self.resource_create(data, path, "/api/action/resource_update")

  def upload_datasets(self, path, description):
    """upload datasets under the package

    Args:
        path (str): file path for uploading
        description (str): description for the dataset
    """
    filename = os.path.basename(path)
    extension = os.path.splitext(filename)[1][1:].lower()
    
    data = {
      'package_id': self.package['id'],
      'name': filename,
      'format': extension,
      'description': description
    }

    resources = self.package['resources']
    if len(resources) > 0:
      target_resource = None
      for resource in reversed(resources):
        if filename == resource['name']:
          target_resource = resource
          break

      if target_resource == None:
        self.resource_create(data, path)
      else:
        print ('Resource "{}" already exists, it will be overwritten'.format(target_resource['name']))
        data['id'] = target_resource['id']
        self.resource_update(data, path)
    else:
      self.resource_create(data, path)

Le code source pour appeler OpenAfricaUploader.py pour télécharger un fichier ressemble à ceci:

import os
from OpenAfricaUploader import OpanAfricaUploader

uploader = OpanAfricaUploader(args.key)
uploader.create_package('rw-water-vectortiles','Vector Tiles for rural water supply systems in Rwanda')
uploader.upload_datasets(os.path.abspath('../data/rwss.mbtiles'), 'mbtiles format of Mapbox Vector Tiles which was created by tippecanoe.')

Je vais expliquer un par un.

constructeur

Ce module a l'URL du site portail de base définie dans le constructeur à l'avance pour le téléchargement vers openAFRICA.

Remplacez l'URL de self.data_portal = 'https: // africaopendata.org' par l'URL de l'API CKAN utilisée par votre organisation.

  def __init__(self, api_key):
    """Constructor

    Args:
        api_key (string): CKAN api key
    """
    self.data_portal = 'https://africaopendata.org'
    self.APIKEY = api_key
    self.ckan = ckanapi.RemoteCKAN(self.data_portal, apikey=self.APIKEY)

L'appel au constructeur ressemble à ceci: Spécifiez la clé API CKAN de votre compte dans args.key.

uploader = OpanAfricaUploader(args.key)

Créer un package

Créez un package à l'aide de l'API package_create. À ce moment-là, spécifiez ce qui suit dans l'argument.

--name = La chaîne de caractères spécifiée ici sera l'URL du package --title = Titre du package --owner_org = ID de l'organisation cible sur le portail CKAN

Si la création réussit, les informations du package seront renvoyées sous forme de valeur de retour. S'il existe déjà, une erreur se produira, donc j'écris un processus pour obtenir les informations sur le package existant dans le processus d'exception.

  def create_package(self, url, title):
    """create new package if it does not exist yet.

    Args:
        url (str): the url of package eg. https://open.africa/dataset/{package url}
        title (str): the title of package
    """
    package_name = url
    package_title = title
    try:
        print ('Creating "{package_title}" package'.format(**locals()))
        self.package = self.ckan.action.package_create(name=package_name,
                                            title=package_title,
                                            owner_org = 'water-and-sanitation-corporation-ltd-wasac')
    except (ckanapi.ValidationError) as e:
        if (e.error_dict['__type'] == 'Validation Error' and
          e.error_dict['name'] == ['That URL is already in use.']):
            print ('"{package_title}" package already exists'.format(**locals()))
            self.package = self.ckan.action.package_show(id=package_name)
        else:
            raise

La façon d'appeler cette fonction est la suivante

uploader.create_package('rw-water-vectortiles','Vector Tiles for rural water supply systems in Rwanda')

Création et mise à jour des ressources

Les ressources sont créées avec une fonction appelée resource_create. Vous pouvez utiliser l'API REST / api / action / resource_create pour transmettre les données binaires et les informations de fichier à télécharger.

def resource_create(self, data, path, api="/api/action/resource_create"):
    self.api_url = self.data_portal + api
    print ('Creating "{}"'.format(data['name']))
    r = requests.post(self.api_url,
                      data=data,
                      headers={'Authorization': self.APIKEY},
                      files=[('upload', open(path, 'rb'))])

    if r.status_code != 200:
        print ('Error while creating resource: {0}'.format(r.content))
    else:
      print ('Uploaded "{}" successfully'.format(data['name']))

Cependant, si vous n'utilisez que resource_create, vous ne pouvez ajouter que des ressources, et le nombre augmentera régulièrement à chaque mise à jour, utilisez donc l'API / api / action / resource_update pour mettre à jour s'il y a des ressources existantes. Je le ferai.

L'utilisation de «resource_update» est fondamentalement la même que de «resource_create», la seule différence est de savoir s'il y a ou non «resource_id» dans «data».

def resource_update(self, data, path):
    self.resource_create(data, path, "/api/action/resource_update")

Une fonction appelée upload_datasets combine resource_create et resource_update d'une manière agréable pour mettre à jour les ressources existantes si elles existent et en créer de nouvelles si elles n'existent pas.

def upload_datasets(self, path, description):
    #Séparez le nom du fichier de l'extension
    filename = os.path.basename(path)
    extension = os.path.splitext(filename)[1][1:].lower()
    
    #Créer des données pour la création de ressources
    data = {
      'package_id': self.package['id'], #ID du package
      'name': filename,                 #Nom de fichier à mettre à jour
      'format': extension,              #Format (ici, extension)
      'description': description        #Description du fichier
    }

    #S'il existe déjà une ressource dans le package, vérifiez s'il existe une ressource portant le même nom que le nom de fichier à télécharger.
    resources = self.package['resources']
    if len(resources) > 0:
      target_resource = None
      for resource in reversed(resources):
        if filename == resource['name']:
          target_resource = resource
          break

      if target_resource == None:
        #Ressource s'il n'y a pas de ressource avec le même nom_Appelez créer
        self.resource_create(data, path)
      else:
        #S'il y a une ressource, définissez l'ID dans les données et la ressource_Mise à jour de l'appel
        print ('Resource "{}" already exists, it will be overwritten'.format(target_resource['name']))
        data['id'] = target_resource['id']
        self.resource_update(data, path)
    else:
      #Ressource si aucune ressource_Appelez créer
      self.resource_create(data, path)

La manière d'appeler la fonction upload_datasets est la suivante.

 uploader.upload_datasets(os.path.abspath('../data/rwss.mbtiles'), 'mbtiles format of Mapbox Vector Tiles which was created by tippecanoe.')

Rendre la source du téléchargement appelable à partir de la ligne de commande

Vous pouvez l'appeler depuis la ligne de commande avec upload2openafrica.py.

import os
import argparse
from OpenAfricaUploader import OpanAfricaUploader

def get_args():
  prog = "upload2openafrica.py"
  usage = "%(prog)s [options]"
  parser = argparse.ArgumentParser(prog=prog, usage=usage)
  parser.add_argument("--key", dest="key", help="Your CKAN api key", required=True)
  parser.add_argument("--pkg", dest="package", help="Target url of your package", required=True)
  parser.add_argument("--title", dest="title", help="Title of your package", required=True)
  parser.add_argument("--file", dest="file", help="Relative path of file which you would like to upload", required=True)
  parser.add_argument("--desc", dest="description", help="any description for your file", required=True)
  args = parser.parse_args()

  return args

if __name__ == "__main__":
  args = get_args()

  uploader = OpanAfricaUploader(args.key)
  uploader.create_package(args.package,args.title)
  uploader.upload_datasets(os.path.abspath(args.file), args.description)

Lors de son utilisation, cela ressemble à ce qui suit. Je crée un script shell appelé upload_mbtiles.sh. Assurez-vous de définir la variable d'environnement sur CKAN_API_KEY.


#!/bin/bash

pipenv run python upload2openafrica.py \
  --key ${CKAN_API_KEY} \
  --pkg rw-water-vectortiles \
  --title "Vector Tiles for rural water supply systems in Rwanda" \
  --file ../data/rwss.mbtiles \
  --desc "mbtiles format of Mapbox Vector Tiles which was created by tippecanoe."

Vous pouvez désormais télécharger des données ouvertes à l'aide de l'API CKAN.

Automatisation de la liaison de données

Cependant, il est difficile de lier manuellement avec CKAN à chaque fois, je vais donc l'automatiser avec Github Action. Le fichier de workflow ressemble à ceci:

name: openAFRICA upload

on:
  push:
    branches: [ master ]
    #Ici, le flux de travail s'exécute lorsque le dossier de données et ci-dessous sont mis à jour.
    paths:
      - "data/**"

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python 3.8
      uses: actions/setup-python@v2
      with:
        python-version: 3.8
    - name: Install dependencies
      #Commencez par définir les paramètres initiaux de Pipenv.
      run: |
        cd scripts
        pip install pipenv
        pipenv install
    - name: upload to openAFRICA
      #CKAN in Secrets sur la page Paramètres du référentiel Github_API_Si vous vous inscrivez avec le nom KEY, vous pouvez utiliser les variables d'environnement comme suit.
      env:
        CKAN_API_KEY: ${{secrets.CKAN_API_KEY}}
      #Après cela, j'appellerai le script shell
      run: |
        cd scripts
        ./upload_mbtiles.sh

Avec cela seul, une fois le fichier téléchargé sur Github, il peut être automatiquement lié à la plate-forme de données ouvertes. L'image suivante est l'écran lorsque Github Aciton de la Rwanda Water Authority est exécuté.

image.png

Sommaire

L'API CKAN est utilisée sur diverses plates-formes open source au pays et à l'étranger. L'API CKAN peut implémenter la liaison de données relativement facilement en utilisant Python. De plus, si les données ouvertes sont gérées sur Github, elles peuvent être liées plus facilement et automatiquement à l'aide de Github Action.

Nous espérons que le module créé pour openAFRICA sera utile pour utiliser les données ouvertes en utilisant d'autres CKAN au Japon et à l'étranger.

Recommended Posts

Télécharger en tant que données ouvertes à l'aide de l'API CKAN en Python et lier automatiquement avec les actions Github
Obtenez des données Youtube en Python à l'aide de l'API Youtube Data
Pour envoyer automatiquement des e-mails avec des pièces jointes à l'aide de l'API Gmail en Python
Importez un fichier JPG à l'aide de l'API Google Drive en Python
Obtenez des données LEAD à l'aide de l'API REST de Marketo en Python
Jouez avec l'API de données YouTube v3 à l'aide du client Python de l'API Google
Ouvrez UTF-8 avec BOM en Python
Obtenez des données supplémentaires vers LDAP avec python
Essayez d'utiliser l'API Wunderlist en Python
Créez automatiquement la documentation de l'API Python avec Sphinx
Essayez d'utiliser l'API Kraken avec Python
Obtenez des données alimentaires avec l'API Amazon (Python)
Essayez de travailler avec des données binaires en Python
Tweet à l'aide de l'API Twitter en Python
Obtenez les données de l'API Google Fit en Python
Créer une feuille de calcul Google à l'aide de l'API Python / Google Data
Convertir les données csv, tsv en une matrice avec MovieLens en utilisant python comme exemple
Vérifier automatiquement les scripts Python avec GitHub + Travis-CI + pycodestyle
[Python] Obtenez tous les commentaires à l'aide de Youtube Data Api
Essayez d'utiliser l'API BitFlyer Ligntning en Python
Obtenir l'URL de l'image à l'aide de l'API Flickr en Python
Obtenez des données sur le cours de l'action avec l'API Quandl [Python]
Jugons les émotions à l'aide de l'API Emotion en Python
Création récente de classement à l'aide de l'API Qiita avec Python
Téléchargement anonyme d'images à l'aide de l'API Imgur (à l'aide de Python)
[WP REST API v2] Télécharger des images avec Python
Préparer un pseudo serveur API à l'aide d'actions GitHub
Essayez d'utiliser l'API ChatWork et l'API Qiita en Python
Essayez d'utiliser l'API DropBox Core avec Python
Enregistrez collectivement des données dans Firestore à l'aide d'un fichier csv en Python
Paramètres initiaux lors de l'utilisation de l'API foursquare avec python
Jouer avec l'API d'intelligence artificielle locale de l'utilisateur en Python
Utilisez Cursur qui se ferme automatiquement avec sqlite3 en Python
[Python] Obtenez des données insight à l'aide de l'API Google My Business
OpenVINO utilisant l'API Python d'Inference Engine dans un environnement PC
Utilisons les données ouvertes de "Mamebus" en Python
Traiter les données csv avec python (traitement du comptage à l'aide de pandas)
Utiliser l'API de recherche de la Bibliothèque du Parlement national en Python