mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-14 12:16:31 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
7da705f87e
12 changed files with 68 additions and 46 deletions
|
@ -5,6 +5,7 @@ from dateutil import parser
|
||||||
import pytz
|
import pytz
|
||||||
import requests
|
import requests
|
||||||
from requests import HTTPError
|
from requests import HTTPError
|
||||||
|
from urllib3.exceptions import RequestError
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
|
@ -298,7 +299,7 @@ def get_data(url):
|
||||||
'Accept': 'application/json; charset=utf-8',
|
'Accept': 'application/json; charset=utf-8',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
except ConnectionError:
|
except RequestError:
|
||||||
raise ConnectorException()
|
raise ConnectorException()
|
||||||
if not resp.ok:
|
if not resp.ok:
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|
|
@ -204,7 +204,7 @@ def handle_follow_reject(activity):
|
||||||
def handle_create(activity):
|
def handle_create(activity):
|
||||||
''' someone did something, good on them '''
|
''' someone did something, good on them '''
|
||||||
if activity['object'].get('type') not in \
|
if activity['object'].get('type') not in \
|
||||||
['Note', 'Comment', 'Quotation', 'Review']:
|
['Note', 'Comment', 'Quotation', 'Review', 'GeneratedNote']:
|
||||||
# if it's an article or unknown type, ignore it
|
# if it's an article or unknown type, ignore it
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
17
bookwyrm/migrations/0061_auto_20201030_2157.py
Normal file
17
bookwyrm/migrations/0061_auto_20201030_2157.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 3.0.7 on 2020-10-30 21:57
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bookwyrm', '0060_auto_20201030_2010'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='GeneratedStatus',
|
||||||
|
new_name='GeneratedNote',
|
||||||
|
),
|
||||||
|
]
|
|
@ -6,7 +6,7 @@ from .book import Book, Work, Edition, Author
|
||||||
from .connector import Connector
|
from .connector import Connector
|
||||||
from .relationship import UserFollows, UserFollowRequest, UserBlocks
|
from .relationship import UserFollows, UserFollowRequest, UserBlocks
|
||||||
from .shelf import Shelf, ShelfBook
|
from .shelf import Shelf, ShelfBook
|
||||||
from .status import Status, GeneratedStatus, Review, Comment, Quotation
|
from .status import Status, GeneratedNote, Review, Comment, Quotation
|
||||||
from .status import Favorite, Boost, Notification, ReadThrough
|
from .status import Favorite, Boost, Notification, ReadThrough
|
||||||
from .tag import Tag
|
from .tag import Tag
|
||||||
from .user import User
|
from .user import User
|
||||||
|
@ -16,5 +16,5 @@ from .import_job import ImportJob, ImportItem
|
||||||
from .site import SiteSettings, SiteInvite, PasswordReset
|
from .site import SiteSettings, SiteInvite, PasswordReset
|
||||||
|
|
||||||
cls_members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
|
cls_members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
|
||||||
activity_models = {c[0]: c[1].activity_serializer for c in cls_members \
|
activity_models = {c[0]: c[1] for c in cls_members \
|
||||||
if hasattr(c[1], 'activity_serializer')}
|
if hasattr(c[1], 'activity_serializer')}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
''' base model with default fields '''
|
''' base model with default fields '''
|
||||||
|
from datetime import datetime
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
@ -69,6 +70,8 @@ class ActivitypubMixin:
|
||||||
value = getattr(self, mapping.model_key)
|
value = getattr(self, mapping.model_key)
|
||||||
if hasattr(value, 'remote_id'):
|
if hasattr(value, 'remote_id'):
|
||||||
value = value.remote_id
|
value = value.remote_id
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
value = value.isoformat()
|
||||||
fields[mapping.activity_key] = mapping.activity_formatter(value)
|
fields[mapping.activity_key] = mapping.activity_formatter(value)
|
||||||
|
|
||||||
if pure:
|
if pure:
|
||||||
|
|
|
@ -3,7 +3,6 @@ import re
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.http import http_date
|
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
|
|
||||||
from bookwyrm import activitypub
|
from bookwyrm import activitypub
|
||||||
|
@ -63,16 +62,8 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
||||||
ActivityMapping('id', 'remote_id'),
|
ActivityMapping('id', 'remote_id'),
|
||||||
|
|
||||||
ActivityMapping('authors', 'ap_authors'),
|
ActivityMapping('authors', 'ap_authors'),
|
||||||
ActivityMapping(
|
ActivityMapping('first_published_date', 'first_published_date'),
|
||||||
'first_published_date',
|
ActivityMapping('published_date', 'published_date'),
|
||||||
'first_published_date',
|
|
||||||
activity_formatter=lambda d: http_date(d.timestamp()) if d else None
|
|
||||||
),
|
|
||||||
ActivityMapping(
|
|
||||||
'published_date',
|
|
||||||
'published_date',
|
|
||||||
activity_formatter=lambda d: http_date(d.timestamp()) if d else None
|
|
||||||
),
|
|
||||||
|
|
||||||
ActivityMapping('title', 'title'),
|
ActivityMapping('title', 'title'),
|
||||||
ActivityMapping('sort_title', 'sort_title'),
|
ActivityMapping('sort_title', 'sort_title'),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
''' models for storing different kinds of Activities '''
|
''' models for storing different kinds of Activities '''
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.http import http_date
|
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
|
@ -62,11 +61,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
ActivityMapping('id', 'remote_id'),
|
ActivityMapping('id', 'remote_id'),
|
||||||
ActivityMapping('url', 'remote_id'),
|
ActivityMapping('url', 'remote_id'),
|
||||||
ActivityMapping('inReplyTo', 'reply_parent'),
|
ActivityMapping('inReplyTo', 'reply_parent'),
|
||||||
ActivityMapping(
|
ActivityMapping('published', 'published_date'),
|
||||||
'published',
|
|
||||||
'published_date',
|
|
||||||
activity_formatter=lambda d: http_date(d.timestamp())
|
|
||||||
),
|
|
||||||
ActivityMapping('attributedTo', 'user'),
|
ActivityMapping('attributedTo', 'user'),
|
||||||
ActivityMapping('to', 'ap_to'),
|
ActivityMapping('to', 'ap_to'),
|
||||||
ActivityMapping('cc', 'ap_cc'),
|
ActivityMapping('cc', 'ap_cc'),
|
||||||
|
@ -116,13 +111,13 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
return activitypub.Tombstone(
|
return activitypub.Tombstone(
|
||||||
id=self.remote_id,
|
id=self.remote_id,
|
||||||
url=self.remote_id,
|
url=self.remote_id,
|
||||||
deleted=http_date(self.deleted_date.timestamp()),
|
deleted=self.deleted_date.isoformat(),
|
||||||
published=http_date(self.deleted_date.timestamp()),
|
published=self.deleted_date.isoformat()
|
||||||
).serialize()
|
).serialize()
|
||||||
return ActivitypubMixin.to_activity(self, **kwargs)
|
return ActivitypubMixin.to_activity(self, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class GeneratedStatus(Status):
|
class GeneratedNote(Status):
|
||||||
''' these are app-generated messages about user activity '''
|
''' these are app-generated messages about user activity '''
|
||||||
@property
|
@property
|
||||||
def ap_pure_content(self):
|
def ap_pure_content(self):
|
||||||
|
|
|
@ -21,7 +21,7 @@ def create_generated_note(user, content, mention_books=None):
|
||||||
parser.feed(content)
|
parser.feed(content)
|
||||||
content = parser.get_output()
|
content = parser.get_output()
|
||||||
|
|
||||||
status = models.GeneratedStatus.objects.create(
|
status = models.GeneratedNote.objects.create(
|
||||||
user=user,
|
user=user,
|
||||||
content=content,
|
content=content,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
|
{% load fr_display %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% include 'user_header.html' with user=user %}
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<nav class="breadcrumb has-succeeds-separator" aria-label="breadcrumbs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/user/{{ user.username }}">{% include 'snippets/username.html' with user=user %}</a></li>
|
{% for shelf_tab in shelves %}
|
||||||
<li><a href="/user/{{ user.username }}/shelves">Shelves</a></li>
|
<li class="{% if shelf_tab.identifier == shelf.identifier %}is-active{% endif %}">
|
||||||
<li class="is-active"><a href="#" aria-current="page">{{ shelf.name }}</a></li>
|
<a href="/user/{{ user | username }}/shelf/{{ shelf_tab.identifier }}">{{ shelf_tab.name }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title">{{ shelf.name }}</h2>
|
|
||||||
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% if shelf.books %}
|
{% if shelf.books.all|length > 0 %}
|
||||||
<table class="table is-striped is-fullwidth">
|
<table class="table is-striped is-fullwidth">
|
||||||
|
|
||||||
<tr class="book-preview">
|
<tr class="book-preview">
|
||||||
|
|
|
@ -3,9 +3,20 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'user_header.html' with user=user %}
|
{% include 'user_header.html' with user=user %}
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
{% for shelf in shelves %}
|
||||||
|
<li class="{% if true %}is-active{% endif %}">
|
||||||
|
<a href="/user/{{ user | username }}/shelves/{{ shelf.identifier }}">{{ shelf.name }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<h2 class="title">{{ shelf.name }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% for shelf in shelves %}
|
{% for shelf in shelves %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<h2 class="title">{{ shelf.name }}</h2>
|
|
||||||
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -299,7 +299,7 @@ def notifications_page(request):
|
||||||
return TemplateResponse(request, 'notifications.html', data)
|
return TemplateResponse(request, 'notifications.html', data)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def user_page(request, username, subpage=None):
|
def user_page(request, username, subpage=None, shelf=None):
|
||||||
''' profile page for a user '''
|
''' profile page for a user '''
|
||||||
try:
|
try:
|
||||||
user = get_user_from_username(username)
|
user = get_user_from_username(username)
|
||||||
|
@ -323,18 +323,22 @@ def user_page(request, username, subpage=None):
|
||||||
return TemplateResponse(request, 'following.html', data)
|
return TemplateResponse(request, 'following.html', data)
|
||||||
if subpage == 'shelves':
|
if subpage == 'shelves':
|
||||||
data['shelves'] = user.shelf_set.all()
|
data['shelves'] = user.shelf_set.all()
|
||||||
return TemplateResponse(request, 'user_shelves.html', data)
|
if shelf:
|
||||||
|
data['shelf'] = user.shelf_set.get(identifier=shelf)
|
||||||
|
else:
|
||||||
|
data['shelf'] = user.shelf_set.first()
|
||||||
|
return TemplateResponse(request, 'shelf.html', data)
|
||||||
|
|
||||||
data['shelf_count'] = user.shelf_set.count()
|
data['shelf_count'] = user.shelf_set.count()
|
||||||
shelves = []
|
shelves = []
|
||||||
for shelf in user.shelf_set.all():
|
for user_shelf in user.shelf_set.all():
|
||||||
if not shelf.books.count():
|
if not user_shelf.books.count():
|
||||||
continue
|
continue
|
||||||
shelves.append({
|
shelves.append({
|
||||||
'name': shelf.name,
|
'name': user_shelf.name,
|
||||||
'remote_id': shelf.remote_id,
|
'remote_id': user_shelf.remote_id,
|
||||||
'books': shelf.books.all()[:3],
|
'books': user_shelf.books.all()[:3],
|
||||||
'size': shelf.books.count(),
|
'size': user_shelf.books.count(),
|
||||||
})
|
})
|
||||||
if len(shelves) > 2:
|
if len(shelves) > 2:
|
||||||
break
|
break
|
||||||
|
@ -599,8 +603,5 @@ def shelf_page(request, username, shelf_identifier):
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return JsonResponse(shelf.to_activity(**request.GET))
|
return JsonResponse(shelf.to_activity(**request.GET))
|
||||||
|
|
||||||
data = {
|
return user_page(
|
||||||
'shelf': shelf,
|
request, username, subpage='shelves', shelf=shelf_identifier)
|
||||||
'user': user,
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, 'shelf.html', data)
|
|
||||||
|
|
Loading…
Reference in a new issue