mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-04 23:36:32 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
9bf79bf9b9
36 changed files with 708 additions and 447 deletions
|
@ -2,7 +2,8 @@
|
|||
SECRET_KEY="7(2w1sedok=aznpq)ta1mc4i%4h=xx@hxwx*o57ctsuml0x%fr"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG=false
|
||||
DEBUG=true
|
||||
USE_HTTPS=false
|
||||
|
||||
DOMAIN=your.domain.here
|
||||
#EMAIL=your@email.here
|
||||
|
@ -42,6 +43,21 @@ EMAIL_HOST_PASSWORD=emailpassword123
|
|||
EMAIL_USE_TLS=true
|
||||
EMAIL_USE_SSL=false
|
||||
|
||||
# S3 configuration
|
||||
USE_S3=false
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
|
||||
# Commented are example values if you use a non-AWS, S3-compatible service
|
||||
# AWS S3 should work with only AWS_STORAGE_BUCKET_NAME and AWS_S3_REGION_NAME
|
||||
# non-AWS S3-compatible services will need AWS_STORAGE_BUCKET_NAME,
|
||||
# along with both AWS_S3_CUSTOM_DOMAIN and AWS_S3_ENDPOINT_URL
|
||||
|
||||
# AWS_STORAGE_BUCKET_NAME= # "example-bucket-name"
|
||||
# AWS_S3_CUSTOM_DOMAIN=None # "example-bucket-name.s3.fr-par.scw.cloud"
|
||||
# AWS_S3_REGION_NAME=None # "fr-par"
|
||||
# AWS_S3_ENDPOINT_URL=None # "https://s3.fr-par.scw.cloud"
|
||||
|
||||
# Preview image generation can be computing and storage intensive
|
||||
# ENABLE_PREVIEW_IMAGES=True
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ SECRET_KEY="7(2w1sedok=aznpq)ta1mc4i%4h=xx@hxwx*o57ctsuml0x%fr"
|
|||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG=false
|
||||
USE_HTTPS=true
|
||||
|
||||
DOMAIN=your.domain.here
|
||||
EMAIL=your@email.here
|
||||
|
@ -42,6 +43,21 @@ EMAIL_HOST_PASSWORD=emailpassword123
|
|||
EMAIL_USE_TLS=true
|
||||
EMAIL_USE_SSL=false
|
||||
|
||||
# S3 configuration
|
||||
USE_S3=false
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
|
||||
# Commented are example values if you use a non-AWS, S3-compatible service
|
||||
# AWS S3 should work with only AWS_STORAGE_BUCKET_NAME and AWS_S3_REGION_NAME
|
||||
# non-AWS S3-compatible services will need AWS_STORAGE_BUCKET_NAME,
|
||||
# along with both AWS_S3_CUSTOM_DOMAIN and AWS_S3_ENDPOINT_URL
|
||||
|
||||
# AWS_STORAGE_BUCKET_NAME= # "example-bucket-name"
|
||||
# AWS_S3_CUSTOM_DOMAIN=None # "example-bucket-name.s3.fr-par.scw.cloud"
|
||||
# AWS_S3_REGION_NAME=None # "fr-par"
|
||||
# AWS_S3_ENDPOINT_URL=None # "https://s3.fr-par.scw.cloud"
|
||||
|
||||
# Preview image generation can be computing and storage intensive
|
||||
# ENABLE_PREVIEW_IMAGES=True
|
||||
|
||||
|
|
|
@ -11,10 +11,7 @@ def site_settings(request): # pylint: disable=unused-argument
|
|||
return {
|
||||
"site": models.SiteSettings.objects.get(),
|
||||
"active_announcements": models.Announcement.active_announcements(),
|
||||
"static_url": settings.STATIC_URL,
|
||||
"media_url": settings.MEDIA_URL,
|
||||
"static_path": settings.STATIC_PATH,
|
||||
"media_path": settings.MEDIA_PATH,
|
||||
"media_full_url": settings.MEDIA_FULL_URL,
|
||||
"preview_images_enabled": settings.ENABLE_PREVIEW_IMAGES,
|
||||
"request_protocol": request_protocol,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import csv
|
||||
import logging
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.models import ImportJob, ImportItem
|
||||
from bookwyrm.tasks import app
|
||||
|
@ -100,7 +102,10 @@ def handle_imported_book(source, user, item, include_reviews, privacy):
|
|||
# shelve the book if it hasn't been shelved already
|
||||
if item.shelf and not existing_shelf:
|
||||
desired_shelf = models.Shelf.objects.get(identifier=item.shelf, user=user)
|
||||
models.ShelfBook.objects.create(book=item.book, shelf=desired_shelf, user=user)
|
||||
shelved_date = item.date_added or timezone.now()
|
||||
models.ShelfBook.objects.create(
|
||||
book=item.book, shelf=desired_shelf, user=user, shelved_date=shelved_date
|
||||
)
|
||||
|
||||
for read in item.reads:
|
||||
# check for an existing readthrough with the same dates
|
||||
|
|
34
bookwyrm/migrations/0078_add_shelved_date.py
Normal file
34
bookwyrm/migrations/0078_add_shelved_date.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Generated by Django 3.2.4 on 2021-07-03 08:25
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
def copy_created_date(app_registry, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
ShelfBook = app_registry.get_model("bookwyrm", "ShelfBook")
|
||||
ShelfBook.objects.all().update(shelved_date=models.F("created_date"))
|
||||
|
||||
|
||||
def do_nothing(app_registry, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0077_auto_20210623_2155"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="shelfbook",
|
||||
options={"ordering": ("-shelved_date",)},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="shelfbook",
|
||||
name="shelved_date",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.RunPython(copy_created_date, reverse_code=do_nothing),
|
||||
]
|
|
@ -1,6 +1,7 @@
|
|||
""" puttin' books on shelves """
|
||||
import re
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import activitypub
|
||||
from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin
|
||||
|
@ -69,6 +70,7 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel):
|
|||
"Edition", on_delete=models.PROTECT, activitypub_field="book"
|
||||
)
|
||||
shelf = models.ForeignKey("Shelf", on_delete=models.PROTECT)
|
||||
shelved_date = models.DateTimeField(default=timezone.now)
|
||||
user = fields.ForeignKey(
|
||||
"User", on_delete=models.PROTECT, activitypub_field="actor"
|
||||
)
|
||||
|
@ -86,4 +88,4 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel):
|
|||
you can't put a book on shelf twice"""
|
||||
|
||||
unique_together = ("book", "shelf")
|
||||
ordering = ("-created_date",)
|
||||
ordering = ("-shelved_date", "-created_date", "-updated_date")
|
||||
|
|
|
@ -11,6 +11,7 @@ from PIL import Image, ImageDraw, ImageFont, ImageOps, ImageColor
|
|||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.uploadedfile import InMemoryUploadedFile
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Avg
|
||||
|
||||
from bookwyrm import models, settings
|
||||
|
@ -319,9 +320,9 @@ def save_and_cleanup(image, instance=None):
|
|||
|
||||
try:
|
||||
try:
|
||||
old_path = instance.preview_image.path
|
||||
old_path = instance.preview_image.name
|
||||
except ValueError:
|
||||
old_path = ""
|
||||
old_path = None
|
||||
|
||||
# Save
|
||||
image.save(image_buffer, format="jpeg", quality=75)
|
||||
|
@ -342,8 +343,8 @@ def save_and_cleanup(image, instance=None):
|
|||
instance.save()
|
||||
|
||||
# Clean up old file after saving
|
||||
if os.path.exists(old_path):
|
||||
os.remove(old_path)
|
||||
if old_path and default_storage.exists(old_path):
|
||||
default_storage.delete(old_path)
|
||||
|
||||
finally:
|
||||
image_buffer.close()
|
||||
|
|
|
@ -58,6 +58,7 @@ SECRET_KEY = env("SECRET_KEY")
|
|||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = env.bool("DEBUG", True)
|
||||
USE_HTTPS = env.bool("USE_HTTPS", False)
|
||||
|
||||
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", ["*"])
|
||||
|
||||
|
@ -74,6 +75,7 @@ INSTALLED_APPS = [
|
|||
"django_rename_app",
|
||||
"bookwyrm",
|
||||
"celery",
|
||||
"storages",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -179,19 +181,51 @@ USE_L10N = True
|
|||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_PATH = "%s/%s" % (DOMAIN, env("STATIC_ROOT", "static"))
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static"))
|
||||
MEDIA_URL = "/images/"
|
||||
MEDIA_PATH = "%s/%s" % (DOMAIN, env("MEDIA_ROOT", "images"))
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images"))
|
||||
|
||||
USER_AGENT = "%s (BookWyrm/%s; +https://%s/)" % (
|
||||
requests.utils.default_user_agent(),
|
||||
VERSION,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Storage
|
||||
|
||||
PROTOCOL = "http"
|
||||
if USE_HTTPS:
|
||||
PROTOCOL = "https"
|
||||
|
||||
USE_S3 = env.bool("USE_S3", False)
|
||||
|
||||
if USE_S3:
|
||||
# AWS settings
|
||||
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID")
|
||||
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY")
|
||||
AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME")
|
||||
AWS_S3_CUSTOM_DOMAIN = env("AWS_S3_CUSTOM_DOMAIN")
|
||||
AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME", "")
|
||||
AWS_S3_ENDPOINT_URL = env("AWS_S3_ENDPOINT_URL")
|
||||
AWS_DEFAULT_ACL = "public-read"
|
||||
AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=86400"}
|
||||
# S3 Static settings
|
||||
STATIC_LOCATION = "static"
|
||||
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, STATIC_LOCATION)
|
||||
STATICFILES_STORAGE = "bookwyrm.storage_backends.StaticStorage"
|
||||
# S3 Media settings
|
||||
MEDIA_LOCATION = "images"
|
||||
MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, MEDIA_LOCATION)
|
||||
MEDIA_FULL_URL = MEDIA_URL
|
||||
DEFAULT_FILE_STORAGE = "bookwyrm.storage_backends.ImagesStorage"
|
||||
# I don't know if it's used, but the site crashes without it
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static"))
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images"))
|
||||
else:
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static"))
|
||||
MEDIA_URL = "/images/"
|
||||
MEDIA_FULL_URL = "%s://%s%s" % (PROTOCOL, DOMAIN, MEDIA_URL)
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images"))
|
||||
|
|
|
@ -72,6 +72,14 @@ body {
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.preserve-whitespace p {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
.display-inline p {
|
||||
display: inline !important;
|
||||
}
|
||||
|
||||
/** Shelving
|
||||
******************************************************************************/
|
||||
|
||||
|
|
17
bookwyrm/storage_backends.py
Normal file
17
bookwyrm/storage_backends.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
"""Handles backends for storages"""
|
||||
from storages.backends.s3boto3 import S3Boto3Storage
|
||||
|
||||
|
||||
class StaticStorage(S3Boto3Storage): # pylint: disable=abstract-method
|
||||
"""Storage class for Static contents"""
|
||||
|
||||
location = "static"
|
||||
default_acl = "public-read"
|
||||
|
||||
|
||||
class ImagesStorage(S3Boto3Storage): # pylint: disable=abstract-method
|
||||
"""Storage class for Image files"""
|
||||
|
||||
location = "images"
|
||||
default_acl = "public-read"
|
||||
file_overwrite = False
|
|
@ -1,5 +1,10 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load i18n %}{% load bookwyrm_tags %}{% load humanize %}{% load utilities %}{% load layout %}
|
||||
{% load i18n %}
|
||||
{% load bookwyrm_tags %}
|
||||
{% load humanize %}
|
||||
{% load utilities %}
|
||||
{% load static %}
|
||||
{% load layout %}
|
||||
|
||||
{% block title %}{{ book|book_title }}{% endblock %}
|
||||
|
||||
|
@ -321,5 +326,5 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/vendor/tabs.js"></script>
|
||||
<script src="{% static "js/vendor/tabs.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="display-inline">
|
||||
{% if user.summary %}
|
||||
{{ user.summary|to_markdown|safe|truncatechars_html:40 }}
|
||||
{% else %} {% endif %}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% extends 'feed/feed_layout.html' %}
|
||||
{% extends 'feed/layout.html' %}
|
||||
{% load i18n %}
|
||||
{% block panel %}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% extends 'feed/feed_layout.html' %}
|
||||
{% extends 'feed/layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block panel %}
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Updates" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="columns">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="column is-one-third">
|
||||
<h2 class="title is-5">{% trans "Your books" %}</h2>
|
||||
{% if not suggested_books %}
|
||||
<p>{% trans "There are no books here right now! Try searching for a book to get started" %}</p>
|
||||
{% else %}
|
||||
{% with active_book=request.GET.book %}
|
||||
<div class="tab-group">
|
||||
<div class="tabs is-small">
|
||||
<ul role="tablist">
|
||||
{% for shelf in suggested_books %}
|
||||
{% if shelf.books %}
|
||||
{% with shelf_counter=forloop.counter %}
|
||||
<li>
|
||||
<p>
|
||||
{% if shelf.identifier == 'to-read' %}{% trans "To Read" %}
|
||||
{% elif shelf.identifier == 'reading' %}{% trans "Currently Reading" %}
|
||||
{% elif shelf.identifier == 'read' %}{% trans "Read" %}
|
||||
{% else %}{{ shelf.name }}{% endif %}
|
||||
</p>
|
||||
<div class="tabs is-small is-toggle">
|
||||
<ul>
|
||||
{% for book in shelf.books %}
|
||||
<li class="{% if active_book == book.id|stringformat:'d' %}is-active{% elif not active_book and shelf_counter == 1 and forloop.first %}is-active{% endif %}">
|
||||
<a
|
||||
href="{{ request.path }}?book={{ book.id }}"
|
||||
id="tab-book-{{ book.id }}"
|
||||
role="tab"
|
||||
aria-label="{{ book.title }}"
|
||||
aria-selected="{% if active_book == book.id|stringformat:'d' %}true{% elif shelf_counter == 1 and forloop.first %}true{% else %}false{% endif %}"
|
||||
aria-controls="book-{{ book.id }}">
|
||||
{% include 'snippets/book_cover.html' with book=book cover_class='is-h-m' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% for shelf in suggested_books %}
|
||||
{% with shelf_counter=forloop.counter %}
|
||||
{% for book in shelf.books %}
|
||||
<div
|
||||
class="suggested-tabs card"
|
||||
role="tabpanel"
|
||||
id="book-{{ book.id }}"
|
||||
{% if active_book and active_book == book.id|stringformat:'d' %}{% elif not active_book and shelf_counter == 1 and forloop.first %}{% else %} hidden{% endif %}
|
||||
aria-labelledby="tab-book-{{ book.id }}">
|
||||
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
<div>
|
||||
<p class="mb-2">{% include 'snippets/book_titleby.html' with book=book %}</p>
|
||||
{% include 'snippets/shelve_button/shelve_button.html' with book=book %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header-icon is-hidden-tablet">
|
||||
{% trans "Close" as button_text %}
|
||||
{% include 'snippets/toggle/toggle_button.html' with label=button_text controls_text="book" controls_uid=book.id class="delete" nonbutton=True pressed=True %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{% include 'snippets/create_status.html' with book=book %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% if goal %}
|
||||
<section class="section">
|
||||
<div class="block">
|
||||
<h3 class="title is-4">{% blocktrans with yar=goal.year %}{{ year }} Reading Goal{% endblocktrans %}</h3>
|
||||
{% include 'snippets/goal_progress.html' with goal=goal %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="column is-two-thirds" id="feed">
|
||||
{% block panel %}{% endblock %}
|
||||
|
||||
{% if activities %}
|
||||
{% include 'snippets/pagination.html' with page=activities path=path anchor="#feed" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/vendor/tabs.js"></script>
|
||||
{% endblock %}
|
110
bookwyrm/templates/feed/layout.html
Normal file
110
bookwyrm/templates/feed/layout.html
Normal file
|
@ -0,0 +1,110 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% trans "Updates" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="columns">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="column is-one-third">
|
||||
<section class="block">
|
||||
<h2 class="title is-4">{% trans "Your books" %}</h2>
|
||||
{% if not suggested_books %}
|
||||
<p>{% trans "There are no books here right now! Try searching for a book to get started" %}</p>
|
||||
{% else %}
|
||||
{% with active_book=request.GET.book %}
|
||||
<div class="tab-group">
|
||||
<div class="tabs is-small">
|
||||
<ul role="tablist">
|
||||
{% for shelf in suggested_books %}
|
||||
{% if shelf.books %}
|
||||
{% with shelf_counter=forloop.counter %}
|
||||
<li>
|
||||
<p>
|
||||
{% if shelf.identifier == 'to-read' %}{% trans "To Read" %}
|
||||
{% elif shelf.identifier == 'reading' %}{% trans "Currently Reading" %}
|
||||
{% elif shelf.identifier == 'read' %}{% trans "Read" %}
|
||||
{% else %}{{ shelf.name }}{% endif %}
|
||||
</p>
|
||||
<div class="tabs is-small is-toggle">
|
||||
<ul>
|
||||
{% for book in shelf.books %}
|
||||
<li class="{% if active_book == book.id|stringformat:'d' %}is-active{% elif not active_book and shelf_counter == 1 and forloop.first %}is-active{% endif %}">
|
||||
<a
|
||||
href="{{ request.path }}?book={{ book.id }}"
|
||||
id="tab-book-{{ book.id }}"
|
||||
role="tab"
|
||||
aria-label="{{ book.title }}"
|
||||
aria-selected="{% if active_book == book.id|stringformat:'d' %}true{% elif shelf_counter == 1 and forloop.first %}true{% else %}false{% endif %}"
|
||||
aria-controls="book-{{ book.id }}">
|
||||
{% include 'snippets/book_cover.html' with book=book cover_class='is-h-m' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% for shelf in suggested_books %}
|
||||
{% with shelf_counter=forloop.counter %}
|
||||
{% for book in shelf.books %}
|
||||
<div
|
||||
class="suggested-tabs card"
|
||||
role="tabpanel"
|
||||
id="book-{{ book.id }}"
|
||||
{% if active_book and active_book == book.id|stringformat:'d' %}{% elif not active_book and shelf_counter == 1 and forloop.first %}{% else %} hidden{% endif %}
|
||||
aria-labelledby="tab-book-{{ book.id }}">
|
||||
|
||||
<div class="card-header">
|
||||
<div class="card-header-title">
|
||||
<div>
|
||||
<p class="mb-2">{% include 'snippets/book_titleby.html' with book=book %}</p>
|
||||
{% include 'snippets/shelve_button/shelve_button.html' with book=book %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-header-icon is-hidden-tablet">
|
||||
{% trans "Close" as button_text %}
|
||||
{% include 'snippets/toggle/toggle_button.html' with label=button_text controls_text="book" controls_uid=book.id class="delete" nonbutton=True pressed=True %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
{% include 'snippets/create_status.html' with book=book %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if goal %}
|
||||
<section class="block">
|
||||
<div class="block">
|
||||
<h3 class="title is-4">{% blocktrans with yar=goal.year %}{{ year }} Reading Goal{% endblocktrans %}</h3>
|
||||
{% include 'snippets/goal_progress.html' with goal=goal %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="column is-two-thirds" id="feed">
|
||||
{% block panel %}{% endblock %}
|
||||
|
||||
{% if activities %}
|
||||
{% include 'snippets/pagination.html' with page=activities path=path anchor="#feed" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{% static "js/vendor/tabs.js" %}"></script>
|
||||
{% endblock %}
|
|
@ -1,4 +1,4 @@
|
|||
{% extends 'feed/feed_layout.html' %}
|
||||
{% extends 'feed/layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block panel %}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% trans "Welcome" %}{% endblock %}
|
||||
|
||||
|
@ -9,7 +10,7 @@
|
|||
<div class="modal-background"></div>
|
||||
<div class="modal-card is-fullwidth">
|
||||
<header class="modal-card-head">
|
||||
<img class="image logo mr-2" src="{% if site.logo_small %}/images/{{ site.logo_small }}{% else %}/static/images/logo-small.png{% endif %}" aria-hidden="true">
|
||||
<img class="image logo mr-2" src="{% if site.logo_small %}{% get_media_prefix %}{{ site.logo_small }}{% else %}{% static "images/logo-small.png" %}{% endif %}" aria-hidden="true">
|
||||
<h1 class="modal-card-title" id="get-started-header">
|
||||
{% blocktrans %}Welcome to {{ site_name }}!{% endblocktrans %}
|
||||
<span class="subtitle is-block">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% trans "Import Status" %}{% endblock %}
|
||||
|
||||
|
@ -156,5 +157,5 @@
|
|||
{% endspaceless %}{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/check_all.js"></script>
|
||||
<script src="{% static "js/check_all.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
{% load layout %}{% load i18n %}
|
||||
{% load layout %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{% get_lang %}">
|
||||
<head>
|
||||
<title>{% block title %}BookWyrm{% endblock %} | {{ site.name }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/static/css/vendor/bulma.min.css">
|
||||
<link rel="stylesheet" href="/static/css/vendor/icons.css">
|
||||
<link rel="stylesheet" href="/static/css/bookwyrm.css">
|
||||
<link rel="stylesheet" href="{% static "css/vendor/bulma.min.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/vendor/icons.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/bookwyrm.css" %}">
|
||||
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}{{ media_url }}{{ site.favicon }}{% else %}/static/images/favicon.ico{% endif %}">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}{% get_media_prefix %}{{ site.favicon }}{% else %}{% static "images/favicon.ico" %}{% endif %}">
|
||||
|
||||
{% if preview_images_enabled is True %}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
@ -30,7 +32,7 @@
|
|||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">
|
||||
<img class="image logo" src="{% if site.logo_small %}{{ media_url }}{{ site.logo_small }}{% else %}/static/images/logo-small.png{% endif %}" alt="Home page">
|
||||
<img class="image logo" src="{% if site.logo_small %}{% get_media_prefix %}{{ site.logo_small }}{% else %}{% static "images/logo-small.png" %}{% endif %}" alt="Home page">
|
||||
</a>
|
||||
<form class="navbar-item column" action="/search/">
|
||||
<div class="field has-addons">
|
||||
|
@ -242,8 +244,8 @@
|
|||
<script>
|
||||
var csrf_token = '{{ csrf_token }}';
|
||||
</script>
|
||||
<script src="/static/js/bookwyrm.js"></script>
|
||||
<script src="/static/js/localstorage.js"></script>
|
||||
<script src="{% static "js/bookwyrm.js" %}"></script>
|
||||
<script src="{% static "js/localstorage.js" %}"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
{% load static %}
|
||||
|
||||
<div class="columns">
|
||||
<div class="column is-narrow is-hidden-mobile">
|
||||
<figure class="block is-w-xl">
|
||||
<img src="{% if site.logo %}/images/{{ site.logo }}{% else %}/static/images/logo.png{% endif %}" alt="BookWyrm logo">
|
||||
<img src="{% if site.logo %}/images/{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}" alt="BookWyrm logo">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
<img class="avatar image {% if large %}is-96x96{% elif medium %}is-48x48{% else %}is-32x32{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}" {% if ariaHide %}aria-hidden="true"{% endif %} alt="{{ user.alt_text }}">
|
||||
{% load static %}
|
||||
|
||||
<img class="avatar image {% if large %}is-96x96{% elif medium %}is-48x48{% else %}is-32x32{% endif %}" src="{% if user.avatar %}{% get_media_prefix %}{{ user.avatar }}{% else %}{% static "images/default_avi.jpg" %}{% endif %}" {% if ariaHide %}aria-hidden="true"{% endif %} alt="{{ user.alt_text }}">
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% spaceless %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
<figure
|
||||
class="
|
||||
|
@ -20,14 +21,14 @@
|
|||
class="book-cover"
|
||||
|
||||
{% if book.cover %}
|
||||
src="{% if img_path is None %}/images/{% else %}{{ img_path }}{% endif %}{{ book.cover }}"
|
||||
src="{% if img_path is None %}{% get_media_prefix %}{% else %}{{ img_path }}{% endif %}{{ book.cover }}"
|
||||
itemprop="thumbnailUrl"
|
||||
|
||||
{% if book.alt_text %}
|
||||
alt="{{ book.alt_text }}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
src="/static/images/no_cover.jpg"
|
||||
src="{% static "images/no_cover.jpg" %}"
|
||||
alt="{% trans "No cover" %}"
|
||||
{% endif %}
|
||||
>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
{% load static %}
|
||||
|
||||
{% if preview_images_enabled is True %}
|
||||
{% if image %}
|
||||
<meta name="twitter:image" content="{{ request.scheme }}://{{ media_path }}{{ image }}">
|
||||
<meta name="og:image" content="{{ request.scheme }}://{{ media_path }}{{ image }}">
|
||||
<meta name="twitter:image" content="{{ media_full_url }}{{ image }}">
|
||||
<meta name="og:image" content="{{ media_full_url }}{{ image }}">
|
||||
{% else %}
|
||||
<meta name="twitter:image" content="{{ request.scheme }}://{{ media_path }}{{ site.preview_image }}">
|
||||
<meta name="og:image" content="{{ request.scheme }}://{{ media_path }}{{ site.preview_image }}">
|
||||
<meta name="twitter:image" content="{{ media_full_url }}{{ site.preview_image }}">
|
||||
<meta name="og:image" content="{{ media_full_url }}{{ site.preview_image }}">
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<meta name="twitter:image" content="{{ request.scheme }}://{% if site.logo %}{{ media_path }}{{ site.logo }}{% else %}{{ static_path }}/images/logo.png{% endif %}">
|
||||
<meta name="og:image" content="{{ request.scheme }}://{% if site.logo %}{{ media_path }}{{ site.logo }}{% else %}{{ static_path }}/images/logo.png{% endif %}">
|
||||
<meta name="twitter:image" content="{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}">
|
||||
<meta name="og:image" content="{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}">
|
||||
{% endif %}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% load bookwyrm_tags %}
|
||||
{% load markdown %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% with status_type=status.status_type %}
|
||||
<div
|
||||
|
@ -111,12 +112,12 @@
|
|||
<div class="column is-narrow">
|
||||
<figure class="image is-128x128">
|
||||
<a
|
||||
href="/images/{{ attachment.image }}"
|
||||
href="{% get_media_prefix %}{{ attachment.image }}"
|
||||
target="_blank"
|
||||
aria-label="{% trans 'Open image in new window' %}"
|
||||
>
|
||||
<img
|
||||
src="/images/{{ attachment.image }}"
|
||||
src="{% get_media_prefix %}{{ attachment.image }}"
|
||||
|
||||
{% if attachment.caption %}
|
||||
alt="{{ attachment.caption }}"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
{% load status_display %}
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load static %}
|
||||
|
||||
<div class="media">
|
||||
<figure class="media-left" aria-hidden="true">
|
||||
|
@ -18,7 +19,7 @@
|
|||
itemtype="https://schema.org/Person"
|
||||
>
|
||||
{% if status.user.avatar %}
|
||||
<meta itemprop="image" content="/images/{{ status.user.avatar }}">
|
||||
<meta itemprop="image" content="{% get_media_prefix %}{{ status.user.avatar }}">
|
||||
{% endif %}
|
||||
|
||||
<a
|
||||
|
|
|
@ -28,8 +28,10 @@
|
|||
</div>
|
||||
|
||||
{% if user.summary %}
|
||||
<div class="column box has-background-white-bis content">
|
||||
{% spaceless %}
|
||||
<div class="column box has-background-white-bis content preserve-whitespace">
|
||||
{{ user.summary|to_markdown|safe }}
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
{% include 'snippets/authors.html' %}
|
||||
</td>
|
||||
<td data-title="{% trans "Shelved" %}">
|
||||
{{ book.created_date|naturalday }}
|
||||
{{ book.shelved_date|naturalday }}
|
||||
</td>
|
||||
{% latest_read_through book user as read_through %}
|
||||
<td data-title="{% trans "Started" %}">
|
||||
|
|
|
@ -3,6 +3,8 @@ from collections import namedtuple
|
|||
import csv
|
||||
import pathlib
|
||||
from unittest.mock import patch
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from django.test import TestCase
|
||||
import responses
|
||||
|
@ -13,6 +15,10 @@ from bookwyrm.importers.importer import import_data, handle_imported_book
|
|||
from bookwyrm.settings import DOMAIN
|
||||
|
||||
|
||||
def make_date(*args):
|
||||
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
||||
|
||||
|
||||
class GoodreadsImport(TestCase):
|
||||
"""importing from goodreads csv"""
|
||||
|
||||
|
@ -130,22 +136,25 @@ class GoodreadsImport(TestCase):
|
|||
|
||||
shelf.refresh_from_db()
|
||||
self.assertEqual(shelf.books.first(), self.book)
|
||||
self.assertEqual(
|
||||
shelf.shelfbook_set.first().shelved_date, make_date(2020, 10, 21)
|
||||
)
|
||||
|
||||
readthrough = models.ReadThrough.objects.get(user=self.user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
# I can't remember how to create dates and I don't want to look it up.
|
||||
self.assertEqual(readthrough.start_date.year, 2020)
|
||||
self.assertEqual(readthrough.start_date.month, 10)
|
||||
self.assertEqual(readthrough.start_date.day, 21)
|
||||
self.assertEqual(readthrough.finish_date.year, 2020)
|
||||
self.assertEqual(readthrough.finish_date.month, 10)
|
||||
self.assertEqual(readthrough.finish_date.day, 25)
|
||||
self.assertEqual(readthrough.start_date, make_date(2020, 10, 21))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||
|
||||
def test_handle_imported_book_already_shelved(self):
|
||||
"""goodreads import added a book, this adds related connections"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
shelf = self.user.shelf_set.filter(identifier="to-read").first()
|
||||
models.ShelfBook.objects.create(shelf=shelf, user=self.user, book=self.book)
|
||||
models.ShelfBook.objects.create(
|
||||
shelf=shelf,
|
||||
user=self.user,
|
||||
book=self.book,
|
||||
shelved_date=make_date(2020, 2, 2),
|
||||
)
|
||||
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||
|
@ -164,15 +173,15 @@ class GoodreadsImport(TestCase):
|
|||
|
||||
shelf.refresh_from_db()
|
||||
self.assertEqual(shelf.books.first(), self.book)
|
||||
self.assertEqual(
|
||||
shelf.shelfbook_set.first().shelved_date, make_date(2020, 2, 2)
|
||||
)
|
||||
self.assertIsNone(self.user.shelf_set.get(identifier="read").books.first())
|
||||
|
||||
readthrough = models.ReadThrough.objects.get(user=self.user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
self.assertEqual(readthrough.start_date.year, 2020)
|
||||
self.assertEqual(readthrough.start_date.month, 10)
|
||||
self.assertEqual(readthrough.start_date.day, 21)
|
||||
self.assertEqual(readthrough.finish_date.year, 2020)
|
||||
self.assertEqual(readthrough.finish_date.month, 10)
|
||||
self.assertEqual(readthrough.finish_date.day, 25)
|
||||
self.assertEqual(readthrough.start_date, make_date(2020, 10, 21))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||
|
||||
def test_handle_import_twice(self):
|
||||
"""re-importing books"""
|
||||
|
@ -198,16 +207,14 @@ class GoodreadsImport(TestCase):
|
|||
|
||||
shelf.refresh_from_db()
|
||||
self.assertEqual(shelf.books.first(), self.book)
|
||||
self.assertEqual(
|
||||
shelf.shelfbook_set.first().shelved_date, make_date(2020, 10, 21)
|
||||
)
|
||||
|
||||
readthrough = models.ReadThrough.objects.get(user=self.user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
# I can't remember how to create dates and I don't want to look it up.
|
||||
self.assertEqual(readthrough.start_date.year, 2020)
|
||||
self.assertEqual(readthrough.start_date.month, 10)
|
||||
self.assertEqual(readthrough.start_date.day, 21)
|
||||
self.assertEqual(readthrough.finish_date.year, 2020)
|
||||
self.assertEqual(readthrough.finish_date.month, 10)
|
||||
self.assertEqual(readthrough.finish_date.day, 25)
|
||||
self.assertEqual(readthrough.start_date, make_date(2020, 10, 21))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_handle_imported_book_review(self, _):
|
||||
|
@ -229,9 +236,7 @@ class GoodreadsImport(TestCase):
|
|||
review = models.Review.objects.get(book=self.book, user=self.user)
|
||||
self.assertEqual(review.content, "mixed feelings")
|
||||
self.assertEqual(review.rating, 2)
|
||||
self.assertEqual(review.published_date.year, 2019)
|
||||
self.assertEqual(review.published_date.month, 7)
|
||||
self.assertEqual(review.published_date.day, 8)
|
||||
self.assertEqual(review.published_date, make_date(2019, 7, 8))
|
||||
self.assertEqual(review.privacy, "unlisted")
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
|
@ -256,9 +261,7 @@ class GoodreadsImport(TestCase):
|
|||
review = models.ReviewRating.objects.get(book=self.book, user=self.user)
|
||||
self.assertIsInstance(review, models.ReviewRating)
|
||||
self.assertEqual(review.rating, 2)
|
||||
self.assertEqual(review.published_date.year, 2019)
|
||||
self.assertEqual(review.published_date.month, 7)
|
||||
self.assertEqual(review.published_date.day, 8)
|
||||
self.assertEqual(review.published_date, make_date(2019, 7, 8))
|
||||
self.assertEqual(review.privacy, "unlisted")
|
||||
|
||||
def test_handle_imported_book_reviews_disabled(self):
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import csv
|
||||
import pathlib
|
||||
from unittest.mock import patch
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from django.test import TestCase
|
||||
import responses
|
||||
|
@ -12,6 +14,10 @@ from bookwyrm.importers.importer import import_data, handle_imported_book
|
|||
from bookwyrm.settings import DOMAIN
|
||||
|
||||
|
||||
def make_date(*args):
|
||||
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
||||
|
||||
|
||||
class LibrarythingImport(TestCase):
|
||||
"""importing from librarything tsv"""
|
||||
|
||||
|
@ -125,13 +131,8 @@ class LibrarythingImport(TestCase):
|
|||
|
||||
readthrough = models.ReadThrough.objects.get(user=self.user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
# I can't remember how to create dates and I don't want to look it up.
|
||||
self.assertEqual(readthrough.start_date.year, 2007)
|
||||
self.assertEqual(readthrough.start_date.month, 4)
|
||||
self.assertEqual(readthrough.start_date.day, 16)
|
||||
self.assertEqual(readthrough.finish_date.year, 2007)
|
||||
self.assertEqual(readthrough.finish_date.month, 5)
|
||||
self.assertEqual(readthrough.finish_date.day, 8)
|
||||
self.assertEqual(readthrough.start_date, make_date(2007, 4, 16))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||
|
||||
def test_handle_imported_book_already_shelved(self):
|
||||
"""librarything import added a book, this adds related connections"""
|
||||
|
@ -160,14 +161,11 @@ class LibrarythingImport(TestCase):
|
|||
shelf.refresh_from_db()
|
||||
self.assertEqual(shelf.books.first(), self.book)
|
||||
self.assertIsNone(self.user.shelf_set.get(identifier="read").books.first())
|
||||
|
||||
readthrough = models.ReadThrough.objects.get(user=self.user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
self.assertEqual(readthrough.start_date.year, 2007)
|
||||
self.assertEqual(readthrough.start_date.month, 4)
|
||||
self.assertEqual(readthrough.start_date.day, 16)
|
||||
self.assertEqual(readthrough.finish_date.year, 2007)
|
||||
self.assertEqual(readthrough.finish_date.month, 5)
|
||||
self.assertEqual(readthrough.finish_date.day, 8)
|
||||
self.assertEqual(readthrough.start_date, make_date(2007, 4, 16))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||
|
||||
def test_handle_import_twice(self):
|
||||
"""re-importing books"""
|
||||
|
@ -198,13 +196,8 @@ class LibrarythingImport(TestCase):
|
|||
|
||||
readthrough = models.ReadThrough.objects.get(user=self.user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
# I can't remember how to create dates and I don't want to look it up.
|
||||
self.assertEqual(readthrough.start_date.year, 2007)
|
||||
self.assertEqual(readthrough.start_date.month, 4)
|
||||
self.assertEqual(readthrough.start_date.day, 16)
|
||||
self.assertEqual(readthrough.finish_date.year, 2007)
|
||||
self.assertEqual(readthrough.finish_date.month, 5)
|
||||
self.assertEqual(readthrough.finish_date.day, 8)
|
||||
self.assertEqual(readthrough.start_date, make_date(2007, 4, 16))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_handle_imported_book_review(self, _):
|
||||
|
@ -226,9 +219,7 @@ class LibrarythingImport(TestCase):
|
|||
review = models.Review.objects.get(book=self.book, user=self.user)
|
||||
self.assertEqual(review.content, "chef d'oeuvre")
|
||||
self.assertEqual(review.rating, 5)
|
||||
self.assertEqual(review.published_date.year, 2007)
|
||||
self.assertEqual(review.published_date.month, 5)
|
||||
self.assertEqual(review.published_date.day, 8)
|
||||
self.assertEqual(review.published_date, make_date(2007, 5, 8))
|
||||
self.assertEqual(review.privacy, "unlisted")
|
||||
|
||||
def test_handle_imported_book_reviews_disabled(self):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from collections import namedtuple
|
||||
|
||||
from django.db import IntegrityError
|
||||
from django.db.models import OuterRef, Subquery
|
||||
from django.db.models import OuterRef, Subquery, F
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.paginator import Paginator
|
||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||
|
@ -69,7 +69,8 @@ class Shelf(View):
|
|||
reviews = privacy_filter(request.user, reviews)
|
||||
|
||||
books = books.annotate(
|
||||
rating=Subquery(reviews.values("rating")[:1])
|
||||
rating=Subquery(reviews.values("rating")[:1]),
|
||||
shelved_date=F("shelfbook__shelved_date"),
|
||||
).prefetch_related("authors")
|
||||
|
||||
paginated = Paginator(
|
||||
|
|
44
bw-dev
44
bw-dev
|
@ -31,6 +31,17 @@ function initdb {
|
|||
execweb python manage.py initdb
|
||||
}
|
||||
|
||||
function awscommand {
|
||||
# expose env vars
|
||||
export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
|
||||
export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
|
||||
export AWS_DEFAULT_REGION=${AWS_S3_REGION_NAME}
|
||||
# first arg is mountpoint, second is the whole aws command
|
||||
docker run --rm -it -v $1\
|
||||
-e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_DEFAULT_REGION\
|
||||
amazon/aws-cli $2
|
||||
}
|
||||
|
||||
CMD=$1
|
||||
if [ -n "$CMD" ]; then
|
||||
shift
|
||||
|
@ -83,6 +94,19 @@ case "$CMD" in
|
|||
generate_preview_images)
|
||||
runweb python manage.py generate_preview_images $@
|
||||
;;
|
||||
copy_media_to_s3)
|
||||
awscommand "bookwyrm_media_volume:/images"\
|
||||
"s3 cp /images s3://${AWS_STORAGE_BUCKET_NAME}/images\
|
||||
--endpoint-url ${AWS_S3_ENDPOINT_URL}\
|
||||
--recursive --acl public-read"
|
||||
;;
|
||||
set_cors_to_s3)
|
||||
awscommand "$(pwd):/bw"\
|
||||
"s3api put-bucket-cors\
|
||||
--bucket ${AWS_STORAGE_BUCKET_NAME}\
|
||||
--endpoint-url ${AWS_S3_ENDPOINT_URL}\
|
||||
--cors-configuration file:///bw/$@"
|
||||
;;
|
||||
runweb)
|
||||
runweb "$@"
|
||||
;;
|
||||
|
@ -91,6 +115,24 @@ case "$CMD" in
|
|||
;;
|
||||
*)
|
||||
set +x # No need to echo echo
|
||||
echo "Unrecognised command. Try: build, up, initdb, migrate, bash, shell, dbshell, restart_celery, update, populate_streams, generate_preview_images"
|
||||
echo "Unrecognised command. Try:"
|
||||
echo " up [container]"
|
||||
echo " run"
|
||||
echo " initdb"
|
||||
echo " makemigrations [migration]"
|
||||
echo " migrate [migration]"
|
||||
echo " bash"
|
||||
echo " shell"
|
||||
echo " dbshell"
|
||||
echo " restart_celery"
|
||||
echo " collectstatic"
|
||||
echo " build"
|
||||
echo " clean"
|
||||
echo " populate_streams"
|
||||
echo " generate_preview_images [--all]"
|
||||
echo " copy_media_to_s3"
|
||||
echo " set_cors_to_s3 [cors file]"
|
||||
echo " runweb [command]"
|
||||
echo " rundb [command]"
|
||||
;;
|
||||
esac
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
BIN
locale/zh_Hant/LC_MESSAGES/django.mo
Normal file
BIN
locale/zh_Hant/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
|
@ -15,3 +15,5 @@ requests==2.22.0
|
|||
responses==0.10.14
|
||||
django-rename-app==0.1.2
|
||||
pytz>=2021.1
|
||||
boto3==1.17.88
|
||||
django-storages==1.11.1
|
||||
|
|
Loading…
Reference in a new issue