kopia lustrzana https://gitlab.com/marnanel/chapeau
new Audience model, plus tests. Not yet linked in to Thing.
rodzic
63a3a82356
commit
51b8d7f347
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.2.1 on 2019-06-04 17:52
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_kepi', '0002_auto_20190527_1458'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='thing',
|
||||
name='f_type',
|
||||
field=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),
|
||||
),
|
||||
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')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1,11 +1,7 @@
|
|||
from django_kepi.models.thing import Thing, ThingField, create
|
||||
from django_kepi.models.following import Following
|
||||
from django_kepi.models.actor import Actor
|
||||
|
||||
def new_activity_identifier():
|
||||
# we have to keep this in for now,
|
||||
# to pacify makemigrations
|
||||
return None
|
||||
from django_kepi.models.audience import Audience
|
||||
|
||||
#######################
|
||||
|
||||
|
@ -15,5 +11,5 @@ __all__ = [
|
|||
'ThingField',
|
||||
'create',
|
||||
'Following',
|
||||
'new_activity_identifier',
|
||||
'Audience',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
from django.db import models
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('django_kepi')
|
||||
|
||||
FIELD_AUDIENCE = 0x01 # literally "audience"
|
||||
FIELD_TO = 0x02
|
||||
FIELD_CC = 0x04
|
||||
|
||||
BLIND = 0x70
|
||||
FIELD_BTO = BLIND + FIELD_TO
|
||||
FIELD_BCC = BLIND + FIELD_CC
|
||||
# the spec doesn't allow for blind audience; idk why
|
||||
|
||||
FIELD_CHOICES = [
|
||||
(FIELD_AUDIENCE, 'audience'),
|
||||
(FIELD_TO, 'to'),
|
||||
(FIELD_CC, 'cc'),
|
||||
(FIELD_BTO, 'bto'),
|
||||
(FIELD_BCC, 'bcc'),
|
||||
]
|
||||
|
||||
FIELD_NAMES = dict([(v,f) for (f,v) in FIELD_CHOICES])
|
||||
|
||||
class Audience(models.Model):
|
||||
|
||||
parent = models.ForeignKey(
|
||||
'django_kepi.Thing',
|
||||
on_delete = models.CASCADE,
|
||||
)
|
||||
|
||||
field = models.PositiveSmallIntegerField(
|
||||
choices = FIELD_CHOICES,
|
||||
)
|
||||
|
||||
recipient = models.CharField(
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
@property
|
||||
def blind(self):
|
||||
return (self.field & BLIND) != 0
|
||||
|
||||
def __str__(self):
|
||||
return '[%s %12s %s]' % (
|
||||
self.parent.number,
|
||||
self.get_field_display(),
|
||||
self.recipient,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def add_audiences_for(cls, thing,
|
||||
field, value):
|
||||
|
||||
"""
|
||||
Add new Audiences for a given Thing.
|
||||
"value" is a list of strings.
|
||||
|
||||
This function only adds Audiences of
|
||||
a single field type. This is
|
||||
deliberately asymmetrical to
|
||||
get_audiences_for(), which returns
|
||||
all Audiences of all field types.
|
||||
The difference is because of
|
||||
where it's needed.
|
||||
"""
|
||||
|
||||
if field not in FIELD_NAMES:
|
||||
raise ValueError('%s is not an audience field' % (
|
||||
field,
|
||||
))
|
||||
logger.debug('Adding Audiences for %s: %s=%s',
|
||||
thing.number, field, value)
|
||||
|
||||
field = FIELD_NAMES[field]
|
||||
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
|
||||
for line in value:
|
||||
a = Audience(
|
||||
parent = thing,
|
||||
field = field,
|
||||
recipient = str(line),
|
||||
)
|
||||
a.save()
|
||||
logger.debug(' -- created %s',
|
||||
a)
|
||||
|
||||
@classmethod
|
||||
def get_audiences_for(cls, thing,
|
||||
hide_blind = False,
|
||||
):
|
||||
|
||||
result = defaultdict(lambda: [])
|
||||
|
||||
for a in Audience.objects.filter(
|
||||
parent=thing,
|
||||
):
|
||||
|
||||
if hide_blind and a.blind:
|
||||
logger.debug('Not counting %s because blind fields are hidden',
|
||||
a)
|
||||
continue
|
||||
|
||||
result[a.get_field_display()].append(a.recipient)
|
||||
|
||||
result = dict(result)
|
||||
|
||||
logger.debug('Audience is: %s', result)
|
||||
|
||||
return result
|
|
@ -0,0 +1,77 @@
|
|||
from django.test import TestCase
|
||||
from django_kepi.models import create, Audience
|
||||
from . import create_local_person, REMOTE_FRED, REMOTE_JIM
|
||||
|
||||
class TestAudience(TestCase):
|
||||
|
||||
def test_add_audiences_for(self):
|
||||
narcissus = create_local_person(
|
||||
name = 'narcissus',
|
||||
)
|
||||
|
||||
like = create(
|
||||
f_type = 'Like',
|
||||
f_actor = narcissus,
|
||||
f_object = narcissus,
|
||||
)
|
||||
|
||||
a = Audience.add_audiences_for(
|
||||
thing = like,
|
||||
field = 'to',
|
||||
value = [
|
||||
REMOTE_FRED,
|
||||
REMOTE_JIM,
|
||||
],
|
||||
)
|
||||
|
||||
results = Audience.objects.filter(
|
||||
parent = like,
|
||||
)
|
||||
|
||||
self.assertEqual(len(results), 2)
|
||||
self.assertEqual(results[0].recipient, REMOTE_FRED)
|
||||
self.assertEqual(results[1].recipient, REMOTE_JIM)
|
||||
|
||||
def test_get_audiences_for(self):
|
||||
narcissus = create_local_person(
|
||||
name = 'narcissus',
|
||||
)
|
||||
|
||||
like = create(
|
||||
f_type = 'Like',
|
||||
f_actor = narcissus,
|
||||
f_object = narcissus,
|
||||
)
|
||||
|
||||
for fieldname in ['to', 'cc', 'bcc']:
|
||||
a = Audience.add_audiences_for(
|
||||
thing = like,
|
||||
field = fieldname,
|
||||
value = [
|
||||
REMOTE_FRED,
|
||||
REMOTE_JIM,
|
||||
],
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
Audience.get_audiences_for(like),
|
||||
{'to': ['https://remote.example.org/users/fred',
|
||||
'https://remote.example.org/users/jim'],
|
||||
'cc': ['https://remote.example.org/users/fred',
|
||||
'https://remote.example.org/users/jim'],
|
||||
'bcc': ['https://remote.example.org/users/fred',
|
||||
'https://remote.example.org/users/jim']
|
||||
})
|
||||
|
||||
self.assertDictEqual(
|
||||
Audience.get_audiences_for(like,
|
||||
hide_blind = True,
|
||||
),
|
||||
{'to': ['https://remote.example.org/users/fred',
|
||||
'https://remote.example.org/users/jim'],
|
||||
'cc': ['https://remote.example.org/users/fred',
|
||||
'https://remote.example.org/users/jim'],
|
||||
})
|
||||
|
||||
|
||||
|
Ładowanie…
Reference in New Issue