TolBot: Task 5
Status  Approved
Wiki x
Summary Update COVID-19 pandemic data
Page(s) Pages:
Period daily
Language Python
Supervision automatic
Excl. compl.? No

Requests for approval

edit

Source

edit
Version 1.3.0, updated 21 Sep 2024.
run = True

languages = {
	'en': {
		'page': 'Template:COVID-19 data/data',
		'summary': '[[m:User:TolBot/Task 5|Task 5]]: update COVID-19 pandemic data'
	},
	'fr': {
		'page': 'Modèle:Données de la pandémie de Covid-19/données',
		'summary': '[[m:User:TolBot/Task 5|Tâche 5]]: mettre à jour les données de la pandémie de COVID-19'
	},
	'et': {
		'page': 'Mall:Koroonapandeemia riigiti/andmed',
		'summary': '[[m:User:TolBot/Task 5|Ülesanne 5]]: värskendada COVID-19 pandeemia andmeid'
	},
	'af': {
		'page': 'Sjabloon:Koronaviruspandemie van 2019-2020/data',
		'summary': '[[m:User:TolBot/Task 5|Taak 5]]: dateer COVID-19-pandemiedata op'
	},
	'uk': {
		'page': 'Шаблон:Пандемія COVID-19 за країнами та територіями/дані',
		'summary': '[[m:User:TolBot/Task 5|Завдання 5]]: оновити дані про пандемію COVID-19'
	},
	'zh': {
		'page': 'Template:COVID-19 data/data',
		'summary': '[[m:User:TolBot/Task 5|任務5]]:更新 COVID-19 疫情数据'
	}
}

bot_username = ''
bot_password = ''
bot_credentials = (bot_username, bot_password)

import requests
import json
import pandas as pd
import re

api = 'https://{0}.wikipedia.org/w/api.php?format=json&formatversion=2'

owid_url = 'https://catalog.ourworldindata.org/garden/covid/latest/compact/compact.csv'

