Merge pull request #270 from mouse-reeve/user-active

User active
This commit is contained in:
Mouse Reeve 2020-11-01 11:07:04 -08:00 committed by GitHub
commit a242a476d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 27 deletions

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.7 on 2020-11-01 17:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bookwyrm', '0062_auto_20201031_1936'),
]
operations = [
migrations.AddField(
model_name='user',
name='last_active_date',
field=models.DateTimeField(auto_now=True),
),
]

View file

@ -59,6 +59,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
@property
def ap_tag(self):
''' books or (eventually) users tagged in a post '''
tags = []
for book in self.mention_books.all():
tags.append(activitypub.Link(
@ -117,7 +118,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
**kwargs
)
def to_activity(self, **kwargs):
def to_activity(self, pure=False):
''' return tombstone if the status is deleted '''
if self.deleted:
return activitypub.Tombstone(
@ -126,7 +127,12 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
deleted=self.deleted_date.isoformat(),
published=self.deleted_date.isoformat()
).serialize()
return ActivitypubMixin.to_activity(self, **kwargs)
return ActivitypubMixin.to_activity(self, pure=pure)
def save(self, *args, **kwargs):
self.user.last_active_date = timezone.now()
self.user.save()
super().save(*args, **kwargs)
class GeneratedNote(Status):
@ -227,6 +233,11 @@ class Favorite(ActivitypubMixin, BookWyrmModel):
activity_serializer = activitypub.Like
def save(self, *args, **kwargs):
self.user.last_active_date = timezone.now()
self.user.save()
super().save(*args, **kwargs)
class Meta:
''' can't fav things twice '''
@ -267,6 +278,11 @@ class ReadThrough(BookWyrmModel):
blank=True,
null=True)
def save(self, *args, **kwargs):
self.user.last_active_date = timezone.now()
self.user.save()
super().save(*args, **kwargs)
NotificationType = models.TextChoices(
'NotificationType',

View file

@ -69,6 +69,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
remote_id = models.CharField(max_length=255, null=True, unique=True)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
last_active_date = models.DateTimeField(auto_now=True)
manually_approves_followers = models.BooleanField(default=False)
# ---- activitypub serialization settings for this model ----- #
@ -167,28 +168,30 @@ class User(OrderedCollectionPageMixin, AbstractUser):
return activity_object
@receiver(models.signals.pre_save, sender=User)
def execute_before_save(sender, instance, *args, **kwargs):
''' populate fields for new local users '''
# this user already exists, no need to poplate fields
if instance.id:
return
if not instance.local:
# we need to generate a username that uses the domain (webfinger format)
actor_parts = urlparse(instance.remote_id)
instance.username = '%s@%s' % (instance.username, actor_parts.netloc)
return
def save(self, *args, **kwargs):
''' populate fields for new local users '''
# this user already exists, no need to populate fields
if self.id:
return
# populate fields for local users
instance.remote_id = 'https://%s/user/%s' % (DOMAIN, instance.username)
instance.localname = instance.username
instance.username = '%s@%s' % (instance.username, DOMAIN)
instance.actor = instance.remote_id
instance.inbox = '%s/inbox' % instance.remote_id
instance.shared_inbox = 'https://%s/inbox' % DOMAIN
instance.outbox = '%s/outbox' % instance.remote_id
if not instance.private_key:
instance.private_key, instance.public_key = create_key_pair()
if not self.local:
# generate a username that uses the domain (webfinger format)
actor_parts = urlparse(self.remote_id)
self.username = '%s@%s' % (self.username, actor_parts.netloc)
return
# populate fields for local users
self.remote_id = 'https://%s/user/%s' % (DOMAIN, self.username)
self.localname = self.username
self.username = '%s@%s' % (self.username, DOMAIN)
self.actor = self.remote_id
self.inbox = '%s/inbox' % self.remote_id
self.shared_inbox = 'https://%s/inbox' % DOMAIN
self.outbox = '%s/outbox' % self.remote_id
if not self.private_key:
self.private_key, self.public_key = create_key_pair()
super().save(*args, **kwargs)
@receiver(models.signals.post_save, sender=User)

View file

@ -17,7 +17,7 @@ status_types = [
'comment',
'quotation',
'boost',
'generatedstatus'
'generatednote'
]
status_path = r'%s/(%s)/(?P<status_id>\d+)' % \
(local_user_path, '|'.join(status_types))

View file

@ -4,13 +4,15 @@ from PIL import Image
import dateutil.parser
from dateutil.parser import ParserError
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, permission_required
from django.core.exceptions import PermissionDenied
from django.core.files.base import ContentFile
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.core.exceptions import PermissionDenied
from django.utils import timezone
from bookwyrm import books_manager
from bookwyrm import forms, models, outgoing
@ -32,7 +34,9 @@ def user_login(request):
password = login_form.data['password']
user = authenticate(request, username=username, password=password)
if user is not None:
# successful login
login(request, user)
user.last_active_date = timezone.now()
return redirect(request.GET.get('next', '/'))
login_form.non_field_errors = 'Username or password are incorrect'

View file

@ -1,4 +1,7 @@
''' responds to various requests to /.well-know '''
from datetime import datetime
from dateutil.relativedelta import relativedelta
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.http import JsonResponse
@ -52,6 +55,16 @@ def nodeinfo(request):
status_count = models.Status.objects.filter(user__local=True).count()
user_count = models.User.objects.count()
month_ago = datetime.now() - relativedelta(months=1)
last_month_count = models.User.objects.filter(
last_active_date__gt=month_ago
).count()
six_months_ago = datetime.now() - relativedelta(months=6)
six_month_count = models.User.objects.filter(
last_active_date__gt=six_months_ago
).count()
return JsonResponse({
'version': '2.0',
'software': {
@ -64,8 +77,8 @@ def nodeinfo(request):
'usage': {
'users': {
'total': user_count,
'activeMonth': user_count, # TODO
'activeHalfyear': user_count, # TODO
'activeMonth': last_month_count,
'activeHalfyear': six_month_count,
},
'localPosts': status_count,
},