Intermediate checkin

trilby-heavy
Marnanel Thurman 2020-03-16 17:54:02 +00:00
rodzic c4a07a9e52
commit e07706865e
9 zmienionych plików z 266 dodań i 40 usunięć

Wyświetl plik

@ -0,0 +1,56 @@
# Generated by Django 2.2.4 on 2020-03-16 17:23
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('trilby_api', '0009_auto_20191218_1751'),
]
operations = [
migrations.CreateModel(
name='Person',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('remote_url', models.URLField(blank=True, max_length=255, null=True, unique=True)),
('remote_username', models.CharField(blank=True, max_length=255, null=True)),
('icon_image', models.ImageField(help_text='A small square image used to identify you.', null=True, upload_to='', verbose_name='icon')),
('header_image', models.ImageField(help_text="A large image, wider than it's tall, which appears at the top of your profile page.", null=True, upload_to='', verbose_name='header image')),
('display_name', models.TextField(help_text='Your name, in human-friendly form. Something like "Alice Liddell".', verbose_name='display name')),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('publicKey', models.TextField(blank=True, null=True, verbose_name='public key')),
('privateKey', models.TextField(blank=True, null=True, verbose_name='private key')),
('note', models.TextField(default='', help_text='Your biography. Something like "I enjoy falling down rabbitholes."', max_length=255, verbose_name='bio')),
('auto_follow', models.BooleanField(default=True, help_text='If True, follow requests will be accepted automatically.')),
],
),
migrations.RemoveField(
model_name='trilbyuser',
name='actor',
),
migrations.CreateModel(
name='Status',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content', models.TextField()),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('sensitive', models.BooleanField()),
('spoiler_text', models.CharField(max_length=255)),
('visibility', models.CharField(max_length=255)),
('language', models.CharField(max_length=255)),
('idempotency_key', models.CharField(max_length=255)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='trilby_api.Person')),
('in_reply_to_id', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='trilby_api.Status')),
],
),
migrations.AddField(
model_name='person',
name='local_user',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

Wyświetl plik

@ -5,47 +5,217 @@ import kepi.bowler_pub.models as kepi_models
import kepi.bowler_pub.signals as kepi_signals
import kepi.bowler_pub.find as kepi_find
from kepi.bowler_pub.create import create
import kepi.bowler_pub.crypto as crypto
from django.utils.timezone import now
from django.core.exceptions import ValidationError
import logging
logger = logging.Logger('kepi')
class TrilbyUser(AbstractUser):
"""
A Django user.
"""
pass
actor = models.OneToOneField(
kepi_models.AcPerson,
on_delete=models.CASCADE,
unique=True,
default=None,
class Person(models.Model):
remote_url = models.URLField(
max_length = 255,
null = True,
blank = True,
unique = True,
)
remote_username = models.CharField(
max_length = 255,
null = True,
blank = True,
)
local_user = models.OneToOneField(
to = TrilbyUser,
on_delete = models.CASCADE,
null = True,
blank = True,
)
icon_image = models.ImageField(
help_text="A small square image used to identify you.",
null=True,
verbose_name='icon',
)
header_image = models.ImageField(
help_text="A large image, wider than it's tall, which appears "+\
"at the top of your profile page.",
null=True,
verbose_name='header image',
)
@property
def icon_or_default(self):
if self.icon_image:
return self.icon_image
which = ord(self.id[1]) % 10
return uri_to_url('/static/defaults/avatar_{}.jpg'.format(
which,
))
@property
def header_or_default(self):
if self.header_image:
return self.header_image
return uri_to_url('/static/defaults/header.jpg')
display_name = models.TextField(
verbose_name='display name',
help_text = 'Your name, in human-friendly form. '+\
'Something like "Alice Liddell".',
)
created_at = models.DateTimeField(
default = now,
)
publicKey = models.TextField(
blank=True,
null=True,
verbose_name='public key',
)
privateKey = models.TextField(
blank=True,
null=True,
verbose_name='private key',
)
note = models.TextField(
max_length=255,
help_text="Your biography. Something like "+\
'"I enjoy falling down rabbitholes."',
default='',
verbose_name='bio',
)
auto_follow = models.BooleanField(
default=True,
help_text="If True, follow requests will be accepted automatically.",
)
@property
def following_count(self):
return 0 # FIXME
@property
def followers_count(self):
return 0 # FIXME
@property
def statuses_count(self):
return 0 # FIXME
@property
def acct(self):
return 'FIXME' # FIXME
@property
def username(self):
if remote_url is not None:
return remote_username
else:
return local_user.username
def _generate_keys(self):
logger.info('%s: generating key pair.',
self.url)
key = crypto.Key()
self.privateKey = key.private_as_pem()
self.publicKey = key.public_as_pem()
def save(self, *args, **kwargs):
# Validate: either remote or local but not both or neither.
remote_set = \
self.remote_url is not None and \
self.remote_username is not None
if self.pk is None and self.actor is None:
local_set = \
self.local_user is not None
name = self.get_username()
logger.info('Creating AcPerson for new user "%s".',
name)
spec = {
'name': name,
'id': '@'+name,
'type': 'Person',
}
new_person = create(
value = spec,
run_delivery = False,
if local_set == remote_set:
raise ValidationError(
"Either local or remote fields must be set."
)
self.actor = new_person
# Create keys, if we're local and we don't have them.
logger.info(' -- new AcPerson is %s',
new_person)
if local_set and self.privateKey is None and self.publicKey is None:
self._generate_keys()
# All good.
super().save(*args, **kwargs)
###################
class Status(models.Model):
# TODO: The original design has the serial number
# monotonically but unpredictably increasing.
@property
def url(self):
return 'FIXME' # FIXME
@property
def uri(self):
return 'FIXME' # FIXME
account = models.ForeignKey(
'Person',
on_delete = models.DO_NOTHING,
)
in_reply_to_id = models.ForeignKey(
'self',
on_delete = models.DO_NOTHING,
)
content = models.TextField(
)
created_at = models.DateTimeField(
default = now,
)
# TODO Media
sensitive = models.BooleanField(
)
spoiler_text = models.CharField(
max_length = 255,
)
visibility = models.CharField(
max_length = 255,
)
language = models.CharField(
max_length = 255,
)
idempotency_key = models.CharField(
max_length = 255,
)
###################
class Notification(models.Model):
FOLLOW = 'F'

Wyświetl plik

@ -40,7 +40,6 @@ class UserSerializer(serializers.ModelSerializer):
)
note = serializers.CharField(
source='f_summary',
)
following_count = serializers.IntegerField()

Wyświetl plik

@ -1,18 +1,19 @@
from kepi.trilby_api.models import TrilbyUser
from kepi.trilby_api.models import *
PUBLIC = "https://www.w3.org/ns/activitystreams#Public"
def create_local_trilbyuser(name='jemima'):
def create_local_person(name='jemima'):
from kepi.bowler_pub.tests import create_local_person
from kepi.trilby_api.models import TrilbyUser
person = create_local_person(name=name)
result = TrilbyUser(
user = TrilbyUser(
username = name,
actor = person)
result.save()
)
user.save()
result = Person(
local_user = user,
)
return result

Wyświetl plik

@ -10,7 +10,7 @@ class TestIntegration(TestCase):
settings.KEPI['LOCAL_OBJECT_HOSTNAME'] = 'testserver'
def _create_alice(self):
self._alice = create_local_trilbyuser(name='alice')
self._alice = create_local_person(name='alice')
def test_post(self):

Wyświetl plik

@ -20,7 +20,7 @@ class TestNotifications(TestCase):
@httpretty.activate
def test_follow(self):
alice = create_local_trilbyuser(name='alice')
alice = create_local_person(name='alice')
fred_keys = json.load(open(DEFAULT_KEYS_FILENAME, 'r'))
@ -84,7 +84,7 @@ class TestNotifications(TestCase):
@httpretty.activate
def test_favourite(self):
alice = create_local_trilbyuser(name='alice')
alice = create_local_person(name='alice')
status = create_local_status(
content = 'Curiouser and curiouser!',

Wyświetl plik

@ -109,7 +109,7 @@ class TestRest(TestCase):
)
def _user_test(self, name):
alice = create_local_trilbyuser(name='alice')
alice = create_local_person(name='alice')
request = self.factory.get(
'/api/v1/accounts/'+name,
@ -149,7 +149,7 @@ class TestStatuses(TestCase):
settings.KEPI['LOCAL_OBJECT_HOSTNAME'] = 'testserver'
def _create_alice(self):
self._alice = create_local_trilbyuser(name='alice')
self._alice = create_local_person(name='alice')
def _create_status(self):
self._status = create_local_status(

Wyświetl plik

@ -8,7 +8,7 @@ from django.conf import settings
class TestStatus(TestCase):
def _create_alice(self):
self._alice = create_local_trilbyuser(name='alice')
self._alice = create_local_person(name='alice')
self._alice_status = create_local_status(
posted_by = self._alice,

Wyświetl plik

@ -31,7 +31,7 @@ class PublicTimeline(TestCase):
self.assertEqual(len(response), 0)
def test_public_singleton(self):
self._alice = create_local_trilbyuser(name='alice')
self._alice = create_local_person(name='alice')
self._status = create_local_status(
content = 'Hello world.',
@ -47,7 +47,7 @@ class PublicTimeline(TestCase):
)
def test_public_singleton_hidden(self):
self._alice = create_local_trilbyuser(name='alice')
self._alice = create_local_person(name='alice')
self._status = create_local_status(
content = 'Hello world.',