forked from mirrors/bookwyrm
commit
a242a476d9
6 changed files with 81 additions and 27 deletions
18
bookwyrm/migrations/0063_user_last_active_date.py
Normal file
18
bookwyrm/migrations/0063_user_last_active_date.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue