pageviews: Séparation script/module & session
This commit is contained in:
parent
5f7a3ddf69
commit
adba50d786
|
@ -1,3 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
__pycache__
|
||||||
.cache
|
.cache
|
||||||
dist
|
dist
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,18 @@
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
|
||||||
|
# Session de requêtage HTTP
|
||||||
|
session = requests.Session()
|
||||||
|
|
||||||
|
# Réalise 10 essais de récupération de chaque page, en augmentant
|
||||||
|
# progressivement le délai entre chaque essai
|
||||||
|
retry = urllib3.util.Retry(total=10, backoff_factor=0.3)
|
||||||
|
adapter = requests.adapters.HTTPAdapter(max_retries=retry)
|
||||||
|
session.mount('http', adapter)
|
||||||
|
session.mount('https', adapter)
|
||||||
|
|
||||||
|
# Identification du robot
|
||||||
|
session.headers['User-Agent'] = (
|
||||||
|
'WikimedicaDiseaseSearch/0.1 '
|
||||||
|
'(https://gitlab.com/matteodelabre/wikimedica-disease-search)'
|
||||||
|
)
|
|
@ -0,0 +1,56 @@
|
||||||
|
from .http import session
|
||||||
|
import mwclient
|
||||||
|
|
||||||
|
|
||||||
|
def instanciate(site):
|
||||||
|
"""
|
||||||
|
Instancie un objet permettant d’accéder à l’API d’un site MediaWiki.
|
||||||
|
|
||||||
|
:param site: Nom de domaine du site.
|
||||||
|
:return: Instance mwclient.Site pour le site.
|
||||||
|
"""
|
||||||
|
return mwclient.Site(site, pool=session)
|
||||||
|
|
||||||
|
|
||||||
|
def article_canonical(site, article):
|
||||||
|
"""
|
||||||
|
Obtient le nom canonique d’un article après avoir suivi les redirections.
|
||||||
|
|
||||||
|
:param site: Instance mwclient.Site pour le site ciblé.
|
||||||
|
:param article: Article ciblé dans le site.
|
||||||
|
:return: Nom canonique de l’article.
|
||||||
|
"""
|
||||||
|
original_page = site.pages[article]
|
||||||
|
|
||||||
|
if not original_page.exists:
|
||||||
|
raise Exception(
|
||||||
|
'Article « {} » inexistant sur {}'
|
||||||
|
.format(article, site.host)
|
||||||
|
)
|
||||||
|
|
||||||
|
return original_page.resolve_redirect().name
|
||||||
|
|
||||||
|
|
||||||
|
def article_redirects(site, article):
|
||||||
|
"""
|
||||||
|
Récupère l’ensemble des pages redirigeant vers un article.
|
||||||
|
|
||||||
|
:param site: Instance mwclient.Site pour le site ciblé.
|
||||||
|
:param article: Article ciblé dans le site.
|
||||||
|
:return: Liste des pages redirigeant vers `article`.
|
||||||
|
"""
|
||||||
|
response = site.api('query', prop='redirects', titles=article)
|
||||||
|
page_response = list(response['query']['pages'].values())[0]
|
||||||
|
|
||||||
|
if 'missing' in page_response:
|
||||||
|
raise Exception(
|
||||||
|
'Article « {} » inexistant sur {}'
|
||||||
|
.format(article, site.host)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Si la réponse n’inclut pas la clé « redirects », c’est qu’il n’existe
|
||||||
|
# aucune redirection vers la page
|
||||||
|
return [
|
||||||
|
item['title'] for item in
|
||||||
|
page_response['redirects']
|
||||||
|
] if 'redirects' in page_response else []
|
|
@ -0,0 +1,137 @@
|
||||||
|
import collections
|
||||||
|
from datetime import datetime
|
||||||
|
from .http import session
|
||||||
|
import numpy
|
||||||
|
from scipy import stats
|
||||||
|
|
||||||
|
# Chemin racine pour les API Wikimedia
|
||||||
|
wikimedia_base_path = 'https://wikimedia.org/api/rest_v1'
|
||||||
|
|
||||||
|
# Patron d’accès à l’API pageviews de Wikimedia
|
||||||
|
wikimedia_pageviews_path = '/' + '/'.join([
|
||||||
|
'metrics', 'pageviews', 'per-article', '{project}',
|
||||||
|
'{access}', '{agent}', '{article}', '{granularity}',
|
||||||
|
'{start}', '{end}'
|
||||||
|
])
|
||||||
|
|
||||||
|
# Format de dates utilisée pour l’API Wikimedia
|
||||||
|
wikimedia_date_format = '%Y%m%d'
|
||||||
|
|
||||||
|
# Date de première disponibilité des pageviews sur l’API Wikimedia
|
||||||
|
wikimedia_pageviews_start = datetime(2015, 7, 1)
|
||||||
|
|
||||||
|
# Tableau contenant tous les jours de l’année de 1 à 365
|
||||||
|
year_all_days = numpy.arange(1, 366)
|
||||||
|
|
||||||
|
|
||||||
|
def _year_date_distance(a, b):
|
||||||
|
"""
|
||||||
|
Calcule la distance entre deux jours de l’année.
|
||||||
|
|
||||||
|
:example: _year_date_distance(10, 360) == 15
|
||||||
|
:example: _year_date_distance(numpy.array([10, 182, 355]), 182)
|
||||||
|
== [172, 0, 173]
|
||||||
|
:param a: Première valeur (peut être un tableau numpy).
|
||||||
|
:param b: Seconde valeur (peut être un tableau numpy).
|
||||||
|
:return: Valeur de la distance.
|
||||||
|
"""
|
||||||
|
return numpy.stack((
|
||||||
|
numpy.mod(a - b, len(year_all_days)),
|
||||||
|
numpy.mod(b - a, len(year_all_days))
|
||||||
|
)).min(axis=0)
|
||||||
|
|
||||||
|
|
||||||
|
def smooth(views, scale):
|
||||||
|
"""
|
||||||
|
Lisse un tableau de valeurs contenant une valeur par jour de l’année en
|
||||||
|
utilisant un noyau gaussien.
|
||||||
|
|
||||||
|
À la bordure de fin ou de début d’année, le lissage est fait avec l’autre
|
||||||
|
morceau de l’année.
|
||||||
|
|
||||||
|
:param views: Données à lisser.
|
||||||
|
:param scale: Variance du noyau gaussien.
|
||||||
|
:return: Données lissées.
|
||||||
|
"""
|
||||||
|
ref_pdf = stats.norm.pdf(
|
||||||
|
_year_date_distance(year_all_days, 1),
|
||||||
|
scale=scale
|
||||||
|
)
|
||||||
|
|
||||||
|
pdf_matrix = numpy.stack([
|
||||||
|
numpy.roll(ref_pdf, day - 1)
|
||||||
|
for day in year_all_days
|
||||||
|
])
|
||||||
|
|
||||||
|
return pdf_matrix.dot(views)
|
||||||
|
|
||||||
|
|
||||||
|
def get(project, article):
|
||||||
|
"""
|
||||||
|
Obtient le nombre de visites sur une page Wikipédia par jour.
|
||||||
|
|
||||||
|
:param project: Projet Wikipédia ciblé.
|
||||||
|
:param article: Article ciblé dans le site.
|
||||||
|
:return: Compteur associant chaque jour à son nombre de visites.
|
||||||
|
"""
|
||||||
|
# Soumet une requête à l’API REST pour obtenir les vues de l’article
|
||||||
|
res = session.get(wikimedia_base_path + wikimedia_pageviews_path.format(
|
||||||
|
project=project,
|
||||||
|
article=article,
|
||||||
|
access='all-access',
|
||||||
|
agent='user',
|
||||||
|
granularity='daily',
|
||||||
|
start=wikimedia_pageviews_start.strftime(wikimedia_date_format),
|
||||||
|
end=datetime.today().strftime(wikimedia_date_format)
|
||||||
|
))
|
||||||
|
|
||||||
|
data = res.json()
|
||||||
|
|
||||||
|
# Vérifie que la réponse reçue indique un succès
|
||||||
|
if res.status_code != 200:
|
||||||
|
if 'detail' in data:
|
||||||
|
detail = data['detail']
|
||||||
|
message = ', '.join(detail) if type(detail) == list else detail
|
||||||
|
raise Exception(message)
|
||||||
|
else:
|
||||||
|
raise Exception('Erreur {}'.format(res.status_code))
|
||||||
|
|
||||||
|
# Construit le dictionnaire résultant
|
||||||
|
return collections.Counter(dict(
|
||||||
|
(record['timestamp'][:8], record['views'])
|
||||||
|
for record in data['items']
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def mean(views):
|
||||||
|
"""
|
||||||
|
Calcule les vues moyennes par jour de l’année à partir d’un ensemble de
|
||||||
|
vues enregistrées (omettant le 29 février pour les années bissextiles).
|
||||||
|
|
||||||
|
:param views: Vues enregistrées par date.
|
||||||
|
:return: Tableau de taille 365 contenant le nombre de visites moyennes pour
|
||||||
|
chaque jour de l’année.
|
||||||
|
"""
|
||||||
|
# Fait la moyenne pour chaque jour hormis le 29 février
|
||||||
|
accumulator = {}
|
||||||
|
datemonth_format = '%m%d'
|
||||||
|
|
||||||
|
for day in range(1, 366):
|
||||||
|
datemonth = datetime.fromordinal(day).strftime(datemonth_format)
|
||||||
|
accumulator[datemonth] = []
|
||||||
|
|
||||||
|
for date_str, views in views.items():
|
||||||
|
date = datetime.strptime(date_str, wikimedia_date_format)
|
||||||
|
|
||||||
|
if not (date.month == 2 and date.day == 29):
|
||||||
|
datemonth = date.strftime(datemonth_format)
|
||||||
|
accumulator[datemonth].append(views)
|
||||||
|
|
||||||
|
for datemonth, value in accumulator.items():
|
||||||
|
accumulator[datemonth] = sum(value) / len(value) if value else 0
|
||||||
|
|
||||||
|
# Rassemble les valeurs moyennes pour chaque jour dans l'ordre de l'année
|
||||||
|
return [item[1] for item in sorted(
|
||||||
|
list(accumulator.items()),
|
||||||
|
key=lambda x: x[0]
|
||||||
|
)]
|
|
@ -0,0 +1,91 @@
|
||||||
|
import calendar
|
||||||
|
import collections
|
||||||
|
from datetime import datetime
|
||||||
|
from fetch import wikipedia_pageviews, mediawiki_api
|
||||||
|
import logging
|
||||||
|
import matplotlib
|
||||||
|
from matplotlib import pyplot
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Objet pour afficher le journal d’exécution
|
||||||
|
logger = logging.getLogger('pageviews')
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
if len(sys.argv) < 4:
|
||||||
|
print("""Utilisation : {} [project] [output] [article]...
|
||||||
|
Représente sur un graphe les statistiques moyenne de vue de pages wiki.
|
||||||
|
|
||||||
|
Paramètres :
|
||||||
|
|
||||||
|
project Projet Wikipédia ciblé (par exemple fr.wikipedia.org).
|
||||||
|
output Nom du fichier de sortie où sera sauvé le graphe, ou '-' pour
|
||||||
|
afficher le résultat à l’écran.
|
||||||
|
article Nom(s) d’article(s) Wikipédia ciblé(s).
|
||||||
|
|
||||||
|
Au moins un article doit être donné. Le nombre de visites est lissé avec un
|
||||||
|
noyau gaussien d’écart-type 10 jours. Les redirections d’article sont suivies
|
||||||
|
et toute visite sur une page de redirection pointant vers l’article est
|
||||||
|
dénombrée comme une visite sur la page canonique.
|
||||||
|
""".format(sys.argv[0]), end='', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
project = sys.argv[1]
|
||||||
|
output = sys.argv[2]
|
||||||
|
articles = sys.argv[3:]
|
||||||
|
|
||||||
|
site = mediawiki_api.instanciate(project)
|
||||||
|
output_to_file = output != '-'
|
||||||
|
|
||||||
|
if output_to_file:
|
||||||
|
matplotlib.use('pgf')
|
||||||
|
matplotlib.rcParams.update({
|
||||||
|
'pgf.texsystem': 'xelatex',
|
||||||
|
'font.family': 'serif',
|
||||||
|
'text.usetex': True,
|
||||||
|
'pgf.rcfonts': False,
|
||||||
|
})
|
||||||
|
|
||||||
|
fig = pyplot.figure(figsize=(4.7, 3.3))
|
||||||
|
ax = fig.add_subplot(111)
|
||||||
|
|
||||||
|
ax.set_xlabel('Jours de l’année')
|
||||||
|
ax.set_xticks([
|
||||||
|
datetime(1, month, 1).toordinal()
|
||||||
|
for month in range(1, 13)
|
||||||
|
])
|
||||||
|
|
||||||
|
ax.set_xticklabels(calendar.month_abbr[1:13])
|
||||||
|
|
||||||
|
for article in articles:
|
||||||
|
canonical = mediawiki_api.article_canonical(site, article)
|
||||||
|
|
||||||
|
if article != canonical:
|
||||||
|
logger.info(
|
||||||
|
'Suivi de la redirection de « {} » en « {} »'
|
||||||
|
.format(article, canonical)
|
||||||
|
)
|
||||||
|
|
||||||
|
del article
|
||||||
|
|
||||||
|
redirects = mediawiki_api.article_redirects(site, canonical)
|
||||||
|
mean_views = wikipedia_pageviews.mean(sum(
|
||||||
|
(wikipedia_pageviews.get(project, page)
|
||||||
|
for page in redirects + [canonical]),
|
||||||
|
start=collections.Counter()
|
||||||
|
))
|
||||||
|
|
||||||
|
ax.plot(
|
||||||
|
wikipedia_pageviews.smooth(mean_views, 10),
|
||||||
|
label=canonical
|
||||||
|
)
|
||||||
|
|
||||||
|
ax.set_ylabel('Vues par jour')
|
||||||
|
|
||||||
|
fig.legend(framealpha=1)
|
||||||
|
fig.autofmt_xdate()
|
||||||
|
fig.tight_layout()
|
||||||
|
|
||||||
|
if output_to_file:
|
||||||
|
pyplot.savefig(output)
|
||||||
|
else:
|
||||||
|
pyplot.show()
|
|
@ -1,282 +0,0 @@
|
||||||
import calendar
|
|
||||||
import collections
|
|
||||||
from datetime import datetime
|
|
||||||
import logging
|
|
||||||
import matplotlib
|
|
||||||
import numpy
|
|
||||||
from matplotlib import pyplot
|
|
||||||
import requests
|
|
||||||
from scipy import stats
|
|
||||||
import sys
|
|
||||||
import mwclient
|
|
||||||
|
|
||||||
|
|
||||||
# Chemin racine pour les API Wikimedia
|
|
||||||
wikimedia_base_path = 'https://wikimedia.org/api/rest_v1'
|
|
||||||
|
|
||||||
# Patron d’accès à l’API pageviews de Wikimedia
|
|
||||||
wikimedia_pageviews_path = '/' + '/'.join([
|
|
||||||
'metrics', 'pageviews', 'per-article', '{project}',
|
|
||||||
'{access}', '{agent}', '{article}', '{granularity}',
|
|
||||||
'{start}', '{end}'
|
|
||||||
])
|
|
||||||
|
|
||||||
# Format de dates utilisée pour l’API Wikimedia
|
|
||||||
wikimedia_date_format = '%Y%m%d'
|
|
||||||
|
|
||||||
# Date de première disponibilité des pageviews sur l’API Wikimedia
|
|
||||||
wikimedia_pageviews_start = datetime(2015, 7, 1)
|
|
||||||
|
|
||||||
# Objet pour afficher le journal d’exécution
|
|
||||||
logger = logging.getLogger('pageviews')
|
|
||||||
|
|
||||||
# Tableau contenant tous les jours de l’année de 1 à 365
|
|
||||||
year_all_days = numpy.arange(1, 366)
|
|
||||||
|
|
||||||
|
|
||||||
def year_date_distance(a, b):
|
|
||||||
"""
|
|
||||||
Calcule la distance entre deux jours de l’année.
|
|
||||||
|
|
||||||
:example: year_date_distance(10, 360) == 15
|
|
||||||
:example: year_date_distance(numpy.array([10, 182, 355]), 182)
|
|
||||||
== [172, 0, 173]
|
|
||||||
:param a: Première valeur (peut être un tableau numpy).
|
|
||||||
:param b: Seconde valeur (peut être un tableau numpy).
|
|
||||||
:return: Valeur de la distance.
|
|
||||||
"""
|
|
||||||
return numpy.stack((
|
|
||||||
numpy.mod(a - b, len(year_all_days)),
|
|
||||||
numpy.mod(b - a, len(year_all_days))
|
|
||||||
)).min(axis=0)
|
|
||||||
|
|
||||||
|
|
||||||
def year_smooth_gaussian(data, scale):
|
|
||||||
"""
|
|
||||||
Lisse un tableau de valeurs contenant une valeur par jour de l’année en
|
|
||||||
utilisant un noyau gaussien.
|
|
||||||
|
|
||||||
À la bordure de fin ou de début d’année, le lissage est fait avec l’autre
|
|
||||||
morceau de l’année.
|
|
||||||
|
|
||||||
:param data: Données à lisser.
|
|
||||||
:param scale: Variance du noyau gaussien.
|
|
||||||
:return: Données lissées.
|
|
||||||
"""
|
|
||||||
ref_pdf = stats.norm.pdf(year_date_distance(year_all_days, 1), scale=scale)
|
|
||||||
pdf_matrix = numpy.stack([
|
|
||||||
numpy.roll(ref_pdf, day - 1)
|
|
||||||
for day in year_all_days
|
|
||||||
])
|
|
||||||
return pdf_matrix.dot(data)
|
|
||||||
|
|
||||||
|
|
||||||
def wikimedia_page_views(site, article):
|
|
||||||
"""
|
|
||||||
Obtient le nombre de visites sur une page Wikipédia par jour.
|
|
||||||
|
|
||||||
:param site: Site Wikipédia ciblé.
|
|
||||||
:param article: Article ciblé dans le site.
|
|
||||||
:return: Compteur associant chaque jour à son nombre de visites.
|
|
||||||
"""
|
|
||||||
# Soumet une requête à l’API REST pour obtenir les vues de l’article
|
|
||||||
res = requests.get(wikimedia_base_path + wikimedia_pageviews_path.format(
|
|
||||||
project=site.host,
|
|
||||||
article=article,
|
|
||||||
access='all-access',
|
|
||||||
agent='user',
|
|
||||||
granularity='daily',
|
|
||||||
start=wikimedia_pageviews_start.strftime(wikimedia_date_format),
|
|
||||||
end=datetime.today().strftime(wikimedia_date_format)
|
|
||||||
))
|
|
||||||
|
|
||||||
data = res.json()
|
|
||||||
|
|
||||||
# Vérifie que la réponse reçue indique un succès
|
|
||||||
if res.status_code != 200:
|
|
||||||
if 'detail' in data:
|
|
||||||
detail = data['detail']
|
|
||||||
message = ', '.join(detail) if type(detail) == list else detail
|
|
||||||
raise Exception(message)
|
|
||||||
else:
|
|
||||||
raise Exception('Erreur {}'.format(res.status_code))
|
|
||||||
|
|
||||||
# Construit le dictionnaire résultant
|
|
||||||
return collections.Counter(dict(
|
|
||||||
(record['timestamp'][:8], record['views'])
|
|
||||||
for record in data['items']
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
def wikimedia_article_canonical_name(site, article):
|
|
||||||
"""
|
|
||||||
Obtient le nom canonique d’un article après avoir suivi les redirections.
|
|
||||||
|
|
||||||
:param site: Objet empaquetant l’API du site Wikipédia ciblé.
|
|
||||||
:param article: Article ciblé dans le site.
|
|
||||||
:return: Nom canonique de l’article.
|
|
||||||
"""
|
|
||||||
original_page = site.pages[article]
|
|
||||||
|
|
||||||
if not original_page.exists:
|
|
||||||
raise Exception(
|
|
||||||
'Article « {} » inexistant sur {}'
|
|
||||||
.format(article, site.host)
|
|
||||||
)
|
|
||||||
|
|
||||||
return original_page.resolve_redirect().name
|
|
||||||
|
|
||||||
|
|
||||||
def wikimedia_article_views(site, article):
|
|
||||||
"""
|
|
||||||
Obtient le nombre de visites sur un article Wikipédia, incluant la page
|
|
||||||
canonique et toutes les pages redirigées vers celle-ci, par jour.
|
|
||||||
|
|
||||||
:param site: Objet empaquetant l’API du site Wikipédia ciblé.
|
|
||||||
:param article: Article ciblé dans le site.
|
|
||||||
:return: Liste de couples contenant d’une part un jour et d’autre part
|
|
||||||
le nombre de visites associées à ce jour.
|
|
||||||
"""
|
|
||||||
# Récupération de l’ensemble des pages qui redirigent vers la page donnée
|
|
||||||
response = site.api('query', prop='redirects', titles=article)
|
|
||||||
page_response = list(response['query']['pages'].values())[0]
|
|
||||||
|
|
||||||
if 'missing' in page_response:
|
|
||||||
raise Exception(
|
|
||||||
'Article « {} » inexistant sur {}'
|
|
||||||
.format(article, site.host)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Si la réponse n’inclut pas la clé « redirects », c’est qu’il n’existe
|
|
||||||
# aucune redirection vers la page
|
|
||||||
redirects = [
|
|
||||||
item['title'] for item in
|
|
||||||
page_response['redirects']
|
|
||||||
] if 'redirects' in page_response else []
|
|
||||||
|
|
||||||
# Somme le nombre de visites sur chacune des pages
|
|
||||||
return sum(
|
|
||||||
(wikimedia_page_views(site, page)
|
|
||||||
for page in redirects + [article]),
|
|
||||||
start=collections.Counter()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def wikimedia_mean_article_views(site, article):
|
|
||||||
"""
|
|
||||||
Obtient des statistiques moyennes sur les vues d’un article Wikipédia par
|
|
||||||
jour de l’année (omettant le 29 février pour les années bissextiles).
|
|
||||||
|
|
||||||
:param site: Objet empaquetant l’API du site Wikipédia ciblé.
|
|
||||||
:param article: Article ciblé dans le site.
|
|
||||||
:return: Tableau de taille 365 contenant le nombre de visites moyennes pour
|
|
||||||
chaque jour de l’année.
|
|
||||||
"""
|
|
||||||
data = wikimedia_article_views(site, article)
|
|
||||||
|
|
||||||
# Fait la moyenne pour chaque jour hormis le 29 février
|
|
||||||
accumulator = {}
|
|
||||||
datemonth_format = '%m%d'
|
|
||||||
|
|
||||||
for day in range(1, 366):
|
|
||||||
datemonth = datetime.fromordinal(day).strftime(datemonth_format)
|
|
||||||
accumulator[datemonth] = []
|
|
||||||
|
|
||||||
for date_str, views in data.items():
|
|
||||||
date = datetime.strptime(date_str, wikimedia_date_format)
|
|
||||||
|
|
||||||
if not (date.month == 2 and date.day == 29):
|
|
||||||
datemonth = date.strftime(datemonth_format)
|
|
||||||
accumulator[datemonth].append(views)
|
|
||||||
|
|
||||||
for datemonth, value in accumulator.items():
|
|
||||||
accumulator[datemonth] = sum(value) / len(value) if value else 0
|
|
||||||
|
|
||||||
# Rassemble les valeurs moyennes pour chaque jour dans l'ordre de l'année
|
|
||||||
return [item[1] for item in sorted(
|
|
||||||
list(accumulator.items()),
|
|
||||||
key=lambda x: x[0]
|
|
||||||
)]
|
|
||||||
|
|
||||||
|
|
||||||
def create_year_plot():
|
|
||||||
"""
|
|
||||||
Initialise un graphe avec en abscisse les 365 jours d’une année non
|
|
||||||
bissextile.
|
|
||||||
|
|
||||||
:return: Figure et axes Matplotlib.
|
|
||||||
"""
|
|
||||||
fig = pyplot.figure(figsize=(4.7, 3.3))
|
|
||||||
ax = fig.add_subplot(111)
|
|
||||||
|
|
||||||
ax.set_xlabel('Jours de l’année')
|
|
||||||
ax.set_xticks([
|
|
||||||
datetime(1, month, 1).toordinal()
|
|
||||||
for month in range(1, 13)
|
|
||||||
])
|
|
||||||
|
|
||||||
ax.set_xticklabels(calendar.month_abbr[1:13])
|
|
||||||
return fig, ax
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
if len(sys.argv) < 4:
|
|
||||||
print("""Utilisation : {} [project] [output] [article]...
|
|
||||||
Obtient les statistiques moyenne de vue de pages wiki.
|
|
||||||
|
|
||||||
Paramètres :
|
|
||||||
|
|
||||||
project Projet Wikipédia ciblé (par exemple fr.wikipedia.org).
|
|
||||||
output Nom du fichier de sortie où sera sauvé le graphe, ou '-' pour
|
|
||||||
afficher le résultat à l’écran.
|
|
||||||
article Nom(s) d’article(s) Wikipédia ciblé(s).
|
|
||||||
|
|
||||||
Au moins un article doit être donné. Le nombre de visites est lissé avec un
|
|
||||||
noyau gaussien d’écart-type 10 jours. Les redirections d’article sont suivies
|
|
||||||
et toute visite sur une page de redirection pointant vers l’article est
|
|
||||||
dénombrée comme une visite sur la page canonique.
|
|
||||||
""".format(sys.argv[0]), end='', file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
project = sys.argv[1]
|
|
||||||
output = sys.argv[2]
|
|
||||||
articles = sys.argv[3:]
|
|
||||||
|
|
||||||
site = mwclient.Site(project)
|
|
||||||
output_to_file = output != '-'
|
|
||||||
|
|
||||||
if output_to_file:
|
|
||||||
matplotlib.use('pgf')
|
|
||||||
matplotlib.rcParams.update({
|
|
||||||
'pgf.texsystem': 'xelatex',
|
|
||||||
'font.family': 'serif',
|
|
||||||
'text.usetex': True,
|
|
||||||
'pgf.rcfonts': False,
|
|
||||||
})
|
|
||||||
|
|
||||||
fig, ax = create_year_plot()
|
|
||||||
|
|
||||||
for article in articles:
|
|
||||||
canonical_article = wikimedia_article_canonical_name(site, article)
|
|
||||||
|
|
||||||
if article != canonical_article:
|
|
||||||
logger.info(
|
|
||||||
'Suivi de la redirection de « {} » en « {} »'
|
|
||||||
.format(article, canonical_article)
|
|
||||||
)
|
|
||||||
|
|
||||||
data = wikimedia_mean_article_views(site, canonical_article)
|
|
||||||
ax.plot(year_smooth_gaussian(data, 10), label=canonical_article)
|
|
||||||
|
|
||||||
ax.set_ylabel('Vues par jour')
|
|
||||||
|
|
||||||
fig.legend(framealpha=1)
|
|
||||||
fig.autofmt_xdate()
|
|
||||||
fig.tight_layout()
|
|
||||||
|
|
||||||
if output_to_file:
|
|
||||||
pyplot.savefig(output)
|
|
||||||
else:
|
|
||||||
pyplot.show()
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import calendar
|
||||||
|
import collections
|
||||||
|
from datetime import datetime
|
||||||
|
from fetch import wikipedia_pageviews, mediawiki_api
|
||||||
|
import logging
|
||||||
|
import matplotlib
|
||||||
|
from matplotlib import pyplot
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Objet pour afficher le journal d’exécution
|
||||||
|
logger = logging.getLogger('pageviews')
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
if len(sys.argv) < 4:
|
||||||
|
print("""Utilisation : {} [project] [output] [article]...
|
||||||
|
Représente sur un graphe les statistiques moyennes de vues de pages wiki.
|
||||||
|
|
||||||
|
Paramètres :
|
||||||
|
|
||||||
|
project Projet Wikipédia ciblé (par exemple fr.wikipedia.org).
|
||||||
|
output Nom du fichier de sortie où sera sauvé le graphe, ou '-' pour
|
||||||
|
afficher le résultat à l’écran.
|
||||||
|
article Nom(s) d’article(s) Wikipédia ciblé(s).
|
||||||
|
|
||||||
|
Au moins un article doit être donné. Le nombre de visites est lissé avec un
|
||||||
|
noyau gaussien d’écart-type 10 jours. Les redirections d’article sont suivies
|
||||||
|
et toute visite sur une page de redirection pointant vers l’article est
|
||||||
|
dénombrée comme une visite sur la page canonique.
|
||||||
|
""".format(sys.argv[0]), end='', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
project = sys.argv[1]
|
||||||
|
output = sys.argv[2]
|
||||||
|
articles = sys.argv[3:]
|
||||||
|
|
||||||
|
site = mediawiki_api.instanciate(project)
|
||||||
|
output_to_file = output != '-'
|
||||||
|
|
||||||
|
if output_to_file:
|
||||||
|
matplotlib.use('pgf')
|
||||||
|
matplotlib.rcParams.update({
|
||||||
|
'pgf.texsystem': 'xelatex',
|
||||||
|
'font.family': 'serif',
|
||||||
|
'text.usetex': True,
|
||||||
|
'pgf.rcfonts': False,
|
||||||
|
})
|
||||||
|
|
||||||
|
fig = pyplot.figure(figsize=(4.7, 3.3))
|
||||||
|
ax = fig.add_subplot(111)
|
||||||
|
|
||||||
|
ax.set_xlabel('Jours de l’année')
|
||||||
|
ax.set_xticks([
|
||||||
|
datetime(1, month, 1).toordinal()
|
||||||
|
for month in range(1, 13)
|
||||||
|
])
|
||||||
|
|
||||||
|
ax.set_xticklabels(calendar.month_abbr[1:13])
|
||||||
|
|
||||||
|
for article in articles:
|
||||||
|
canonical = mediawiki_api.article_canonical(site, article)
|
||||||
|
|
||||||
|
if article != canonical:
|
||||||
|
logger.info(
|
||||||
|
'Suivi de la redirection de « {} » en « {} »'
|
||||||
|
.format(article, canonical)
|
||||||
|
)
|
||||||
|
|
||||||
|
del article
|
||||||
|
|
||||||
|
redirects = mediawiki_api.article_redirects(site, canonical)
|
||||||
|
mean_views = wikipedia_pageviews.mean(sum(
|
||||||
|
(wikipedia_pageviews.get(project, page)
|
||||||
|
for page in redirects + [canonical]),
|
||||||
|
start=collections.Counter()
|
||||||
|
))
|
||||||
|
|
||||||
|
ax.plot(
|
||||||
|
wikipedia_pageviews.smooth(mean_views, 10),
|
||||||
|
label=canonical
|
||||||
|
)
|
||||||
|
|
||||||
|
ax.set_ylabel('Vues par jour')
|
||||||
|
|
||||||
|
fig.legend(framealpha=1)
|
||||||
|
fig.autofmt_xdate()
|
||||||
|
fig.tight_layout()
|
||||||
|
|
||||||
|
if output_to_file:
|
||||||
|
pyplot.savefig(output)
|
||||||
|
else:
|
||||||
|
pyplot.show()
|
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"name": "wikimedica-disease-search",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"main": "index.js",
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.7.2",
|
|
||||||
"@babel/preset-env": "^7.7.1",
|
|
||||||
"@babel/preset-react": "^7.7.0",
|
|
||||||
"parcel-bundler": "^1.12.4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"react": "^16.12.0",
|
|
||||||
"react-dom": "^16.12.0"
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue