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:
Michael Hall 2018-06-13 17:37:41 -04:00
parent 94961f09bb
commit aed1dc2b14
6 changed files with 163 additions and 2 deletions

View 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'),
]

View 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)

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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: