Removing d_k/types.py and doing the whole thing via polymorphic models.

Model names are their ActivityPub types.

As a result, Thing is renamed to Object.
2019-08-17
Marnanel Thurman 2019-08-05 15:25:20 +01:00
rodzic 6c3e8edc55
commit 31c1eb6fd5
40 zmienionych plików z 169 dodań i 734 usunięć

Wyświetl plik

@ -2,24 +2,24 @@ from django.contrib import admin
from polymorphic.admin import *
from django_kepi.models import *
class ThingChildAdmin(PolymorphicChildModelAdmin):
base_model = Thing
class ObjectChildAdmin(PolymorphicChildModelAdmin):
base_model = Object
@admin.register(Actor)
class ActorChildAdmin(ThingChildAdmin):
class ActorChildAdmin(ObjectChildAdmin):
base_model = Actor
@admin.register(Item)
class ItemChildAdmin(ThingChildAdmin):
class ItemChildAdmin(ObjectChildAdmin):
base_model = Item
@admin.register(Activity)
class ActivityChildAdmin(ThingChildAdmin):
class ActivityChildAdmin(ObjectChildAdmin):
base_model = Activity
@admin.register(Thing)
class ThingParentAdmin(PolymorphicParentModelAdmin):
base_model = Thing
@admin.register(Object)
class ObjectParentAdmin(PolymorphicParentModelAdmin):
base_model = Object
child_models = (
Actor,
Item,

Wyświetl plik

@ -1,4 +1,4 @@
import django_kepi.types as types
from django_kepi.models import *
import logging
logger = logging.getLogger(name='django_kepi')
@ -19,7 +19,7 @@ def create(
value, is_local_user, run_side_effects)
if value is None:
logger.warn(" -- it's ludicrous to create an object with no value")
logger.warn(" -- it's ludicrous to create an Object with no value")
return None
# Remove the "f_" prefix, which exists so that we can write
@ -33,40 +33,28 @@ def create(
for k,v in value.copy().items():
if not isinstance(k, str):
logger.warn('Things can only have keys which are strings: %s',
logger.warn('Objects can only have keys which are strings: %s',
str(k))
continue
if 'type' not in value:
logger.warn("Things must have a type; dropping message")
logger.warn("Objects must have a type; dropping message")
return None
value['type'] = value['type'].title()
if 'id' in value:
if is_local_user:
logger.warn('Removing "id" field at Thing creation')
logger.warn('Removing "id" field at local Object creation')
del value['id']
else:
if not is_local_user:
logger.warn("Remote things must have an id; dropping message")
logger.warn("Remote Objects must have an id; dropping message")
return None
try:
type_spec = types.ACTIVITYPUB_TYPES[value['type']]
except KeyError:
logger.info('Unknown thing type: %s; dropping message',
value['type'])
return None
if 'class' not in type_spec:
logger.info('Type %s can\'t be instantiated',
value['type'])
return None
try:
import django_kepi.models as kepi_models
cls = getattr(locals()['kepi_models'], type_spec['class'])
cls = getattr(locals()['kepi_models'], value['type'])
except KeyError:
# shouldn't happen!
logger.warn("The class '%s' wasn't exported properly",
@ -74,11 +62,7 @@ def create(
return None
logger.debug('Class for %s is %s', value['type'], cls)
########################
# XXX Check what fields we need, based on type_spec.
# XXX implement this.
del value['type']
########################

Wyświetl plik

@ -278,8 +278,8 @@ def deliver(
incoming = False,
):
try:
activity = django_kepi.models.Thing.objects.get(number=activity_id)
except django_kepi.models.Thing.DoesNotExist:
activity = django_kepi.models.Object.objects.get(number=activity_id)
except django_kepi.models.Object.DoesNotExist:
logger.warn("Can't deliver activity %s because it doesn't exist",
activity_id)
return None

Wyświetl plik

@ -1,86 +0,0 @@
import logging
from django.conf import settings
import datetime
logger = logging.getLogger(name='django_kepi')
def _embellish_Note(thing, user=None):
if 'summary' not in thing:
thing['summary'] = None
if 'sensitive' not in thing:
thing['sensitive'] = False
if 'attachment' not in thing:
thing['attachment'] = []
if 'tag' not in thing:
thing['tag'] = []
if 'to' not in thing:
thing['to'] = ['https://www.w3.org/ns/activitystreams#Public']
if 'cc' not in thing:
thing['cc'] = [user.activity_followers]
if 'attributedTo' not in thing:
thing['attributedTo'] = user.activity_id
## Conversation structure
if 'inReplyTo' not in thing:
thing['inReplyTo'] = None
# XXX Not sure about the 'conversation' tag.
# See https://github.com/tootsuite/mastodon/issues/4213 .
## Content map
if 'contentMap' not in thing and 'content' in thing:
thing['contentMap'] = {
settings.LANGUAGE_CODE: thing['content'],
}
elif 'content' not in thing and 'contentMap' in thing:
# just pick one
thing['content'] = thing['contentMap'].values()[0]
## Atom feeds
if 'atomUri' not in thing:
thing['atomUri'] = thing['url']
if 'inReplyToUri' not in thing:
thing['inReplyToAtomUri'] = thing['inReplyTo']
## All done
return thing
def embellish(thing, user=None):
if 'type' not in thing:
logger.debug('embellish: object does not contain a type: %s',
str(thing))
raise ValueError('object does not contain a type!')
f_type = thing['type']
logger.debug('embellish: received thing of type %s', f_type)
###### general embellishments
if 'id' not in thing:
logger.debug('embellish: object does not contain a type: %s',
str(thing))
raise ValueError('object does not contain an id!')
if 'url' not in thing:
thing['url'] = thing['id']
if 'published' not in thing:
thing['published'] = datetime.datetime.now().isoformat(
timespec='seconds',
)+'Z'
###### special embellishments per "type"
if f_type=='Note':
thing = _embellish_Note(thing, user)
else:
logger.warn('don\'t know how to embellish things of type %s',
f_type)
return thing

Wyświetl plik

@ -74,7 +74,7 @@ def find_local(path,
def find_remote(url,
do_not_fetch=False):
from django_kepi.models.thing import Thing
from django_kepi.models.thing import Object
logger.debug('%s: find remote', url)
@ -89,14 +89,14 @@ def find_remote(url,
# We fetched it in the past.
try:
result = Thing.objects.get(
result = Object.objects.get(
remote_url = url,
)
logger.debug('%s: already fetched, and it\'s %s',
url, result)
return result
except Thing.DoesNotExist:
except Object.DoesNotExist:
logger.debug('%s: we already know it wasn\'t there',
url)

Wyświetl plik

@ -22,7 +22,7 @@ class Command(BaseCommand):
# This can certainly be optimised a lot
count = 0
for thing in django_kepi.models.Thing.objects.all():
for thing in django_kepi.models.Object.objects.all():
usable = True
for f,v in filters:

Wyświetl plik

@ -1,111 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-02 15:53
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.CreateModel(
name='CachedRemoteText',
fields=[
('address', models.URLField(primary_key=True, serialize=False)),
('content', models.TextField(default=None, null=True)),
],
),
migrations.CreateModel(
name='Following',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('follower', models.URLField(max_length=255)),
('following', models.URLField(max_length=255)),
('pending', models.BooleanField(default=True)),
],
),
migrations.CreateModel(
name='IncomingMessage',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('received_date', models.DateTimeField(auto_now_add=True)),
('content_type', models.CharField(default='', max_length=255)),
('date', models.CharField(default='', max_length=255)),
('digest', models.CharField(default='', max_length=255)),
('host', models.CharField(default='', max_length=255)),
('path', models.CharField(default='', max_length=255)),
('signature', models.CharField(default='', max_length=255)),
('body', models.TextField(default='')),
('waiting_for', models.URLField(default=None, null=True)),
],
),
migrations.CreateModel(
name='Thing',
fields=[
('number', models.CharField(default='', max_length=8, primary_key=True, serialize=False, unique=True)),
('f_type', models.CharField(choices=[('Accept', 'Accept'), ('Add', 'Add'), ('Create', 'Create'), ('Delete', 'Delete'), ('Follow', 'Follow'), ('Like', 'Like'), ('Reject', 'Reject'), ('Remove', 'Remove'), ('Undo', 'Undo'), ('Update', 'Update')], max_length=255)),
('remote_url', models.URLField(default=None, max_length=255, null=True, unique=True)),
('f_actor', models.URLField(blank=True, max_length=255)),
('f_name', models.CharField(blank=True, max_length=255)),
('active', models.BooleanField(default=True)),
('other_fields', models.TextField(default='')),
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_django_kepi.thing_set+', to='contenttypes.ContentType')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='Activity',
fields=[
('thing_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='django_kepi.Thing')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('django_kepi.thing',),
),
migrations.CreateModel(
name='Actor',
fields=[
('thing_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='django_kepi.Thing')),
('privateKey', models.TextField()),
('publicKey', models.TextField()),
('auto_follow', models.BooleanField(default=True)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('django_kepi.thing',),
),
migrations.CreateModel(
name='Item',
fields=[
('thing_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='django_kepi.Thing')),
('f_content', models.CharField(blank=True, max_length=255)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('django_kepi.thing',),
),
migrations.CreateModel(
name='Audience',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field', models.PositiveSmallIntegerField(choices=[(1, 'audience'), (2, 'to'), (4, 'cc'), (114, 'bto'), (116, 'bcc')])),
('recipient', models.CharField(max_length=255)),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_kepi.Thing')),
],
),
]

Wyświetl plik

@ -1,22 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-03 15:59
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Mention',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('from_status', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='django_kepi.Item')),
('to_actor', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='django_kepi.Actor')),
],
),
]

Wyświetl plik

@ -1,24 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-04 14:04
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0002_mention'),
]
operations = [
migrations.CreateModel(
name='Fetch',
fields=[
('url', models.URLField(primary_key=True, serialize=False)),
('date', models.DateField(default=datetime.datetime)),
],
),
migrations.DeleteModel(
name='CachedRemoteText',
),
]

Wyświetl plik

@ -1,19 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-04 14:10
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0003_auto_20190704_1404'),
]
operations = [
migrations.AlterField(
model_name='fetch',
name='date',
field=models.DateTimeField(default=datetime.datetime),
),
]

Wyświetl plik

@ -1,24 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-08 17:51
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0004_auto_20190704_1410'),
]
operations = [
migrations.AddField(
model_name='item',
name='f_attributedTo',
field=models.CharField(blank=True, max_length=255),
),
migrations.AlterField(
model_name='fetch',
name='date',
field=models.DateTimeField(default=datetime.datetime.now),
),
]

Wyświetl plik

@ -1,18 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-09 04:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0005_auto_20190708_1751'),
]
operations = [
migrations.AlterField(
model_name='mention',
name='to_actor',
field=models.CharField(max_length=255),
),
]

Wyświetl plik

@ -1,19 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-11 11:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0006_auto_20190709_0441'),
]
operations = [
migrations.AddField(
model_name='actor',
name='f_preferredUsername',
field=models.CharField(default='', max_length=255),
preserve_default=False,
),
]

Wyświetl plik

@ -1,18 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-11 17:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0007_actor_f_preferredusername'),
]
operations = [
migrations.AlterField(
model_name='thing',
name='f_type',
field=models.CharField(choices=[('Accept', 'Accept'), ('Activity', 'Activity'), ('Actor', 'Actor'), ('Add', 'Add'), ('Announce', 'Announce'), ('Application', 'Application'), ('Article', 'Article'), ('Audio', 'Audio'), ('Create', 'Create'), ('Delete', 'Delete'), ('Document', 'Document'), ('Event', 'Event'), ('Follow', 'Follow'), ('Group', 'Group'), ('Image', 'Image'), ('Like', 'Like'), ('Note', 'Note'), ('Object', 'Object'), ('Organization', 'Organization'), ('Page', 'Page'), ('Person', 'Person'), ('Place', 'Place'), ('Profile', 'Profile'), ('Reject', 'Reject'), ('Relationship', 'Relationship'), ('Remove', 'Remove'), ('Service', 'Service'), ('Undo', 'Undo'), ('Update', 'Update'), ('Video', 'Video')], max_length=255),
),
]

Wyświetl plik

@ -1,37 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-11 17:08
from django.db import migrations, models
import django_kepi.models.thing
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0008_auto_20190711_1703'),
]
operations = [
migrations.RemoveField(
model_name='thing',
name='f_actor',
),
migrations.RemoveField(
model_name='thing',
name='f_name',
),
migrations.AlterField(
model_name='thing',
name='number',
field=models.CharField(default=django_kepi.models.thing._new_number, max_length=8, primary_key=True, serialize=False, unique=True),
),
migrations.AlterField(
model_name='thing',
name='other_fields',
field=models.TextField(blank=True, default=''),
),
migrations.AlterField(
model_name='thing',
name='remote_url',
field=models.URLField(blank=True, default=None, max_length=255, null=True, unique=True),
),
]

Wyświetl plik

@ -1,23 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-11 17:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0009_auto_20190711_1708'),
]
operations = [
migrations.AlterField(
model_name='actor',
name='privateKey',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='actor',
name='publicKey',
field=models.TextField(blank=True, null=True),
),
]

Wyświetl plik

@ -1,23 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-18 12:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0010_auto_20190711_1711'),
]
operations = [
migrations.RenameField(
model_name='actor',
old_name='privateKey',
new_name='f_privateKey',
),
migrations.RenameField(
model_name='actor',
old_name='publicKey',
new_name='f_publicKey',
),
]

Wyświetl plik

@ -1,18 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-18 13:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0011_auto_20190718_1219'),
]
operations = [
migrations.AddField(
model_name='incomingmessage',
name='is_local_user',
field=models.BooleanField(default=False),
),
]

Wyświetl plik

@ -1,18 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-21 16:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0012_incomingmessage_is_local_user'),
]
operations = [
migrations.AddField(
model_name='thing',
name='f_actor',
field=models.CharField(blank=True, default=None, max_length=255, null=True),
),
]

Wyświetl plik

@ -1,34 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-26 15:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0013_thing_f_actor'),
]
operations = [
migrations.CreateModel(
name='Collection',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.URLField(max_length=255)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='django_kepi.Actor')),
],
),
migrations.CreateModel(
name='CollectionMember',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('member', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='django_kepi.Thing')),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_kepi.Collection')),
],
),
migrations.AddConstraint(
model_name='collection',
constraint=models.UniqueConstraint(fields=('owner', 'name'), name='owner and name'),
),
]

Wyświetl plik

@ -1,18 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-26 16:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0014_auto_20190726_1515'),
]
operations = [
migrations.AddField(
model_name='incomingmessage',
name='target_collection',
field=models.CharField(default='', max_length=255),
),
]

Wyświetl plik

@ -1,17 +0,0 @@
# Generated by Django 2.2.1 on 2019-07-30 15:29
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0015_incomingmessage_target_collection'),
]
operations = [
migrations.RemoveField(
model_name='incomingmessage',
name='target_collection',
),
]