owid_translator = {
	'countries': {
		'OWID_WRL': 'XW',
		'OWID_EU27': 'EU',
		'OWID_CYN': 'XC', # Northern Cyprus
		'OWID_KOS': 'XK', # Kosovo
		'OWID_TRS': 'XT', # Transnistria
		'ABW': 'AW',
		'AFG': 'AF',
		'AGO': 'AO',
		'AIA': 'AI',
		'ALA': 'AX',
		'ALB': 'AL',
		'AND': 'AD',
		'ARE': 'AE',
		'ARG': 'AR',
		'ARM': 'AM',
		'ASM': 'AS',
		'ATA': 'AQ',
		'ATF': 'TF',
		'ATG': 'AG',
		'AUS': 'AU',
		'AUT': 'AT',
		'AZE': 'AZ',
		'BDI': 'BI',
		'BEL': 'BE',
		'BEN': 'BJ',
		'BES': 'BQ',
		'BFA': 'BF',
		'BGD': 'BD',
		'BGR': 'BG',
		'BHR': 'BH',
		'BHS': 'BS',
		'BIH': 'BA',
		'BLM': 'BL',
		'BLR': 'BY',
		'BLZ': 'BZ',
		'BMU': 'BM',
		'BOL': 'BO',
		'BRA': 'BR',
		'BRB': 'BB',
		'BRN': 'BN',
		'BTN': 'BT',
		'BVT': 'BV',
		'BWA': 'BW',
		'CAF': 'CF',
		'CAN': 'CA',
		'CCK': 'CC',
		'CHE': 'CH',
		'CHL': 'CL',
		'CHN': 'CN',
		'CIV': 'CI',
		'CMR': 'CM',
		'COD': 'CD',
		'COG': 'CG',
		'COK': 'CK',
		'COL': 'CO',
		'COM': 'KM',
		'CPV': 'CV',
		'CRI': 'CR',
		'CUB': 'CU',
		'CUW': 'CW',
		'CXR': 'CX',
		'CYM': 'KY',
		'CYP': 'CY',
		'CZE': 'CZ',
		'DEU': 'DE',
		'DJI': 'DJ',
		'DMA': 'DM',
		'DNK': 'DK',
		'DOM': 'DO',
		'DZA': 'DZ',
		'ECU': 'EC',
		'EGY': 'EG',
		'ERI': 'ER',
		'ESH': 'EH',
		'ESP': 'ES',
		'EST': 'EE',
		'ETH': 'ET',
		'FIN': 'FI',
		'FJI': 'FJ',
		'FLK': 'FK',
		'FRA': 'FR',
		'FRO': 'FO',
		'FSM': 'FM',
		'GAB': 'GA',
		'GBR': 'GB',
		'GEO': 'GE',
		'GGY': 'GG',
		'GHA': 'GH',
		'GIB': 'GI',
		'GIN': 'GN',
		'GLP': 'GP',
		'GMB': 'GM',
		'GNB': 'GW',
		'GNQ': 'GQ',
		'GRC': 'GR',
		'GRD': 'GD',
		'GRL': 'GL',
		'GTM': 'GT',
		'GUF': 'GF',
		'GUM': 'GU',
		'GUY': 'GY',
		'HKG': 'HK',
		'HMD': 'HM',
		'HND': 'HN',
		'HRV': 'HR',
		'HTI': 'HT',
		'HUN': 'HU',
		'IDN': 'ID',
		'IMN': 'IM',
		'IND': 'IN',
		'IOT': 'IO',
		'IRL': 'IE',
		'IRN': 'IR',
		'IRQ': 'IQ',
		'ISL': 'IS',
		'ISR': 'IL',
		'ITA': 'IT',
		'JAM': 'JM',
		'JEY': 'JE',
		'JOR': 'JO',
		'JPN': 'JP',
		'KAZ': 'KZ',
		'KEN': 'KE',
		'KGZ': 'KG',
		'KHM': 'KH',
		'KIR': 'KI',
		'KNA': 'KN',
		'KOR': 'KR',
		'KWT': 'KW',
		'LAO': 'LA',
		'LBN': 'LB',
		'LBR': 'LR',
		'LBY': 'LY',
		'LCA': 'LC',
		'LIE': 'LI',
		'LKA': 'LK',
		'LSO': 'LS',
		'LTU': 'LT',
		'LUX': 'LU',
		'LVA': 'LV',
		'MAC': 'MO',
		'MAF': 'MF',
		'MAR': 'MA',
		'MCO': 'MC',
		'MDA': 'MD',
		'MDG': 'MG',
		'MDV': 'MV',
		'MEX': 'MX',
		'MHL': 'MH',
		'MKD': 'MK',
		'MLI': 'ML',
		'MLT': 'MT',
		'MMR': 'MM',
		'MNE': 'ME',
		'MNG': 'MN',
		'MNP': 'MP',
		'MOZ': 'MZ',
		'MRT': 'MR',
		'MSR': 'MS',
		'MTQ': 'MQ',
		'MUS': 'MU',
		'MWI': 'MW',
		'MYS': 'MY',
		'MYT': 'YT',
		'NAM': 'NA',
		'NCL': 'NC',
		'NER': 'NE',
		'NFK': 'NF',
		'NGA': 'NG',
		'NIC': 'NI',
		'NIU': 'NU',
		'NLD': 'NL',
		'NOR': 'NO',
		'NPL': 'NP',
		'NRU': 'NR',
		'NZL': 'NZ',
		'OMN': 'OM',
		'PAK': 'PK',
		'PAN': 'PA',
		'PCN': 'PN',
		'PER': 'PE',
		'PHL': 'PH',
		'PLW': 'PW',
		'PNG': 'PG',
		'POL': 'PL',
		'PRI': 'PR',
		'PRK': 'KP',
		'PRT': 'PT',
		'PRY': 'PY',
		'PSE': 'PS',
		'PYF': 'PF',
		'QAT': 'QA',
		'REU': 'RE',
		'ROU': 'RO',
		'RUS': 'RU',
		'RWA': 'RW',
		'SAU': 'SA',
		'SDN': 'SD',
		'SEN': 'SN',
		'SGP': 'SG',
		'SGS': 'GS',
		'SHN': 'SH',
		'SJM': 'SJ',
		'SLB': 'SB',
		'SLE': 'SL',
		'SLV': 'SV',
		'SMR': 'SM',
		'SOM': 'SO',
		'SPM': 'PM',
		'SRB': 'RS',
		'SSD': 'SS',
		'STP': 'ST',
		'SUR': 'SR',
		'SVK': 'SK',
		'SVN': 'SI',
		'SWE': 'SE',
		'SWZ': 'SZ',
		'SXM': 'SX',
		'SYC': 'SC',
		'SYR': 'SY',
		'TCA': 'TC',
		'TCD': 'TD',
		'TGO': 'TG',
		'THA': 'TH',
		'TJK': 'TJ',
		'TKL': 'TK',
		'TKM': 'TM',
		'TLS': 'TL',
		'TON': 'TO',
		'TTO': 'TT',
		'TUN': 'TN',
		'TUR': 'TR',
		'TUV': 'TV',
		'TWN': 'TW',
		'TZA': 'TZ',
		'UGA': 'UG',
		'UKR': 'UA',
		'UMI': 'UM',
		'URY': 'UY',
		'USA': 'US',
		'UZB': 'UZ',
		'VAT': 'VA',
		'VCT': 'VC',
		'VEN': 'VE',
		'VGB': 'VG',
		'VIR': 'VI',
		'VNM': 'VN',
		'VUT': 'VU',
		'WLF': 'WF',
		'WSM': 'WS',
		'YEM': 'YE',
		'ZAF': 'ZA',
		'ZMB': 'ZM',
		'ZWE': 'ZW'
	},
	'columns': {
		'people_vaccinated': 'total_vaccinated',
		'people_fully_vaccinated': 'fully_vaccinated',
		'total_vaccinations': 'vaccine_doses',
		'total_cases': 'cases',
		'total_deaths': 'deaths',
		'people_vaccinated_per_hundred': 'percent_vaccinated',
		'people_fully_vaccinated_per_hundred': 'percent_fully_vaccinated',
		'total_deaths_per_million': 'deaths_per_million',
		'population': 'population'
	}
}

