parent
b25ae9e354
commit
c1234449f7
44
experimentations/get_mobilizon.py
Normal file
44
experimentations/get_mobilizon.py
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/python3
|
||||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
|
||||
# getting the name of the directory
|
||||
# where the this file is present.
|
||||
current = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Getting the parent directory name
|
||||
# where the current directory is present.
|
||||
parent = os.path.dirname(current)
|
||||
|
||||
# adding the parent directory to
|
||||
# the sys.path.
|
||||
sys.path.append(parent)
|
||||
sys.path.append(parent + "/src")
|
||||
|
||||
from src.agenda_culturel.import_tasks.downloader import *
|
||||
from src.agenda_culturel.import_tasks.extractor import *
|
||||
from src.agenda_culturel.import_tasks.importer import *
|
||||
from src.agenda_culturel.import_tasks.custom_extractors import *
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
u2e = URL2Events(SimpleDownloader(), mobilizon.CExtractor())
|
||||
url = "https://mobilizon.fr/@attac63/events?"
|
||||
url_human = "htthttps://mobilizon.fr/@attac63/events"
|
||||
|
||||
try:
|
||||
events = u2e.process(url, url_human, cache = "cache-attac63.html", default_values = {}, published = True)
|
||||
|
||||
exportfile = "events-attac63.json"
|
||||
print("Saving events to file {}".format(exportfile))
|
||||
with open(exportfile, "w") as f:
|
||||
json.dump(events, f, indent=4, default=str)
|
||||
except Exception as e:
|
||||
print("Exception: " + str(e))
|
@ -160,6 +160,8 @@ def run_recurrent_import_internal(rimport, downloader, req_id):
|
||||
extractor = mille_formes.CExtractor()
|
||||
elif rimport.processor == RecurrentImport.PROCESSOR.AMISCERISES:
|
||||
extractor = amisdutempsdescerises.CExtractor()
|
||||
elif rimport.processor == RecurrentImport.PROCESSOR.MOBILIZON:
|
||||
extractor = mobilizon.CExtractor()
|
||||
else:
|
||||
extractor = None
|
||||
|
||||
|
182
src/agenda_culturel/import_tasks/custom_extractors/mobilizon.py
Normal file
182
src/agenda_culturel/import_tasks/custom_extractors/mobilizon.py
Normal file
@ -0,0 +1,182 @@
|
||||
from ..extractor import *
|
||||
import json
|
||||
import dateutil.parser
|
||||
from datetime import datetime, timezone
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# A class dedicated to get events from les amis du temps des cerises
|
||||
# Website https://amisdutempsdescerises.org/
|
||||
class CExtractor(Extractor):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.no_downloader = True
|
||||
|
||||
# Source code adapted from https://framagit.org/Marc-AntoineA/mobilizon-client-python
|
||||
def _request(self, body, data):
|
||||
|
||||
headers = {}
|
||||
|
||||
response = requests.post(url=self._api_end_point, json={ "query": body, "variables": data }, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
response_json = response.json()
|
||||
if 'errors' in response_json:
|
||||
raise Exception(f'Errors while requesting { body }. { str(response_json["errors"]) }')
|
||||
|
||||
return response_json['data']
|
||||
else:
|
||||
raise Exception(f'Error while requesting. Status code: { response.status_code }')
|
||||
|
||||
def _oncoming_events_number(self):
|
||||
|
||||
query = '''
|
||||
query($preferredUsername: String!, $afterDatetime: DateTime) {
|
||||
group(preferredUsername: $preferredUsername) {
|
||||
organizedEvents(afterDatetime: $afterDatetime) {
|
||||
total,
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
today = datetime.now(timezone.utc).isoformat()
|
||||
data = {
|
||||
'preferredUsername': self._group_id,
|
||||
'afterDatetime': today
|
||||
}
|
||||
r = self._request(query, data)
|
||||
return r['group']['organizedEvents']['total']
|
||||
|
||||
|
||||
def _oncoming_events(self):
|
||||
def _oncoming_events_page(page):
|
||||
query = '''
|
||||
query($preferredUsername: String!, $afterDatetime: DateTime, $page: Int) {
|
||||
group(preferredUsername: $preferredUsername) {
|
||||
organizedEvents(afterDatetime: $afterDatetime, page: $page) {
|
||||
elements {
|
||||
id,
|
||||
title,
|
||||
url,
|
||||
beginsOn,
|
||||
endsOn,
|
||||
options {
|
||||
showStartTime,
|
||||
showEndTime,
|
||||
timezone
|
||||
},
|
||||
attributedTo {
|
||||
avatar {
|
||||
url,
|
||||
}
|
||||
name,
|
||||
preferredUsername,
|
||||
},
|
||||
description,
|
||||
onlineAddress,
|
||||
physicalAddress {
|
||||
locality,
|
||||
description,
|
||||
region
|
||||
},
|
||||
tags {
|
||||
title,
|
||||
id,
|
||||
slug
|
||||
},
|
||||
picture {
|
||||
url
|
||||
},
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
today = datetime.now(timezone.utc).isoformat()
|
||||
data = {
|
||||
'preferredUsername': self._group_id,
|
||||
'afterDatetime': today,
|
||||
'page': page
|
||||
}
|
||||
r = self._request(query, data)
|
||||
return r['group']['organizedEvents']['elements']
|
||||
|
||||
number_events = self._oncoming_events_number()
|
||||
|
||||
events = []
|
||||
page = 1
|
||||
while len(events) < number_events:
|
||||
events.extend(_oncoming_events_page(page))
|
||||
page += 1
|
||||
return events
|
||||
|
||||
def extract(
|
||||
self, content, url, url_human=None, default_values=None, published=False
|
||||
):
|
||||
self.set_header(url)
|
||||
self.clear_events()
|
||||
|
||||
if "@" in url:
|
||||
|
||||
# TODO: quand on a
|
||||
# https://mobilizon.fr/@xr_clermont_ferrand@mobilizon.extinctionrebellion.fr/events
|
||||
# on doit retourner :
|
||||
# https://mobilizon.extinctionrebellion.fr/@xr_clermont_ferrand/events
|
||||
|
||||
# split url to identify server url and actor id
|
||||
elems = [x for x in url.split('/') if len(x) > 0 and x[0] == "@"]
|
||||
if len(elems) == 1:
|
||||
params = elems[0].split('@')
|
||||
if len(params) == 2:
|
||||
self._api_end_point = "https://" + urlparse(url).netloc + "/api"
|
||||
self._group_id = params[1]
|
||||
else:
|
||||
self._api_end_point = "https://" + params[2] + "/api"
|
||||
self._group_id = params[1]
|
||||
|
||||
events = self._oncoming_events()
|
||||
|
||||
|
||||
for e in events:
|
||||
title = e["title"]
|
||||
event_url = e["url"]
|
||||
image = e["picture"]["url"]
|
||||
location = e["physicalAddress"]["description"] + ', ' + e["physicalAddress"]["locality"]
|
||||
soup = BeautifulSoup(e["description"], "html.parser")
|
||||
|
||||
description = soup.text
|
||||
start = dateutil.parser.isoparse(e["beginsOn"])
|
||||
end = dateutil.parser.isoparse(e["endsOn"])
|
||||
|
||||
start_day = start.date()
|
||||
start_time = start.time() if e["options"]["showStartTime"] else None
|
||||
end_day = end.date()
|
||||
end_time = end.time() if e["options"]["showEndTime"] else None
|
||||
|
||||
|
||||
self.add_event(
|
||||
default_values,
|
||||
title,
|
||||
None,
|
||||
start_day,
|
||||
location,
|
||||
description,
|
||||
[],
|
||||
uuids=[event_url],
|
||||
recurrences=None,
|
||||
url_human=event_url,
|
||||
start_time=start_time,
|
||||
published=published,
|
||||
image=image,
|
||||
end_day=end_day,
|
||||
end_time=end_time)
|
||||
|
||||
return self.get_structure()
|
@ -20,6 +20,11 @@ class Extractor(ABC):
|
||||
self.events = []
|
||||
self.downloader = None
|
||||
self.has_2nd_method = False
|
||||
|
||||
# defined it to true in herited classes will
|
||||
# avoid the importer to use the downloader on the url
|
||||
# (used for extractors that are self-sufficient)
|
||||
self.no_downloader = False
|
||||
|
||||
# parameters used by the downloader to get the content
|
||||
self.referer = ""
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2025-02-12 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0146_alter_recurrentimport_processor'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recurrentimport',
|
||||
name='processor',
|
||||
field=models.CharField(choices=[('ical', 'ical'), ('icalnobusy', 'ical no busy'), ('icalnovc', 'ical no VC'), ('lacoope', 'lacoope.org'), ('lacomedie', 'la comédie'), ('lefotomat', 'le fotomat'), ('lapucealoreille', "la puce à l'oreille"), ('Plugin wordpress MEC', 'Plugin wordpress MEC'), ('Facebook events', "Événements d'une page FB"), ('Billetterie CF', 'Billetterie Clermont-Ferrand'), ('arachnee', 'Arachnée concert'), ('rio', 'Le Rio'), ('raymonde', 'La Raymonde'), ('apidae', 'Agenda apidae tourisme'), ('iguana', 'Agenda iguana (médiathèques)'), ('Mille formes', 'Mille formes'), ('Amis cerises', 'Les Amis du Temps des Cerises'), ('Mobilizon', 'Mobilizon')], default='ical', max_length=20, verbose_name='Processor'),
|
||||
),
|
||||
]
|
@ -2135,6 +2135,7 @@ class RecurrentImport(models.Model):
|
||||
IGUANA = 'iguana', _('Agenda iguana (médiathèques)')
|
||||
MILLEFORMES = 'Mille formes', _('Mille formes')
|
||||
AMISCERISES = 'Amis cerises', _('Les Amis du Temps des Cerises')
|
||||
MOBILIZON = 'Mobilizon', _('Mobilizon')
|
||||
|
||||
class DOWNLOADER(models.TextChoices):
|
||||
SIMPLE = "simple", _("simple")
|
||||
|
@ -45,4 +45,5 @@ django-cache-cleaner==0.1.0
|
||||
emoji==2.14.0
|
||||
django-honeypot==1.2.1
|
||||
django-autoslug==1.9.9
|
||||
django-debug-toolbar-template-profiler==2.1.0
|
||||
django-debug-toolbar-template-profiler==2.1.0
|
||||
requests==2.32.3
|
Loading…
x
Reference in New Issue
Block a user