bookwyrm/fedireads/models/user.py

149 lines
5 KiB
Python
Raw Normal View History

2020-02-11 23:17:21 +00:00
''' database schema for user data '''
from Crypto import Random
from Crypto.PublicKey import RSA
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.dispatch import receiver
from fedireads.models import Shelf
from fedireads.settings import DOMAIN
2020-02-17 03:17:11 +00:00
from fedireads.utils.models import FedireadsModel
2020-02-11 23:17:21 +00:00
class User(AbstractUser):
''' a user who wants to read books '''
private_key = models.TextField(blank=True, null=True)
public_key = models.TextField(blank=True, null=True)
actor = models.CharField(max_length=255, unique=True)
inbox = models.CharField(max_length=255, unique=True)
shared_inbox = models.CharField(max_length=255, blank=True, null=True)
federated_server = models.ForeignKey(
'FederatedServer',
on_delete=models.PROTECT,
null=True,
)
outbox = models.CharField(max_length=255, unique=True)
summary = models.TextField(blank=True, null=True)
local = models.BooleanField(default=True)
fedireads_user = models.BooleanField(default=True)
2020-02-11 23:17:21 +00:00
localname = models.CharField(
max_length=255,
null=True,
unique=True
)
# name is your display name, which you can change at will
name = models.CharField(max_length=100, blank=True, null=True)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
2020-02-19 06:44:13 +00:00
followers = models.ManyToManyField(
'self',
symmetrical=False,
through='UserRelationship',
through_fields=('user_object', 'user_subject'),
related_name='following'
2020-02-19 06:44:13 +00:00
)
2020-02-19 07:26:42 +00:00
favorites = models.ManyToManyField(
'Status',
symmetrical=False,
through='Favorite',
through_fields=('user', 'status'),
related_name='favorite_statuses'
)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
manually_approves_followers = models.BooleanField(default=False)
2020-02-11 23:17:21 +00:00
2020-02-17 03:17:11 +00:00
@property
def absolute_id(self):
''' users are identified by their username, so overriding this prop '''
2020-02-18 01:53:40 +00:00
model_name = type(self).__name__.lower()
username = self.localname or self.username
return 'https://%s/%s/%s' % (DOMAIN, model_name, username)
2020-02-11 23:17:21 +00:00
2020-02-17 03:17:11 +00:00
2020-02-19 06:44:13 +00:00
class UserRelationship(FedireadsModel):
''' many-to-many through table for followers '''
user_subject = models.ForeignKey(
'User',
on_delete=models.PROTECT,
related_name='user_subject'
)
user_object = models.ForeignKey(
'User',
on_delete=models.PROTECT,
related_name='user_object'
)
# follow or follow_request for pending TODO: blocking?
status = models.CharField(max_length=100, default='follows', null=True)
relationship_id = models.CharField(max_length=100)
class Meta:
constraints = [
2020-02-23 21:24:23 +00:00
models.UniqueConstraint(
fields=['user_subject', 'user_object'],
name='followers_unique'
)
]
2020-02-19 06:44:13 +00:00
@property
def absolute_id(self):
''' use shelf identifier as absolute id '''
base_path = self.user_subject.absolute_id
return '%s#%s/%d' % (base_path, self.status, self.id)
2020-02-17 03:17:11 +00:00
class FederatedServer(FedireadsModel):
2020-02-11 23:17:21 +00:00
''' store which server's we federate with '''
server_name = models.CharField(max_length=255, unique=True)
# federated, blocked, whatever else
status = models.CharField(max_length=255, default='federated')
# is it mastodon, fedireads, etc
application_type = models.CharField(max_length=255, null=True)
@receiver(models.signals.pre_save, sender=User)
def execute_before_save(sender, instance, *args, **kwargs):
2020-02-15 22:38:46 +00:00
''' populate fields for new local users '''
2020-02-11 23:17:21 +00:00
# this user already exists, no need to poplate fields
if instance.id or not instance.local:
return
# populate fields for local users
instance.localname = instance.username
instance.username = '%s@%s' % (instance.username, DOMAIN)
2020-02-18 01:53:40 +00:00
instance.actor = instance.absolute_id
instance.inbox = '%s/inbox' % instance.absolute_id
2020-02-11 23:17:21 +00:00
instance.shared_inbox = 'https://%s/inbox' % DOMAIN
2020-02-18 01:53:40 +00:00
instance.outbox = '%s/outbox' % instance.absolute_id
2020-02-11 23:17:21 +00:00
if not instance.private_key:
random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
instance.private_key = key.export_key().decode('utf8')
instance.public_key = key.publickey().export_key().decode('utf8')
@receiver(models.signals.post_save, sender=User)
def execute_after_save(sender, instance, created, *args, **kwargs):
''' create shelves for new users '''
if not instance.local or not created:
return
shelves = [{
'name': 'To Read',
2020-02-15 22:38:46 +00:00
'identifier': 'to-read',
2020-02-11 23:17:21 +00:00
}, {
'name': 'Currently Reading',
2020-02-15 22:38:46 +00:00
'identifier': 'reading',
2020-02-11 23:17:21 +00:00
}, {
'name': 'Read',
2020-02-15 22:38:46 +00:00
'identifier': 'read',
2020-02-11 23:17:21 +00:00
}]
for shelf in shelves:
Shelf(
name=shelf['name'],
2020-02-15 22:38:46 +00:00
identifier=shelf['identifier'],
2020-02-11 23:17:21 +00:00
user=instance,
editable=False
).save()