# Login
def login(api, session, credentials):
  token_params = {
      'action': 'query',
      'meta': 'tokens',
      'type': 'login'
  }
  token_q = session.post(api, data=token_params).json()
  token = token_q['query']['tokens']['logintoken']
  username, password = credentials
  login_params = {
      'action': 'login',
      'lgname': username,
      'lgpassword': password,
      'lgtoken': token
  }
  login_q = session.post(api, data=login_params).json()
  return session

# Get and parse current data on page
def parse_json(api, session, page):
  params = {
      'action': 'parse',
      'prop': 'wikitext',
      'page': page
  }
  q = session.post(api, data=params).json()
  wikitext = q['parse']['wikitext']
  data = json.loads(wikitext)
  return data

def get(url):
  return pd.read_csv(url).groupby('code').max('date')

# Update with OWID data
def update_owid(owid_data, data, translator):
  for owid_code, location_data in owid_data.iterrows():
    if owid_code in translator['countries']:
      code = translator['countries'][owid_code]
      for owid_col, col in translator['columns'].items():
        if owid_col in location_data:
          if not code in data:
            data[code] = {}
          if not pd.isna(location_data[owid_col]):
            data[code][col] = location_data[owid_col]
  return data

# Old update_owid() for reference, used with:
# https://covid.ourworldindata.org/data/latest/owid-covid-latest.json
# (broken now that GitHub repo not updated)
#
# def update_owid(owid_data, data, translator):
#   for owid_code, location_data in owid_data.items():
#     if owid_code in translator['countries']:
#       for owid_col, col in translator['columns'].items():
#         if owid_col in location_data:
#           code = translator['countries'][owid_code]
#           if not code in data:
#             data[code] = {}
#           if location_data[owid_col]:
#             data[code][col] = location_data[owid_col]
#   return data

# Edit page with updated data
def edit(api, session, page, data, summary):
  text = json.dumps(data, ensure_ascii=False, sort_keys=True)
  token_params = {
      'action': 'query',
      'meta': 'tokens'
  }
  token_q = session.post(api, data=token_params).json()
  token = token_q['query']['tokens']['csrftoken']
  edit_params = {
      'action': 'edit',
      'title': page,
      'text': text,
      'summary': summary,
      'token': token
  }
  edit_q = session.post(api, data=edit_params).json()
  return edit_q

session = requests.Session()
if run:
  for lang in languages:
    login(api.format(lang), session, bot_credentials)
owid_data = get(owid_url)
for lang, lang_data in languages.items():
  data = parse_json(api.format(lang), session, lang_data['page'])
  data = update_owid(owid_data, data, owid_translator)
  if run:
    print(lang, edit(
      api.format(lang),
      session,
      lang_data['page'],
      data,
      lang_data['summary']
    ))
  else:
    print(
      '\n\n', lang, '\n',
      json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True)
    )

Licensing

edit

This work (all source code in this level 2 section) is licensed under:

Version history

edit
  • 1.0.0: Initial release. 23 Jul 2021.
  • 1.1.0: Use JSON latest data instead of CSV all data. 26 Jul 2021.
  • 1.1.1: Add percent fully vaccinated. 31 Aug 2021.
  • 1.1.2: Add deaths per million. 23 Sep 2021.
  • 1.2.0: Make compatible with multiple language Wikipedias. 15 Oct 2021.
  • 1.2.1: Add population. 16 Oct 2021.
  • 1.2.2: Cleanup code. 27 Dec 2021.
  • 1.3.0: Rewrite get() and update_owid() to handle CSV input due to GitHub dataset deprecation. 21 Sep 2024.