Add ActivityPub sources for Event and Place data. Not yet using it for federation, or making use of ActivityPub actions
This commit is contained in:
parent
94961f09bb
commit
aed1dc2b14
6 changed files with 163 additions and 2 deletions
12
events/activity_pub/urls.py
Normal file
12
events/activity_pub/urls.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""
|
||||
ActivityPub compliant views
|
||||
"""
|
||||
from django.urls import path, include
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path('events.json', views.events_list, name='ap-events-list'),
|
||||
path('places.json', views.places_list, name='ap-places-list'),
|
||||
]
|
140
events/activity_pub/views.py
Normal file
140
events/activity_pub/views.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
import pytz
|
||||
from collections import Mapping, OrderedDict
|
||||
|
||||
from django.db import models
|
||||
from django.shortcuts import resolve_url
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils import timezone
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.decorators import api_view, throttle_classes
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.utils import representation
|
||||
from rest_framework.utils.serializer_helpers import ReturnDict
|
||||
|
||||
from events.models import Event, Place, Team
|
||||
|
||||
|
||||
def localized_time(dt, tz='UTC'):
|
||||
event_tz = pytz.timezone(tz)
|
||||
print("Searchable timezone: %s" % tz)
|
||||
return dt.astimezone(event_tz).strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
|
||||
class CollectionSerializer(serializers.ListSerializer):
|
||||
def to_representation(self, data):
|
||||
"""
|
||||
List of object instances -> List of dicts of primitive datatypes.
|
||||
"""
|
||||
# Dealing with nested relationships, data can be a Manager,
|
||||
# so, first get a queryset from the Manager if needed
|
||||
iterable = data.all() if isinstance(data, models.Manager) else data
|
||||
|
||||
repr_data = OrderedDict({
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"summary": self.child.verbose_name_plural,
|
||||
"type": "Collection",
|
||||
"totalItems": len(iterable),
|
||||
"items": [self.child.to_representation(item) for item in iterable],
|
||||
})
|
||||
repr_data.move_to_end('@context', last=False)
|
||||
repr_data.move_to_end('items')
|
||||
return repr_data
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
ret = super(serializers.ListSerializer, self).data
|
||||
return ReturnDict(ret, serializer=self)
|
||||
|
||||
|
||||
class APGroupSerializer(serializers.ModelSerializer):
|
||||
verbose_name_plural = 'Groups'
|
||||
|
||||
context = serializers.CharField(label='context', default='https://www.w3.org/ns/activitystreams')
|
||||
type = serializers.CharField(label='type', default='Group')
|
||||
id = serializers.CharField(source='get_absolute_url')
|
||||
name = serializers.CharField(source='__str__')
|
||||
url = serializers.CharField(source='web_url')
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = CollectionSerializer
|
||||
model = Team
|
||||
fields = ('context', 'type', 'id', 'name', 'url')
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super(APGroupSerializer, self).to_representation(instance)
|
||||
data['@context'] = data['context']
|
||||
del data['context']
|
||||
data.move_to_end('@context', last=False)
|
||||
|
||||
data['id'] = 'http://%s%s' % (Site.objects.get_current().domain, data['id'])
|
||||
return data
|
||||
|
||||
|
||||
class APPlaceSerializer(serializers.ModelSerializer):
|
||||
verbose_name_plural = 'Places'
|
||||
|
||||
context = serializers.CharField(label='context', default='https://www.w3.org/ns/activitystreams')
|
||||
type = serializers.CharField(label='type', default='Place')
|
||||
id = serializers.CharField(source='get_absolute_url')
|
||||
name = serializers.CharField(source='__str__')
|
||||
latitude = serializers.DecimalField(max_digits=10, decimal_places=5)
|
||||
longitude = serializers.DecimalField(max_digits=10, decimal_places=5)
|
||||
url = serializers.URLField(source='place_url')
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = CollectionSerializer
|
||||
model = Place
|
||||
fields = ('context', 'type', 'id', 'name', 'latitude', 'longitude', 'url')
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super(APPlaceSerializer, self).to_representation(instance)
|
||||
data['@context'] = data['context']
|
||||
del data['context']
|
||||
data.move_to_end('@context', last=False)
|
||||
|
||||
data['id'] = 'http://%s%s' % (Site.objects.get_current().domain, data['id'])
|
||||
return data
|
||||
|
||||
|
||||
class APEventSerializer(serializers.ModelSerializer):
|
||||
verbose_name_plural = 'Events'
|
||||
|
||||
context = serializers.CharField( default='https://www.w3.org/ns/activitystreams')
|
||||
type = serializers.CharField(default='Event')
|
||||
id = serializers.CharField(source='get_absolute_url')
|
||||
name = serializers.CharField()
|
||||
attributedTo = APGroupSerializer(source='team')
|
||||
location = APPlaceSerializer(source='place')
|
||||
startTime = serializers.DateTimeField(source='start_time')
|
||||
endTime = serializers.DateTimeField(source='end_time')
|
||||
image = serializers.URLField(source='team.card_img_url')
|
||||
url = serializers.URLField(source='web_url')
|
||||
published = serializers.DateTimeField(source='created_time')
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = CollectionSerializer
|
||||
model = Event
|
||||
fields = ('context', 'type', 'id', 'name', 'startTime', 'endTime', 'attributedTo', 'location', 'image', 'url', 'published')
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super(APEventSerializer, self).to_representation(instance)
|
||||
data['@context'] = data['context']
|
||||
del data['context']
|
||||
data.move_to_end('@context', last=False)
|
||||
|
||||
if data['image'].startswith('/'):
|
||||
data['image'] = '/'.join(data['id'].split('/')[:3]) + data['image']
|
||||
|
||||
data['id'] = 'http://%s%s' % (Site.objects.get_current().domain, data['id'])
|
||||
return data
|
||||
|
||||
@api_view(['GET'])
|
||||
def events_list(request):
|
||||
serializer = APEventSerializer(Event.objects.filter(end_time__gte=timezone.now()), many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@api_view(['GET'])
|
||||
def places_list(request):
|
||||
serializer = APPlaceSerializer(Place.objects.all(), many=True)
|
||||
return Response(serializer.data)
|
|
@ -33,6 +33,9 @@ class Place(models.Model):
|
|||
place_url = models.URLField(help_text=_('URL for the Place Homepage'), verbose_name=_('URL of the Place'), max_length=200, blank=True, null=True)
|
||||
cover_img = models.URLField(_("Place photo"), null=True, blank=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('show-place', kwargs={'place_id': self.id})
|
||||
|
||||
def __str__(self):
|
||||
return u'%s, %s' % (self.name, self.city.name)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.contrib.sites.models import Site
|
|||
from django.contrib.auth.models import User, Group, AnonymousUser
|
||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.shortcuts import reverse
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
|
@ -300,6 +301,9 @@ class Team(models.Model):
|
|||
def moderators(self):
|
||||
return [member.user for member in Member.objects.filter(team=self, role__in=(Member.ADMIN, Member.MODERATOR))]
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('show-team', kwargs={'team_id': self.id})
|
||||
|
||||
def __str__(self):
|
||||
return u'%s' % (self.name)
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class Searchable(models.Model):
|
|||
@property
|
||||
def local_start_time(self, val=None):
|
||||
if val is not None:
|
||||
self.start_time = val.astimezone(python.utc)
|
||||
self.start_time = val.astimezone(pytz.utc)
|
||||
else:
|
||||
if self.start_time is None:
|
||||
return None
|
||||
|
@ -46,7 +46,7 @@ class Searchable(models.Model):
|
|||
@property
|
||||
def local_end_time(self, val=None):
|
||||
if val is not None:
|
||||
self.end_time = val.astimezone(python.utc)
|
||||
self.end_time = val.astimezone(pytz.utc)
|
||||
else:
|
||||
if self.end_time is None:
|
||||
return None
|
||||
|
|
|
@ -110,6 +110,8 @@ urlpatterns = [
|
|||
|
||||
path('oauth/', include('social_django.urls', namespace='social')),
|
||||
|
||||
path('activity_pub/', include('events.activity_pub.urls')),
|
||||
|
||||
path('<str:team_slug>/', views.show_team_by_slug, name='show-team-by-slug'),
|
||||
]
|
||||
if settings.DEBUG:
|
||||
|
|
Loading…
Reference in a new issue