Wyświetl plik

@ -1,31 +0,0 @@
# Generated by Django 2.2.1 on 2019-08-05 00:03
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_kepi', '0016_remove_incomingmessage_target_collection'),
]
operations = [
migrations.RemoveField(
model_name='thing',
name='other_fields',
),
migrations.CreateModel(
name='ThingField',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field', models.CharField(max_length=255)),
('value', models.CharField(blank=True, default=None, max_length=255, null=True)),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='django_kepi.Thing')),
],
),
migrations.AddConstraint(
model_name='thingfield',
constraint=models.UniqueConstraint(fields=('parent', 'field'), name='parent_field'),
),
]

Wyświetl plik

@ -1,22 +1,27 @@
from .thing import Thing, ThingField
from .activity import Activity
from .actor import Actor
from .thing import Object
from .thingfield import ThingField
from .activity import Activity, Create, Update, Delete, Follow, Add, Remove, \
Like, Undo, Accept, Reject, Announce
from .actor import Actor, Application, Group, Organization, Person, Service
from .audience import Audience, AUDIENCE_FIELD_NAMES
from .following import Following
from .mention import Mention
from .item import Item
from .item import Item, Article, Audio, Document, Event, Image, Note, Page, \
Place, Profile, Relationship, Video
from .collection import Collection, CollectionMember
__all__ = [
'Thing',
'Object',
'ThingField',
'Activity',
'Actor',
'Activity', 'Create', 'Update', 'Delete', 'Follow', 'Add', 'Remove', \
'Like', 'Undo', 'Accept', 'Reject', 'Announce', 'Actor',
'Application', 'Group', 'Organization', 'Person', 'Service',
'Audience',
'AUDIENCE_FIELD_NAMES',
'Following',
'Mention',
'Item',
'Item', 'Article', 'Audio', 'Document', 'Event', 'Image', 'Note', \
'Page', 'Place', 'Profile', 'Relationship', 'Video',
'Collection',
'CollectionMember',
]

Wyświetl plik

@ -5,7 +5,7 @@ logger = logging.getLogger(name='django_kepi')
######################
class Activity(thing.Thing):
class Activity(thing.Object):
@property
def activity_form(self):
@ -38,3 +38,39 @@ class Activity(thing.Thing):
outbox = find_local(outbox_url,
object_to_store=self)
##############################
class Create(Activity):
pass
class Update(Activity):
pass
class Delete(Activity):
pass
class Follow(Activity):
pass
class Add(Activity):
pass
class Remove(Activity):
pass
class Like(Activity):
pass
class Undo(Activity):
pass
class Accept(Activity):
pass
class Reject(Activity):
pass
class Announce(Activity):
# aka "boost"
pass

Wyświetl plik

@ -8,9 +8,9 @@ logger = logging.getLogger(name='django_kepi')
######################
class Actor(thing.Thing):
class Actor(thing.Object):
"""
An Actor is a kind of Thing representing a person,
An Actor is a kind of Object representing a person,
an organisation, a bot, or anything else that can
post stuff and interact with other Actors.
@ -76,3 +76,20 @@ class Actor(thing.Thing):
return self.list_path(name)
return super().__getitem__(name)
##############################
class Application(Actor):
pass
class Group(Actor):
pass
class Organization(Actor):
pass
class Person(Actor):
pass
class Service(Actor):
pass

Wyświetl plik

@ -28,7 +28,7 @@ AUDIENCE_FIELD_NAMES = dict([(v,f) for (f,v) in FIELD_CHOICES])
class Audience(models.Model):
parent = models.ForeignKey(
'django_kepi.Thing',
'django_kepi.Object',
on_delete = models.CASCADE,
)
@ -56,7 +56,7 @@ class Audience(models.Model):
field, value):
"""
Add new Audiences for a given Thing.
Add new Audiences for a given Object.
"value" is a list of strings.
This function only adds Audiences of

Wyświetl plik

@ -133,7 +133,7 @@ class CollectionMember(models.Model):
)
member = models.ForeignKey(
'django_kepi.Thing',
'django_kepi.Object',
on_delete = models.DO_NOTHING,
)

Wyświetl plik

@ -6,7 +6,7 @@ logger = logging.getLogger(name='django_kepi')
######################
class Item(thing.Thing):
class Item(thing.Object):
f_content = models.CharField(
max_length=255,
@ -118,3 +118,40 @@ class Item(thing.Thing):
def conversation(self):
# FIXME I really don't understand conversation values
return None
##############################
class Article(Item):
pass
class Audio(Item):
pass
class Document(Item):
pass
class Event(Item):
pass
class Image(Item):
pass
class Note(Item):
# Why isn't it a subclass of Document?
pass
class Page(Item):
# i.e. a web page
pass
class Place(Item):
pass
class Profile(Item):
pass
class Relationship(Item):
pass
class Video(Item):
pass

Wyświetl plik

@ -10,22 +10,17 @@ import random
import json
import datetime
import warnings
from django_kepi.types import ACTIVITYPUB_TYPES
logger = logging.getLogger(name='django_kepi')
######################
ACTIVITY_TYPES = sorted(ACTIVITYPUB_TYPES.keys())
ACTIVITY_TYPE_CHOICES = [(x,x) for x in ACTIVITY_TYPES]
def _new_number():
return '%08x' % (random.randint(0, 0xffffffff),)
######################
class Thing(PolymorphicModel):
class Object(PolymorphicModel):
number = models.CharField(
max_length=8,
@ -34,11 +29,6 @@ class Thing(PolymorphicModel):
default=_new_number,
)
f_type = models.CharField(
max_length=255,
choices=ACTIVITY_TYPE_CHOICES,
)
f_actor = models.CharField(
max_length=255,
default=None,
@ -131,14 +121,14 @@ class Thing(PolymorphicModel):
return result
@property
def activity_type(self):
return self.f_type
def f_type(self):
return self.__class__.__name__
@property
def activity_form(self):
result = {
'id': self.url,
'type': self.get_f_type_display(),
'type': self.f_type,
}
for name in dir(self):
@ -283,7 +273,7 @@ class Thing(PolymorphicModel):
try:
self.delete()
except AssertionError:
except:
logger.info(' -- deletion failed, probably because of '+\
'https://code.djangoproject.com/ticket/23076')
@ -336,8 +326,8 @@ def _normalise_type_for_thing(v):
return v # also booleans
elif isinstance(v, list):
return v # and lists as well
elif isinstance(v, Thing):
return v.url # Things can deal with themselves
elif isinstance(v, Object):
return v.url # Objects can deal with themselves
# okay, it's something weird

Wyświetl plik

@ -26,7 +26,7 @@ class ThingField(models.Model):
)
parent = models.ForeignKey(
'django_kepi.Thing',
'django_kepi.Object',
on_delete = models.DO_NOTHING,
)

Wyświetl plik

@ -2,7 +2,6 @@ import logging
from django.conf import settings
from django_kepi.find import find
from django_kepi.delivery import deliver
from django_kepi.types import ACTIVITYPUB_TYPES
logger = logging.getLogger(name='django_kepi')
@ -85,35 +84,16 @@ def reject(activity):
def create(activity):
import django_kepi.models as kepi_models
from django_kepi.create import create as kepi_create
raw_material = dict([('f_'+f, v)
for f,v in activity['object'].items()])
if 'f_type' not in raw_material:
logger.warn('Attempt to use Create to create '+\
'something without a type. '+\
'Deleting original Create.')
if issubclass(getattr(kepi_models,
raw_material['f_type']),
kepi_models.Activity):
return False
if raw_material['f_type'] not in ACTIVITYPUB_TYPES:
logger.warn('Attempt to use Create to create '+\
'an object of type %s, which is unknown. '+\
'Deleting original Create.',
raw_material['f_type'])
return False
if 'class' not in ACTIVITYPUB_TYPES[raw_material['f_type']]:
logger.warn('Attempt to use Create to create '+\
'an object of type %s, which is abstract. '+\
'Deleting original Create.',
raw_material['f_type'])
return False
if ACTIVITYPUB_TYPES[raw_material['f_type']]['class']=='Activity':
logger.warn('Attempt to use Create to create '+\
'an object of type %s. '+\
'Create can only create non-activities. '+\

Wyświetl plik

@ -217,13 +217,13 @@ class ThingView(KepiView):
def activity_get(self, request, *args, **kwargs):
try:
logger.debug('Looking up Thing by id==%s',
logger.debug('Looking up Object by id==%s',
kwargs['id'])
activity_object = Thing.objects.get(
activity_object = Object.objects.get(
number=kwargs['id'],
)
except Thing.DoesNotExist:
except Object.DoesNotExist:
logger.info(' -- unknown: %s', kwargs)
return None
except django.core.exceptions.ValidationError:
@ -325,7 +325,7 @@ class AllUsersView(KepiView):
logger.debug('Finding all users.')
return Thing.objects.filter(f_type='Person')
return Object.objects.filter(f_type='Person')
def _modify_list_item(self, obj):
return obj.activity_form

Wyświetl plik

@ -1,5 +1,5 @@
from django.test import TestCase
from django_kepi.models import Thing
from django_kepi.models import Object
REMOTE_ID_1 = 'https://users.example.com/activity/1'
@ -9,12 +9,12 @@ SAMPLE_NOTE = {
"type": "Note",
}
class TestThing(TestCase):
class TestObject(TestCase):
def test_bad_type(self):
with self.assertRaisesMessage(ValueError, "is not a thing type"):
Thing.create(
Object.create(
f_id = REMOTE_ID_1,
f_type = "Wombat",
)
@ -22,7 +22,7 @@ class TestThing(TestCase):
def test_remote_no_id(self):
with self.assertRaisesMessage(ValueError, "Remote things must have an id"):
Thing.create(
Object.create(
f_type = "Create",
f_actor = "https://example.com/user/fred",
f_object = SAMPLE_NOTE,
@ -31,27 +31,27 @@ class TestThing(TestCase):
def test_create_create_wrong_params(self):
with self.assertRaisesMessage(ValueError, "Wrong parameters for thing type"):
Thing.create(
Object.create(
f_id = REMOTE_ID_1,
f_type = "Create",
)
with self.assertRaisesMessage(ValueError, "Wrong parameters for thing type"):
Thing.create(
Object.create(
f_id = REMOTE_ID_1,
f_actor = REMOTE_FRED,
f_type = "Create",
)
with self.assertRaisesMessage(ValueError, "Wrong parameters for thing type"):
Thing.create(
Object.create(
f_id = REMOTE_ID_1,
f_target = REMOTE_FRED,
f_type = "Create",
)
def test_create_create(self):
Thing.create(
Object.create(
f_id = REMOTE_ID_1,
f_type = "Create",
f_actor = REMOTE_FRED,
@ -59,6 +59,6 @@ class TestThing(TestCase):
)
self.assertEqual(
Thing.objects.filter(remote_url=REMOTE_ID_1).count(),
Object.objects.filter(remote_url=REMOTE_ID_1).count(),
1,
)

Wyświetl plik

@ -1,6 +1,6 @@
from django.test import TestCase, Client
from django_kepi.delivery import deliver
from django_kepi.models import Thing
from django_kepi.models import Object
import django_kepi.views
from unittest.mock import Mock, patch
from . import *
@ -22,9 +22,9 @@ REMOTE_PATH_NAMES = {
def _message_became_activity(url=ACTIVITY_ID):
try:
result = Thing.objects.get(remote_url=url)
result = Object.objects.get(remote_url=url)
return True
except Thing.DoesNotExist:
except Object.DoesNotExist:
return False
class TestDeliverTasks(TestCase):
@ -36,7 +36,7 @@ class TestDeliverTasks(TestCase):
remote_user_endpoints,
):
a = Thing.create(**activity_fields)
a = Object.create(**activity_fields)
a.save()
for who, what in remote_user_details.items():
@ -211,7 +211,7 @@ class TestDelivery(TestCase):
self._set_up_remote_request_mocks()
self._set_up_local_user_mocks()
like = Thing.create(
like = Object.create(
f_type = 'Like',
f_actor = LOCAL_ALICE,
f_object = REMOTE_FRED,

Wyświetl plik

@ -1,52 +0,0 @@
from django.test import TestCase
from unittest import skip
from django_kepi.embellish import embellish
from . import *
import logging
logger = logging.getLogger(name='django_kepi')
@skip("Decide whether we're keeping this")
class TestEmbellish(TestCase):
def test_embellish_note(self):
SOURCE = {
'id': 'https://example.com/users/fred/status/1234',
'type': 'Note',
'content': 'Hello world',
}
EXPECTING = {
'id': 'https://example.com/users/fred/status/1234',
'url': 'https://example.com/users/fred/status/1234',
'atomUri': 'https://example.com/users/fred/status/1234',
'type': 'Note',
'content': 'Hello world',
'summary': None,
"attributedTo": "https://example.com/users/fred",
"to":["https://www.w3.org/ns/activitystreams#Public"],
"cc":["https://example.com/users/fred/followers"],
"sensitive": False,
"inReplyTo": None,
"inReplyToAtomUri": None,
"contentMap":{"en-us": 'Hello world',},
"attachment":[],
"tag":[],
}
# (plus "published")
# (plus "conversation", wtf?)
user = create_local_person(
name = 'Fred',
url = 'https://example.com/users/fred',
)
result = embellish(SOURCE,
user=user)
self.maxDiff = None
self.assertDictContainsSubset(
EXPECTING,
result,
)

Wyświetl plik

@ -1,6 +1,5 @@
from django.test import TestCase
from django_kepi.find import find
from django_kepi.models import Thing
from django_kepi.create import create
from django.conf import settings
from . import *

Wyświetl plik

@ -5,7 +5,6 @@ from django_kepi.create import create
from django_kepi.models.audience import Audience, AUDIENCE_FIELD_NAMES
from django_kepi.models.mention import Mention
from django_kepi.models.item import Item
from django_kepi.models.thing import Thing
from django_kepi.models.following import Following
from django_kepi.models.activity import Activity
from django.test import Client

Wyświetl plik

@ -5,7 +5,7 @@ from django_kepi.create import create
from django_kepi.models.audience import Audience, AUDIENCE_FIELD_NAMES
from django_kepi.models.mention import Mention
from django_kepi.models.item import Item
from django_kepi.models.thing import Thing
from django_kepi.models.thing import Object
from django_kepi.models.activity import Activity
from django.test import Client
from urllib.parse import urlparse
@ -231,13 +231,13 @@ class TestOutbox(TestCase):
@httpretty.activate
def test_unwrapped_object(self):
items_before = list(Thing.objects.all())
items_before = list(Object.objects.all())
self._send(
content = OBJECT_FORM,
)
items_after = list(Thing.objects.all())
items_after = list(Object.objects.all())
# This should have created two objects:
# the Note we sent, and an implict Create.
@ -279,7 +279,7 @@ class TestOutbox(TestCase):
)
self.assertEqual(
len(Thing.objects.filter(f_actor=json.dumps(ALICE_ID))),
len(Object.objects.filter(f_actor=json.dumps(ALICE_ID))),
1)
# TODO When Actors have liked() and Things have likes(),

Wyświetl plik

@ -1,6 +1,6 @@
from django.test import TestCase, Client
from django_kepi.validation import IncomingMessage, validate
from django_kepi.models import Thing
from django_kepi.models import Object
from unittest import skip
from unittest.mock import Mock, patch
from . import *
@ -52,9 +52,9 @@ MESSAGE_CONTEXT = ["https://www.w3.org/ns/activitystreams",
def _message_became_activity(url=ACTIVITY_ID):
try:
result = Thing.objects.get(remote_url=url)
result = Object.objects.get(remote_url=url)
return True
except Thing.DoesNotExist:
except Object.DoesNotExist:
return False
class ResultWrapper(object):