Compare commits

...

997 commits
v0.6.3 ... main

Author SHA1 Message Date
Mouse Reeve
d16faac6da
Merge pull request #3460 from hughrun/3426
fix failing test from #3432
2024-10-20 18:50:46 -07:00
Hugh Rundle
750cedd078
fix failing test 2024-10-21 12:41:16 +11:00
Mouse Reeve
14dba48415
Merge pull request #3437 from hughrun/published_date
Fix post dates being inconsistent
2024-10-17 15:39:13 -07:00
Mouse Reeve
09bbaf0671
Merge pull request #3442 from bookwyrm-social/dependabot/pip/django-4.2.16
Bump django from 4.2.15 to 4.2.16
2024-10-17 15:33:48 -07:00
Mouse Reeve
f2eb3fdccb
Merge pull request #3458 from bookwyrm-social/update_locales
Update locales
2024-10-17 15:33:33 -07:00
Mouse Reeve
20026b968d
Merge pull request #3454 from Guanchishan/patch-3
show Wikidata link on author page
2024-10-17 15:33:15 -07:00
Mouse Reeve
7ecf3b65d7
Merge pull request #3451 from Guanchishan/patch-1
Fix IntegrityError caused by duplicate periodic task creation
2024-10-17 15:23:31 -07:00
Mouse Reeve
0a3fc58669
Check for wikidata when determining if links are present 2024-10-17 15:18:48 -07:00
Mouse Reeve
0ccbf8d739 Fixes inconsistent wording in source text 2024-10-17 14:56:20 -07:00
Mouse Reeve
8594c8c0f6 Updates locales 2024-10-17 14:55:07 -07:00
Mouse Reeve
f1b0e4b9d6
Merge pull request #3432 from hughrun/3426
add test for resolving users with aliases
2024-10-17 14:49:33 -07:00
Mouse Reeve
1923db31b1
Merge pull request #3434 from hughrun/signed_requests
sign all AP requests
2024-10-17 14:13:55 -07:00
Davidzdh
f8650bbb1c
show Wikidata link on author page
Supporting Wikidata is very wonderful. And how about make Wikidata link visible on author page? 
Modified bookwyrm\templates\author\author.html
2024-10-13 19:02:33 +09:00
Davidzdh
6143aa66e3
Fix IntegrityError when creating periodic tasks
Change PeriodicTask.objects.get_or_create() to PeriodicTask.objects.update_or_create().

This change prevents a potential IntegrityError when creating a periodic task due to duplicate primary key. By using update_or_create, if the record already exists, it will be updated instead of attempting to insert a new record with the same primary key, ensuring the process completes without error.
2024-10-11 20:28:27 +09:00
dependabot[bot]
999829dc8b
Bump django from 4.2.15 to 4.2.16
Bumps [django](https://github.com/django/django) from 4.2.15 to 4.2.16.
- [Commits](https://github.com/django/django/compare/4.2.15...4.2.16)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-08 21:28:47 +00:00
Hugh Rundle
514c54f30a
black formatting 2024-09-19 19:46:03 +10:00
Adeodato Simó
6745a9a3f5
Add test for #3365 fix (#5) 2024-09-19 19:38:33 +10:00
Hugh Rundle
bee9dafc9d
update tests 2024-09-18 19:51:43 +10:00
Hugh Rundle
dd45823790
enable localised dates 2024-09-18 19:40:44 +10:00
Hugh Rundle
a61ca162b0
Fix post dates being inconsistent
Makes all dates fixed except if in the last day, in which case they are relative times.

Fixes #3365
2024-09-14 14:33:02 +10:00
Hugh Rundle
cfe10fa874
update error message 2024-09-09 10:19:03 +10:00
Mouse Reeve
13381b9b4d
Merge pull request #3435 from hughrun/docker-version
docker-compose 'version' has been deprecated
2024-09-08 17:16:10 -07:00
Hugh Rundle
c0ce22a607
docker-compose 'version' has been deprecated 2024-09-09 10:04:16 +10:00
Hugh Rundle
9c89e0fbaf
sign all AP requests
We have been attempting unsigned GET requests and then if they fail,
sending another signed request within the same loop.

This not only causes single-threaded instances to fail, confusing new
users, but is also unnecessary, since servers that don't require
requests to be signed will just ignore the signed headers.

Signing all AP requests simplifies things and should see a
marginal increase in speed when interacting with new servers
that require signed payloads.
2024-09-09 09:46:04 +10:00
Hugh Rundle
1a93ba0cac
check alias with new user data
Instead of breaking a bunch of existing tests...
2024-09-09 08:47:59 +10:00
Hugh Rundle
ed55e052f6
add test for resolving users with aliases 2024-09-08 18:11:47 +10:00
Mouse Reeve
cbdb59d3cf
Merge pull request #3408 from bookwyrm-social/dependabot/pip/aiohttp-3.10.2
Bump aiohttp from 3.9.4 to 3.10.2
2024-09-02 05:39:26 -07:00
Dato Simó
80248d3c1d Convert min_confidence param to string to appease mypy 2024-08-31 20:16:41 -03:00
Mouse Reeve
904aa6c49a
Merge pull request #3394 from matthewmincher/feature/user-shelf-preview-order
Order user shelf previews by book shelved date
2024-08-27 18:38:21 -07:00
Mouse Reeve
4123478058
Merge pull request #3423 from bookwyrm-social/misc-tests
Adds some unit tests
2024-08-27 18:33:43 -07:00
Mouse Reeve
3555ef9d2e
Merge pull request #3416 from bookwyrm-social/edit-status-header
Sets edit status header to indicate status type
2024-08-27 14:37:26 -07:00
Hugh Rundle
23dfe3924d
Merge pull request #3418 from bookwyrm-social/hide-ratings
Hide ratings
2024-08-27 19:06:07 +10:00
Mouse Reeve
abb97e6044
Merge branch 'main' into edit-status-header 2024-08-26 13:34:49 -07:00
Mouse Reeve
a6912dc0c2
Merge branch 'main' into hide-ratings 2024-08-26 12:35:47 -07:00
Mouse Reeve
df607a0e45
Merge pull request #3419 from bookwyrm-social/test-fix
Uses workaround for test that fails ~sometimes~
2024-08-26 12:35:32 -07:00
Mouse Reeve
bd773f41c7 Uses much simpler approach to ensuring test result order 2024-08-26 11:33:01 -07:00
Mouse Reeve
9f5ca7ae60 Uses workaround for test that fails ~sometimes~ 2024-08-25 17:53:43 -07:00
Mouse Reeve
698a0b113c Adds some unit tests 2024-08-24 14:54:52 -07:00
Mouse Reeve
c9155bdd78 Adds migration 2024-08-23 18:17:36 -07:00
Mouse Reeve
23471f698c Prettifies javascript 2024-08-23 17:41:02 -07:00
Mouse Reeve
612098475a Adds option to show ratings to the user settings panel 2024-08-23 17:34:52 -07:00
Mouse Reeve
f53bb62b2b Allow users to hide ratings in the UI 2024-08-23 17:32:03 -07:00
Mouse Reeve
142ed70d4d Sets edit status header to indicate status type
Fixes #2671
2024-08-23 16:42:39 -07:00
Mouse Reeve
413c26bc5e
Merge pull request #3135 from hughrun/csv
csv import and export fixes
2024-08-23 16:29:04 -07:00
Mouse Reeve
7ff1ab974e
Merge pull request #3411 from timothyjrogers/fix-link-verification-modal-unauthenticated-buttons
Narrowed is_authenticated check in verfication_modal to only restrict…
2024-08-12 19:11:07 -07:00
Tim Rogers
cf61279e18 Narrowed is_authenticated check in verfication_modal to only restrict report button 2024-08-12 19:56:26 -05:00
Mouse Reeve
6ec3783736
Merge branch 'main' into dependabot/pip/aiohttp-3.10.2 2024-08-11 17:12:21 -07:00
Mouse Reeve
cf753afaa9
Merge pull request #3410 from timothyjrogers/trim-search-whitespace
Trim search whitespace
2024-08-11 17:12:04 -07:00
Tim Rogers
f68e33ffc6 Added additional test cases for searches with leading and trailing whitespace 2024-08-11 14:02:24 -05:00
Tim Rogers
2bb77d9bf8 Updated search view to trim leading and trailing whitespace for author, book, and list query values 2024-08-11 13:01:48 -05:00
Hugh Rundle
7fc54c509c
fixes for bookwyrm csv import
- fix tests
- revert change to GenericImporter tests
- import the review name
- add extra properties to ImportItem
2024-08-10 16:37:30 +10:00
Mouse Reeve
95c2798fc7
Merge pull request #3401 from dato/comment_progress_keep_start_date
Fix reading progress `start_date` bug
2024-08-09 13:07:01 -07:00
Mouse Reeve
66dd39e6d8
Merge pull request #3151 from dato/celery_inmem
In-memory Celery backend for tests
2024-08-09 13:02:53 -07:00
Mouse Reeve
96a2fa5afc
Merge pull request #3402 from dato/ostatus_no_empty_titles
Avoid empty <title> in templates
2024-08-09 12:43:06 -07:00
Mouse Reeve
9e73ab048b
Merge pull request #3407 from bookwyrm-social/dependabot/pip/django-4.2.15
Bump django from 4.2.14 to 4.2.15
2024-08-09 12:40:24 -07:00
dependabot[bot]
fc33edb749
Bump aiohttp from 3.9.4 to 3.10.2
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.4 to 3.10.2.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.4...v3.10.2)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 18:22:34 +00:00
dependabot[bot]
b45435803a
Bump django from 4.2.14 to 4.2.15
Bumps [django](https://github.com/django/django) from 4.2.14 to 4.2.15.
- [Commits](https://github.com/django/django/compare/4.2.14...4.2.15)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 19:33:05 +00:00
Adeodato Simó
69962bb7c9 Avoid empty <title> in templates
Newer versions of tidylib complain about this.
2024-07-28 21:58:26 -03:00
Hugh Rundle
8aba8caae9
merge migration 2024-07-28 21:08:14 +10:00
Adeodato Simó
3d28019146 Use in-memory backends for Celery in tests and workflows
At the moment, this doesn't have much of an effect since most task
calls are mock'd out.
2024-07-28 06:42:11 -03:00
Adeodato Simó
f4133e0236 celerywyrm: allow broker and result backend from the environment
This allows to easily configure an in-memory transport for tests.
2024-07-28 06:42:11 -03:00
Adeodato Simó
041f2fc413 edit_readthrough: set start_date/finish_date iff present in request
Fixes: #3164
2024-07-28 05:58:38 -03:00
Adeodato Simó
812221456b Add failing test case for comment progress clobbering start_date 2024-07-28 05:56:25 -03:00
Mouse Reeve
43577f3ca0
Merge branch 'main' into csv 2024-07-27 12:26:21 -07:00
Mouse Reeve
7f773b3dbd
Merge pull request #3398 from bookwyrm-social/pylint3
Upgrade pylint to 3.x
2024-07-27 12:03:21 -07:00
Mouse Reeve
7311526f2e
Merge pull request #3375 from bookwyrm-social/pr-template
Reorganizes PR template a bit
2024-07-27 12:01:32 -07:00
Mouse Reeve
355a6071ff
Merge branch 'main' into pylint3 2024-07-27 11:48:58 -07:00
Mouse Reeve
e41b27e5f4
Merge branch 'main' into pr-template 2024-07-27 11:48:48 -07:00
Mouse Reeve
eef055159e
Merge pull request #3400 from dato/upgrade_sqlparse
Add an up-to-date sqlparse to requirements.txt
2024-07-27 11:48:38 -07:00
Adeodato Simó
08876512eb Actions: run makemigrations check with increased verbosity 2024-07-27 15:27:09 -03:00
Adeodato Simó
afd44e109c Add an up-to-date sqlparse to requirements.txt
This is used in utils/db.py to format triggers.

A migration is needed for minor whitespace fixes between
0.4.4 and 0.5.1.
2024-07-27 15:19:43 -03:00
Mouse Reeve
f0e5c334e7
Merge pull request #3399 from dato/pylint_useless_suppression_cleanup
pylint suppression cleanup
2024-07-27 09:21:17 -07:00
Mouse Reeve
300eeac0e1
Merge pull request #3374 from bookwyrm-social/landing-page-books
Use a simpler query for books to show on the landing page
2024-07-27 08:47:02 -07:00
Mouse Reeve
dc54f28e17 Type fix 2024-07-27 08:44:44 -07:00
Mouse Reeve
8ce29849a9
Merge pull request #3393 from bookwyrm-social/dependabot/pip/django-4.2.14
Bump django from 4.2.11 to 4.2.14
2024-07-27 08:36:17 -07:00
Adeodato Simó
0b87aacfce pylint: enable useless-suppression lint and perform cleanup 2024-07-27 03:47:35 -03:00
Adeodato Simó
81ee5e945f pylint: remove unused global suppressions 2024-07-27 03:42:27 -03:00
Adeodato Simó
1a0fbac76c pylint: upgrade to 3.2.6
This only requires fixing:

    E0606: Possibly using variable 'results' before assignment
    E0606: Possibly using variable 'input_type' before assignment
2024-07-27 03:38:47 -03:00
Adeodato Simó
2cdbddca09 .pylintrc: use symbolic names for message suppressions 2024-07-27 03:10:47 -03:00
Hugh Rundle
1608ca6401
improve formatting 2024-07-27 13:16:34 +10:00
Hugh Rundle
93c6b76dab
Merge branch 'main' into csv 2024-07-27 13:04:38 +10:00
Hugh Rundle
94dfbbcc05
fix BookwyrmBooksImporter and tests
- change class attribute to instance attribute for mappings
- remove comment from test
- order import retry jobs in generic importer test

This last change seems innocuous but I may be missing something more fundamental  - it was otherwise failing when multiple tests are run, I think because running tests in parallel led to import jobs getting out of order?
2024-07-27 12:55:15 +10:00
Matthew Mincher
f6eb4f4f27
Add test for shelf order 2024-07-21 13:42:44 +01:00
Matthew Mincher
acc68147dc
Order user shelf previews by book shelved date 2024-07-21 12:39:05 +01:00
Bart Schuurmans
ab307388f4
Merge pull request #3384 from lo48576/fix/css-path-prefix-with-s3-backend
Fix CSS path prefix when S3 storage is used
2024-07-14 13:24:02 +02:00
dependabot[bot]
8a235bcda1
Bump django from 4.2.11 to 4.2.14
Bumps [django](https://github.com/django/django) from 4.2.11 to 4.2.14.
- [Commits](https://github.com/django/django/compare/4.2.11...4.2.14)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 22:16:44 +00:00
Hugh Rundle
06d6360082
merge latest changes and add tests 2024-06-30 18:54:59 +10:00
Hugh Rundle
e5b260e3ee
Merge branch 'main' into csv 2024-06-29 16:03:15 +10:00
Mouse Reeve
3236003107
Merge pull request #3378 from hughrun/get-books-for-user
possible fix for #3372 - user export timeouts
2024-06-22 20:24:36 -07:00
YOSHIOKA Takuma
1a2f434514
Fix CSS path prefix when S3 storage is used
django-sass-processor 1.4 looks up OPTIONS using `sass_processor`
instead of `staticfiles`.

Fixes #3383.
2024-06-13 05:03:10 +09:00
Hugh Rundle
1d4119e853
LOL
remove Q import so pylint doesn't grumble
2024-06-09 10:59:11 +10:00
Hugh Rundle
261e794c1c
possible fix for #3372 - user export timeouts
This definitely needs to be tested on a large DB but I believe it may fix the timeouts b.s. gets when running user exports.

Instead of a gigantic single DB query with heaps of joins, we instead just do a series of simple queries and then use union()
to pull them into a de-duped queryset.

If I understand the results from explain() correctly, this is a massive reduction in DB work:

Unique  (cost=195899.15..198201.71 rows=11808 width=19220)

vs

Unique  (cost=150.28..153.44 rows=16 width=19220)
2024-06-09 10:34:22 +10:00
Mouse Reeve
2e675474a9 Reorganizes PR template a bit
I found the template a little overwhelming, so this is an attempt to
make it a little more navigable and slightly less effortful.
2024-06-08 08:31:34 -07:00
Mouse Reeve
3f08d6d8c4 Use a simpler query for books to show on the landing page 2024-06-08 08:18:54 -07:00
Mouse Reeve
3545a1c3b6
Merge pull request #3362 from hughrun/gh-actions
add GitHub templates for PRs and releases
2024-06-01 18:50:26 -07:00
Mouse Reeve
4d3d5d15d0
Merge pull request #3370 from Tak/add-hashtag-to-context
Add `as:Hashtag` to activitypub context
2024-06-01 18:48:38 -07:00
Bart Schuurmans
d90e8e56d5
Merge pull request #3341 from Minnozz/django-4.2
Upgrade to Django 4.2
2024-05-31 17:04:17 +02:00
Bart Schuurmans
eca246fc61 Fix lint 2024-05-31 16:59:24 +02:00
Bart Schuurmans
aa2312e8af Merge branch 'main' into django-4.2 2024-05-31 16:52:48 +02:00
Bart Schuurmans
61d9e0c260
Move comment to separate line
Co-authored-by: Adeodato Simó <73768+dato@users.noreply.github.com>
2024-05-31 16:49:34 +02:00
Bart Schuurmans
44eedd09d9
Merge pull request #1 from dato/django-4.2
minor contributions to django 4.2 upgrade
2024-05-31 16:49:06 +02:00
Levi Bard
46544451d4 Add as:Hashtag to activitypub context 2024-05-23 09:40:25 +02:00
Mouse Reeve
4e987a0e66
Merge pull request #3369 from bookwyrm-social/dependabot/pip/requests-2.32.0
Bump requests from 2.31.0 to 2.32.0
2024-05-21 14:47:34 -07:00
dependabot[bot]
332286cdff
---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 05:41:48 +00:00
Hugh Rundle
e4035c6da6
add GitHub templates for PRs and releases
This commit adds:

1. a release template for GitHub releases.
2. a pull request template to help contributors and maintainers to understand and merge PRs faster, and label them correctly.

When using GitHub automated release notes, PRs will be split into sections based on the following labels:

- `breaking-change` or `config-change`
- `dependencies`
- `enhancement`
- `fix` or `bug`
- `plumbing`, `tests` or `deployment`
- all other PRs

Any labels not currently in use will be added once the PR for this commit is finalised and merged.
2024-04-28 13:30:47 +10:00
Adeodato Simó
e6ee169c3e
Narrow down bare type: ignore pragmas 2024-04-26 21:20:43 -03:00
Adeodato Simó
29f852b57e
consolidate multiple cache.delete() calls into cache.delete_many() 2024-04-26 15:36:38 -03:00
Bart Schuurmans
acae063652 Fix new warnings from pylint upgrade 2024-04-26 13:59:16 +02:00
Bart Schuurmans
c32f9faaa0 Upgrade pylint to 2.17.7 2024-04-26 13:41:01 +02:00
Bart Schuurmans
e7f95ef4c2 Modify update_fields in save() when modifying objects
https://docs.djangoproject.com/en/5.0/releases/4.2/#setting-update-fields-in-model-save-may-now-be-required
2024-04-25 15:53:53 +02:00
Bart Schuurmans
a6c2ce15dd Early return 2024-04-25 15:51:32 +02:00
Bart Schuurmans
7604d0acdb Simplify ObjectMixin broadcast kwarg 2024-04-25 10:31:24 +02:00
Bart Schuurmans
77832cbec7 Add merge migration 2024-04-25 10:14:07 +02:00
Bart Schuurmans
de67c73237 Add merge migration 2024-04-25 10:14:07 +02:00
Bart Schuurmans
f38622fdc9 Define CSRF_TRUSTED_ORIGINS 2024-04-25 10:14:07 +02:00
Bart Schuurmans
051dab77bb Replace deprecated CICharField with custom collation for case-insensitivity 2024-04-25 10:14:07 +02:00
Bart Schuurmans
2896219e88 Switch from django-redis to the built-in Redis cache backend 2024-04-25 10:14:07 +02:00
Bart Schuurmans
03ac846b5d Migrate from pytz to zoneinfo 2024-04-25 10:14:07 +02:00
Bart Schuurmans
39c2a0feae Update qrcode to 7.4.2 2024-04-25 10:14:07 +02:00
Bart Schuurmans
22986a08f0 Update pytest to 8.1.1 2024-04-25 10:14:07 +02:00
Bart Schuurmans
f6bbe673ca Update responses to 0.25.0 2024-04-25 10:14:07 +02:00
Bart Schuurmans
f324a3cd1d Update pytest-xdist to 3.5.0 2024-04-25 10:14:07 +02:00
Bart Schuurmans
039160e004 Update pytest-env to 1.1.3 2024-04-25 10:14:07 +02:00
Bart Schuurmans
a1ff5a478e Update types-Pillow to 10.2.0.20240331 2024-04-25 10:14:07 +02:00
Bart Schuurmans
1cb86197d5 Update types-requests to 2.31.0.20240311 2024-04-25 10:14:07 +02:00
Bart Schuurmans
2537886b4d Group version constraints for indirect dependencies and change to >= 2024-04-25 10:14:07 +02:00
Bart Schuurmans
1474c0d3aa Remove protobuf as a direct dependency 2024-04-25 10:14:07 +02:00
Bart Schuurmans
e46bc2e9a1 Update redis-py to 5.0.3 2024-04-25 10:14:07 +02:00
Bart Schuurmans
01b37026eb Update Markdown to 3.6 2024-04-25 10:14:07 +02:00
Bart Schuurmans
9ebda3fbe8 Update celery to 5.3.6 2024-04-25 10:14:07 +02:00
Bart Schuurmans
b6174d9101 Update bleach to 6.1.0 2024-04-25 10:14:06 +02:00
Bart Schuurmans
1303f539c3 Update psycopg to 2.9.9 2024-04-25 10:13:21 +02:00
Bart Schuurmans
624115bf11 Use headers dict instead of HTTP_* kwargs or request.META 2024-04-25 10:13:21 +02:00
Bart Schuurmans
224fae7a87 Fix mypy errors 2024-04-25 10:13:21 +02:00
Bart Schuurmans
869bc5a376 Update mypy to 1.7.1 2024-04-25 10:13:21 +02:00
Bart Schuurmans
d80a0146bd Update django-stubs to 4.2.7 2024-04-25 10:13:21 +02:00
Bart Schuurmans
e1fd57a1d6 Fix constructor arguments to SessionMiddleware in tests 2024-04-25 10:13:21 +02:00
Bart Schuurmans
1f8ba4df3e Update python-dateutil to 2.9.0.post0 2024-04-25 10:13:21 +02:00
Bart Schuurmans
c11725a5c8 Update pyotp to 2.9.0 2024-04-25 10:13:21 +02:00
Bart Schuurmans
309147bd98 Update pycryptodome to 3.20.0 2024-04-25 10:13:21 +02:00
Bart Schuurmans
1276112214 Update opentelemetry dependencies 2024-04-25 10:13:21 +02:00
Bart Schuurmans
e9325b8798 Update libsass to 0.23.0 2024-04-25 10:13:21 +02:00
Bart Schuurmans
e0a14ea2ba Update django-sass-processor to 1.4 2024-04-25 10:13:21 +02:00
Bart Schuurmans
69c273486c Update django-model-utils to 4.4.0 2024-04-25 10:13:19 +02:00
Bart Schuurmans
ffb3549e06 Update django-imagekit to 5.0.0 2024-04-25 10:12:30 +02:00
Bart Schuurmans
16e1b17a33 Update django-csp to 3.8 2024-04-25 10:12:30 +02:00
Bart Schuurmans
3dfbc44c9a Update django-celery-beat to 2.6.0 2024-04-25 10:12:30 +02:00
Bart Schuurmans
23bf089004 Update boto3 to 1.34.74 2024-04-25 10:12:30 +02:00
Bart Schuurmans
b5ef9f6241 Configure STORAGES using OPTIONS instead of subclassing 2024-04-25 10:12:30 +02:00
Bart Schuurmans
4fa823e8df Update django-storages to 1.14.2
The problem that boto3 closes files has been worked around in django-storages.
2024-04-25 10:12:30 +02:00
Bart Schuurmans
cfcb873235 Update pytest-cov to 5.0.0 2024-04-25 10:12:30 +02:00
Bart Schuurmans
0007c86a2c Update environs to 11.0.0 2024-04-25 10:12:30 +02:00
Bart Schuurmans
984d7fb7d8 Update pytest-django to 4.8.0 2024-04-25 10:12:30 +02:00
Bart Schuurmans
92a94d2fdc django.utils.timezone.utc alias is deprecated 2024-04-25 10:12:30 +02:00
Bart Schuurmans
0d621b68e0 Reorder operations in save() overrides
Accessing many-to-many relations before saving is no longer allowed.

Reorder all operations consistently:
1. Validations
2. Modify own fields
3. Perform save by calling super().save()
4. Modify related objects and clear caches

Especially clearing caches should be done after actually saving, otherwise the old data can be
re-added immediately by another request before the new data is written.
2024-04-25 10:12:30 +02:00
Bart Schuurmans
47fdad9c87 Use new STORAGES setting 2024-04-25 10:12:30 +02:00
Bart Schuurmans
3349817a0b settings.USE_L10N is deprecated 2024-04-25 10:12:30 +02:00
Bart Schuurmans
45bd67cb04 Add migration resulting from Django 4.2 upgrade 2024-04-25 10:12:29 +02:00
Bart Schuurmans
2f4010b93b Upgrade Django to 4.2
- https://docs.djangoproject.com/en/5.0/releases/4.0/
- https://docs.djangoproject.com/en/5.0/releases/4.1/
- https://docs.djangoproject.com/en/5.0/releases/4.2/
2024-04-25 10:12:29 +02:00
Mouse Reeve
c4b21ee258
Merge pull request #3114 from SMillerDev/feat/api/oauth
feat: add OAuth authentication
2024-04-24 15:45:54 -07:00
Mouse Reeve
ad830dd885
Merge pull request #3350 from Minnozz/custom-port
Correctly handle serving BookWyrm on custom port
2024-04-24 15:27:01 -07:00
Mouse Reeve
366c647585
Merge pull request #3359 from bookwyrm-social/dependabot/pip/aiohttp-3.9.4
Bump aiohttp from 3.9.2 to 3.9.4
2024-04-24 15:13:30 -07:00
Bart Schuurmans
4f58b11330 Include the correct protocol and port in remote IDs 2024-04-24 15:35:19 +02:00
Bart Schuurmans
609bc15406 Support http:// protocol in BookWyrm connector 2024-04-24 15:30:47 +02:00
Bart Schuurmans
c42db40a63 Construct absolute URLs with the correct protocol and port 2024-04-24 15:30:47 +02:00
Bart Schuurmans
3aefbb548e Allow serving BookWyrm on a non-standard port 2024-04-24 15:30:47 +02:00
Bart Schuurmans
baea105c18 pytest.ini env values should be unquoted
Otherwise the quotes end up in the strings.
2024-04-24 15:30:47 +02:00
Bart Schuurmans
c73d1fff6a Remove unnecessary exceptions from validate_url_domain 2024-04-24 15:30:47 +02:00
Bart Schuurmans
3d183a393f
Merge pull request #3360 from hughrun/move-fix
refactor Move for more redundancy
2024-04-24 15:30:19 +02:00
Bart Schuurmans
f24fdf73b5 Update to match newer code style 2024-04-24 15:08:48 +02:00
Bart Schuurmans
839ab2fafd
Merge branch 'main' into move-fix 2024-04-24 14:56:32 +02:00
Bart Schuurmans
637f19b208
Merge pull request #3336 from Minnozz/s3-url-protocol
Support AWS_S3_URL_PROTOCOL
2024-04-24 14:53:55 +02:00
Bart Schuurmans
031223104f Clarify AWS_S3_URL_PROTOCOL in .env.example 2024-04-24 14:46:57 +02:00
Hugh Rundle
6684d60526
refactor Move for more redundancy
As outlined in #3354, a use `Move` fails if the user is moving from a BookWyrm server to another BookWrym server.
This is because:

1. the original code did not announce changes to alsoKnownAs;
2. the original code always checked the locally saved profile rather than refetching the remote data;

This commit fixes both these problems by forcing `MoveUser` to always perform a "refresh" of the local data from the remote, and by saving the user with broadcast=True when updating alsoKnownAs ids.
2024-04-22 13:35:08 +10:00
dependabot[bot]
cca58023ed
Bump aiohttp from 3.9.2 to 3.9.4
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.2 to 3.9.4.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.2...v3.9.4)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-18 15:51:34 +00:00
Bart Schuurmans
bf5c08dbf3 Add docker-compose.override.yml to .gitignore 2024-04-15 13:17:00 +02:00
Bart Schuurmans
be872ed672 Support AWS_S3_URL_PROTOCOL
- Allow setting in .env
- Default to PROTOCOL (same as before)
- Propagate to django-storages so it generates the correct URLs in sass_src
2024-04-15 13:16:51 +02:00
Bart Schuurmans
70f803a1f6
Merge pull request #3353 from dato/fix_quotation_str_pagenum
Fix creation of quotations with no end position
2024-04-15 13:11:55 +02:00
Adeodato Simó
4304cd4a79
use re.escape 2024-04-13 21:26:41 -03:00
Adeodato Simó
8733369605
test_quotation_page_serialization: add test with no position 2024-04-13 21:26:41 -03:00
Adeodato Simó
df78cc64a6
Quotation._format_position: do not treat page numbers as integers
Fixes: #3352
2024-04-13 21:26:41 -03:00
Adeodato Simó
f844abcad9
test_quotation_page_serialization: use strings for page numbers
This follows from #3273, "Allow page numbers to be text, instead of
integers".
2024-04-13 21:26:39 -03:00
Bart Schuurmans
21a39f8170
Merge pull request #3228 from hughrun/user-export
Fix user exports to deal with s3 storage
2024-04-13 22:53:58 +02:00
Hugh Rundle
c3c46144fe
add merge migration 2024-04-13 12:39:40 +10:00
Hugh Rundle
d48d312c0a
Merge branch 'main' into user-export 2024-04-13 12:26:13 +10:00
Hugh Rundle
501fb45528
export avatars to own directory
Saving avatars to /images is problematic because it changes the original filepath from avatars/filename to images/avatars/filename.
In this PR prior to this commit, imports failed as they are looking for a file path beginning with "avatar"
2024-04-13 12:03:35 +10:00
Bart Schuurmans
7d581759da
Merge pull request #3342 from hbrunn/main-pilkit
[FIX] make sure to get Pillow>=10 compatible pilkit
2024-04-11 14:52:22 +02:00
Bart Schuurmans
d5a536ae36 Change pilkit constraint to the version that does work 2024-04-11 14:45:13 +02:00
Bart Schuurmans
26f92db5d8 Merge branch 'main' into main-pilkit 2024-04-11 14:43:10 +02:00
Bart Schuurmans
5686c5ae5d
Merge pull request #3356 from Minnozz/quick-fix-frontend-ci
Install same version of eslint in CI as in dev-tools
2024-04-10 22:10:07 +02:00
Bart Schuurmans
9d9e64399c Install same version of eslint in CI as in dev-tools 2024-04-10 21:26:34 +02:00
Mouse Reeve
b6aba44e42
Merge pull request #3355 from bookwyrm-social/merge-migration
Adds merge migration
2024-04-09 06:04:15 -05:00
Mouse Reeve
3ffbb242a4 Black 2024-04-09 05:59:01 -05:00
Mouse Reeve
af0bd90c15 Adds merge migration 2024-04-09 05:57:27 -05:00
Mouse Reeve
73630331d1
Merge pull request #3299 from Minnozz/absorb
Track which Author/Work/Edition a duplicate has been merged into
2024-04-09 05:55:44 -05:00
Mouse Reeve
ca6dbcb483
Merge pull request #3348 from Minnozz/more-indexes
Define more indexes for slow queries
2024-04-04 15:18:07 -07:00
Bart Schuurmans
e1c54b2933 Remove optimizations with adverse effects
`if not audience` actually causes the entire query to be evaluated, before .values_list() is called.
2024-04-04 13:47:56 +02:00
Bart Schuurmans
439cb3ccaa Remove unnecessary conversions between list and set 2024-04-04 13:15:31 +02:00
Bart Schuurmans
321397a349 Specify which column DISTINCT should apply to 2024-04-03 21:28:22 +02:00
Bart Schuurmans
464a0298c6 Add index for finding active (and local) users 2024-04-03 21:27:52 +02:00
Bart Schuurmans
0501ce39cd Add index for looking up User by username 2024-04-03 21:15:24 +02:00
Bart Schuurmans
4d5a30d953 Add index for looking up KeyPair by remote id 2024-04-03 21:11:27 +02:00
Bart Schuurmans
5cfe7eca6f Add index for finding all statuses in a thread 2024-04-03 21:11:09 +02:00
Bart Schuurmans
5082806b82
Merge pull request #3338 from Minnozz/fix-nginx-location
Make nginx config safer
2024-04-03 19:22:16 +02:00
Mouse Reeve
d1d91f0c2b
Merge pull request #3347 from bookwyrm-social/dependabot/pip/pillow-10.3.0
Bump pillow from 10.2.0 to 10.3.0
2024-04-03 10:01:59 -07:00
dependabot[bot]
ea0ade955b
Bump pillow from 10.2.0 to 10.3.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.2.0 to 10.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.2.0...10.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 16:45:11 +00:00
Mouse Reeve
f085d3d0fe
Merge pull request #3346 from Minnozz/status-remote-id-index
Add index on Status.remote_id
2024-04-02 13:02:35 -07:00
Bart Schuurmans
4bbdd0b2d0 Add index on Status.remote_id
This field is often used in WHERE-clauses in queries that are very slow on bookwyrm.social.
2024-04-02 21:54:30 +02:00
Sean Molenaar
d5fb21f330
Merge branch 'main' into feat/api/oauth 2024-04-01 22:35:19 +02:00
Mouse Reeve
f28800af7f
Merge pull request #3339 from Minnozz/fix-file-leaks
Fix resource leaks
2024-03-31 12:43:19 -07:00
Mouse Reeve
cb3fd0cfc1
Merge branch 'main' into feat/api/oauth 2024-03-31 12:41:12 -07:00
Bart Schuurmans
72ed878eeb
Merge pull request #3343 from Minnozz/update-codeql
Update CodeQL workflows to v3
2024-03-30 22:01:49 +01:00
Bart Schuurmans
f666951934 Update CodeQL workflows to v3
https://github.blog/changelog/2024-01-12-code-scanning-deprecation-of-codeql-action-v2/
2024-03-30 21:56:44 +01:00
Holger Brunn
fcd0087589 [FIX] make sure to get Pillow>=10 compatible pilkit 2024-03-30 01:58:41 +01:00
Bart Schuurmans
ffee29d8e2 Fix resource leaks
Rewrite places where files (or other resources) are opened but not closed to "with" blocks, which
automatically call close() at the end of the scope.

Also simplify some tests where images need to be saved to a model field: an opened file can be
passed directly to FileField.save().
2024-03-29 20:14:10 +01:00
Bart Schuurmans
75bc4f8cb0 Make nginx config safer
Instead of allowing all image files anywhere, and disallowing non-image file under /images/, only
allow image files under /images/ and don't match non-image files elsewhere. They get proxied to web
instead and result in a 404 there.

For example, the old config allowed /exports/foo.jpg to be served, while the new config does not.
2024-03-29 15:04:38 +01:00
Bart Schuurmans
e7ae0fdf93
Merge pull request #3337 from prolibre/apport-perso
flower 2.0.1 fixes a few link bugs (particularly for favicon)
2024-03-29 14:45:59 +01:00
Bart Schuurmans
5d597f1ca9 Use new "with ()" style 2024-03-29 14:25:08 +01:00
Bart Schuurmans
0ac9d12d1c Merge branch 'main' into user-export 2024-03-29 14:23:10 +01:00
Bart Schuurmans
e74de94640
Merge pull request #3334 from ccamara/patch-1
Remove twitter from README.md
2024-03-29 14:21:49 +01:00
Bart Schuurmans
1464d09a43
Merge pull request #3320 from dato/better-fmt-patch-calls
bulk-fmt: bracket-wrap calls to patch() for better readability
2024-03-29 14:19:16 +01:00
Anthony
2272e7a326 flower 2.0.1 fixes a few link bugs (particularly for favicon) 2024-03-29 12:07:52 +01:00
Bart Schuurmans
2bbe3d4c32 Test user export archive contents 2024-03-28 13:50:55 +01:00
Bart Schuurmans
bb5d8152f1 Fix mypy error 2024-03-28 13:21:30 +01:00
Bart Schuurmans
dabf7c6e10 User export testing fixes 2024-03-28 13:09:21 +01:00
Bart Schuurmans
cdbc1d172c Fix double exports subdir in S3 user export 2024-03-27 23:28:24 +01:00
Adeodato Simó
3133a47b7c
Merge from main into 'better-fmt-patch-calls'
Conflicts:
	bookwyrm/tests/test_book_search.py
2024-03-27 17:13:08 -03:00
Bart Schuurmans
c6ca547d58 Fix migration formatting 2024-03-27 20:41:59 +01:00
Bart Schuurmans
797d5cb508 Update BookwyrmExportJob tests 2024-03-27 20:39:57 +01:00
Adeodato Simó
699d637bae
Fix detection of unlisted posts (#3258)
Merged from dato/fix_unlisted_set_from_activity.
2024-03-27 16:29:09 -03:00
Bart Schuurmans
9afd0ebb54 Update migrations 2024-03-27 20:15:06 +01:00
Bart Schuurmans
9685ae5a0a Consolidate BookwyrmExportJob into two tasks
Creating the export JSON and export TAR are now the only two tasks.
2024-03-27 20:13:49 +01:00
Carlos Cámara
98600440d8
Remove twitter from README.md
The Twitter/X account doesn't seem to exist, so removing the badge
2024-03-26 17:14:09 +00:00
Bart Schuurmans
ed2e9e5ea8 Merge migration 2024-03-26 13:41:39 +01:00
Bart Schuurmans
ef57c0bc8b Check last user export too in post handler 2024-03-26 13:41:39 +01:00
Bart Schuurmans
145c67dd21 Merge BookwyrmExportJob export_data field back into one with dynamic storage backend 2024-03-26 13:41:39 +01:00
Bart Schuurmans
6a67943408
Merge branch 'main' into user-export 2024-03-26 13:15:40 +01:00
Mouse Reeve
9dfa218ba5
Merge pull request #3333 from bookwyrm-social/locales
Updates locales and version number
2024-03-25 16:36:51 -07:00
Mouse Reeve
bf52eeaa9e Bump version to 0.7.3. 2024-03-25 16:15:02 -07:00
Mouse Reeve
011e4a27a6 Updates locales and adds missing trimmed on blocktrans 2024-03-25 16:13:00 -07:00
Mouse Reeve
7192449b21
Merge pull request #3325 from Minnozz/author-search-vector
Rework author search
2024-03-25 14:41:25 -07:00
Bart Schuurmans
d9bf848cfa Fix pylint warnings 2024-03-25 18:25:43 +01:00
Bart Schuurmans
bd95bcd50b Add test for special character in cover filename 2024-03-25 18:14:45 +01:00
Bart Schuurmans
f721289b1d Simplify logic for rendering user exports 2024-03-25 18:14:45 +01:00
Bart Schuurmans
a51402241b Refactor creation of user export archive 2024-03-25 18:14:45 +01:00
Bart Schuurmans
e0decbfd1d Fix urlescaped relative path to cover image in export
Fixes #3292
2024-03-25 17:59:39 +01:00
Bart Schuurmans
aee8dc16af Fix pylint warning 2024-03-24 13:27:01 +01:00
Bart Schuurmans
5bd66cb3f7 Only generate signed S3 link to user export when user clicks download 2024-03-24 13:08:33 +01:00
Bart Schuurmans
ab7b0893e0 User exports: handle files that no longer exist on file storage 2024-03-24 12:47:26 +01:00
Bart Schuurmans
471233c1dc Use different export job fields for the different storage backends
This way, the database definition is not depdendent on the runtime configuration.
2024-03-24 12:46:42 +01:00
Bart Schuurmans
073f62d5bb Add exports_volume to docker-compose.yml
Exports should be written to a Docker volume instead of to the bind mount (= source directory). This
way they are shared between different containers even when they run on different machines.
2024-03-24 12:08:29 +01:00
Bart Schuurmans
a770689245 Merge branch 'main' into user-export 2024-03-24 12:07:14 +01:00
Bart Schuurmans
69f464418d Remove problematic migration
This migration is dependent on the runtime configuration (.env); a structural fix will follow.
2024-03-24 12:06:44 +01:00
Bart Schuurmans
f11c80162a
Merge pull request #3331 from Minnozz/revert-docker-mount-ro
Revert "docker-compose.yml: make all bind mounts read only"
2024-03-24 11:30:56 +01:00
Bart Schuurmans
7c2fa746ae Revert "docker-compose.yml: make all bind mounts read only"
This reverts commit 864304f128.
2024-03-24 11:23:23 +01:00
Hugh Rundle
03587dfdc7
migrations 2024-03-24 20:56:20 +11:00
Hugh Rundle
dd27684d4b
set signed s3 url expiry with env value
Adds S3_SIGNED_URL_EXPIRY val to .env and settings (defaults to 15 mins)
Note that this is reset every time the user loads the exports page
and is independent of the _creation_ of export files.
2024-03-24 20:53:49 +11:00
Bart Schuurmans
caebebeb37
Merge pull request #3261 from bSolt/book-series-3256
Add book series by title in feed posts
2024-03-23 20:01:03 +01:00
Bart Schuurmans
592914dc91 Render series number with comma and outside of link on book page 2024-03-23 19:51:20 +01:00
Bart Schuurmans
2915133223
Merge branch 'main' into book-series-3256 2024-03-23 19:37:07 +01:00
Bart Schuurmans
2d2ccd51df Factor out book series info into separate template 2024-03-23 19:35:24 +01:00
Bart Schuurmans
4a690e675a BookDataModel: add dry_run argument to merge_into 2024-03-23 19:28:57 +01:00
Bart Schuurmans
fb82c7a579 Add test for merging authors 2024-03-23 19:28:57 +01:00
Bart Schuurmans
6f191acb27 BookDataModel: fix absorbing data from array and partial date fields 2024-03-23 19:28:57 +01:00
Bart Schuurmans
7fb079cb43 PartialDate: fix __eq__ method 2024-03-23 19:28:57 +01:00
Bart Schuurmans
7066e2815b BookDataModel.merge_into: return and log absorbed fields 2024-03-23 19:28:57 +01:00
Bart Schuurmans
e04cd79ff8 Redirect to new URL when a merged object is requested 2024-03-23 19:28:57 +01:00
Bart Schuurmans
5e123972e8 BookDataModel: implement merge_into method 2024-03-23 19:28:57 +01:00
Bart Schuurmans
b3753ab6da Add MergedBookDataModel 2024-03-23 19:28:57 +01:00
Bart Schuurmans
b8995bd4b1 Add tests for author search 2024-03-23 19:26:51 +01:00
Bart Schuurmans
769d9726e5 Add book search test cases for author aliases 2024-03-23 19:26:51 +01:00
Bart Schuurmans
36222afa79 Switch author search from TrigramSimilarity to SearchQuery 2024-03-23 19:26:51 +01:00
Bart Schuurmans
0795b4d171 Include Author aliases in Book search vector 2024-03-23 19:26:51 +01:00
Bart Schuurmans
2de35f3fc7 Calculate Author search vector with name and aliases 2024-03-23 19:26:51 +01:00
Mouse Reeve
bac52eef3e
Merge pull request #3275 from ccamara/wikidata
Add wikidata field for authors
2024-03-23 08:12:09 -07:00
Mouse Reeve
8bbac458a6
Merge pull request #3217 from dato/switch_edition_invalidate_active_shelves
Invalidate `active_shelf` when switching editions
2024-03-23 07:59:40 -07:00
Mouse Reeve
5b71e94888
Merge branch 'main' into user-export 2024-03-23 07:55:46 -07:00
Mouse Reeve
a914a44fba
Removes unnecessary redeclaration of wikidata model field in Author 2024-03-23 07:54:54 -07:00
Mouse Reeve
8e088a6d53
Merge branch 'main' into switch_edition_invalidate_active_shelves 2024-03-23 07:53:24 -07:00
Mouse Reeve
b508b4cd33
Merge pull request #3323 from Minnozz/docker-bind-ro
Docker: make bind mounts of source code read only
2024-03-23 07:51:00 -07:00
Mouse Reeve
886d6ec9f7
Merge branch 'main' into docker-bind-ro 2024-03-23 07:48:27 -07:00
Mouse Reeve
21f75da75e
Merge pull request #3328 from Minnozz/escape-query-in-link
Escape search query in generated URLs
2024-03-23 07:46:04 -07:00
Mouse Reeve
20db968315
Merge pull request #3322 from Minnozz/fix-font-download
Fix font download
2024-03-23 07:36:43 -07:00
Bart Schuurmans
c3d25c59c5 Escape search query in generated URLs
Otherwise, a query containing '&' or other special characters results in a broken URL.
2024-03-21 16:48:34 +01:00
Bart Schuurmans
3cde6dbe5a
Merge pull request #3326 from Minnozz/black-required-version
black: specify major version 22 only
2024-03-21 16:30:56 +01:00
Bart Schuurmans
682bb3b62f dev-tools: relax black version constraint 2024-03-21 16:25:29 +01:00
Bart Schuurmans
b5b9eddaf0 CI: relax black version constraints 2024-03-20 12:46:37 +01:00
Bart Schuurmans
ab430e0208 requirements.txt: add black
This way, IDEs can be set up to use the black version from the environment instead of a globally
available/bundled black version.
2024-03-20 12:43:17 +01:00
Bart Schuurmans
e13e4237f4 black: specify required-version
This ensures consistent formatting among different contributors / development setups.

https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#required-version
2024-03-20 12:26:21 +01:00
Bart Schuurmans
762786839c
Merge pull request #3134 from dato/trigger_migrations
Support trigger migrations
2024-03-20 12:11:34 +01:00
Bart Schuurmans
4ca52c0b38
Merge branch 'main' into trigger_migrations 2024-03-20 11:47:54 +01:00
Bart Schuurmans
6a87713f9f Recalculate all book search vectors after fixing the author trigger 2024-03-20 11:45:12 +01:00
Mouse Reeve
d08147c6d9
Merge pull request #3244 from bookwyrm-social/dependabot/pip/pillow-10.2.0
Bump pillow from 10.0.1 to 10.2.0
2024-03-19 15:10:30 -07:00
Bart Schuurmans
f423834bd0 Catch the correct exception type from Pillow 2024-03-19 12:42:52 +01:00
Mouse Reeve
d304ceb437
Merge pull request #3324 from bookwyrm-social/dependabot/pip/django-3.2.25
Bump django from 3.2.24 to 3.2.25
2024-03-18 15:05:30 -07:00
dependabot[bot]
47afe34d97
Bump django from 3.2.24 to 3.2.25
Bumps [django](https://github.com/django/django) from 3.2.24 to 3.2.25.
- [Commits](https://github.com/django/django/compare/3.2.24...3.2.25)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 21:48:21 +00:00
Bart Schuurmans
4d23edddca Make sure /images/ and /static/ exist now that the bind mount is read only
Otherwise the static_volume and media_volume can't be mounted there.
2024-03-18 21:35:12 +01:00
Bart Schuurmans
68cb94daf2 docker-compose.yml: don't automatically start dev-tools by assigning profile 2024-03-18 21:34:51 +01:00
Bart Schuurmans
864304f128 docker-compose.yml: make all bind mounts read only
Except dev-tools, since it needs to be able to change the source.
2024-03-18 21:34:09 +01:00
Bart Schuurmans
7690247ab4 Font download: log the exact error 2024-03-18 20:34:47 +01:00
Bart Schuurmans
3367b20965 Font download: destination dir is allowed to exist
Without this argument, an existing directory (but not the file) causes an error.
2024-03-18 20:23:31 +01:00
Bart Schuurmans
748418590f docker-compose.yml: mount static_volume for flower
Because flower also uses BookwyrmConfig, it wants to download fonts, and will download them to an
incorrect location if the static_volume is not mounted.
2024-03-18 20:22:19 +01:00
Bart Schuurmans
ccf2b16d73 requirements.txt: make typing-Pillow match Pillow 2024-03-18 19:52:40 +01:00
dependabot[bot]
3be227fc86 Bump pillow from 10.0.1 to 10.2.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.0.1 to 10.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.0.1...10.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 19:51:24 +01:00
Adeodato Simó
a6dc5bd13f
Make get_file_size robust against typing errors 2024-03-18 15:03:07 -03:00
Adeodato Simó
518da3b9cf Merge from main into 'user-export'
Conflicts:
	bookwyrm/models/bookwyrm_export_job.py
	requirements.txt
2024-03-18 14:47:34 -03:00
Adeodato Simó
2cf7ed477d Consolidate test_posgres.py into test_book_search.py
These are tests I missed when first writing trigger tests in
test_book_search.py.
2024-03-17 22:38:44 -03:00
Adeodato Simó
cceccd1ecf
Merge from main into 'trigger_migrations'
Conflicts:
	requirements.txt
2024-03-17 21:54:15 -03:00
Adeodato Simó
beb49af514
Upgade django-pgtrigger to 4.11 2024-03-17 21:46:34 -03:00
Adeodato Simó
90bd893568 Fix remaining instances of bad-classmethod-argument 2024-03-17 21:28:55 -03:00
Adeodato Simó
e2c9ea3cd2 Fix instances of bad-classmethod-argument in recently edited files 2024-03-17 21:28:55 -03:00
Adeodato Simó
4b9fe0af0c Remove nesting in several with..patch calls 2024-03-17 20:57:39 -03:00
Adeodato Simó
1b9e0546e6 Bracket-wrap calls to patch() for better readability 2024-03-17 20:34:12 -03:00
Bart Schuurmans
8cf52e0a77
Merge pull request #3318 from Minnozz/ci-annotations
CI: update pytest setup and show annotations on PRs
2024-03-17 11:24:01 +01:00
Bart Schuurmans
0282e20b89
Merge branch 'main' into book-series-3256 2024-03-16 11:23:40 +01:00
Bart Schuurmans
4e20e43037 CI: merge all Python actions into one file 2024-03-13 23:36:26 +01:00
Bart Schuurmans
383e6533e1 CI: use pytest-github-actions-annotate-failures 2024-03-13 23:35:05 +01:00
Bart Schuurmans
74fdd9a85a CI: simplify pytest setup 2024-03-13 23:35:05 +01:00
Bart Schuurmans
6af0a08838 CI: use actions/setup-python@v5 and cache pip 2024-03-13 23:35:03 +01:00
Bart Schuurmans
12b469a0d6 CI: use actions/checkout@v4 2024-03-13 23:33:40 +01:00
Mouse Reeve
288743b686
Merge pull request #3315 from Minnozz/fix-pytest-env
pytest.ini: define ALLOWED_HOSTS
2024-03-13 15:29:15 -07:00
Mouse Reeve
a3465e6154
Merge pull request #3303 from MaggieFero/main
Upgrade Python Version and Several Other Packages for Security
2024-03-13 15:28:54 -07:00
Bart Schuurmans
3ba528ecdd pytest.ini: define ALLOWED_HOSTS
This fixes running `./bw-dev pytest` locally when having a different value defined for
`ALLOWED_HOSTS` in `.env`.
2024-03-11 20:12:46 +01:00
Adeodato Simó
304c47863b
FileLinkForm: fix duplicate check (#3311)
Merged from: Minnozz/filelink-duplicate-check.
2024-03-11 15:10:28 -03:00
Mouse Reeve
b68a4cc392
Merge branch 'main' into filelink-duplicate-check 2024-03-09 07:37:26 -08:00
Mouse Reeve
6dfb5000cc
Merge pull request #3305 from dato/export_catch_missing_key_icon
json_export: also detect absent "icon" key
2024-03-09 07:37:14 -08:00
Bart Schuurmans
8d018b872f FileLinkForm: fix duplicate check 2024-03-09 15:49:42 +01:00
Adeodato Simó
9e7b040b73
Fix shelving date changing when changing editions (#3193)
Merged from  from jakejack13/switch-edition
Fixes: #3139.
2024-03-03 18:48:04 -03:00
Adeodato Simó
09c3d9c0dc
json_export: also detect absent "icon" key 2024-03-03 18:42:27 -03:00
Mouse Reeve
dd9d68c97d
Merge pull request #3096 from bookwyrm-social/image-ap-serialization
Changes to how images are serialized
2024-03-02 18:58:08 -08:00
Margaret Fero
d138395c75 Add linter exclusion for TBookWyrmModel 2024-03-02 17:43:49 -08:00
Margaret Fero
91fe4ad535 Fix spacing for linter 2024-03-02 17:31:16 -08:00
Margaret Fero
9fa09d5ebe Add extra space required by linter 2024-03-02 17:30:37 -08:00
Margaret Fero
eadb0e640f Fix typo in operator 2024-03-02 17:29:42 -08:00
Margaret Fero
be140d5e5a Pin setuptools at 65.5.1 2024-03-02 17:20:48 -08:00
Margaret Fero
22c4155c7c Upgrade pytest to 6.2.5 2024-03-02 16:09:34 -08:00
Margaret Fero
498dc35d99 Upgrade Pylint to 2.15.0 2024-03-02 16:09:06 -08:00
Margaret Fero
0f5a3e9163 Pin Tornado at 6.3.3 2024-03-02 16:08:41 -08:00
Margaret Fero
da2636fa29 Add grpcio pin @ 1.57.0 2024-03-02 16:07:50 -08:00
Margaret Fero
c1520da56d Upgrade flower to 2.0.0 2024-03-02 16:05:11 -08:00
Margaret Fero
fee3fdd5a8 Upgrade django-compressor to 4.4 2024-03-02 16:04:37 -08:00
Margaret Fero
c944824ac7 Upgrade django-celery-beat to 2.5.0 2024-03-02 16:04:06 -08:00
Margaret Fero
4312e9bba0 Upgrade Celery to 5.3.1 2024-03-02 16:03:19 -08:00
Margaret Fero
39da471f79 Disable Pylint Failure for imghdr deprecation for now 2024-03-02 15:59:17 -08:00
Margaret Fero
570017d3b0 Upgrade Python Version from 3.9 to 3.11 2024-03-02 15:57:06 -08:00
Margaret Fero
3652ac8100
Alphabetize requirements.txt
Alphabetize requirements.txt for developer convenience; this helps to find duplicates and unnecessarily-pinned subdependencies, as well as making the file easier to read and use.
2024-03-02 15:41:06 -08:00
Margaret Fero
f8fd76cff0
Remove duplicate types-requests==2.31.0.2
The types-requests==2.31.0.2 dependency was double-listed right next to each other; this commit removes one.
2024-03-02 13:57:09 -08:00
Margaret Fero
206ed9f7fb
Merge pull request #2 from bookwyrm-social/main
No Actual Changes
2024-03-02 13:55:24 -08:00
Mouse Reeve
218171e9bc
Merge pull request #3300 from MaggieFero/MaggieFero-add-timeouts-to-requests.get
Add timeouts to requests.get
2024-03-01 22:49:44 -08:00
Margaret Fero
50b811d9aa
Typo fix
Add a comma
2024-03-01 20:11:14 -08:00
Margaret Fero
1ae9870862
Add timeout to base_activity.py
An instance of requests.get was missing a timeout; this commit adds a timeout of 15 as used in other places in this codebase which already have timeouts.
2024-03-01 20:02:40 -08:00
Margaret Fero
db97d76a24
Add timeout to isbn.py
An instance of requests.get in isbn.py lacks a timeout, and this commit adds one with a default of 15 as used other places in the code, where requests.get does already have a timeout.
2024-03-01 19:58:11 -08:00
Mouse Reeve
354388cc8f
Merge pull request #3238 from hughrun/export-fixes
fix multiple issues from user exports config changes
2024-02-29 16:16:25 -08:00
Mouse Reeve
2c59908ddd
Merge branch 'main' into export-fixes 2024-02-29 16:10:20 -08:00
Mouse Reeve
6a70eadba8
Merge pull request #3284 from NetspherePub/072NginxSecurityFixed
Adds production.conf security configuration missing in version 0.7.2
2024-02-29 15:55:56 -08:00
Mouse Reeve
ec52460f02
Merge pull request #3274 from Minnozz/author-search
Add search for author
2024-02-29 15:55:12 -08:00
Adeodato Simó
1fabe51261
Move ratings and reviews when switching editions (#3117)
Merged from mattlehrer/move-ratings-and-reviews-when-switching-editions.
Fixes: #2926.
2024-02-21 18:48:32 -03:00
Adeodato Simó
e6b6bd648d
Merge branch 'main' into move-ratings-and-reviews-when-switching-editions 2024-02-21 18:42:18 -03:00
Mouse Reeve
9d7965780d
Merge pull request #3285 from polarbirke/fix-label-input-association-for-shelves-filter
Fix label and input association for shelves filter
2024-02-20 16:56:57 -08:00
Mouse Reeve
333fb03c2c
Merge pull request #3290 from bookwyrm-social/korean-locale
Korean locale
2024-02-20 16:56:26 -08:00
Mouse Reeve
8f537ef56a Adds missing migration for Korean locale 2024-02-20 16:45:16 -08:00
Mouse Reeve
6163e1a6be
Merge pull request #3283 from NetspherePub/ko_KR
Add Korean (ko-kr) to LANGUAGES and locale.
2024-02-20 16:44:31 -08:00
Ross Chapman
dd1999eb8e
Adds view tests for shelf filters (#3162)
* Adds test file

* Adds success assertion

* Updates tests

* Updates shelf books creation

* Updates assertion to use isbn for Edition model

* Updates query

* trigger workflow test

* Updates validate_html

* Updates comment and test

* Fixes none test

* Adds management command to clear all deleted user data

* Adds success message

---------

Co-authored-by: Mouse Reeve <mousereeve@riseup.net>
Co-authored-by: Mouse Reeve <mouse.reeve@gmail.com>
2024-02-20 16:25:01 -08:00
Søren Birkemeyer
4c0d5ede86 Fix label and input association for shelves filter
This PR correctly associates label and text input of the shelves
filter via for- and id-attributes. With the association in place,
the aria-label can be removed (the label will be announced by
assistive software when the input is focused). This also fixes the
issue that the aria-label was not translated, whereas the label is.
2024-02-10 16:24:52 +00:00
FoW
1c587c5e53 Adds production.conf security configuration missing in version 0.7.2 2024-02-10 17:54:25 +09:00
FoW
ddd13a3e2e Add Korean (ko-kr) to LANGUAGES and locale 2024-02-10 16:17:25 +09:00
Mouse Reeve
7469f1f4ca
Merge pull request #3281 from bookwyrm-social/dependabot/pip/django-3.2.24
Bump django from 3.2.23 to 3.2.24
2024-02-07 14:54:50 -08:00
dependabot[bot]
363cb79951
Bump django from 3.2.23 to 3.2.24
Bumps [django](https://github.com/django/django) from 3.2.23 to 3.2.24.
- [Commits](https://github.com/django/django/compare/3.2.23...3.2.24)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 22:50:00 +00:00
Hugh Rundle
46a158d701
Merge branch 'main' into export-fixes 2024-02-06 18:31:19 +11:00
Hugh Rundle
8773caa26b
Merge pull request #4 from dato/data_upload_max_size_mb
Support DATA_UPLOAD_MAX_MEMORY_MiB, only, in .env
2024-02-06 18:25:44 +11:00
Carlos Camara
89d8537e1b Add wikidata field to author's template 2024-02-05 22:08:34 +00:00
Carlos Cámara
71f527eb1b
Merge branch 'main' into wikidata 2024-02-04 20:34:51 +01:00
Adeodato Simó
4a9d69e169
Support DATA_UPLOAD_MAX_MEMORY_MiB, only, in .env
Since arithmetic is not allowed in .env files, a change in unit for
the variable seems most usable.
2024-02-04 15:34:04 -03:00
Mouse Reeve
d97747078e
Merge pull request #3276 from bookwyrm-social/fixes-version-number
Fixes version number mistakenly reverted
2024-02-03 19:49:48 -08:00
Mouse Reeve
db629255db Fixes version number mistakenly reverted 2024-02-03 18:27:59 -08:00
Carlos Cámara
6ac38564e2 Add wikidata field for authors 2024-02-03 22:55:33 +00:00
Bart Schuurmans
6c9ca0bf19 Add search for author 2024-02-03 21:55:46 +01:00
Mouse Reeve
6b1ffbc634
Merge pull request #3185 from bookwyrm-social/check-version-number
Check version number asynchronously
2024-02-03 08:25:52 -08:00
Mouse Reeve
748c934986 Merge migrations upon merge migrations 2024-02-03 08:20:12 -08:00
Mouse Reeve
f7580c59a5 Merge branch 'main' into check-version-number 2024-02-03 08:19:46 -08:00
Mouse Reeve
4e2b8af147 Adds merge migration 2024-02-03 08:02:51 -08:00
Mouse Reeve
48f8ee57a6 Merge branch 'main' into check-version-number 2024-02-03 08:02:15 -08:00
Mouse Reeve
faf45cf956
Merge pull request #3273 from bookwyrm-social/WesleyAC-freeform-page-number
Allow page numbers to be text, instead of integers
2024-02-03 08:00:34 -08:00
Mouse Reeve
a1ac9494b2 Allow admins to un-schedule tasks 2024-02-03 08:00:07 -08:00
Mouse Reeve
6d5752fb4e Adds merge migration for page numbering fix 2024-02-03 07:40:23 -08:00
Mouse Reeve
37aa7ad2f6 Merge branch 'freeform-page-number' of github.com:WesleyAC/bookwyrm into WesleyAC-freeform-page-number 2024-02-03 07:38:02 -08:00
Mouse Reeve
e0667c6a03
Merge pull request #3237 from Minnozz/status-title-description
Improve OpenGraph tags for status and book pages
2024-02-03 07:37:00 -08:00
Mouse Reeve
103da863c4
Merge pull request #3239 from Minnozz/user-agent
Replace python-requests with BookWyrm in user agent
2024-02-03 07:27:58 -08:00
Mouse Reeve
fa66284000
Merge pull request #3253 from skobkin/patch-autocomplete-fictionbook-format
Adding FictionBook format ("FB2", "FB3") to autocomplete options in "get a copy" block.
2024-02-03 07:26:58 -08:00
Mouse Reeve
0f0420ce04
Merge pull request #3257 from dato/prefer_shared_inbox
Use shared inboxes for mentions too
2024-02-03 07:25:51 -08:00
Mouse Reeve
438d88d8d4
Merge pull request #3260 from bSolt/fix-widths-2023
Fix awkward layout for tablets on /confirm-email, /login, /invite, and /preferences/reactivate
2024-02-03 07:18:35 -08:00
Mouse Reeve
5f2f321ed5
Merge branch 'main' into export-fixes 2024-02-03 07:04:05 -08:00
Mouse Reeve
45cc3dc979
Merge pull request #3249 from dato/cookie_age_setting
Set SESSION_COOKIE_AGE from environment
2024-02-03 07:03:12 -08:00
Mouse Reeve
9c5f6c527b
Fixes translation tags 2024-02-03 06:51:23 -08:00
Mouse Reeve
efa29b269c
Merge pull request #3269 from bookwyrm-social/dependabot/pip/aiohttp-3.9.2
Bump aiohttp from 3.9.0 to 3.9.2
2024-01-30 18:03:00 -08:00
Jacob Kerr
2ba7dff845 Fixed shelving date changing when changing editions 2024-01-30 16:53:59 -05:00
Hugh Rundle
21a8570035
Merge pull request #3207 from rsk2/issue-3187
Hide "year in the books" for newly registered users
2024-01-31 07:03:43 +11:00
Hugh Rundle
ef6fd608fa
Merge branch 'main' into issue-3187 2024-01-30 18:47:07 +11:00
dependabot[bot]
b05621005e
Bump aiohttp from 3.9.0 to 3.9.2
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.0 to 3.9.2.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.0...v3.9.2)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-30 00:10:13 +00:00
Hugh Rundle
3675a4cf3f
disable user exports if using azure 2024-01-29 14:28:30 +11:00
Hugh Rundle
5f7be848fc
subclass boto3 session instead of adding new env value
Thanks Dato!
2024-01-29 14:10:36 +11:00
Hugh Rundle
f96ddaa3e1
Merge pull request #3 from dato/export_job_inject_aws_endpoint_setting
Subclass boto3.Session to use AWS_S3_ENDPOINT_URL
2024-01-29 13:49:45 +11:00
Hugh Rundle
adff3c4251
allow user exports with s3
also undoes a line space change in settings.py to make the PR cleaner
2024-01-29 13:45:35 +11:00
Hugh Rundle
765fc1e43d
fix tests 2024-01-29 12:28:37 +11:00
Adeodato Simó
c106b2a988
Subclass boto3.Session to use AWS_S3_ENDPOINT_URL
As of 0.1.13, the s3-tar library uses an environment variable
(`S3_ENDPOINT_URL`) to determine the AWS endpoint. See:
https://github.com/xtream1101/s3-tar/blob/0.1.13/s3_tar/utils.py#L25-L29.

To save BookWyrm admins from having to set it (e.g., through `.env`)
when they are already setting `AWS_S3_ENDPOINT_URL`, we create a Session
class that unconditionally uses that URL, and feed it to S3Tar.
2024-01-28 22:21:44 -03:00
Hugh Rundle
2c231acebe
linting and tests 2024-01-28 20:35:47 +11:00
Hugh Rundle
a3e05254b5
fix avatar import path 2024-01-28 15:56:44 +11:00
Hugh Rundle
582e97e4a5
Merge branch 'image-serialize' into user-export
pulls Mouse's fix for imagefile serialization
2024-01-28 15:12:15 +11:00
Hugh Rundle
0d619f7eb4
Merge branch 'main' into user-export 2024-01-28 15:11:02 +11:00
Hugh Rundle
2bb9a85591
various fixes
- use signed url for s3 downloads
- re-arrange tar.gz file to match original
- delete all working files after tarring
- import from s3 export

TODO

- check local export and import
- fix error when avatar missing
- deal with multiple s3 storage options (e.g. Azure)
2024-01-28 15:07:55 +11:00
Braden Solt
6add81cf15 move outside of authors "if" 2024-01-27 11:02:42 -07:00
Braden Solt
629acbaa19 add series number on posts in the feed 2024-01-27 10:58:57 -07:00
Braden Solt
940274b1c2 classes that fix widths 2024-01-26 15:47:55 -07:00
Adeodato Simó
accb3273f1
When determining privacy, check for unlisted early
If `followers_url` is found in `to`, the post may still be _unlisted_
if `"https://www.w3.org/ns/activitystreams#Public"` appears in `cc`.
Hence this should be checked earlier.
2024-01-26 06:45:54 -03:00
Adeodato Simó
8ac873419f
refactor: eagerly use a set in recipients, get_recipients 2024-01-26 06:29:59 -03:00
Adeodato Simó
31babdfa51
Always prefer shared inboxes when computing receipent lists
This avoids duplicate submissions to remote instances when mentioning
followers (i.e., `POST /user/foo/inbox` followed by `POST /inbox`, which
results in two separate `add_status` tasks, and might generate duplicates
in the target instance).
2024-01-26 06:18:02 -03:00
Adeodato Simó
80ad36e75b
Include SESSION_COOKIE_AGE in .env.example
Suggested-by: Alexey Skobkin <skobkin-ru@ya.ru>
2024-01-25 20:28:15 +01:00
Adeodato Simó
500e4eb4f5
Merge from main to avoid conflicts 2024-01-25 20:27:54 +01:00
Adeodato Simó
82f9aa9da4
Set SESSION_COOKIE_AGE from environment, default to one month
While we do wish for a longer maximum age (up to one year, see #3082),
we only want to do that after termination of active sessions is
implemented (see #2278).

In the meantime, by reading and setting the variable from settings,
we allow site admins to alter the default.
2024-01-25 20:27:24 +01:00
Alexey Skobkin
2d4b11aaee
Adding FictionBook format ("FB2", "FB3") to autocomplete options in "Get a copy" block. 2024-01-25 01:50:10 +03:00
Mouse Reeve
193aeff4d2
Merge pull request #3245 from WesleyAC/redis-aof-auto-compact
Add redis automatic rewrite configuration.
2024-01-24 08:26:45 -08:00
Rohan Sureshkumar
c4596544a3 Issue-3187: fix failing tests 2024-01-24 19:18:46 +05:30
Wesley Aptekar-Cassels
30ba8d37dc Add redis automatic rewrite configuration.
This should hopefully prevent the AOF file from growing too large.
2024-01-23 18:19:31 -05:00
Bart Schuurmans
eb6bea013f Fix pylint warning 2024-01-21 11:04:08 +01:00
Bart Schuurmans
646b27b7a7 OpenGraph: fall back on book cover when preview images are disabled 2024-01-20 17:34:52 +01:00
Bart Schuurmans
ea9d3f8ba1 Use Status.page_image for OpenGraph tags 2024-01-20 17:34:52 +01:00
Bart Schuurmans
290ee997b3 Refactor OpenGraph tags logic 2024-01-20 17:34:52 +01:00
Bart Schuurmans
ad56024ffe Add Status.page_image property 2024-01-20 17:34:52 +01:00
Bart Schuurmans
f7b4d9ea50 Give individual status page a title and OpenGraph description 2024-01-20 17:34:52 +01:00
Bart Schuurmans
6cb3b97144 Replace python-requests with BookWyrm in user agent
Fixes #3108
2024-01-20 16:15:17 +01:00
Hugh Rundle
a563275308
fix comment in env example 2024-01-20 13:27:30 +11:00
Hugh Rundle
ddc35a7a52
fix multiple issues from user exports config changes
- improve nginx config
- fix DATA_UPLOAD_MAX_MEMORY_SIZE default not being an int
- translate fallback value in id_to_username template tag
- make location of setting to turn on user exports easier to locate for admins

fixes #3227
fixes #3231
fixes #3232
fixes #3236
2024-01-20 13:19:13 +11:00
Hugh Rundle
26c37de2d4
linting 2024-01-20 07:16:42 +11:00
Mouse Reeve
fd0b1d90b0
Merge pull request #3229 from verymilan/nginx-ttf
nginx: fix missing ttf static files
2024-01-18 14:43:05 -08:00
Milan
dd5c314bd5 nginx: also serve svg static files 2024-01-18 22:29:43 +01:00
Milan
a59dcfc890 nginx: fix missing ttf static files 2024-01-18 17:03:02 +01:00
Rohan Sureshkumar
8e2649ba3b Issue-3187: change variable name and code formatting 2024-01-18 21:23:25 +05:30
Rohan
d73141792d
Merge branch 'main' into issue-3187 2024-01-18 21:19:20 +05:30
Hugh Rundle
469172947b
cleanup and linting 2024-01-18 18:43:45 +11:00
Hugh Rundle
833f26fd0e
Merge branch 'main' into user-export 2024-01-18 18:24:56 +11:00
Mouse Reeve
fb5fae4251
Merge pull request #3219 from bSolt/issue-3178
Fix awkward clipping on about page
2024-01-17 15:31:52 -08:00
Mouse Reeve
c22f189c86
Merge pull request #3216 from dato/dev-tools_require_bookworm
Ensure dev-tools uses bookworm
2024-01-17 15:31:43 -08:00
Mouse Reeve
61a6ee29d8
Merge pull request #3224 from hughrun/move-fix
Pass correct user id in Move notification
2024-01-17 14:25:28 -08:00
Mouse Reeve
a585321ef9
Merge pull request #3226 from hughrun/disable-exports
Disable user exports
2024-01-17 14:23:29 -08:00
Hugh Rundle
45d6f1f890
Merge pull request #3215 from ccamara/export_bookshelf
Export bookshelves and review date
2024-01-17 21:20:32 +11:00
Hugh Rundle
b990d9ccd8
Pass correct user id in Move notification
We were passing the *requesting* user's moved_to value to the Move notification template, instead of the id of the user that they are being notified about.
Additionally, the id_to_username template tag had no fallback for if the user_id is None.

This resolves both problems and removes an unnecessary space in a template for when the logged in user made the move.

Fixes #3196
2024-01-17 21:06:04 +11:00
Hugh Rundle
ea7f3c297e
allow js and css 2024-01-17 20:12:06 +11:00
Hugh Rundle
d640e4ac96
disable user exports by default
- new setting to enable user exports defaults to False
- add setting to enable and disable user exports
- do not allow user exports when using s3 storage
- do not serve non-image files from /images/ (requires update to nginx settings)
- increase default file upload limit to 100MB to enable user exports to be imported (can be changed in .env)
2024-01-16 21:32:13 +11:00
Carlos Camara
ddbda3ab9c Fix test_export 2024-01-16 08:12:59 +00:00
bSolt
76a3874662 add bulma classes to fix awkward spacing 2024-01-15 23:25:52 -07:00
Rohan
8144507893
Merge branch 'main' into issue-3187 2024-01-15 17:25:36 +05:30
Rohan Sureshkumar
70adf878e8 Merge branch 'issue-3187' of https://github.com/rsk2/bookwyrm into issue-3187 2024-01-15 17:23:17 +05:30
Rohan Sureshkumar
5ef104b802 Issue-3187: addressing review comments 2024-01-15 17:22:33 +05:30
Hugh Rundle
d4d2734dab
ignore exports dir 2024-01-14 14:14:20 +11:00
Hugh Rundle
62cc6c298f
oops
- remove test export files
- check in emblackened files
2024-01-14 12:19:59 +11:00
Hugh Rundle
cbd08127ef
initial work on fixing user exports with s3
- custom storages
- tar.gz within bucket using s3_tar
- slightly changes export directory structure
- major problems still outstanding re delivering s3 files to end users
2024-01-14 12:14:44 +11:00
Adeodato Simó
eb13eb9882
Invalidate active_shelf when switching editions 2024-01-13 19:00:57 +01:00
Adeodato Simó
9a487b0442
Ensure dev-tools uses bookworm
In 1937177e1 ("dev-tools: use apt source for Node instead of setup script"),
I introduced the use of `Signed-By` with a public key block, which is only
supported in bookworm (bullseye only supports fingerprints, TTBOMK).

Python's Docker images already use bookworm by default, but we explicitly
require it now to avoid build errors if someone has a very old image laying
around (see, e.g., #3190).

(This can be dropped after Debian 13 ‘trixie’ is released.)
2024-01-13 17:55:21 +01:00
Carlos Camara
854eb36618 Export bookshelves and review date 2024-01-13 16:47:51 +00:00
Hugh Rundle
b04ebe397b
Merge pull request #3189 from ccamara/2965_export_readthrough
Export ReadThrough in the csv export
2024-01-12 16:35:41 +11:00
Hugh Rundle
5d13bf8e49
Merge branch 'main' into 2965_export_readthrough 2024-01-12 16:12:01 +11:00
Rohan
6dc95a82d6
Merge branch 'bookwyrm-social:main' into issue-3187 2024-01-09 17:06:22 +05:30
Rohan Sureshkumar
1a682753c0 Issue-3187: changes 2024-01-09 15:31:05 +05:30
Mouse Reeve
a4599d0374
Merge pull request #3205 from bookwyrm-social/revert-3079-deleted_user_follow_request
Revert "Don't show notification for user follow request if the user is inactive"
2024-01-08 10:28:27 -08:00
Mouse Reeve
83ff880603
Revert "Don't show notification for user follow request if the user is inactive" 2024-01-07 08:31:48 -08:00
Carlos Camara
ce18d343e8 Fix pylint error and code format 2024-01-06 09:55:39 +01:00
Carlos Camara
93cab480d6 Code format 2024-01-06 09:50:14 +01:00
Mouse Reeve
1966f1d9a3
Merge pull request #3199 from bookwyrm-social/dependabot/pip/pycryptodome-3.19.1
Bump pycryptodome from 3.16.0 to 3.19.1
2024-01-05 16:54:55 -08:00
dependabot[bot]
f267fc3235
Bump pycryptodome from 3.16.0 to 3.19.1
Bumps [pycryptodome](https://github.com/Legrandin/pycryptodome) from 3.16.0 to 3.19.1.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.16.0...v3.19.1)

---
updated-dependencies:
- dependency-name: pycryptodome
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-05 17:42:04 +00:00
Wesley Aptekar-Cassels
6cd2c91135 Allow page numbers to be text, instead of integers.
Fixes: #2640
2024-01-04 19:09:39 -05:00
Carlos Camara
c2622a510c Change else statement to None vs "" 2024-01-04 11:40:40 +01:00
Carlos Camara
ebcc81dd73 Revert changes to default book
These changes were introduced by mistake in my previous commit.
2024-01-04 11:33:26 +01:00
Carlos Camara
30c9ec9611 Prevent lint error
See @hughrun 's explanation https://github.com/bookwyrm-social/bookwyrm/pull/3189#issuecomment-1876145423
2024-01-04 11:28:17 +01:00
Carlos Camara
51cb70d344 Change readhtrough order 2024-01-04 11:27:17 +01:00
Carlos Camara
9acb5f66fe Convert DateTime to date 2024-01-04 11:26:44 +01:00
Carlos Camara
ae5950f187 Add readthrough fields to text_export.py 2024-01-04 11:10:38 +01:00
Carlos Camara
766a2163dd Code formatting 2024-01-03 20:41:31 +01:00
Carlos Camara
db8c686dd3 Include book Readtrhough in the csv export 2024-01-03 15:43:15 +01:00
Mouse Reeve
597378bb78
Merge pull request #3183 from bookwyrm-social/erase_user_command
Adds management command to clear all deleted user data
2024-01-02 20:13:03 -08:00
Mouse Reeve
9c3e6384f8
Merge pull request #3118 from rosschapman/let-a-user-search-within-their-books
Let a user search books within their shelves
2024-01-02 18:37:33 -08:00
Mouse Reeve
01db77a745 Adds success message 2024-01-02 18:29:55 -08:00
Mouse Reeve
d287581620 Fixes html validation error 2024-01-02 13:31:18 -08:00
Mouse Reeve
193a1c7d54 updates wording and fixes get or create logic 2024-01-02 13:28:25 -08:00
Mouse Reeve
8be9e91d21 Re-use schedules rather than creating new ones 2024-01-02 13:18:26 -08:00
Mouse Reeve
f36af42f41 Adds view to see scheduled tasks 2024-01-02 13:05:44 -08:00
Mouse Reeve
5509941aa4 Adds schedule-able task to check for version updates 2024-01-02 13:05:26 -08:00
Mouse Reeve
d6f7f76c4d Removes outdated/unused version and updating code
I had the bright idea of creating this update script but it doesn't work
and hasn't been maintained, so it's just sitting there causing confusing
and requiring weird things to exist in other places.

Now, the unused `version` field can be removed and I can scrap the
management command for getting versions.
2024-01-02 11:37:01 -08:00
Mouse Reeve
381490e31d Adds management command to clear all deleted user data 2024-01-02 10:50:46 -08:00
Mouse Reeve
addfee0607
Merge pull request #3182 from bookwyrm-social/broken-migration
Removes part of migration causing upgrade issues
2024-01-02 10:50:35 -08:00
Mouse Reeve
2a85378456 Removes part of migration causing upgrade issues 2024-01-02 09:57:41 -08:00
Mouse Reeve
d9a640c809 Fixes version number 2024-01-02 08:36:42 -08:00
Mouse Reeve
0756c5ac5c
Merge pull request #3180 from bookwyrm-social/version-0-7-0
Version 0.7.0 miscellenea
2024-01-01 19:58:15 -08:00
Mouse Reeve
913a19c8f0 Formats migration file 2024-01-01 19:33:49 -08:00
Mouse Reeve
e2249f2515 Updates locales 2024-01-01 19:30:03 -08:00
Mouse Reeve
f72ada4780 Updates javascript cache buster just in case 2024-01-01 19:29:43 -08:00
Mouse Reeve
86d79f537a Adds merge migration 2024-01-01 19:29:24 -08:00
Mouse Reeve
fb16806afe
Merge pull request #3177 from dato/naturalday_partial_fixes
Adjustments to naturalday_partial
2024-01-01 19:16:16 -08:00
Mouse Reeve
ffeca9f908
Merge pull request #3150 from dato/get_representative_atomic
Make get_representative() atomic
2024-01-01 19:14:19 -08:00
Mouse Reeve
45d33c37ea
Merge pull request #3175 from dato/prefer_nodesource
dev-tools: ensure we install Node from upstream
2024-01-01 19:12:02 -08:00
Mouse Reeve
ca79cb1ca7
Merge pull request #3054 from bookwyrm-social/user-migration
User migration via export file
2024-01-01 19:04:43 -08:00
Mouse Reeve
5647477ba7
Merge pull request #3154 from bookwyrm-social/ukrainian
Adds Ukranian locale and updates locales
2024-01-01 18:54:43 -08:00
Adeodato Simó
4711b3bc19
naturalday_partial: simplify/refactor 2024-01-01 18:36:31 +01:00
Adeodato Simó
0d908b594c
naturalday_partial: do not naturalize dates with missing parts 2024-01-01 18:36:31 +01:00
Adeodato Simó
0e3936cb61
naturalday_partial: do naturalize date and datetime objects 2024-01-01 18:36:30 +01:00
Dato Simó
09b2dea995 dev-tools: ensure we install Node from upstream
Fixes: #3173 ("`bw-dev build` fails")
2024-01-01 09:01:21 -03:00
Mouse Reeve
3754718916 Updates locales again 2023-12-30 15:54:06 -08:00
Mouse Reeve
9b3f4933ac Fixes language code for Ukrainian 2023-12-17 06:57:05 -08:00
Mouse Reeve
47cdc14bc0
Update bookwyrm/migrations/0189_alter_user_preferred_language.py
Co-authored-by: Demid <grrrr@protonmail.com>
2023-12-17 06:54:39 -08:00
Mouse Reeve
430e4eb90d
Update bookwyrm/settings.py
Co-authored-by: Demid <grrrr@protonmail.com>
2023-12-17 06:52:49 -08:00
Ross Chapman
b728bb4323 Uses block trans 2023-12-16 12:05:35 -08:00
Ross Chapman
a4172214d1 Updates size of filters panel label 2023-12-15 13:17:23 -08:00
Ross Chapman
fb36958444 Removes unused variable 2023-12-14 13:47:51 -08:00
Ross Chapman
44d21d1ba4 Updates view logic 2023-12-14 13:04:45 -08:00
Ross Chapman
bd3acdbf31 Puts string in template 2023-12-14 12:33:27 -08:00
Ross Chapman
4a4046a704 Shows message if empty and renames "search" to "filter" 2023-12-14 11:30:01 -08:00
Ross Chapman
7cca199a11 Merge branch 'main' into let-a-user-search-within-their-books 2023-12-14 10:25:05 -08:00
Hugh Rundle
1649457372
Merge pull request #3156 from hughrun/user-migration
fix upsert_statuses
2023-12-13 20:59:47 +11:00
Hugh Rundle
7fcadb1d4d
fix upsert_statuses
- remote_id is now updated on import of statuses
- statuses cannot be imported unless source has target listed in alsoKnownAs or movedTo
- add alert boxes to import and export screens advising of the above
- update tests accordingly
2023-12-13 20:55:38 +11:00
Mouse Reeve
5c0e159d43 Adds Ukranian locale and updates locales 2023-12-12 15:42:40 -08:00
Mouse Reeve
000e5e6145
Merge pull request #3152 from bookwyrm-social/fixes-typo-in-move-notice
Fixes incorrect translation and display of moved user page
2023-12-12 14:59:00 -08:00
Mouse Reeve
8bb5a664c5 Fixes incorrect translation and display of moved user page 2023-12-11 20:12:14 -08:00
Mouse Reeve
e032e5491d
Merge pull request #3144 from villasv/patch-1
Update page formatter on ordered collection
2023-12-11 19:54:24 -08:00
Mouse Reeve
4bfa1ca5b8
Merge pull request #3124 from hughrun/softblock
Allow removing followers and fix follow rejections
2023-12-11 19:49:45 -08:00
Adeodato Simó
13374917f3
Make get_representative() atomic 2023-12-11 20:48:32 -03:00
Mouse Reeve
799f842115
Merge pull request #3146 from dato/setup_test_data
Faster tests with setUpTestData
2023-12-11 15:45:40 -08:00
Adeodato Simó
aa67f598dd
Explicitly set doctype to html5 when invoking tidy_document()
Many tests break without this on newer versions of html-tidy.
2023-12-11 19:40:48 -03:00
Adeodato Simó
9d502f5ee2
Use setUpTestData() to speed up tests
Pylint's `bad-classmethod-argument` is disabled for each definition
to avoid rewriting the method bodies just to rename `self` → `cls`.
This can be done gradually, as the setUpTestData methods are modified
along the way.
2023-12-11 19:40:30 -03:00
Mouse Reeve
198c0037c6
Merge pull request #3121 from hughrun/413
Display custom page on 413 errors
2023-12-09 08:31:25 -08:00
Mouse Reeve
e5d292919c
Merge pull request #3143 from dato/test_ordered_collection_use_bulk_create
Use bulk_create to test ordered collections
2023-12-09 08:30:23 -08:00
Mouse Reeve
029b438355 Clarify import/export of book vs user
I think this wording is a little clearer
2023-12-09 08:18:31 -08:00
Mouse Reeve
dd72013225 Small fixes for notifications
Adds a link in the text of the notification, and fixes references to
notification type in the model
2023-12-09 08:09:22 -08:00
Sean Molenaar
5d09c54e57
Merge branch 'main' into feat/api/oauth 2023-12-07 15:38:19 +01:00
Ross Chapman
aac8aa1adf Fixes formatting 2023-12-06 11:36:15 -08:00
Ross Chapman
0f6e567b21 Clean up 2023-12-05 19:49:38 -08:00
Ross Chapman
c65e165aeb Hides filter if shelf empty 2023-12-05 19:37:29 -08:00
Ross Chapman
979162da10 Uses filters, fixes for any shelf 2023-12-05 19:33:59 -08:00
Ross Chapman
b27ed847d5 Fixes result set passed to template 2023-12-05 16:36:58 -08:00
Ross Chapman
d93da4e86d Checkpoint 2023-12-05 15:46:08 -08:00
Victor Villas
8fd05004ea
Update page formatter on ordered collection 2023-12-03 20:03:33 -08:00
Adeodato Simó
5384e4c470
Use bulk_create to test ordered collections 2023-11-30 15:58:48 -03:00
Sean Molenaar
b7ba6f1a36
urls.py: fix style 2023-11-30 11:25:51 +01:00
Matt Lehrer
7f55495287 Merge branch 'move-ratings-and-reviews-when-switching-editions' of github.com:mattlehrer/bookwyrm into move-ratings-and-reviews-when-switching-editions 2023-11-30 11:15:33 +01:00
Matt Lehrer
31a78a5c9e linted 2023-11-30 11:13:11 +01:00
Mouse Reeve
193a36390b
Merge pull request #3083 from hughrun/file-resubmit
use bw-file-resubmit to retain images during validation checks
2023-11-29 15:36:33 -08:00
Mouse Reeve
cf1afefc84
Merge pull request #3133 from dato/search_results_fix_work_order
Do not create a set for already-distinct query result
2023-11-29 15:27:57 -08:00
Mouse Reeve
b8bf3d5bd9
Merge pull request #3138 from bookwyrm-social/dependabot/pip/aiohttp-3.9.0
Bump aiohttp from 3.8.6 to 3.9.0
2023-11-27 17:33:18 -08:00
dependabot[bot]
58f149d889
Bump aiohttp from 3.8.6 to 3.9.0
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.8.6 to 3.9.0.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.8.6...v3.9.0)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 01:02:35 +00:00
Ross Chapman
90cc28986e Merge branch 'main' into let-a-user-search-within-their-books 2023-11-27 11:40:57 -08:00
Adeodato Simó
d6eb390cee
Add test that forces book_authors_search_vector_trigger to execute 2023-11-26 15:59:17 -03:00
Adeodato Simó
b5805accac
Minor improvements to bookwyrm_book trigger code
- do not COALESCE columns that cannot be NULL
- do not bring bookwyrm_book to author names JOIN
- add comments documenting the four steps
2023-11-25 21:49:15 -03:00
Adeodato Simó
bbfbd1e97a
Add tests for trigger code (i.e. how search_vector is computed) 2023-11-25 20:54:49 -03:00
Adeodato Simó
9bcb5b80ea
Further simplify bookwyrm_author trigger 2023-11-25 18:13:40 -03:00
Adeodato Simó
8df408e07e
Define search_vector_trigger via Book.Meta.triggers 2023-11-25 17:02:54 -03:00
Adeodato Simó
bcb3a343d4
Fix JOIN in author_search_vector_trigger, add missing WHERE clause 2023-11-25 16:23:21 -03:00
Adeodato Simó
416a6caf2d
Define author_search_vector_trigger via Author.Meta.triggers
Previously, triggers lived only in a particular migration file. With
this change, code for the triggers resides in the model, and their
lifecycle is managed through normal Django migrations.
2023-11-25 16:17:51 -03:00
Adeodato Simó
44ef928c3c
Alter object row IDs to force test failure in original code 2023-11-25 16:11:01 -03:00
Hugh Rundle
539a9fa212
csv import and export fixes
Adds shelved and published dates for books and their imported reviews.
Provides option to create new (custom) shelves when importing books.

fixes #3004
fixes #2846
fixes #2666
fixes #2411
2023-11-25 17:34:12 +11:00
Adeodato Simó
e4d688665c
Remove index for author.search_vector, which is never used 2023-11-24 22:43:12 -03:00
Adeodato Simó
0299f2e235
Add functional tests for search_vector triggers
As metadata changes, search continues to work.
2023-11-24 22:28:41 -03:00
Adeodato Simó
c997d2d44a
Add test to assert distinct() clause
Also, tweak other `search_title_author()` tests to verify ordering by
edition rank.
2023-11-24 02:28:27 -03:00
Adeodato Simó
e322d3cae1
Do not create a set for already-distinct query result 2023-11-23 23:01:56 -03:00
Hugh Rundle
48904fc60b
Merge pull request #3132 from hughrun/user-migration
notification type migration after merge
2023-11-24 06:51:51 +11:00
Hugh Rundle
99a9a64708
notification type migration after merge 2023-11-24 06:50:32 +11:00
Hugh Rundle
065e15e4db
Merge pull request #3131 from hughrun/user-migration
merge migrations and lint
2023-11-22 21:31:12 +11:00
Hugh Rundle
72c1c6ee3d
merge migrations and lint 2023-11-22 21:29:54 +11:00
Hugh Rundle
0276c15948
Merge branch 'main' into user-migration 2023-11-22 21:00:04 +11:00
Hugh Rundle
c6dea2523c
Merge branch 'main' into softblock 2023-11-22 20:06:02 +11:00
Hugh Rundle
6ba7418121
improve tests and minor cleanup 2023-11-22 20:04:17 +11:00
Hugh Rundle
8ed4a997f8
add comment back to bookwyrm.js 2023-11-21 20:20:11 +11:00
Hugh Rundle
2c9ebba5d7
fix reject PR
- rationalise activitypub.Reject and fix model being undefined
- fix not being able to follow users from followers page: 'delete' option now in user_options dropdown
- revert bookwyrm.js
- fix delete_follow_request deleting instead of rejecting
- add user id to 'remove-follow' path
2023-11-21 20:13:56 +11:00
Mouse Reeve
7c2de92df3
Merge pull request #3128 from bookwyrm-social/test-themes
Give admins option to test if a theme loads correctly
2023-11-20 12:26:09 -08:00
Mouse Reeve
b6325da9ab
Update bookwyrm/tests/views/admin/test_themes.py
Co-authored-by: Adeodato Simó <73768+dato@users.noreply.github.com>
2023-11-20 10:37:12 -08:00
Mouse Reeve
179dbd75aa Adds tests 2023-11-20 10:23:59 -08:00
Mouse Reeve
b022b5a1b7
Merge pull request #3120 from hughrun/permission-required
403 handler
2023-11-20 10:06:24 -08:00
Mouse Reeve
c2742b4d80 Updates migrations 2023-11-20 10:02:49 -08:00
Mouse Reeve
cfe42305be Merge branch 'main' into test-themes 2023-11-20 10:02:23 -08:00
Mouse Reeve
d828ba0bc6 Give admins option to test if a theme loads correctly
If a theme is uploaded incorrectly or has errors in it, users can still
select the theme but it will cause a 500 error on every page, making the
app unusable and also making it impossible for them to switch to a
functional theme.

A better fix would be to fail gracefully, but in lieu of that, this will
at least let admins confirm if a theme is broken safely.
2023-11-20 09:56:51 -08:00
Matt Lehrer
6933f70af3
Merge branch 'bookwyrm-social:main' into move-ratings-and-reviews-when-switching-editions 2023-11-20 09:31:45 +01:00
Mouse Reeve
d94b27b723
Merge branch 'main' into user-migration 2023-11-19 19:18:22 -08:00
Mouse Reeve
3d9f339bd5
Merge pull request #3059 from dato/stable_dates_v2
Partial, stable dates with automatic precision field
2023-11-19 19:17:49 -08:00
Mouse Reeve
1d5cc83347
Merge branch 'main' into permission-required 2023-11-19 19:12:32 -08:00
Mouse Reeve
d8018cb937
Merge pull request #3125 from hughrun/instance-actor
hide instance actor from users
2023-11-19 19:11:17 -08:00
Mouse Reeve
4da96d937e
Merge pull request #3126 from hughrun/savedlist-pagination
fix saved list pagination
2023-11-19 18:57:57 -08:00
Hugh Rundle
446854ccf0
fix saved list pagination
The SavedLists view was passing through an incorrect "path" value. Now it's not.
2023-11-20 12:45:39 +11:00
Hugh Rundle
f011f2bce9
hide instance actor from users
The Instance Actor is required for signing http GET requests but is not a "user" and should not be otherwise interacted with.

- hides instance actor profile page, returning a 404
- excludes instance actor from search results and suggestions including in Getting Started
- replaces link to user profile in user admin page with a brief message box
- replaces panel in user admin page that allows for user to be suspended or removed with a message explaining why that is a very bad idea

fixes #3119
2023-11-20 12:17:52 +11:00
Adeodato Simó
ff1f239a57
Use typing_extensions.Self instead of TypeVar 2023-11-19 15:10:14 -03:00
Adeodato Simó
6aaff28c13
Accept argument in naturalday_partial, downcast format if necessary 2023-11-19 15:10:14 -03:00
Adeodato Simó
aaea1b1b9e
Add tests for naturalday_partial tag 2023-11-19 15:10:13 -03:00
Adeodato Simó
8dbfba17d6
Merge from 'main' into stable_dates 2023-11-19 15:09:52 -03:00
Hugh Rundle
2ba0e3d7ff
Allow removing followers and fix follow rejections
* adds the ability to remove a user from your followers list
* fixes verbs.Reject to process reject activities for previously accepted follows in both directions

fixes #2635
2023-11-19 20:03:48 +11:00
Hugh Rundle
a7fcd898c2
middleware for displaying 413 page
When a RequestDataTooBig exception is thrown, users are largely in the dark about what happened and how it can be fixed.
This commit resolves this by inserting middleware to redirect the request to a custom 413 error page.

This exception is thrown when DATA_UPLOAD_MAX_MEMORY_SIZE is exceeded. The default value is 2.5MB.

Fixes #2340
Fixes #2633
2023-11-18 22:10:36 +11:00
Hugh Rundle
97757fa1ee
fix blocktrans 2023-11-18 15:58:01 +11:00
Hugh Rundle
a56ba0ce1c
always return 403 to POST requests
- POST requests need to receive a 403 error code
- minor wording updates
2023-11-18 13:41:52 +11:00
Hugh Rundle
8ddafafa84
make naming consistent 2023-11-18 12:40:36 +11:00
Hugh Rundle
d620bd7350
add handler for 403s
fixes #3104
2023-11-18 12:36:03 +11:00
Ross Chapman
68f54cf5a4 Initial commit to create PR 2023-11-16 17:20:23 -08:00
Matt Lehrer
f4da9fbf34 remove unnecessary loop.
ReviewRatings are a subclass and are included in the models.Review block
2023-11-16 20:37:46 +01:00
Matt Lehrer
bf81192d73
Merge branch 'main' into move-ratings-and-reviews-when-switching-editions 2023-11-16 10:49:05 +01:00
Sean Molenaar
e144ce19fa
fix: add include import from django.urls 2023-11-16 10:48:06 +01:00
Matt Lehrer
bd920a4630 move reviews to new edition 2023-11-16 10:38:45 +01:00
Matt Lehrer
7684101f15 move ratings to new edition 2023-11-16 10:38:41 +01:00
Mouse Reeve
06568aab88
Merge pull request #3105 from dato/notify_invitation_request
Create notifications for incoming invite requests
2023-11-15 17:27:34 -08:00
Mouse Reeve
5bf27d4fb2
Merge pull request #3115 from bookwyrm-social/dependabot/pip/aiohttp-3.8.6
Bump aiohttp from 3.8.5 to 3.8.6
2023-11-15 17:12:59 -08:00
Mouse Reeve
1a7a843dea Re-creates migrations and removes failing test
I think the test was failing because it was extremely brittle, not
because of anything wrong with the code itself.
2023-11-15 17:08:15 -08:00
Mouse Reeve
62f985edb8 Merge branch 'main' into user-migration 2023-11-15 16:46:18 -08:00
Hugh Rundle
54ec5e2ae0
Fix migrations properly (#3116)
* Revert "fix migrations and linting"
This reverts commit 53e410627f.
* really fix migrations
2023-11-15 19:54:16 +11:00
dependabot[bot]
63530294d4
Bump aiohttp from 3.8.5 to 3.8.6
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.8.5 to 3.8.6.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.8.5...v3.8.6)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-14 23:35:55 +00:00
Sean Molenaar
da4214ad61 feat: add OAuth authentication
Issue GH-2292
2023-11-14 14:18:35 +01:00
Adeodato Simó
01d4381898
Create notifications for incoming invite requests
Closes: #2066
2023-11-14 07:09:04 -03:00
Mouse Reeve
ab9cea1742
Merge pull request #3112 from dato/makemigrations_check
Check no missing migrations in django-tests workflow
2023-11-13 15:21:44 -08:00
Adeodato Simó
b81170c149
Add missing migration from #3099 2023-11-13 19:56:00 -03:00
Adeodato Simó
a884825b3c
Check no missing migrations in django-tests workflow 2023-11-13 19:56:00 -03:00
Hugh Rundle
bbc78f03ae
fix DB migrations (#3111)
fix migrations and linting
2023-11-14 07:21:27 +11:00
Hugh Rundle
d5762f1d52
Merge branch 'main' into user-migration 2023-11-13 21:17:07 +11:00
Hugh Rundle
891b72c79c
update user export file to use ActivityPub objects where possible. (#3109)
* add more context to user export page
* fix BookData fields wrong for files
* use to_activity and to_model where possible
* fixes for import and export
- use AP JSON where possible
- minor template wording updates
* import fixes and updates tests
* minor cleanup
* remove todo for mastodon
2023-11-13 21:14:03 +11:00
Mouse Reeve
ddf94f8714
Merge pull request #3097 from Tak/fix-initdb
Fix `bw-dev initdb`
2023-11-12 09:37:42 -08:00
Mouse Reeve
43324cf43a
Merge pull request #3099 from dato/notification_type_top_level
Create NotificationType as class, not through API
2023-11-12 09:34:28 -08:00
Hugh Rundle
1bedcdaebd
Merge branch 'main' into file-resubmit 2023-11-11 13:14:52 +11:00
Hugh Rundle
f3fc5f6179
add file_resubmit to DUMMY caches settings 2023-11-11 12:45:04 +11:00
Adeodato Simó
99a9dbe5f4
Create NotificationType as class, not through API
This way, we need not list every value again to create the enum.

N.B.: enum values are now accessed as `models.NotificationType.FOO`,
instead of `models.Notification.FOO`.
2023-11-09 22:43:36 -03:00
Adeodato Simó
be9d92b1c2
Remove last references to "seal" in partial_date.py and migration 2023-11-09 14:00:45 -03:00
Adeodato Simó
edfa6b18a1
Rename utils.sealed_date module (and tests) to utils.partial_date 2023-11-09 14:00:44 -03:00
Adeodato Simó
fa80aa54a9
SealedDate renames, pt. 2
• SealedDate -> PartialDate
• MonthSeal  -> MonthParts
• YearSeal   -> YearParts
2023-11-09 14:00:44 -03:00
Adeodato Simó
0e4c5ed439
SealedDate renames, pt. 1
• SealedDateField      -> PartialDateModel
• SealedDateFormField  -> PartialDateFormField
• SealedDateDescriptor -> PartialDateDescriptor
2023-11-09 14:00:44 -03:00
Adeodato Simó
c120fa8c87
Rename: templatetags/{sealed_dates => date_ext}.py 2023-11-09 14:00:44 -03:00
Adeodato Simó
2bb7652dfe
Update partial date migration to latest main 2023-11-09 14:00:22 -03:00
Adeodato Simó
e928027e16
Merge from main for up-to-date migrations 2023-11-09 14:00:14 -03:00
Adeodato Simó
dccac11527
PartialDateField: allow incoming dates without timezone 2023-11-09 13:04:09 -03:00
Levi Bard
ebcacfc6c5 Fix bw-dev initdb 2023-11-09 12:57:45 +01:00
Mouse Reeve
44b14f4933 Fixes workflow errors 2023-11-08 16:00:10 -08:00
Mouse Reeve
774b1095a3
Merge pull request #3094 from hughrun/activitypub-files
fix missing types in `BookData` file fields
2023-11-08 15:27:40 -08:00
Mouse Reeve
0bb4b0d71d Changes to how images are serialized
I'm just going to see if any tests fail?
2023-11-08 15:24:47 -08:00
Hugh Rundle
2248206a66
fix missing types in BookData file fields
activitypub.BookData includes fields for 'files' and 'fileLinks'.
This is a problem because BookData is inherited by Book and Author, neither of which have 'files' as a field in the main model.
Additionally, Author doesn't have a value for 'file_links'.
When serializing to JSON, BookData therefore throws 'TypeError: Object of type _MISSING_TYPE is not JSON serializable'

This fixes the problem by removing links and moving fileLinks to activitypub.Book.
2023-11-08 18:30:49 +11:00
Hugh Rundle
0a5e1048ce
Add more info to user export page (#3093)
- match page title to menu
- change description on IMPORT page from 'readthroughs' to 'reading history'
- provide more information on export page about what is and is not included.
2023-11-07 12:09:06 +11:00
Mouse Reeve
9ddd631549
Merge pull request #3089 from bookwyrm-social/notification-and-download-links
UI changes for notification and download link in import/export flow
2023-11-06 16:31:09 -08:00
Mouse Reeve
1b958a9b31
Merge pull request #3091 from hughrun/notification-and-download-links
show filesize on user downloads page
2023-11-06 16:27:14 -08:00
Hugh Rundle
282f7dd8d6
show filesize on user downloads page
- add column to user download page to display filesize
- adds a filter to display file sizes
- don't download the user downloads page from notifications ;)
2023-11-07 11:04:11 +11:00
Mouse Reeve
e152b625fa
Merge pull request #3090 from bookwyrm-social/user-migration-instructions
User migration instructions
2023-11-06 14:34:52 -08:00
Mouse Reeve
ee88c3b914
Merge pull request #3081 from bookwyrm-social/handle-isbn-error
Fix error produced when an unexpected ISBN format is used
2023-11-06 11:06:46 -08:00
Mouse Reeve
8663e204c7
Merge pull request #3079 from bookwyrm-social/deleted_user_follow_request
Don't show notification for user follow request if the user is inactive
2023-11-06 11:05:50 -08:00
Mouse Reeve
e7a1572450
Merge pull request #3086 from bookwyrm-social/user-deletion
Erase user data and statuses on account deletion
2023-11-06 09:49:06 -08:00
Mouse Reeve
3f038b4d67 Moves if to the right place 2023-11-06 09:42:58 -08:00
Mouse Reeve
06d822d9e0 Alternative format for user import guide 2023-11-06 09:35:04 -08:00
Mouse Reeve
85d1760b97 Changes recent exports table
I thought both dates seemed less necessary (happy to be told otherwise)
and the download link should be more explicit
2023-11-06 08:41:36 -08:00
Mouse Reeve
716e64de68 Changes notification links for user import/export
I found it unexpected that the export notification linked me directly to
the file, and wanted the import link to lead me to the import page
2023-11-06 08:27:30 -08:00
Hugh Rundle
15b7b7eaa7
Merge pull request #3088 from hughrun/user-migration
User migration fixes
2023-11-06 16:06:39 +11:00
Hugh Rundle
d34b70cb7b
remove pointless viewer_aware 2023-11-06 16:01:34 +11:00
Mouse Reeve
ee6e3ed7eb Adds a database field for is_deleted on user 2023-11-05 20:28:23 -08:00
Hugh Rundle
2d185dfb8a
remove unnecessary test data files 2023-11-06 14:51:52 +11:00
Mouse Reeve
27d99a0094 Removes failsafe that was overzealous 2023-11-05 19:47:32 -08:00
Hugh Rundle
93a32f4e15
update import/export user templates
- always explain what export file can be used for
- provide more information about overwrite vs upsert when importing
2023-11-06 14:40:19 +11:00
Hugh Rundle
8d3c2d9bd2
Merge pull request #3085 from bookwyrm-social/migration-explicit-imports
Uses explicit imports to avoid circular import in migrations code
2023-11-06 12:11:59 +11:00
Hugh Rundle
7a6b60772c
Merge pull request #3087 from hughrun/migration-explicit-imports
update references to bookwyrm models in export job
2023-11-06 12:10:47 +11:00
Hugh Rundle
d2f06e804f
update references to bookwyrm models in export job 2023-11-06 12:07:40 +11:00
Hugh Rundle
a93519ec3e
Merge pull request #3027 from dato/find_links_wrapped_punct
Fix parsing of punctuation in format_links()

fixes #2993  
fixes #3049
2023-11-06 09:42:57 +11:00
Hugh Rundle
1190ea7e69
Merge pull request #3078 from bookwyrm-social/tour-fixes
Update tour to reflect changes in #2201
2023-11-06 09:20:44 +11:00
Mouse Reeve
c17a2ec55b Creates snippet for user tag in admin view
The existing display wasn't showing the correct colors and was repeating
code unnecessarily
2023-11-05 10:18:04 -08:00
Mouse Reeve
d3668e413d Removes updates fields that was causing problems 2023-11-05 09:59:49 -08:00
Mouse Reeve
f353b49d36 Another linting issues 2023-11-05 09:53:57 -08:00
Mouse Reeve
47953c84d7 Fixes linting errors
Apparently I didn't have a linter working!
2023-11-05 09:49:38 -08:00
Mouse Reeve
4de9907456 Adds migration tests 2023-11-05 09:26:49 -08:00
Mouse Reeve
61caeed5a3 Adds migration and more tests 2023-11-05 08:51:42 -08:00
Mouse Reeve
5e42afd85a Pass args and kwargs through status deletion 2023-11-05 08:10:03 -08:00
Mouse Reeve
d0c652f0f5
Merge pull request #3084 from bookwyrm-social/find_existing_tests
Adds a couple more tests for find_existing
2023-11-05 08:06:15 -08:00
Mouse Reeve
93a7dd9cf3 Erase user data and statuses on account deletion 2023-11-05 08:00:29 -08:00
Mouse Reeve
9e9e9a9f85 Uses explicit imports to avoid circular import in migrations code 2023-11-05 07:04:05 -08:00
Mouse Reeve
ff2bb513ed Adds migration for notification types 2023-11-05 06:56:10 -08:00
Mouse Reeve
89b87db1c8 Adds merge migration 2023-11-05 06:54:29 -08:00
Mouse Reeve
67822d3cb0
Merge branch 'main' into user-migration 2023-11-05 06:52:48 -08:00
Mouse Reeve
10e0f2224a Adds a couple more tests for find_existing 2023-11-05 06:44:39 -08:00
Mouse Reeve
7104e775d8 Updates working of header tour 2023-11-05 06:36:43 -08:00
Hugh Rundle
d682e55812
swap out django-file-resubmit
- we decided to fork it, so this now uses the inaugural RC release of bw-file-resubmit (will need to be adjusted once we're confident it's ok to push a full release)
- I was accidentally using the wrong widget lol
2023-11-05 16:34:24 +11:00
Adeodato Simó
afad39bf80
Use $ instead of \Z for end of string
They're identical here, since re.M is not used, and the better-known
should be used, for readability.
2023-11-03 19:38:24 -03:00
Adeodato Simó
954a02126e
format_links: parse punctuation inside brackets
Also, consolidate all punctuation tests into a single table-driven one.
2023-11-03 19:38:24 -03:00
Adeodato Simó
7d13cbb10b
Add failing tests for reported bugs in format_links() 2023-11-03 19:38:23 -03:00
Adeodato Simó
294788aa1a
format_links: refactor; support multiple punctuation 2023-11-03 19:38:23 -03:00
Mouse Reeve
116a838eef Fixes typo that confuses isbn 10 and 13 2023-11-02 19:37:58 -07:00
Mouse Reeve
f839038c8f Add test for normalizing isbns in book model
Turns out this was actually working as expected
2023-11-02 19:12:46 -07:00
Mouse Reeve
285c513211 Adds test for invalid isbns and handle isbns with dashes 2023-11-02 19:03:15 -07:00
Mouse Reeve
95ba38524b
Merge pull request #3080 from bookwyrm-social/dependabot/pip/django-3.2.23
Bump django from 3.2.20 to 3.2.23
2023-11-02 18:48:30 -07:00
dependabot[bot]
68f1a69b6a
Bump django from 3.2.20 to 3.2.23
Bumps [django](https://github.com/django/django) from 3.2.20 to 3.2.23.
- [Commits](https://github.com/django/django/compare/3.2.20...3.2.23)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-02 22:13:52 +00:00
Mouse Reeve
8c950237a4
Merge pull request #3077 from bookwyrm-social/locales
Fixes plural string, "URL", and updates locales
2023-11-02 15:06:34 -07:00
Mouse Reeve
e1217f2054 Don't show notification for user follow request if the user is inactive 2023-11-02 15:04:03 -07:00
Mouse Reeve
ae51dcec63
Merge pull request #3039 from dato/ap_image_url
Fix creation of covers for ActivityPub imports
2023-11-02 14:44:44 -07:00
Mouse Reeve
22554f85ad Updates tour to reflect changes in #2201 2023-11-02 14:37:14 -07:00
Mouse Reeve
c1a7e4d9eb
Merge pull request #2201 from bookwyrm-social/header-links
Adds "Your Books" to the header and removed "Feed"
2023-11-02 14:34:47 -07:00
Mouse Reeve
416bbd4d9e Fixes plural string, "URL", and updates locales 2023-11-02 14:33:38 -07:00
Mouse Reeve
45fc10e3bf
Merge pull request #3067 from hughrun/2989
add defaults of None to optional AWS values
2023-11-02 14:24:58 -07:00
Mouse Reeve
0502f6ba42
Merge pull request #3076 from bookwyrm-social/move
Add Move activity for user migration (with small change)
2023-11-01 18:19:56 -07:00
Mouse Reeve
86fd62a09e
Merge pull request #3075 from bookwyrm-social/small-migration-fix
Adds reverse migration for populate sort title
2023-11-01 18:19:34 -07:00
Mouse Reeve
2137737d9b Small changes to get blocktrans to work as expected in move code 2023-11-01 17:19:57 -07:00
Mouse Reeve
621cfa7ed2
Merge pull request #2970 from hughrun/move
Add `Move` activity for user migration
2023-11-01 17:14:34 -07:00
Mouse Reeve
6f9c7f39fb Adds reverse migration for populate sort title
This doesn't impact much, it just allows you to reverse the migration,
which you would probably (hopefully) only want ot do in development.
2023-11-01 16:24:47 -07:00
Hugh Rundle
c486b9c37e
pylint fixes 2023-10-30 21:47:19 +11:00
Hugh Rundle
df43a8e2c5
Use django-file-resubmit plugin
- save cover images to cache when checking author and work for existing records
- fixes #2760
2023-10-30 19:43:39 +11:00
Mouse Reeve
941efb3f72
Merge pull request #3068 from hughrun/3066
make options consistent in celery
2023-10-29 17:30:40 -07:00
Hugh Rundle
d2b2cc0521
make options consistent in celery
- changes 'broadcasts' to 'broadcast' in Celery page in admin section
- re-orders celery queues on admin page to be in English alphabetical order (other than priority levels) - this makes them consistent with the Flower interface
- fixes #3066
2023-10-29 11:55:06 +11:00
Hugh Rundle
853b5f28a4
add defaults of None to optional AWS values
fixes #2989
2023-10-29 11:29:49 +11:00
Hugh Rundle
935779b5e3
Merge pull request #3064 from hughrun/user-migrate
stop pylint constantly whining in user-migration branch
2023-10-28 06:53:25 +11:00
Hugh Rundle
25a2615d5f
stop pylint constantly whining 2023-10-28 06:51:26 +11:00
Hugh Rundle
50ac691126
add alt tag 2023-10-28 06:28:43 +11:00
Hugh Rundle
4d35fd45df
template and migration fixes 2023-10-27 22:22:58 +11:00
Hugh Rundle
6f3b1b565f
fixes to move layout and notifs
- make Move notifications less complicated
- moved users cannot do anything other than unmove or log out
- refactor translations for moved users
2023-10-27 22:00:04 +11:00
Adeodato Simó
1952bb6ddc
fix mypy issues
The three "ignore" directives are:

  - avoid unreadable boilerplate from inherited `Field` methods; and:
  - https://github.com/typeddjango/django-stubs/issues/285#issuecomment-600029858
2023-10-24 18:14:05 -03:00
Adeodato Simó
170d1fe205
fix pylint issues (minus no-else-return) 2023-10-24 17:41:07 -03:00
Adeodato Simó
737ac8e908
Implement PartialDateField using SealedDate and a custom descriptor 2023-10-24 17:30:15 -03:00
Adeodato Simó
9752819bdb
Add support for parsing partial isoformats back 2023-10-24 17:30:13 -03:00
Adeodato Simó
4b47646e28
Fix typing hints in sealed_date module
In particular, SealedDate's class methods always return an instance
of the class they're invoked through (i.e., `SealedDate.from_date_parts`
intentionally never returns `MonthSeal` or `YearSeal`).

To propertly annotate this, a type variable is needed (or the much
simpler `Self` in Python 3.11).
2023-10-24 17:29:04 -03:00
Adeodato Simó
5f619d7a39
Implement SealedDateFormField to preserves partial dates
Note that Django forms _already_ have suppport for partial date data; we
just need to extend it when converting to Python (using SealedDate instead
of returning an error).
2023-10-24 17:29:00 -03:00
Adeodato Simó
777c8b4549
naturalday_partial filter for working with SealedDate 2023-10-24 04:32:27 -03:00
Adeodato Simó
46d80d56a5
Rename SealedDate.__str__ to partial_isoformat
Django uses `str(date)` for backends other than PostgreSQL, so do not
break "YYYY-MM-DD" formatting, just in case.
2023-10-24 04:32:27 -03:00
Adeodato Simó
a9c605ea97
Add SealedDate class for globally-stable, maybe-incomplete dates 2023-10-24 04:32:27 -03:00
Adeodato Simó
52a979da2d
Add failing test case for "January 1st" offset bug 2023-10-24 04:32:27 -03:00
Hugh Rundle
5592a8e08b
Merge pull request #3061 from hughrun/user-migrate
minor pylint and mypy fixes
2023-10-23 21:32:36 +11:00
Hugh Rundle
f30555be0f
minor pylint and mypy fixes 2023-10-23 21:30:17 +11:00
Hugh Rundle
f662e4e049
Merge pull request #3060 from hughrun/user-migrate
Fix texts & linting plus minor bugfixes
2023-10-23 20:54:41 +11:00
Hugh Rundle
e29c93a1e9
complete jobs more sensibly
- fix tuple in tar export I accidentally broke by following pylint blindly
- just use job.set_status to complete jobs since it does everything we need
- fix/avoid Celery "not JSON deserializable" error by not saving whole job including user value
2023-10-23 20:44:52 +11:00
Hugh Rundle
ddec2dbaa9
fix tar types notification docstring 2023-10-23 20:43:49 +11:00
Hugh Rundle
b8fc5c9b7a
fix tests 2023-10-23 20:42:56 +11:00
Hugh Rundle
8477d0b89d
Merge branch 'main' into user-migration 2023-10-22 18:47:41 +11:00
Hugh Rundle
afb5c01947
Merge pull request #3058 from hughrun/user-migrate
oops import Any
2023-10-22 17:57:57 +11:00
Hugh Rundle
2b6852e7a0
oops import Any 2023-10-22 17:56:46 +11:00
Hugh Rundle
d05cf8e59b
Merge pull request #3057 from hughrun/user-migrate
once more into the linting breach!
2023-10-22 17:50:58 +11:00
Hugh Rundle
b6b55b2e65
once more into the linting breach! 2023-10-22 17:49:26 +11:00
Hugh Rundle
c5e536aeaa
Merge pull request #3056 from hughrun/user-migrate
fix tests and linting
2023-10-22 17:27:57 +11:00
Hugh Rundle
07ef12ce8e
fix tests and linting 2023-10-22 17:26:27 +11:00
Hugh Rundle
0c846ca31f
Merge pull request #3055 from hughrun/user-migrate
formatting and linting fixes
2023-10-22 16:56:20 +11:00
Hugh Rundle
0a2efeb5aa
Merge branch 'user-migration' into user-migrate 2023-10-22 16:55:00 +11:00
Hugh Rundle
6222088f15
Merge branch 'user-migrate' of github.com:hughrun/bookwyrm into user-migrate 2023-10-22 16:53:32 +11:00
Hugh Rundle
fd1ebf5f71
formatting and pylint fixes 2023-10-22 16:52:29 +11:00
Hugh Rundle
11a726b40b
Merge pull request #3037 from hughrun/user-migrate
complete most outstanding user migrate tasks
2023-10-22 15:40:22 +11:00
Hugh Rundle
c0a5e55f7f
Merge branch 'user-migration' into user-migrate 2023-10-22 15:38:06 +11:00
Hugh Rundle
b34a491172
run black 2023-10-22 15:34:25 +11:00
Hugh Rundle
a27c652501
admin view for user imports
- makes user_import_time_limit a site setting rather than a value in settings.py (note this applies to exports as well as imports)
- admins can change user_import_time_limit from UI
- admins can cancel stuck user imports
- disabling new imports also disables user imports
2023-10-22 15:07:49 +11:00
Hugh Rundle
836127f369
cooldown period for user exports
add USER_EXPORT_COOLDOWN_HOURS setting for controlling user exports and imports
2023-10-22 10:49:13 +11:00
Hugh Rundle
20114b0059
add notifs and error handling for user export/import 2023-10-22 09:03:28 +11:00
Jascha Ezra Urbach
c9e6dcc2d9
Merge pull request #3053 from bookwyrm-social/develop
Release 0.7.0
2023-10-21 18:30:45 +02:00
Jascha Ezra Urbach
00bf2903bc
Bumped version number to 0.7.0 (#3052) 2023-10-21 18:29:45 +02:00
Adeodato Simó
698e74a496
Minor vocabulary fixes and structured data improvements (#3036)
* Remove duplicate Review object under `rating` property

This was preventing validation, since `rating` is not a valid property
(`review` is, which is created from book.html already).

* Drop `bestRating` property in ratings, since it defaults to 5

See <https://schema.org/bestRating> ("If bestRating is omitted,
5 is assumed").

* Create Rating object (and its enclosing Review) in book/rating.html

* Use `position` property for Book objects in a series

`volumeNumber`, previously used, is only valid for objects of type
PublicationVolume (which series members are not).

* Give URL of book series when setting of `isPartOf`

* series.html: Add empty BookSeries object

---------

Co-authored-by: Adeodato Simó <dato@users.noreply.github.com>
2023-10-21 17:53:24 +02:00
Jascha Ezra Urbach
695c67a714
Merge pull request #3051 from bookwyrm-social/jaschaurbach-patch-1
Update bump-version.sh
2023-10-21 17:28:55 +02:00
Jascha Ezra Urbach
abb6bcd199
Update bump-version.sh
just a little fix
2023-10-21 17:28:44 +02:00
Jascha Ezra Urbach
4e16800b52
Merge pull request #3050 from bookwyrm-social/bump-version-script
Create bump-version.sh
2023-10-21 17:18:31 +02:00
Jascha Urbach
4a9d80268a
Create bump-version.sh
This scripts reads VERSION (should be semantic version),  automatically
suggest a "minor" version update, and ask for input to use either suggestion, or a new value.

creates a pull request with updated VERSION and creates a tag for the new version.
2023-10-21 17:16:55 +02:00
Hugh Rundle
781b01a007
add error handling and status for user exports
* fix Safari not downloading with the correct filename
* add FAILED status
* don't provide download link for stopped jobs
2023-10-21 19:43:44 +11:00
Jascha Ezra Urbach
1685ac1953
Move version out of settings (#3045)
This removes the content of VERSION from settings.py and moves it into a seperate file which makes it easier to update versionnumbers via script in the future.
2023-10-19 11:29:59 +02:00
Mouse Reeve
2237a7eb9e
Merge pull request #3008 from dato/author_in_citation
Cite author in quotations and alt text
2023-10-18 17:31:55 -07:00
Mouse Reeve
caa31de685
Merge pull request #3046 from dato/stylelint-fix
Remaining CSS lints and npm exec cleanup
2023-10-18 17:05:52 -07:00
Adeodato Simó
f88a0f8229
Run stylelint to fix remaining issues
Including deletion of two duplicate stanzas introduced in 33c13608a8 ("Refixing
light and dark themes").
2023-10-18 20:39:12 -03:00
Adeodato Simó
b78d51410b
bw-dev: drop use of npm exec by setting PATH in Docker image 2023-10-18 20:37:13 -03:00
Adeodato Simó
6392a8e01d
Merge pull request #2032 from viviicat/bw-dev-npm-fix
Conflicts:
	bw-dev
	dev-tools/Dockerfile
	bookwyrm/static/css/bookwyrm/_all.scss
	bookwyrm/static/css/themes/bookwyrm-dark.scss
	bookwyrm/static/css/themes/bookwyrm-light.scss
2023-10-18 18:54:53 -03:00
Jascha Ezra Urbach
912269303e
Merge pull request #2960 from hbrunn/main-systemd-sandboxing
Add sandboxing to systemd examples
2023-10-18 21:00:20 +02:00
Jascha Ezra Urbach
abebf82042
Merge pull request #3029 from skmanohar/show-hide-password
Show/Hide password icon
2023-10-18 20:34:12 +02:00
Sidharth
25e8b259f7 Show/Hide password icon 2023-10-18 21:33:37 +05:30
Mouse Reeve
3624763073
Merge pull request #3043 from dato/prettier_rerun_final
Final prettier re-run
2023-10-17 14:28:48 -07:00
Adeodato Simó
d55e0b6ba3
Final JS update to match 'es5' trailing comma style
This is  follow-up to cd247a668 ("Update .prettierrc").
2023-10-17 17:11:59 -03:00
Jascha Ezra Urbach
06923c64c1
Merge pull request #3003 from dato/book_info_first_pub_date
Fallback to showing first published date
2023-10-17 20:16:06 +02:00
Jascha Ezra Urbach
3ade72b90d
Merge pull request #3038 from dato/parsed_date_defaults
Stable defaults for incomplete parsed dates
2023-10-17 20:08:04 +02:00
Jascha Ezra Urbach
67f6c0a5a7
Merge pull request #3042 from bookwyrm-social/fix-prettierrc
Update .prettierrc
2023-10-17 19:52:10 +02:00
Jascha Urbach
cd247a6689
Update .prettierrc
Forgot to change the file pre-merch
2023-10-17 19:49:32 +02:00
Jascha Ezra Urbach
b97dafc303
Merge pull request #3034 from bookwyrm-social/prettier-trailing-comma
introduce .prettierrc
2023-10-17 19:32:04 +02:00
Jascha Ezra Urbach
4d352faae3
Merge branch 'main' into prettier-trailing-comma 2023-10-17 19:31:24 +02:00
Jascha Ezra Urbach
f02faa1b74
Merge pull request #3040 from dato/update-node-installation
dev-tools: use apt source for Node instead of setup script
2023-10-17 19:13:41 +02:00
Adeodato Simó
1937177e1a
dev-tools: use apt source for Node instead of setup script
Extra bits:

  - use a single RUN instruction
  - silence upgrade notices from pip and npm
2023-10-17 02:26:22 -03:00
Adeodato Simó
3251ef0bf5
Fix creation of covers for ActivityPub imports
`cover` comes as a JSON dict, but the code was looking for URL as
an attribute.

(This commit leaves the attribute access in place, just in case
`cover` is updated to serialize as Document proper.)
2023-10-16 17:43:04 -03:00
Adeodato Simó
8afcb9b6d3
Fix tests warning: ImportJob.updated_date received a naive datetime 2023-10-15 22:03:45 -03:00
Adeodato Simó
c02306a66b
Default to Jan 1st too on incomplete dates received from ActivityPub 2023-10-15 19:59:51 -03:00
Adeodato Simó
c066d11eb1
Bugfix: default missing date components to 1, not today's
Fixes: #2660.
2023-10-15 19:49:00 -03:00
Adeodato Simó
8f0f3e6ace
ImportItem: preserve parsed timezones in date_started, date_read
This is a follow-up to b564e514f ("Handle parsed dates that already
have a timezone on import"), which was applied to `date_added` only.

(Appart from consistency, this will allow to apply future parsing fixes
more easily.)
2023-10-15 19:48:02 -03:00
Hugh Rundle
f07d730e03
Merge pull request #2980 from CSDUMMI/upstream2
Complete Migrations of Bookwyrm Accounts across instances

Merging this into `user-migration` branch to enable final work on this within the main Bookwyrm repository. We will pull in the final PR from there into `main` when ready.

Thanks to @CSDUMMI and the crew for this huge job.
2023-10-15 15:19:01 +11:00
Hugh Rundle
a4bfcb34d5
fix tests and clean up
* cleans up some test logging
* cleans up some commented-out code
* adds export_job model tests
* reconsiders some tests in export user view tests
2023-10-15 15:09:19 +11:00
Hugh Rundle
6667178703
Merge pull request #2981 from rg-wood/installable-pwa
Add: installable as PWA
Resolves #2558
2023-10-15 09:06:24 +11:00
Hugh Rundle
c946e7dd82
Merge branch 'main' into installable-pwa 2023-10-15 08:42:17 +11:00
Jascha Urbach
0f79aea36f
introduce .prettierrc
With this pullrequest I introduce .prettierrc with one rule:
'trailingComma': 'none'
2023-10-14 17:52:58 +02:00
Jascha Ezra Urbach
66f62566d6
Merge pull request #3033 from bookwyrm-social/fix-eslint-warning
Fix eslint warning
2023-10-14 17:22:30 +02:00
Jascha Urbach
97adf2f7fd
I changed my own comments to get rid of this warning which I could have better written in the first place. 2023-10-14 17:21:15 +02:00
Jascha Ezra Urbach
0452e8698d
Merge branch 'main' into installable-pwa 2023-10-14 13:57:59 +02:00
Jascha Ezra Urbach
16b7db4639
Merge pull request #3022 from dato/drop_duplicate_isfdb_link
Drop duplicate author link to ISFDB
2023-10-14 13:57:08 +02:00
Jascha Ezra Urbach
d7ba0e3a8a
Merge pull request #3023 from dato/series_name_urlencode
URL-encode book series name when linking to it
2023-10-14 13:56:38 +02:00
Adeodato Simó
17d741039c
Remove duplicate test
(Test case already part of test_format_links_simple_url.)
2023-10-09 21:37:39 -03:00
Adeodato Simó
0043329cc1
Simplify literals in _wrapped 2023-10-09 21:09:52 -03:00
Adeodato Simó
c3c22022f6
Check for punctuation before checking for wrapping parenthesis
This allows to parse `(URL).` correctly, which was not detected
as URL before.
2023-10-09 21:09:51 -03:00
Adeodato Simó
1778c56be0
URL-encode book series name when linking to it
Closes: #3021.
2023-10-09 04:24:05 -03:00
Adeodato Simó
55eb81dbf9
Drop duplicate author link to ISFDB
Closes: #3018.
2023-10-09 01:16:32 -03:00
R.G. Wood
a7e427efc2
Merge branch 'main' into installable-pwa 2023-10-07 13:34:35 +01:00
R.G. Wood
1798abfc3e
Display site name and description for PWA manifest 2023-10-07 13:32:49 +01:00
R.G. Wood
34d5c557d8
Fix syntax styling 2023-10-07 13:32:18 +01:00
Mouse Reeve
3d123bc2f2
Merge pull request #3017 from bookwyrm-social/dependabot/pip/pillow-10.0.1
Bump pillow from 9.4.0 to 10.0.1
2023-10-04 19:00:21 -07:00
dependabot[bot]
7cae5879c8
Bump pillow from 9.4.0 to 10.0.1
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.4.0 to 10.0.1.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.4.0...10.0.1)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-04 01:09:41 +00:00
Mouse Reeve
bcfd4d2efa
Merge pull request #3016 from bookwyrm-social/release-changes
Updates locales and version number for release
2023-10-02 10:22:05 -07:00
Mouse Reeve
e4ba09178f
Merge pull request #2949 from bookwyrm-social/user-search
Allow searching for local users when logged out
2023-10-02 10:21:08 -07:00
Mouse Reeve
703a56940c
Merge pull request #2953 from phildini/add-spanish-articles
Add spanish articles
2023-10-02 10:20:51 -07:00
Mouse Reeve
3deddf6355
Merge pull request #2912 from categulario/fix-form-labels
fix ids of labels in invite request form in admin
2023-10-02 10:04:34 -07:00
Mouse Reeve
b3bfcf8665 Updates test for new logic 2023-10-02 10:02:42 -07:00
Mouse Reeve
458b258ad5
Merge pull request #3007 from hughrun/signed-get
create instance user on instance creation
2023-10-02 09:52:59 -07:00
Mouse Reeve
fcfe34f2f6 Updates locales and version number for release 2023-10-02 09:42:22 -07:00
Hugh Rundle
e34fe9a059
Merge pull request #2917 from jderuiter/mypy-utils
Type annotations for utils
2023-10-01 10:29:52 +11:00
Joeri de Ruiter
d4088ac854
Merge branch 'main' into mypy-utils 2023-09-28 09:43:40 +02:00
Mouse Reeve
33e179e44b
Merge pull request #3010 from bookwyrm-social/locales
Updates locales
2023-09-27 16:52:36 -07:00
Mouse Reeve
2a08170fb5 Updates locales 2023-09-26 18:14:24 -07:00
Hugh Rundle
088b9ab555
Merge branch 'main' into move 2023-09-26 08:10:18 +10:00
Hugh Rundle
3e38fecd55
id_to_username cleanup 2023-09-26 07:45:45 +10:00
Adeodato Simó
7cfdf235bc
Include author in cover image alt text 2023-09-25 16:03:16 -03:00
Adeodato Simó
80a1180090
Include author name when serializing Quotation 2023-09-25 16:03:16 -03:00
Hugh Rundle
b05f2e99e8
undo moves
also cleans up some templates
2023-09-25 22:05:43 +10:00
Hugh Rundle
4fd5e2094a
hopefully fix template 2023-09-25 16:35:02 +10:00
Hugh Rundle
9547edf845
oops close div 2023-09-25 16:21:19 +10:00
Hugh Rundle
d67903fd4b
fix tests and templates 2023-09-25 15:49:25 +10:00
Hugh Rundle
01a56540d0
cleanup 2023-09-25 15:29:01 +10:00
Hugh Rundle
c95f160216
fix MoveUser errors and clean up
- minor template fixes
- notification logic fixes
- don't dedupe on moved_to or also_known_as
- add migration
2023-09-25 15:14:21 +10:00
Hugh Rundle
fe4bc28f37
fix tests 2023-09-24 15:58:52 +10:00
Hugh Rundle
b69031c01a
formatting 2023-09-24 12:31:01 +10:00
Mouse Reeve
bab28a8fc9
Merge pull request #3000 from dato/position_serialization
Minor improvements to Quotation pure content
2023-09-23 17:47:27 -07:00
Adeodato Simó
ec2c5cb546
Only use first publish date if publisher is unknown 2023-09-23 18:23:15 -03:00
Adeodato Simó
2c968e94cb
Fallback to first published date if published date not present 2023-09-23 18:22:02 -03:00
Adeodato Simó
fadf30b942
Also use italics for book title in editions.html template 2023-09-23 17:49:38 -03:00
Adeodato Simó
cc05cabcb5
Note content: use italics for book titles + em-dash for Quotation 2023-09-23 17:49:38 -03:00
Mouse Reeve
ef582f1bc2
Merge pull request #2986 from NetspherePub/2985
Correct EPUB spelling
2023-09-22 17:04:18 -07:00
Mouse Reeve
b75b5cb165
Merge pull request #2987 from JJimenez71/main
Pinned versions of docker containers
2023-09-22 17:03:39 -07:00
Mouse Reeve
0a029e6e01
Merge pull request #2992 from jderuiter/new-edition-issues
parent_work was not always included when needed
2023-09-22 17:00:40 -07:00
Mouse Reeve
85b647b7ab
Merge pull request #3002 from dato/django_serve_static_debug
Serve static files in debug mode
2023-09-22 16:17:58 -07:00
Adeodato Simó
1e495684af
Serve static files in debug mode 2023-09-18 19:01:22 -03:00
Hugh Rundle
5b051631ec
Move MVP
* update User model to allow for moved_to and also_known_as values
* allow users to add aliases (also_known_as) in UI
* allow users to move account to another one (moved_to)
* redirect webfinger to the new account after a move
* present notification to followers inviting to follow at new account

Note: unlike Mastodon we're not running any unfollow/autofollow action here: users can decide for themselves
This makes undoing moves easier.

TODO

There is still a bug with incoming Moves, at least from Mastodon.
This seems to be something to do with Update activities (rather than Move, strictly).
2023-09-18 21:21:04 +10:00
Adeodato Simó
ce3885d4f6
Use endposition when serializing Quotation 2023-09-17 15:18:48 -03:00
Adeodato Simó
1322a0c693
Substitute “p.” for “page” in page progress serialization 2023-09-17 15:18:21 -03:00
Adeodato Simó
25fd7276ea
pure_content() refactor: shorter conditionals 2023-09-17 15:01:04 -03:00
Joeri de Ruiter
af5f71f5ac Revert return type for get_or_set 2023-09-13 10:21:30 +02:00
Joeri de Ruiter
05f8bd0d3c parent_work was not always included in work when needed 2023-09-13 09:46:31 +02:00
Joeri de Ruiter
a5cf912ae8 Fix some annotations 2023-09-13 09:22:53 +02:00
Joeri de Ruiter
a5ede835b2 Dump dependencies related to typing 2023-09-13 09:09:43 +02:00
FoW
d8ba1f4309 Correct EPUB spelling 2023-09-08 22:52:11 +09:00
Joeri de Ruiter
6e9f64262c
Merge branch 'main' into mypy-utils 2023-09-08 08:53:38 +02:00
JJimenez71
2260e14868 Pinned versions of docker containers 2023-09-07 19:30:29 -06:00
CSDUMMI
688978369f Implement self-contained archives to import and export entire users between instances (#38)
Co-authored-by: Daniel Burgess <developerdannymate@gmail.com>
Co-authored-by: Hugh Rundle <hugh@hughrundle.net>
Co-authored-by: dannymate <dannymate@noreply.codeberg.org>
Co-authored-by: hughrun <hughrun@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/GuildAlpha/bookwyrm/pulls/38
Co-authored-by: CSDUMMI <csdummi.misquality@simplelogin.co>
Co-committed-by: CSDUMMI <csdummi.misquality@simplelogin.co>
2023-09-07 22:37:28 +02:00
R.G. Wood
b9851d665e
Add: installable as PWA 2023-09-07 13:41:31 +01:00
Mouse Reeve
a09b2ab45c
Merge pull request #2964 from bookwyrm-social/delete-announcement-view
Makes the delete announcement view post-only
2023-09-05 16:43:31 -07:00
Mouse Reeve
bc870a305f
Merge pull request #2962 from jderuiter/mypy-isbn
Type annotations and tests for isbn
2023-09-01 17:05:01 -07:00
Mouse Reeve
c2196fb704
Merge pull request #2948 from bookwyrm-social/populate-sort-title
Pre-populate sort title in edit book form if not provided
2023-09-01 17:01:41 -07:00
Mouse Reeve
47e8f3c3e6
Merge pull request #2967 from 0x29a/version-bump
chore: bump version to match the latest tag
2023-09-01 17:01:04 -07:00
Mouse Reeve
b0601a0958 Makes deleting announcements only work via POST 2023-09-01 16:59:56 -07:00
Mouse Reeve
4e999657cc
Merge pull request #2972 from hughrun/opensearch
fix opensearch template
2023-09-01 16:57:10 -07:00
Hugh Rundle
d560a6baef
fix opensearch template
* "method" is not a valid attribute of the `Url` element
* "ShortName" cannot be empty - fixed site_name being used before it was assigned
2023-08-30 20:15:20 +10:00
Hugh Rundle
e7ba6a3141
initial work to add 'Move' activity 2023-08-29 21:07:41 +10:00
Holger Brunn
0a9ef9e047 Add sandboxing to systemd examples 2023-08-28 07:29:42 +02:00
Abraham Toriz
4c526dfcaa
Don't rely on ids to target inputs from labels 2023-08-25 13:33:55 -06:00
Abraham Toriz
dfa935bd72
fix pointed ids of labels in invite request form 2023-08-25 13:29:26 -06:00
0x29a
1c9da7b84b chore: bump version to match the latest tag 2023-08-25 14:11:29 +02:00
Joeri de Ruiter
5eae123668
Merge branch 'main' into mypy-isbn 2023-08-22 11:41:34 +02:00
Joeri de Ruiter
567c103e59
Merge branch 'main' into mypy-utils 2023-08-22 11:40:48 +02:00
Mouse Reeve
e5f8e4babc
Merge pull request #2963 from jderuiter/mypy-importers
Type annotations for bookwyrm.importers
2023-08-21 20:39:32 -07:00
Joeri de Ruiter
0686926048 Type annotations for bookwyrm.importers 2023-08-21 16:58:16 +02:00
Joeri de Ruiter
f6d8786179 Type annotations for bookwyrm.isbn 2023-08-21 15:46:50 +02:00
Joeri de Ruiter
3760e3b45c Tests for ISBN hyphenation 2023-08-21 15:46:24 +02:00
Joeri de Ruiter
2e88e73509 Remove returned None to make pylint happy 2023-08-21 14:00:09 +02:00
Joeri de Ruiter
0f2c0c034d Removed TODOs. When data is invalid return None. 2023-08-21 13:28:08 +02:00
Joeri de Ruiter
767cd14639 Stricter checks for bookwyrm.utils 2023-08-21 13:10:12 +02:00
Joeri de Ruiter
8f8587f79d Set **kwargs type to Any for ActivityObject.__init__ 2023-08-21 13:09:42 +02:00
Joeri de Ruiter
ff8e4597e5 Type annotations for utils 2023-08-21 12:56:32 +02:00
Mouse Reeve
0f8da5b738
Merge pull request #2957 from hughrun/redisfix
fix illegal values in redis jobs
2023-08-19 16:41:50 -07:00
Mouse Reeve
c6aaa80c62
Merge pull request #2947 from bookwyrm-social/small-ui-fixes
Small UI fixes
2023-08-19 15:09:12 -07:00
Mouse Reeve
1e0fe6d7c8 Remove duplicate if statement 2023-08-19 15:06:57 -07:00
Hugh Rundle
5ed1441ddb
fix illegal values in redis jobs
1. populate_streams_get_audience

This tries to set status_reply_parent_privacy as None if there is no status.reply_parent, but None is not a valid value for privacy.
This doesn't appear to be  breaking anything but does result in a lot of error messages in the logs.
I have set this to equal the original status.privacy - this won't realy have any effect since it only happens when there is no parent,
however we could set this to "direct" if we want to be highly cautious.

2. rerank_user_task

Again, this doesn't seem to caused major issues, but is throwing errors if the user in question no longer exists for some reason.
This commit checks whether 'user' exists before attempting to rerank.
2023-08-19 08:34:03 +10:00
Margaret Fero
d7adada29c
Add Spanish Articles
Added articles for spanish language to list of articles in settings
2023-08-15 22:53:43 -07:00
Philip James
2826e184d2
Merge pull request #7 from bookwyrm-social/main
Update to main
2023-08-15 21:58:38 -07:00
Mouse Reeve
63b60ad62c Removes "all books" link from profile when there are none 2023-08-06 19:40:59 -07:00
Mouse Reeve
185486c6fc Uses {% empty %} instead of if statements 2023-08-06 19:35:50 -07:00
Mouse Reeve
53c8085207
Merge pull request #2946 from bookwyrm-social/followers-page-breadcrumbs
Adds breadcrumbs and better titles to followers/following pages
2023-08-06 19:13:35 -07:00
Mouse Reeve
a05942fe15 Allow searching for local users when logged out 2023-08-06 18:23:57 -07:00
Mouse Reeve
d9f6449767 Pre-populate sort title in edit book form if not provided
It's confusing to edit a book when this isn't set, so this provides the
best-guess version of the sort title if there isn't one provided, and
allows the user to change it as needed.
2023-08-06 17:57:57 -07:00
Mouse Reeve
15e82ece07
Merge pull request #2854 from bookwyrm-social/report-actions
Record report actions
2023-08-06 16:52:24 -07:00
Mouse Reeve
861d3b1500
Merge pull request #2935 from jderuiter/markdown-import
Convert description from Markdown when importing from Open Library
2023-08-06 16:14:25 -07:00
Mouse Reeve
518f87ef62 Adds merge migration 2023-08-06 16:02:28 -07:00
Mouse Reeve
a166af9990
Merge branch 'main' into report-actions 2023-08-06 16:00:59 -07:00
Mouse Reeve
e76b44fc8f
Merge pull request #2921 from axiomizer/isbn-hyphenation
Isbn hyphenation
2023-08-06 15:59:19 -07:00
Mouse Reeve
b7e7867b9b
Merge pull request #2938 from jderuiter/redirect-referer
On readthrough, progressupdate or status delete return to previous page
2023-08-06 15:41:26 -07:00
Mouse Reeve
e8949bbffd Make sure defaults are set on directory filters 2023-08-06 15:37:26 -07:00
Mouse Reeve
27c40ccf20 Uses comma formatting on user follower/following display values 2023-08-06 15:37:26 -07:00
Mouse Reeve
66250e0dd8 Consistent null states and page titles in user profile views 2023-08-06 15:36:56 -07:00
Mouse Reeve
0e43cc4274 Adds breadcrumbs and better titles to followers/following pages 2023-08-06 15:09:45 -07:00
Mouse Reeve
013c726869
Merge pull request #2918 from jderuiter/dutch
Include Dutch locale
2023-08-05 14:34:05 -07:00
axiomizer
83ad45644b Make hyphenated ISBN a property on the book model 2023-08-03 18:01:02 -04:00
Joeri de Ruiter
2dddb2e3da Revert change to redirect when deleting status 2023-08-03 13:18:35 +02:00
Joeri de Ruiter
a901014e48 Change import of clean 2023-08-02 19:37:52 +02:00
Joeri de Ruiter
ae5c27f3bb Sanitise description from Open Library 2023-08-02 19:30:40 +02:00
Joeri de Ruiter
f4a4b59a14 Merge branch 'main' into markdown-import 2023-08-02 19:19:07 +02:00
Mouse Reeve
0be5cf31dc
Merge branch 'main' into isbn-hyphenation 2023-08-01 21:10:10 -07:00
Mouse Reeve
9d69f2fb3e
Merge pull request #2905 from bookwyrm-social/broken-editions
Adds management command to repair editions in bad state
2023-08-01 21:08:43 -07:00
Mouse Reeve
73f1484025
Merge pull request #2915 from axiomizer/main
Add copy button for ISBN
2023-08-01 20:55:12 -07:00
Mouse Reeve
455b0c82ea Fixes typo and outdated comment 2023-08-01 20:53:06 -07:00
Mouse Reeve
acafa0b417
Merge pull request #2925 from jderuiter/mypy-connectors
Type annotations and related changes for connectors
2023-08-01 20:46:56 -07:00
axiomizer
3ca36fef4d Make copy button screen reader friendly
revert last commit because prettier was run with the wrong version
This reverts commit 5d3883c9a0.
2023-08-01 22:56:05 -04:00
Mouse Reeve
173d0b77ac
Merge pull request #2937 from jderuiter/create-book-fixes
Fixes for create-book
2023-08-01 19:02:33 -07:00
Mouse Reeve
211b60bba2
Merge pull request #2934 from bookwyrm-social/reduce-status-tasks
Only trigger add_status_task when status is first created
2023-08-01 08:30:43 -07:00
Joeri de Ruiter
d1bad521e9 When deleting readthrough, progressupdate or status, return to referer instead of main page 2023-08-01 17:19:10 +02:00
axiomizer
8565367993 Fix pylint issues and failing unit tests 2023-08-01 09:45:13 -04:00
Joeri de Ruiter
220cad8661 Only show author confirmation if new authors are added, show suggestions for parent work and set it correctly. 2023-08-01 15:12:50 +02:00
axiomizer
5d3883c9a0 run prettier on bookwyrm.js 2023-08-01 08:09:10 -04:00
Joeri de Ruiter
1a733746f2 Only remove surrounding p tags if there are no other p tags 2023-08-01 12:17:57 +02:00
Joeri de Ruiter
1a215e9b9e Convert description from Markdown to HTML when importing from Open Library 2023-08-01 11:45:46 +02:00
Mouse Reeve
2f8cf941af
Merge pull request #2933 from joachimesque/remove-links-trailing-punctuation
Remove links trailing punctuation
2023-07-31 18:58:39 -07:00
Mouse Reeve
3336fd0f11 Updates test 2023-07-31 17:48:09 -07:00
Mouse Reeve
f6fba19ac4 Only trigger add_status_task when status is first created
I think the reason I didn't do this initially was so that related users
and books, which are added necessarily after the model instance is
crated, will be part of the object when the task runs, but I have
investigated this and because of the transaction.atomic statement in the
to_model method in bookwyrm/activitypub/base_activity.py and in the
status view (added in this commit), this is not an issue.
2023-07-31 17:23:57 -07:00
Joachim
928d56b566 Fix function docstring 2023-07-31 22:15:09 +02:00
Joachim
e37ed8ea5e Remove links trailing punctuation 2023-07-31 22:12:37 +02:00
Hugh Rundle
c29ca5ad32
Merge pull request #2922 from tararoshan/wip-edit-post-and-save
fix button text when editing posts

Resolves #2642
2023-07-31 16:44:30 +10:00
Hugh Rundle
390f61ff3b
Merge branch 'main' into wip-edit-post-and-save 2023-07-31 16:25:04 +10:00
Hugh Rundle
61283b3d4f
Merge pull request #2929 from joachimesque/search-disable-spellcheck
Disable Spellcheck on header search inputs
2023-07-31 14:42:30 +10:00
Mouse Reeve
588ec80b4c
Merge pull request #2927 from bookwyrm-social/streams-queue
Reduce activity in streams queue from boost tasks
2023-07-30 18:37:49 -07:00
Joachim
6a81f91902 Disable Spellcheck on header search inputs 2023-07-30 22:33:08 +02:00
Mouse Reeve
71dc05f894 Reduce activity in streams queue from boost tasks 2023-07-30 13:07:12 -07:00
Mouse Reeve
30f025dbcd
Merge pull request #2923 from jderuiter/hashtag-naming
Remove '@' from the beginning of hashtag names in activity
2023-07-30 06:27:51 -07:00
Joeri de Ruiter
2920973961 Some small improvements to annotations 2023-07-28 20:54:03 +02:00
Joeri de Ruiter
f07d7b02f1 Type annotations and related changes for bookwyrm.connectors 2023-07-28 17:43:32 +02:00
Joeri de Ruiter
8736f2c6ef Remove '@' from the beginning of hashtag names in activity 2023-07-28 11:20:08 +02:00
Tara Sophia Roshan
c721e17aa0 Change wording from 'Save' to 'Update'
I think this wording is more clear.
2023-07-27 17:45:06 -05:00
Tara Sophia Roshan
9cd599dee3 Indicate edited post is saved
Should close #2642 and #2671. Gives a minimal solution to #2668.
2023-07-27 00:22:12 -05:00
axiomizer
2293c1c5a8 Revert part of "Hyphenate ISBN numbers and add copy button" related to copy button
This partially reverts commit d2c4785af1.
2023-07-25 08:54:49 -04:00
axiomizer
bd26da351a Hyphenate ISBN numbers and add copy button 2023-07-25 08:54:49 -04:00
Joeri de Ruiter
fec6f39e4e Migration to include Dutch in user.preferred_language 2023-07-23 21:34:28 +02:00
Joeri de Ruiter
308dfd1be0 Include Dutch locale 2023-07-23 21:27:42 +02:00
Joeri de Ruiter
0354e53eea Type annotations for utils 2023-07-23 20:50:44 +02:00
axiomizer
9a30a3656a satisfy js linter 2023-07-22 22:34:10 -04:00
Mouse Reeve
07aca2f62c
Merge pull request #2916 from jderuiter/mypy-integration
mypy integration
2023-07-22 18:14:10 -07:00
axiomizer
1bda8a5d9d Revert part of "Hyphenate ISBN numbers and add copy button" related to hyphenation
This partially reverts commit d2c4785af1.
2023-07-22 14:25:58 -04:00
Joeri de Ruiter
75f37d7361 Added mypy to scripts and workflow, and some first annotations for celerywyrm 2023-07-22 13:27:43 +02:00
axiomizer
d2c4785af1 Hyphenate ISBN numbers and add copy button 2023-07-21 21:27:46 -04:00
Mouse Reeve
eae06602a9 Fixes test data 2023-07-21 14:38:28 -07:00
Mouse Reeve
9c5b5d0ac1 Updates version 2023-07-20 19:08:47 -07:00
Mouse Reeve
c4d72829e9
Merge pull request #2879 from bookwyrm-social/reactivation-bug
Don't allow invalid account reactivation
2023-07-20 19:07:18 -07:00
Mouse Reeve
c947360da8
Merge pull request #2910 from WesleyAC/no-unauthed-remote-profile-view
Don't show remote profiles to unauthenticated users
2023-07-20 19:06:50 -07:00
Mouse Reeve
2f2dae074b
Merge pull request #2908 from WesleyAC/celery-functional-queues
Switch from priority queues to function-based queues
2023-07-20 19:05:55 -07:00
Mouse Reeve
9d531fcb62
Merge pull request #2909 from bookwyrm-social/dependabot/pip/aiohttp-3.8.5
build(deps): bump aiohttp from 3.8.3 to 3.8.5
2023-07-20 18:56:25 -07:00
Wesley Aptekar-Cassels
aae1d10eea Don't show remote profiles to unauthenticated users 2023-07-20 20:29:08 -04:00
Wesley Aptekar-Cassels
3e78e398c0 Switch from priority queues to function-based queues
Fixes: #2907
2023-07-20 12:25:30 -04:00
dependabot[bot]
7d1f3deaca
build(deps): bump aiohttp from 3.8.3 to 3.8.5
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.8.3 to 3.8.5.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/v3.8.5/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.8.3...v3.8.5)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-20 15:55:55 +00:00
Mouse Reeve
ccf3a4c5c1 Skip trying to match editions
It's rare that it will be useful, and it was a huge hassle.
2023-07-18 19:33:39 -07:00
Mouse Reeve
8b88de624d Adds test and fixes logic errors 2023-07-17 20:00:45 -07:00
Mouse Reeve
eee4e30e25 Adds managment command to repair editions in bad state 2023-07-17 11:22:59 -07:00
Mouse Reeve
fbb9d75cc8 Avoid server error when encountering broken edition
If an edition is missing its work, this change allows the page to still
load without a server error; this is important because otherwise the
book will break every page it appears on, including the feed page.
2023-07-17 07:07:01 -07:00
Mouse Reeve
107f5b38ca
Merge pull request #2904 from bookwyrm-social/updating-pluralization-error
Fixes plural display on import admin page
2023-07-17 06:40:15 -07:00
Mouse Reeve
ac4276f212
Merge pull request #2834 from zachflanders/2678
Add support for title sort to ignore initial article
2023-07-17 06:04:25 -07:00
Mouse Reeve
6778046906 Formats large numbers 2023-07-17 05:53:23 -07:00
Mouse Reeve
fbb6c41035 Joins entire sentence in import pluralization string 2023-07-17 05:49:40 -07:00
Mouse Reeve
815e788245
Merge pull request #2631 from rritik772/pluralization-error
fixed singularisation/pluralisation
2023-07-17 05:46:34 -07:00
Mouse Reeve
3bd20e3ff8
Set batch size to 1,000
Updated as per the comments on the PR so this is ready to merge
2023-07-17 05:42:17 -07:00
Mouse Reeve
f39a1fd580
Merge pull request #2894 from bookwyrm-social/dependabot/pip/django-3.2.20
build(deps): bump django from 3.2.19 to 3.2.20
2023-07-16 07:15:39 -07:00
Mouse Reeve
0818d5aabb
Merge branch 'main' into report-actions 2023-07-16 07:13:42 -07:00
Mouse Reeve
0832a2fa8e
Merge branch 'main' into reactivation-bug 2023-07-16 07:13:34 -07:00
Mouse Reeve
c2a7b9a77b
Merge pull request #2903 from bookwyrm-social/prettier-update
Fixes prettier JS complaints
2023-07-16 07:13:23 -07:00
Mouse Reeve
245ae35a81 Pin prettier version in github workflow 2023-07-16 06:57:31 -07:00
Mouse Reeve
d56b9f14a2 Handles changing user perms from report 2023-07-16 06:36:32 -07:00
Mouse Reeve
040dca0c31 Places first item of report history at top 2023-07-16 06:11:26 -07:00
Mouse Reeve
a7e6919b96 Fixes confirm email slow and adds test 2023-07-16 05:55:38 -07:00
Mouse Reeve
61037cf38a
Merge pull request #2883 from hughrun/modal-fix
Ensure report button triggers modal
2023-07-15 19:58:36 -07:00
dependabot[bot]
7f3a8f27ab
build(deps): bump django from 3.2.19 to 3.2.20
Bumps [django](https://github.com/django/django) from 3.2.19 to 3.2.20.
- [Commits](https://github.com/django/django/compare/3.2.19...3.2.20)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-05 23:27:36 +00:00
Hugh Rundle
dd92c53410
Merge pull request #2887 from hughrun/federation
add `FEDERATION.md` so other app maintainers understand how BookWyrm behaves in the fediverse.
2023-07-03 09:21:40 +10:00
Hugh Rundle
fad1eb8952
clean up and add stopped-reading shelf type 2023-07-03 08:38:53 +10:00
Hugh Rundle
c875b18e34
complete FEDERATION.md initial file 2023-07-01 14:23:02 +10:00
Hugh Rundle
1841d196ff
Ensure report button triggers modal
Clicking on "report" in any context closed the dropdown menu and required a second click on the dropdown to trigger the modal.
With this change, the modal opens as expected.

Reverses part of #2322
2023-06-26 12:22:14 +10:00
Hugh Rundle
e783c90693
add FEDERATION info 2023-06-26 08:27:14 +10:00
Mouse Reeve
6a949c24e2 Typo fix 2023-06-21 15:52:32 -07:00
Mouse Reeve
11f1a4662e Don't allow invalid account reactivation 2023-06-21 15:47:20 -07:00
Mouse Reeve
4f6a235d77 Reverses order of report action list 2023-06-21 15:05:02 -07:00
Jascha Ezra Urbach
a601be4708
Merge branch 'main' into 2678 2023-06-01 16:53:28 +02:00
Mouse Reeve
c6d23ba26a Record statuses deleted in reports 2023-05-30 11:35:47 -07:00
Philip James
9ff28d97b1
Merge pull request #6 from bookwyrm-social/main
Update to upstream
2023-05-24 23:42:35 -07:00
Mouse Reeve
affaf3d0ba Fixes tests and incorrect class method reference 2023-05-22 06:39:23 -07:00
Mouse Reeve
b3a519c082 Converts report "comments" into broader "actions" table
This table will now track all actions taken on a report, like resolving
it, re-opening it, suspending the reported user, et cetera, in addition
to comments. When there are multiple admins, this change will make it
easier to understand what actions have been taken by whom on a report.
2023-05-16 11:00:33 -07:00
Mouse Reeve
ab146f652a Adds action types to report comments table 2023-05-16 09:02:43 -07:00
Zach Flanders
ad1ddf2bff Fixing batch size 2023-04-29 09:48:01 -05:00
Zach Flanders
490064cdf8 updating sort title migration 2023-04-29 09:43:55 -05:00
Zach Flanders
a1df116c58 Adding batch processing to the sort title migration 2023-04-27 15:30:52 -05:00
Zach Flanders
a6e5939ad2 adding sort title to edit book form 2023-04-26 23:05:03 -05:00
Zach Flanders
3f205f1b10 Merge branch '2678' of https://github.com/zachflanders/bookwyrm into 2678 2023-04-25 21:06:13 -05:00
Zach Flanders
858a93e98a fixing migration 2023-04-25 21:05:11 -05:00
Zach Flanders
f43d7f8c70 fixing test and other checks 2023-04-25 21:00:16 -05:00
Zach Flanders
1985c2d284
Merge branch 'main' into 2678 2023-04-25 19:47:07 -05:00
Zach Flanders
575e1bac4c responding to review comments 2023-04-25 19:46:38 -05:00
Zach Flanders
6b39052fcc Adding test for sort_title population 2023-04-25 07:17:23 -05:00
Zach Flanders
a3013c6224 updating list view 2023-04-25 00:20:54 -05:00
Zach Flanders
21d9cb5fe5 updating shelf view 2023-04-25 00:15:58 -05:00
Zach Flanders
a94a4732ec add support for title sort to ignore initial article 2023-04-24 23:29:55 -05:00
Philip James
6b6ed23e25
Merge pull request #5 from bookwyrm-social/main
Update from upstream
2023-03-16 17:18:01 -07:00
Philip James
c878e11913
Merge pull request #4 from bookwyrm-social/main
Merge Updates from Upstream
2023-03-06 20:39:50 -08:00
Ritik Ranjan
adcf9310a0 Fixed Syntax of pluralisation 2023-01-28 21:23:08 +05:30
Ritik Ranjan
a12dc692ce Merge remote-tracking branch 'origin/main' into pluralization-error 2023-01-28 21:16:36 +05:30
Ritik Ranjan
e5e9e807ca Splited stirng into two then fixed pluralization error. 2023-01-28 21:11:20 +05:30
Ritik Ranjan
fc599f8b9a fixed singularisation/pluralisation
edited two files
2023-01-28 12:10:06 +05:30
Mouse Reeve
e1f6110dc8
Merge branch 'main' into bw-dev-npm-fix 2022-11-14 09:11:16 -08:00
Mouse Reeve
336c62bfc2
Merge branch 'main' into header-links 2022-07-28 11:45:59 -07:00
Mouse Reeve
583d5b3bdb Remove redundant "Your Books" link from menu 2022-07-08 10:57:53 -07:00
Mouse Reeve
891a5d4dd8 Adds "Your Books" to the header and removed "Feed"
The instance icon is already a link to the feed, and I think it's weird
not to have a quick link to your books, since books are important.
2022-07-08 10:51:22 -07:00
Mouse Reeve
51f445bc72
Merge branch 'main' into bw-dev-npm-fix 2022-06-11 20:39:18 -07:00
Vivianne Langdon
51bb4c6f5d make bw-dev consistent 2022-03-17 00:50:04 -07:00
Vivianne Langdon
225957ba8a remove non-css extensions 2022-03-17 00:50:04 -07:00
Vivianne Langdon
54b8d2c3f3 add import rule override for styles 2022-03-17 00:50:04 -07:00
Vivianne Langdon
6f27b5fd2e auto-fixes from running this 2022-03-17 00:50:04 -07:00
Vivianne Langdon
dbd5a02617 specify npm prefix and stylelint configs and adjust ignores 2022-03-17 00:50:04 -07:00
563 changed files with 71469 additions and 14460 deletions

View file

@ -16,6 +16,11 @@ DEFAULT_LANGUAGE="English"
## Leave unset to allow all hosts
# ALLOWED_HOSTS="localhost,127.0.0.1,[::1]"
# Specify when the site is served from a port that is not the default
# for the protocol (80 for HTTP or 443 for HTTPS).
# Probably only necessary in development.
# PORT=1333
MEDIA_ROOT=images/
# Database configuration
@ -71,14 +76,20 @@ ENABLE_THUMBNAIL_GENERATION=true
USE_S3=false
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
# seconds for signed S3 urls to expire
# this is currently only used for user export files
S3_SIGNED_URL_EXPIRY=900
# 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
# along with both AWS_S3_CUSTOM_DOMAIN and AWS_S3_ENDPOINT_URL.
# AWS_S3_URL_PROTOCOL must end in ":" and defaults to the same protocol as
# the BookWyrm instance ("http:" or "https:", based on USE_SSL).
# AWS_STORAGE_BUCKET_NAME= # "example-bucket-name"
# AWS_S3_CUSTOM_DOMAIN=None # "example-bucket-name.s3.fr-par.scw.cloud"
# AWS_S3_URL_PROTOCOL=None # "http:"
# AWS_S3_REGION_NAME=None # "fr-par"
# AWS_S3_ENDPOINT_URL=None # "https://s3.fr-par.scw.cloud"
@ -133,7 +144,14 @@ HTTP_X_FORWARDED_PROTO=false
TWO_FACTOR_LOGIN_VALIDITY_WINDOW=2
TWO_FACTOR_LOGIN_MAX_SECONDS=60
# Additional hosts to allow in the Content-Security-Policy, "self" (should be DOMAIN)
# and AWS_S3_CUSTOM_DOMAIN (if used) are added by default.
# Value should be a comma-separated list of host names.
# Additional hosts to allow in the Content-Security-Policy, "self" (should be
# DOMAIN with optionally ":" + PORT) and AWS_S3_CUSTOM_DOMAIN (if used) are
# added by default. Value should be a comma-separated list of host names.
CSP_ADDITIONAL_HOSTS=
# Time before being logged out (in seconds)
# SESSION_COOKIE_AGE=2592000 # current default: 30 days
# Maximum allowed memory for file uploads (increase if users are having trouble
# uploading BookWyrm export files).
# DATA_UPLOAD_MAX_MEMORY_MiB=100

68
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,68 @@
<!--
Thanks for contributing! This template has some checkboxes that help keep track of what changes go into a release.
To check (tick) a list item, replace the space between square brackets with an x, like this:
- [x] I have checked the box
You can find more information and tips for BookWyrm contributors at https://docs.joinbookwyrm.com/contributing.html
-->
## Description
<!--
Describe what your pull request does here
-->
<!--
For pull requests that relate or close an issue, please include them
below. We like to follow [Github's guidance on linking issues to pull requests](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
For example having the text: "closes #1234" would connect the current pull
request to issue 1234. And when we merge the pull request, Github will
automatically close the issue.
-->
- Related Issue #
- Closes #
## What type of Pull Request is this?
<!-- Check all that apply -->
- [ ] Bug Fix
- [ ] Enhancement
- [ ] Plumbing / Internals / Dependencies
- [ ] Refactor
## Does this PR change settings or dependencies, or break something?
<!-- Check all that apply -->
- [ ] This PR changes or adds default settings, configuration, or .env values
- [ ] This PR changes or adds dependencies
- [ ] This PR introduces other breaking changes
### Details of breaking or configuration changes (if any of above checked)
## Documentation
<!--
Documentation for users, admins, and developers is an important way to keep the BookWyrm community welcoming and make Bookwyrm easy to use.
Our documentation is maintained in a separate repository at https://github.com/bookwyrm-social/documentation
-->
<!-- Check all that apply -->
- [ ] New or amended documentation will be required if this PR is merged
- [ ] I have created a matching pull request in the Documentation repository
- [ ] I intend to create a matching pull request in the Documentation repository after this PR is merged
<!-- Amazing! Thanks for filling that out. Your PR will need to have passing tests and happy linters before we can merge
You will need to check your code with `black`, `pylint`, and `mypy`, or `./bw-dev formatters`
-->
### Tests
<!-- Check one -->
- [ ] My changes do not need new tests
- [ ] All tests I have added are passing
- [ ] I have written tests but need help to make them pass
- [ ] I have not written tests and need help to write them

26
.github/release.yml vendored Normal file
View file

@ -0,0 +1,26 @@
changelog:
exclude:
labels:
- ignore-for-release
categories:
- title: ‼️ Breaking Changes & New Settings ⚙️
labels:
- breaking-change
- config-change
- title: Updated Dependencies 🧸
labels:
- dependencies
- title: New Features 🎉
labels:
- enhancement
- title: Bug Fixes 🐛
labels:
- fix
- bug
- title: Internals/Plumbing 👩‍🔧
- plumbing
- tests
- deployment
- title: Other Changes
labels:
- "*"

View file

@ -1,17 +0,0 @@
name: Python Formatting (run ./bw-dev black to fix)
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: psf/black@22.12.0
with:
version: 22.12.0

View file

@ -36,11 +36,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -51,7 +51,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -65,4 +65,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View file

@ -10,7 +10,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install curlylint
run: pip install curlylint

View file

@ -1,61 +0,0 @@
name: Run Python Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-20.04
services:
postgres:
image: postgres:13
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: hunter2
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
env:
SECRET_KEY: beepbeep
DEBUG: false
USE_HTTPS: true
DOMAIN: your.domain.here
BOOKWYRM_DATABASE_BACKEND: postgres
MEDIA_ROOT: images/
POSTGRES_PASSWORD: hunter2
POSTGRES_USER: postgres
POSTGRES_DB: github_actions
POSTGRES_HOST: 127.0.0.1
CELERY_BROKER: ""
REDIS_BROKER_PORT: 6379
REDIS_BROKER_PASSWORD: beep
USE_DUMMY_CACHE: true
FLOWER_PORT: 8888
EMAIL_HOST: "smtp.mailgun.org"
EMAIL_PORT: 587
EMAIL_HOST_USER: ""
EMAIL_HOST_PASSWORD: ""
EMAIL_USE_TLS: true
ENABLE_PREVIEW_IMAGES: false
ENABLE_THUMBNAIL_GENERATION: true
HTTP_X_FORWARDED_PROTO: false
run: |
pytest -n 3

View file

@ -19,10 +19,11 @@ jobs:
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it.
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install modules
run: npm install stylelint stylelint-config-recommended stylelint-config-standard stylelint-order eslint
# run: npm install stylelint stylelint-config-recommended stylelint-config-standard stylelint-order eslint
run: npm install eslint@^8.9.0
# See .stylelintignore for files that are not linted.
# - name: Run stylelint

View file

@ -14,10 +14,10 @@ jobs:
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it.
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install modules
run: npm install prettier
run: npm install prettier@2.5.1
- name: Run Prettier
run: npx prettier --check bookwyrm/static/js/*.js

View file

@ -1,27 +0,0 @@
name: Pylint
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Analysing the code with pylint
run: |
pylint bookwyrm/

99
.github/workflows/python.yml vendored Normal file
View file

@ -0,0 +1,99 @@
name: Python
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# overrides for .env.example
env:
POSTGRES_HOST: 127.0.0.1
PGPORT: 5432
POSTGRES_USER: postgres
POSTGRES_PASSWORD: hunter2
POSTGRES_DB: github_actions
SECRET_KEY: beepbeep
EMAIL_HOST_USER: ""
EMAIL_HOST_PASSWORD: ""
jobs:
pytest:
name: Tests (pytest)
runs-on: ubuntu-latest
services:
postgres:
image: postgres:13
env: # does not inherit from jobs.build.env
POSTGRES_USER: postgres
POSTGRES_PASSWORD: hunter2
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: pip
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest-github-actions-annotate-failures
- name: Set up .env
run: cp .env.example .env
- name: Check migrations up-to-date
run: python ./manage.py makemigrations --check -v 3
- name: Run Tests
run: pytest -n 3
pylint:
name: Linting (pylint)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: pip
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Analyse code with pylint
run: pylint bookwyrm/
mypy:
name: Typing (mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: pip
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Set up .env
run: cp .env.example .env
- name: Analyse code with mypy
run: mypy bookwyrm celerywyrm
black:
name: Formatting (black; run ./bw-dev black to fix)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: psf/black@stable
with:
version: "22.*"

5
.gitignore vendored
View file

@ -16,6 +16,8 @@
# BookWyrm
.env
/images/
/exports/
/static/
bookwyrm/static/css/bookwyrm.css
bookwyrm/static/css/themes/
!bookwyrm/static/css/themes/bookwyrm-*.scss
@ -36,3 +38,6 @@ nginx/default.conf
#macOS
**/.DS_Store
# Docker
docker-compose.override.yml

1
.prettierrc Normal file
View file

@ -0,0 +1 @@
'trailingComma': 'es5'

View file

@ -3,7 +3,19 @@ ignore=migrations
load-plugins=pylint.extensions.no_self_use
[MESSAGES CONTROL]
disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801,C3001,import-error
disable =
cyclic-import,
duplicate-code,
fixme,
no-member,
raise-missing-from,
too-few-public-methods,
too-many-ancestors,
too-many-instance-attributes,
unnecessary-lambda-assignment,
unsubscriptable-object,
enable =
useless-suppression
[FORMAT]
max-line-length=88

View file

@ -1,4 +1,4 @@
FROM python:3.9
FROM python:3.11
ENV PYTHONUNBUFFERED 1

334
FEDERATION.md Normal file
View file

@ -0,0 +1,334 @@
# Federation
BookWyrm uses the [ActivityPub](http://activitypub.rocks/) protocol to send and receive user activity between other BookWyrm instances and other services that implement ActivityPub. To handle book data, BookWyrm has a handful of extended Activity types which are not part of the standard, but are legible to other BookWyrm instances.
## Activities and Objects
### Users and relationships
User relationship interactions follow the standard ActivityPub spec.
- `Follow`: request to receive statuses from a user, and view their statuses that have followers-only privacy
- `Accept`: approves a `Follow` and finalizes the relationship
- `Reject`: denies a `Follow`
- `Block`: prevent users from seeing one another's statuses, and prevents the blocked user from viewing the actor's profile
- `Update`: updates a user's profile and settings
- `Delete`: deactivates a user
- `Undo`: reverses a `Block` or `Follow`
### Activities
- `Create/Status`: saves a new status in the database.
- `Delete/Status`: Removes a status
- `Like/Status`: Creates a favorite on the status
- `Announce/Status`: Boosts the status into the actor's timeline
- `Undo/*`,: Reverses an `Announce`, `Like`, or `Move`
- `Move/User`: Moves a user from one ActivityPub id to another.
### Collections
User's books and lists are represented by [`OrderedCollection`](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection)
### Statuses
BookWyrm is focused on book reading activities - it is not a general-purpose messaging application. For this reason, BookWyrm only accepts status `Create` activities if they are:
- Direct messages (i.e., `Note`s with the privacy level `direct`, which mention a local user),
- Related to a book (of a custom status type that includes the field `inReplyToBook`),
- Replies to existing statuses saved in the database
All other statuses will be received by the instance inbox, but by design **will not be delivered to user inboxes or displayed to users**.
### Custom Object types
With the exception of `Note`, the following object types are used in Bookwyrm but are not currently provided with a custom JSON-LD `@context` extension IRI. This is likely to change in future to make them true deserialisable JSON-LD objects.
##### Note
Within BookWyrm a `Note` is constructed according to [the ActivityStreams vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note), however `Note`s can only be created as direct messages or as replies to other statuses. As mentioned above, this also applies to incoming `Note`s.
##### Review
A `Review` is a status in response to a book (indicated by the `inReplyToBook` field), which has a title, body, and numerical rating between 0 (not rated) and 5.
Example:
```json
{
"id": "https://example.net/user/library_lurker/review/2",
"type": "Review",
"published": "2023-06-30T21:43:46.013132+00:00",
"attributedTo": "https://example.net/user/library_lurker",
"content": "<p>This is an enjoyable book with great characters.</p>",
"to": ["https://example.net/user/library_lurker/followers"],
"cc": [],
"replies": {
"id": "https://example.net/user/library_lurker/review/2/replies",
"type": "OrderedCollection",
"totalItems": 0,
"first": "https://example.net/user/library_lurker/review/2/replies?page=1",
"last": "https://example.net/user/library_lurker/review/2/replies?page=1",
"@context": "https://www.w3.org/ns/activitystreams"
},
"summary": "Spoilers ahead!",
"tag": [],
"attachment": [],
"sensitive": true,
"inReplyToBook": "https://example.net/book/1",
"name": "What a cracking read",
"rating": 4.5,
"@context": "https://www.w3.org/ns/activitystreams"
}
```
##### Comment
A `Comment` on a book mentions a book and has a message body, reading status, and progress indicator.
Example:
```json
{
"id": "https://example.net/user/library_lurker/comment/9",
"type": "Comment",
"published": "2023-06-30T21:43:46.013132+00:00",
"attributedTo": "https://example.net/user/library_lurker",
"content": "<p>This is a very enjoyable book so far.</p>",
"to": ["https://example.net/user/library_lurker/followers"],
"cc": [],
"replies": {
"id": "https://example.net/user/library_lurker/comment/9/replies",
"type": "OrderedCollection",
"totalItems": 0,
"first": "https://example.net/user/library_lurker/comment/9/replies?page=1",
"last": "https://example.net/user/library_lurker/comment/9/replies?page=1",
"@context": "https://www.w3.org/ns/activitystreams"
},
"summary": "Spoilers ahead!",
"tag": [],
"attachment": [],
"sensitive": true,
"inReplyToBook": "https://example.net/book/1",
"readingStatus": "reading",
"progress": 25,
"progressMode": "PG",
"@context": "https://www.w3.org/ns/activitystreams"
}
```
##### Quotation
A quotation (aka "quote") has a message body, an excerpt from a book including position as a page number or percentage indicator, and mentions a book.
Example:
```json
{
"id": "https://example.net/user/mouse/quotation/13",
"url": "https://example.net/user/mouse/quotation/13",
"inReplyTo": null,
"published": "2020-05-10T02:38:31.150343+00:00",
"attributedTo": "https://example.net/user/mouse",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://example.net/user/mouse/followers"
],
"sensitive": false,
"content": "I really like this quote",
"type": "Quotation",
"replies": {
"id": "https://example.net/user/mouse/quotation/13/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://example.net/user/mouse/quotation/13/replies?only_other_accounts=true&page=true",
"partOf": "https://example.net/user/mouse/quotation/13/replies",
"items": []
}
},
"inReplyToBook": "https://example.net/book/1",
"quote": "To be or not to be, that is the question.",
"position": 50,
"positionMode": "PCT",
"@context": "https://www.w3.org/ns/activitystreams"
}
```
### Custom Objects
##### Work
A particular book, a "work" in the [FRBR](https://en.wikipedia.org/wiki/Functional_Requirements_for_Bibliographic_Records) sense.
Example:
```json
{
"id": "https://bookwyrm.social/book/5988",
"type": "Work",
"authors": [
"https://bookwyrm.social/author/417"
],
"first_published_date": null,
"published_date": null,
"title": "Piranesi",
"sort_title": null,
"subtitle": null,
"description": "**From the *New York Times* bestselling author of *Jonathan Strange & Mr. Norrell*, an intoxicating, hypnotic new novel set in a dreamlike alternative reality.",
"languages": [],
"series": null,
"series_number": null,
"subjects": [
"English literature"
],
"subject_places": [],
"openlibrary_key": "OL20893680W",
"librarything_key": null,
"goodreads_key": null,
"attachment": [
{
"url": "https://bookwyrm.social/images/covers/10226290-M.jpg",
"type": "Image"
}
],
"lccn": null,
"editions": [
"https://bookwyrm.social/book/5989"
],
"@context": "https://www.w3.org/ns/activitystreams"
}
```
##### Edition
A particular _manifestation_ of a Work, in the [FRBR](https://en.wikipedia.org/wiki/Functional_Requirements_for_Bibliographic_Records) sense.
Example:
```json
{
"id": "https://bookwyrm.social/book/5989",
"lastEditedBy": "https://example.net/users/rat",
"type": "Edition",
"authors": [
"https://bookwyrm.social/author/417"
],
"first_published_date": null,
"published_date": "2020-09-15T00:00:00+00:00",
"title": "Piranesi",
"sort_title": null,
"subtitle": null,
"description": "Piranesi's house is no ordinary building; its rooms are infinite, its corridors endless, its walls are lined with thousands upon thousands of statues, each one different from all the others.",
"languages": [
"English"
],
"series": null,
"series_number": null,
"subjects": [],
"subject_places": [],
"openlibrary_key": "OL29486417M",
"librarything_key": null,
"goodreads_key": null,
"isfdb": null,
"attachment": [
{
"url": "https://bookwyrm.social/images/covers/50202953._SX318_.jpg",
"type": "Image"
}
],
"isbn_10": "1526622424",
"isbn_13": "9781526622426",
"oclc_number": null,
"asin": null,
"pages": 272,
"physical_format": null,
"publishers": [
"Bloomsbury Publishing Plc"
],
"work": "https://bookwyrm.social/book/5988",
"@context": "https://www.w3.org/ns/activitystreams"
}
```
#### Shelf
A user's book collection. By default, every user has a `to-read`, `reading`, `read`, and `stopped-reading` shelf which are used to track reading progress. Users may create an unlimited number of additional shelves with their own ids.
Example
```json
{
"id": "https://example.net/user/avid_reader/books/extraspecialbooks-5",
"type": "Shelf",
"totalItems": 0,
"first": "https://example.net/user/avid_reader/books/extraspecialbooks-5?page=1",
"last": "https://example.net/user/avid_reader/books/extraspecialbooks-5?page=1",
"name": "Extra special books",
"owner": "https://example.net/user/avid_reader",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://example.net/user/avid_reader/followers"
],
"@context": "https://www.w3.org/ns/activitystreams"
}
```
#### List
A collection of books that may have items contributed by users other than the one who created the list.
Example:
```json
{
"id": "https://example.net/list/1",
"type": "BookList",
"totalItems": 0,
"first": "https://example.net/list/1?page=1",
"last": "https://example.net/list/1?page=1",
"name": "My cool list",
"owner": "https://example.net/user/avid_reader",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://example.net/user/avid_reader/followers"
],
"summary": "A list of books I like.",
"curation": "curated",
"@context": "https://www.w3.org/ns/activitystreams"
}
```
#### Activities
- `Create`: Adds a shelf or list to the database.
- `Delete`: Removes a shelf or list.
- `Add`: Adds a book to a shelf or list.
- `Remove`: Removes a book from a shelf or list.
## Alternative Serialization
Because BookWyrm uses custom object types that aren't listed in [the standard ActivityStreams Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary), some statuses are transformed into standard types when sent to or viewed by non-BookWyrm services. `Review`s are converted into `Article`s, and `Comment`s and `Quotation`s are converted into `Note`s, with a link to the book and the cover image attached.
In future this may be done with [JSON-LD type arrays](https://www.w3.org/TR/json-ld/#specifying-the-type) instead.
## Other extensions
### Webfinger
Bookwyrm uses the [Webfinger](https://datatracker.ietf.org/doc/html/rfc7033) standard to identify and disambiguate fediverse actors. The [Webfinger documentation on the Mastodon project](https://docs.joinmastodon.org/spec/webfinger/) provides a good overview of how Webfinger is used.
### HTTP Signatures
Bookwyrm uses and requires HTTP signatures for all `POST` requests. `GET` requests are not signed by default, but if Bookwyrm receives a `403` response to a `GET` it will re-send the request, signed by the default server user. This usually will have a user id of `https://example.net/user/bookwyrm.instance.actor`
#### publicKey id
In older versions of Bookwyrm the `publicKey.id` was incorrectly listed in request headers as `https://example.net/user/username#main-key`. As of v0.6.3 the id is now listed correctly, as `https://example.net/user/username/#main-key`. In most ActivityPub implementations this will make no difference as the URL will usually resolve to the same place.
### NodeInfo
Bookwyrm uses the [NodeInfo](http://nodeinfo.diaspora.software/) standard to provide statistics and version information for each instance.
## Further Documentation
See [docs.joinbookwyrm.com/](https://docs.joinbookwyrm.com/) for more documentation.

View file

@ -10,7 +10,6 @@ BookWyrm is a social network for tracking your reading, talking about books, wri
## Links
[![Mastodon Follow](https://img.shields.io/mastodon/follow/000146121?domain=https%3A%2F%2Ftech.lgbt&style=social)](https://tech.lgbt/@bookwyrm)
[![Twitter Follow](https://img.shields.io/twitter/follow/BookWyrmSocial?style=social)](https://twitter.com/BookWyrmSocial)
- [Project homepage](https://joinbookwyrm.com/)
- [Support](https://patreon.com/bookwyrm)

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.7.3

View file

@ -4,7 +4,11 @@ import sys
from .base_activity import ActivityEncoder, Signature, naive_parse
from .base_activity import Link, Mention, Hashtag
from .base_activity import ActivitySerializerError, resolve_remote_id
from .base_activity import (
ActivitySerializerError,
resolve_remote_id,
get_representative,
)
from .image import Document, Image
from .note import Note, GeneratedNote, Article, Comment, Quotation
from .note import Review, Rating
@ -19,6 +23,7 @@ from .verbs import Create, Delete, Undo, Update
from .verbs import Follow, Accept, Reject, Block
from .verbs import Add, Remove
from .verbs import Announce, Like
from .verbs import Move
# this creates a list of all the Activity types that we can serialize,
# so when an Activity comes in from outside, we can check if it's known

View file

@ -1,7 +1,10 @@
""" basics for an activitypub serializer """
from __future__ import annotations
from dataclasses import dataclass, fields, MISSING
from json import JSONEncoder
import logging
from typing import Optional, Union, TypeVar, overload, Any
import requests
from django.apps import apps
@ -10,12 +13,16 @@ from django.utils.http import http_date
from bookwyrm import models
from bookwyrm.connectors import ConnectorException, get_data
from bookwyrm.models import base_model
from bookwyrm.signatures import make_signature
from bookwyrm.settings import DOMAIN, INSTANCE_ACTOR_USERNAME
from bookwyrm.tasks import app, MEDIUM
from bookwyrm.tasks import app, MISC
logger = logging.getLogger(__name__)
# pylint: disable=invalid-name
TBookWyrmModel = TypeVar("TBookWyrmModel", bound=base_model.BookWyrmModel)
class ActivitySerializerError(ValueError):
"""routine problems serializing activitypub json"""
@ -65,7 +72,13 @@ class ActivityObject:
id: str
type: str
def __init__(self, activity_objects=None, **kwargs):
def __init__(
self,
activity_objects: Optional[
dict[str, Union[str, list[str], ActivityObject, base_model.BookWyrmModel]]
] = None,
**kwargs: Any,
):
"""this lets you pass in an object with fields that aren't in the
dataclass, which it ignores. Any field in the dataclass is required or
has a default value"""
@ -101,13 +114,13 @@ class ActivityObject:
# pylint: disable=too-many-locals,too-many-branches,too-many-arguments
def to_model(
self,
model=None,
instance=None,
allow_create=True,
save=True,
overwrite=True,
allow_external_connections=True,
):
model: Optional[type[TBookWyrmModel]] = None,
instance: Optional[TBookWyrmModel] = None,
allow_create: bool = True,
save: bool = True,
overwrite: bool = True,
allow_external_connections: bool = True,
) -> Optional[TBookWyrmModel]:
"""convert from an activity to a model instance. Args:
model: the django model that this object is being converted to
(will guess if not known)
@ -224,7 +237,7 @@ class ActivityObject:
omit = kwargs.get("omit", ())
data = self.__dict__.copy()
# recursively serialize
for (k, v) in data.items():
for k, v in data.items():
try:
if issubclass(type(v), ActivityObject):
data[k] = v.serialize()
@ -237,11 +250,14 @@ class ActivityObject:
pass
data = {k: v for (k, v) in data.items() if v is not None and k not in omit}
if "@context" not in omit:
data["@context"] = "https://www.w3.org/ns/activitystreams"
data["@context"] = [
"https://www.w3.org/ns/activitystreams",
{"Hashtag": "as:Hashtag"},
]
return data
@app.task(queue=MEDIUM)
@app.task(queue=MISC)
@transaction.atomic
def set_related_field(
model_name, origin_model_name, related_field_name, related_remote_id, data
@ -296,14 +312,40 @@ def get_model_from_type(activity_type):
# pylint: disable=too-many-arguments
@overload
def resolve_remote_id(
remote_id,
model=None,
refresh=False,
save=True,
get_activity=False,
allow_external_connections=True,
):
remote_id: str,
model: type[TBookWyrmModel],
refresh: bool = False,
save: bool = True,
get_activity: bool = False,
allow_external_connections: bool = True,
) -> TBookWyrmModel:
...
# pylint: disable=too-many-arguments
@overload
def resolve_remote_id(
remote_id: str,
model: Optional[str] = None,
refresh: bool = False,
save: bool = True,
get_activity: bool = False,
allow_external_connections: bool = True,
) -> base_model.BookWyrmModel:
...
# pylint: disable=too-many-arguments
def resolve_remote_id(
remote_id: str,
model: Optional[Union[str, type[base_model.BookWyrmModel]]] = None,
refresh: bool = False,
save: bool = True,
get_activity: bool = False,
allow_external_connections: bool = True,
) -> base_model.BookWyrmModel:
"""take a remote_id and return an instance, creating if necessary. Args:
remote_id: the unique url for looking up the object in the db or by http
model: a string or object representing the model that corresponds to the object
@ -327,16 +369,12 @@ def resolve_remote_id(
# load the data and create the object
try:
data = get_data(remote_id)
data = get_activitypub_data(remote_id)
except ConnectionError:
logger.info("Could not connect to host for remote_id: %s", remote_id)
return None
except requests.HTTPError as e:
if (e.response is not None) and e.response.status_code == 401:
# This most likely means it's a mastodon with secure fetch enabled.
data = get_activitypub_data(remote_id)
else:
logger.info("Could not connect to host for remote_id: %s", remote_id)
logger.exception("HTTP error - remote_id: %s - error: %s", remote_id, e)
return None
# determine the model implicitly, if not provided
# or if it's a model with subclasses like Status, check again
@ -358,19 +396,15 @@ def resolve_remote_id(
def get_representative():
"""Get or create an actor representing the instance
to sign requests to 'secure mastodon' servers"""
username = f"{INSTANCE_ACTOR_USERNAME}@{DOMAIN}"
email = "bookwyrm@localhost"
try:
user = models.User.objects.get(username=username)
except models.User.DoesNotExist:
user = models.User.objects.create_user(
username=username,
email=email,
local=True,
localname=INSTANCE_ACTOR_USERNAME,
)
return user
to sign outgoing HTTP GET requests"""
return models.User.objects.get_or_create(
username=f"{INSTANCE_ACTOR_USERNAME}@{DOMAIN}",
defaults={
"email": "bookwyrm@localhost",
"local": True,
"localname": INSTANCE_ACTOR_USERNAME,
},
)[0]
def get_activitypub_data(url):
@ -389,6 +423,7 @@ def get_activitypub_data(url):
"Date": now,
"Signature": make_signature("get", sender, url, now),
},
timeout=15,
)
except requests.RequestException:
raise ConnectorException()

View file

@ -1,6 +1,6 @@
""" book and author data """
from dataclasses import dataclass, field
from typing import List
from typing import Optional
from .base_activity import ActivityObject
from .image import Document
@ -11,19 +11,17 @@ from .image import Document
class BookData(ActivityObject):
"""shared fields for all book data and authors"""
openlibraryKey: str = None
inventaireId: str = None
librarythingKey: str = None
goodreadsKey: str = None
bnfId: str = None
viaf: str = None
wikidata: str = None
asin: str = None
aasin: str = None
isfdb: str = None
lastEditedBy: str = None
links: List[str] = field(default_factory=lambda: [])
fileLinks: List[str] = field(default_factory=lambda: [])
openlibraryKey: Optional[str] = None
inventaireId: Optional[str] = None
librarythingKey: Optional[str] = None
goodreadsKey: Optional[str] = None
bnfId: Optional[str] = None
viaf: Optional[str] = None
wikidata: Optional[str] = None
asin: Optional[str] = None
aasin: Optional[str] = None
isfdb: Optional[str] = None
lastEditedBy: Optional[str] = None
# pylint: disable=invalid-name
@ -35,17 +33,19 @@ class Book(BookData):
sortTitle: str = None
subtitle: str = None
description: str = ""
languages: List[str] = field(default_factory=lambda: [])
languages: list[str] = field(default_factory=list)
series: str = ""
seriesNumber: str = ""
subjects: List[str] = field(default_factory=lambda: [])
subjectPlaces: List[str] = field(default_factory=lambda: [])
subjects: list[str] = field(default_factory=list)
subjectPlaces: list[str] = field(default_factory=list)
authors: List[str] = field(default_factory=lambda: [])
authors: list[str] = field(default_factory=list)
firstPublishedDate: str = ""
publishedDate: str = ""
cover: Document = None
fileLinks: list[str] = field(default_factory=list)
cover: Optional[Document] = None
type: str = "Book"
@ -58,22 +58,21 @@ class Edition(Book):
isbn10: str = ""
isbn13: str = ""
oclcNumber: str = ""
pages: int = None
pages: Optional[int] = None
physicalFormat: str = ""
physicalFormatDetail: str = ""
publishers: List[str] = field(default_factory=lambda: [])
publishers: list[str] = field(default_factory=list)
editionRank: int = 0
type: str = "Edition"
# pylint: disable=invalid-name
@dataclass(init=False)
class Work(Book):
"""work instance of a book object"""
lccn: str = ""
editions: List[str] = field(default_factory=lambda: [])
editions: list[str] = field(default_factory=list)
type: str = "Work"
@ -83,12 +82,12 @@ class Author(BookData):
"""author of a book"""
name: str
isni: str = None
viafId: str = None
gutenbergId: str = None
born: str = None
died: str = None
aliases: List[str] = field(default_factory=lambda: [])
isni: Optional[str] = None
viafId: Optional[str] = None
gutenbergId: Optional[str] = None
born: Optional[str] = None
died: Optional[str] = None
aliases: list[str] = field(default_factory=list)
bio: str = ""
wikipediaLink: str = ""
type: str = "Author"

View file

@ -18,7 +18,6 @@ class OrderedCollection(ActivityObject):
type: str = "OrderedCollection"
# pylint: disable=invalid-name
@dataclass(init=False)
class OrderedCollectionPrivate(OrderedCollection):
"""an ordered collection with privacy settings"""

View file

@ -1,5 +1,5 @@
""" actor serializer """
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Dict
from .base_activity import ActivityObject
@ -35,9 +35,11 @@ class Person(ActivityObject):
endpoints: Dict = None
name: str = None
summary: str = None
icon: Image = field(default_factory=lambda: {})
icon: Image = None
bookwyrmUser: bool = False
manuallyApprovesFollowers: str = False
discoverable: str = False
hideFollows: str = False
movedTo: str = None
alsoKnownAs: dict[str] = None
type: str = "Person"

View file

@ -22,7 +22,6 @@ class Verb(ActivityObject):
self.object.to_model(allow_external_connections=allow_external_connections)
# pylint: disable=invalid-name
@dataclass(init=False)
class Create(Verb):
"""Create activity"""
@ -33,7 +32,6 @@ class Create(Verb):
type: str = "Create"
# pylint: disable=invalid-name
@dataclass(init=False)
class Delete(Verb):
"""Create activity"""
@ -63,7 +61,6 @@ class Delete(Verb):
# if we can't find it, we don't need to delete it because we don't have it
# pylint: disable=invalid-name
@dataclass(init=False)
class Update(Verb):
"""Update activity"""
@ -171,9 +168,19 @@ class Reject(Verb):
type: str = "Reject"
def action(self, allow_external_connections=True):
"""reject a follow request"""
obj = self.object.to_model(save=False, allow_create=False)
"""reject a follow or follow request"""
for model_name in ["UserFollowRequest", "UserFollows", None]:
model = apps.get_model(f"bookwyrm.{model_name}") if model_name else None
if obj := self.object.to_model(
model=model,
save=False,
allow_create=False,
allow_external_connections=allow_external_connections,
):
# Reject the first model that can be built.
obj.reject()
break
@dataclass(init=False)
@ -217,7 +224,6 @@ class Like(Verb):
self.to_model(allow_external_connections=allow_external_connections)
# pylint: disable=invalid-name
@dataclass(init=False)
class Announce(Verb):
"""boosting a status"""
@ -231,3 +237,30 @@ class Announce(Verb):
def action(self, allow_external_connections=True):
"""boost"""
self.to_model(allow_external_connections=allow_external_connections)
@dataclass(init=False)
class Move(Verb):
"""a user moving an object"""
object: str
type: str = "Move"
origin: str = None
target: str = None
def action(self, allow_external_connections=True):
"""move"""
object_is_user = resolve_remote_id(remote_id=self.object, model="User")
if object_is_user:
model = apps.get_model("bookwyrm.MoveUser")
self.to_model(
model=model,
save=True,
allow_external_connections=allow_external_connections,
)
else:
# we might do something with this to move other objects at some point
pass

View file

@ -8,7 +8,7 @@ from opentelemetry import trace
from bookwyrm import models
from bookwyrm.redis_store import RedisStore, r
from bookwyrm.tasks import app, LOW, MEDIUM, HIGH
from bookwyrm.tasks import app, STREAMS, IMPORT_TRIGGERED
from bookwyrm.telemetry import open_telemetry
@ -32,7 +32,7 @@ class ActivityStream(RedisStore):
stream_id = self.stream_id(user_id)
return f"{stream_id}-unread-by-type"
def get_rank(self, obj): # pylint: disable=no-self-use
def get_rank(self, obj):
"""statuses are sorted by date published"""
return obj.published_date.timestamp()
@ -112,7 +112,7 @@ class ActivityStream(RedisStore):
trace.get_current_span().set_attribute("status_privacy", status.privacy)
trace.get_current_span().set_attribute(
"status_reply_parent_privacy",
status.reply_parent.privacy if status.reply_parent else None,
status.reply_parent.privacy if status.reply_parent else status.privacy,
)
# direct messages don't appear in feeds, direct comments/reviews/etc do
if status.privacy == "direct" and status.status_type == "Note":
@ -139,14 +139,14 @@ class ActivityStream(RedisStore):
| (
Q(following=status.user) & Q(following=status.reply_parent.user)
) # if the user is following both authors
).distinct()
)
# only visible to the poster's followers and tagged users
elif status.privacy == "followers":
audience = audience.filter(
Q(following=status.user) # if the user is following the author
)
return audience.distinct()
return audience.distinct("id")
@tracer.start_as_current_span("ActivityStream.get_audience")
def get_audience(self, status):
@ -156,7 +156,7 @@ class ActivityStream(RedisStore):
status_author = models.User.objects.filter(
is_active=True, local=True, id=status.user.id
).values_list("id", flat=True)
return list(set(list(audience) + list(status_author)))
return list(set(audience) | set(status_author))
def get_stores_for_users(self, user_ids):
"""convert a list of user ids into redis store ids"""
@ -183,15 +183,13 @@ class HomeStream(ActivityStream):
def get_audience(self, status):
trace.get_current_span().set_attribute("stream_id", self.key)
audience = super()._get_audience(status)
if not audience:
return []
# if the user is following the author
audience = audience.filter(following=status.user).values_list("id", flat=True)
# if the user is the post's author
status_author = models.User.objects.filter(
is_active=True, local=True, id=status.user.id
).values_list("id", flat=True)
return list(set(list(audience) + list(status_author)))
return list(set(audience) | set(status_author))
def get_statuses_for_user(self, user):
return models.Status.privacy_filter(
@ -239,9 +237,7 @@ class BooksStream(ActivityStream):
)
audience = super()._get_audience(status)
if not audience:
return models.User.objects.none()
return audience.filter(shelfbook__book__parent_work=work).distinct()
return audience.filter(shelfbook__book__parent_work=work)
def get_audience(self, status):
# only show public statuses on the books feed,
@ -329,10 +325,9 @@ def add_status_on_create(sender, instance, created, *args, **kwargs):
remove_status_task.delay(instance.id)
return
# To avoid creating a zillion unnecessary tasks caused by re-saving the model,
# check if it's actually ready to send before we go. We're trusting this was
# set correctly by the inbox or view
if not instance.ready:
# We don't want to create multiple add_status_tasks for each status, and because
# the transactions are atomic, on_commit won't run until the status is ready to add.
if not created:
return
# when creating new things, gotta wait on the transaction
@ -343,7 +338,11 @@ def add_status_on_create(sender, instance, created, *args, **kwargs):
def add_status_on_create_command(sender, instance, created):
"""runs this code only after the database commit completes"""
priority = HIGH
# boosts trigger 'saves" twice, so don't bother duplicating the task
if sender == models.Boost and not created:
return
priority = STREAMS
# check if this is an old status, de-prioritize if so
# (this will happen if federation is very slow, or, more expectedly, on csv import)
if instance.published_date < timezone.now() - timedelta(
@ -353,7 +352,7 @@ def add_status_on_create_command(sender, instance, created):
if instance.user.local:
return
# an out of date remote status is a low priority but should be added
priority = LOW
priority = IMPORT_TRIGGERED
add_status_task.apply_async(
args=(instance.id,),
@ -497,7 +496,7 @@ def remove_statuses_on_unshelve(sender, instance, *args, **kwargs):
# ---- TASKS
@app.task(queue=LOW)
@app.task(queue=STREAMS)
def add_book_statuses_task(user_id, book_id):
"""add statuses related to a book on shelve"""
user = models.User.objects.get(id=user_id)
@ -505,7 +504,7 @@ def add_book_statuses_task(user_id, book_id):
BooksStream().add_book_statuses(user, book)
@app.task(queue=LOW)
@app.task(queue=STREAMS)
def remove_book_statuses_task(user_id, book_id):
"""remove statuses about a book from a user's books feed"""
user = models.User.objects.get(id=user_id)
@ -513,7 +512,7 @@ def remove_book_statuses_task(user_id, book_id):
BooksStream().remove_book_statuses(user, book)
@app.task(queue=MEDIUM)
@app.task(queue=STREAMS)
def populate_stream_task(stream, user_id):
"""background task for populating an empty activitystream"""
user = models.User.objects.get(id=user_id)
@ -521,7 +520,7 @@ def populate_stream_task(stream, user_id):
stream.populate_streams(user)
@app.task(queue=MEDIUM)
@app.task(queue=STREAMS)
def remove_status_task(status_ids):
"""remove a status from any stream it might be in"""
# this can take an id or a list of ids
@ -536,7 +535,7 @@ def remove_status_task(status_ids):
)
@app.task(queue=HIGH)
@app.task(queue=STREAMS)
def add_status_task(status_id, increment_unread=False):
"""add a status to any stream it should be in"""
status = models.Status.objects.select_subclasses().get(id=status_id)
@ -548,7 +547,7 @@ def add_status_task(status_id, increment_unread=False):
stream.add_status(status, increment_unread=increment_unread)
@app.task(queue=MEDIUM)
@app.task(queue=STREAMS)
def remove_user_statuses_task(viewer_id, user_id, stream_list=None):
"""remove all statuses by a user from a viewer's stream"""
stream_list = [streams[s] for s in stream_list] if stream_list else streams.values()
@ -558,7 +557,7 @@ def remove_user_statuses_task(viewer_id, user_id, stream_list=None):
stream.remove_user_statuses(viewer, user)
@app.task(queue=MEDIUM)
@app.task(queue=STREAMS)
def add_user_statuses_task(viewer_id, user_id, stream_list=None):
"""add all statuses by a user to a viewer's stream"""
stream_list = [streams[s] for s in stream_list] if stream_list else streams.values()
@ -568,7 +567,7 @@ def add_user_statuses_task(viewer_id, user_id, stream_list=None):
stream.add_user_statuses(viewer, user)
@app.task(queue=MEDIUM)
@app.task(queue=STREAMS)
def handle_boost_task(boost_id):
"""remove the original post and other, earlier boosts"""
instance = models.Status.objects.get(id=boost_id)

View file

@ -1,4 +1,5 @@
"""Do further startup configuration and initialization"""
import os
import urllib
import logging
@ -14,16 +15,16 @@ def download_file(url, destination):
"""Downloads a file to the given path"""
try:
# Ensure our destination directory exists
os.makedirs(os.path.dirname(destination))
os.makedirs(os.path.dirname(destination), exist_ok=True)
with urllib.request.urlopen(url) as stream:
with open(destination, "b+w") as outfile:
outfile.write(stream.read())
except (urllib.error.HTTPError, urllib.error.URLError):
logger.info("Failed to download file %s", url)
except OSError:
logger.info("Couldn't open font file %s for writing", destination)
except: # pylint: disable=bare-except
logger.info("Unknown error in file download")
except (urllib.error.HTTPError, urllib.error.URLError) as err:
logger.error("Failed to download file %s: %s", url, err)
except OSError as err:
logger.error("Couldn't open font file %s for writing: %s", destination, err)
except Exception as err: # pylint:disable=broad-except
logger.error("Unknown error in file download: %s", err)
class BookwyrmConfig(AppConfig):
@ -32,7 +33,6 @@ class BookwyrmConfig(AppConfig):
name = "bookwyrm"
verbose_name = "BookWyrm"
# pylint: disable=no-self-use
def ready(self):
"""set up OTLP and preview image files, if desired"""
if settings.OTEL_EXPORTER_OTLP_ENDPOINT or settings.OTEL_EXPORTER_CONSOLE:

View file

@ -1,35 +1,68 @@
""" using a bookwyrm instance as a source of book data """
from __future__ import annotations
from dataclasses import asdict, dataclass
from functools import reduce
import operator
from typing import Optional, Union, Any, Literal, overload
from django.contrib.postgres.search import SearchRank, SearchQuery
from django.db.models import F, Q
from django.db.models.query import QuerySet
from bookwyrm import models
from bookwyrm import connectors
from bookwyrm.settings import MEDIA_FULL_URL
# pylint: disable=arguments-differ
def search(query, min_confidence=0, filters=None, return_first=False):
@overload
def search(
query: str,
*,
min_confidence: float = 0,
filters: Optional[list[Any]] = None,
return_first: Literal[False],
) -> QuerySet[models.Edition]:
...
@overload
def search(
query: str,
*,
min_confidence: float = 0,
filters: Optional[list[Any]] = None,
return_first: Literal[True],
) -> Optional[models.Edition]:
...
def search(
query: str,
*,
min_confidence: float = 0,
filters: Optional[list[Any]] = None,
return_first: bool = False,
books: Optional[QuerySet[models.Edition]] = None,
) -> Union[Optional[models.Edition], QuerySet[models.Edition]]:
"""search your local database"""
filters = filters or []
if not query:
return []
return None if return_first else []
query = query.strip()
results = None
# first, try searching unique identifiers
# unique identifiers never have spaces, title/author usually do
if not " " in query:
results = search_identifiers(query, *filters, return_first=return_first)
results = search_identifiers(
query, *filters, return_first=return_first, books=books
)
# if there were no identifier results...
if not results:
# then try searching title/author
results = search_title_author(
query, min_confidence, *filters, return_first=return_first
query, min_confidence, *filters, return_first=return_first, books=books
)
return results
@ -66,8 +99,18 @@ def format_search_result(search_result):
).json()
def search_identifiers(query, *filters, return_first=False):
"""tries remote_id, isbn; defined as dedupe fields on the model"""
def search_identifiers(
query,
*filters,
return_first=False,
books=None,
) -> Union[Optional[models.Edition], QuerySet[models.Edition]]:
"""search Editions by deduplication fields
Best for cases when we can assume someone is searching for an exact match on
commonly unique data identifiers like isbn or specific library ids.
"""
books = books or models.Edition.objects
if connectors.maybe_isbn(query):
# Oh did you think the 'S' in ISBN stood for 'standard'?
normalized_isbn = query.strip().upper().rjust(10, "0")
@ -78,7 +121,7 @@ def search_identifiers(query, *filters, return_first=False):
for f in models.Edition._meta.get_fields()
if hasattr(f, "deduplication_field") and f.deduplication_field
]
results = models.Edition.objects.filter(
results = books.filter(
*filters, reduce(operator.or_, (Q(**f) for f in or_filters))
).distinct()
@ -87,11 +130,18 @@ def search_identifiers(query, *filters, return_first=False):
return results
def search_title_author(query, min_confidence, *filters, return_first=False):
def search_title_author(
query,
min_confidence,
*filters,
return_first=False,
books=None,
) -> QuerySet[models.Edition]:
"""searches for title and author"""
books = books or models.Edition.objects
query = SearchQuery(query, config="simple") | SearchQuery(query, config="english")
results = (
models.Edition.objects.filter(*filters, search_vector=query)
books.filter(*filters, search_vector=query)
.annotate(rank=SearchRank(F("search_vector"), query))
.filter(rank__gt=min_confidence)
.order_by("-rank")
@ -102,7 +152,7 @@ def search_title_author(query, min_confidence, *filters, return_first=False):
# filter out multiple editions of the same work
list_results = []
for work_id in set(editions_of_work[:30]):
for work_id in editions_of_work[:30]:
result = (
results.filter(parent_work=work_id)
.order_by("-rank", "-edition_rank")
@ -122,11 +172,11 @@ class SearchResult:
title: str
key: str
connector: object
view_link: str = None
author: str = None
year: str = None
cover: str = None
confidence: int = 1
view_link: Optional[str] = None
author: Optional[str] = None
year: Optional[str] = None
cover: Optional[str] = None
confidence: float = 1.0
def __repr__(self):
# pylint: disable=consider-using-f-string

View file

@ -1,7 +1,11 @@
""" functionality outline for a book data connector """
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Optional, TypedDict, Any, Callable, Union, Iterator
from urllib.parse import quote_plus
import imghdr
# pylint: disable-next=deprecated-module
import imghdr # Deprecated in 3.11 for removal in 3.13; no good alternative yet
import logging
import re
import asyncio
@ -16,33 +20,38 @@ from bookwyrm import activitypub, models, settings
from bookwyrm.settings import USER_AGENT
from .connector_manager import load_more_data, ConnectorException, raise_not_valid_url
from .format_mappings import format_mappings
from ..book_search import SearchResult
logger = logging.getLogger(__name__)
JsonDict = dict[str, Any]
class ConnectorResults(TypedDict):
"""TypedDict for results returned by connector"""
connector: AbstractMinimalConnector
results: list[SearchResult]
class AbstractMinimalConnector(ABC):
"""just the bare bones, for other bookwyrm instances"""
def __init__(self, identifier):
def __init__(self, identifier: str):
# load connector settings
info = models.Connector.objects.get(identifier=identifier)
self.connector = info
# the things in the connector model to copy over
self_fields = [
"base_url",
"books_url",
"covers_url",
"search_url",
"isbn_search_url",
"name",
"identifier",
]
for field in self_fields:
setattr(self, field, getattr(info, field))
self.base_url = info.base_url
self.books_url = info.books_url
self.covers_url = info.covers_url
self.search_url = info.search_url
self.isbn_search_url = info.isbn_search_url
self.name = info.name
self.identifier = info.identifier
def get_search_url(self, query):
def get_search_url(self, query: str) -> str:
"""format the query url"""
# Check if the query resembles an ISBN
if maybe_isbn(query) and self.isbn_search_url and self.isbn_search_url != "":
@ -54,13 +63,21 @@ class AbstractMinimalConnector(ABC):
# searched as free text. This, instead, only searches isbn if it's isbn-y
return f"{self.search_url}{quote_plus(query)}"
def process_search_response(self, query, data, min_confidence):
def process_search_response(
self, query: str, data: Any, min_confidence: float
) -> list[SearchResult]:
"""Format the search results based on the format of the query"""
if maybe_isbn(query):
return list(self.parse_isbn_search_data(data))[:10]
return list(self.parse_search_data(data, min_confidence))[:10]
async def get_results(self, session, url, min_confidence, query):
async def get_results(
self,
session: aiohttp.ClientSession,
url: str,
min_confidence: float,
query: str,
) -> Optional[ConnectorResults]:
"""try this specific connector"""
# pylint: disable=line-too-long
headers = {
@ -69,60 +86,68 @@ class AbstractMinimalConnector(ABC):
),
"User-Agent": USER_AGENT,
}
params = {"min_confidence": min_confidence}
params = {"min_confidence": str(min_confidence)}
try:
async with session.get(url, headers=headers, params=params) as response:
if not response.ok:
logger.info("Unable to connect to %s: %s", url, response.reason)
return
return None
try:
raw_data = await response.json()
except aiohttp.client_exceptions.ContentTypeError as err:
logger.exception(err)
return
return None
return {
"connector": self,
"results": self.process_search_response(
return ConnectorResults(
connector=self,
results=self.process_search_response(
query, raw_data, min_confidence
),
}
)
except asyncio.TimeoutError:
logger.info("Connection timed out for url: %s", url)
except aiohttp.ClientError as err:
logger.info(err)
return None
@abstractmethod
def get_or_create_book(self, remote_id):
def get_or_create_book(self, remote_id: str) -> Optional[models.Book]:
"""pull up a book record by whatever means possible"""
@abstractmethod
def parse_search_data(self, data, min_confidence):
def parse_search_data(
self, data: Any, min_confidence: float
) -> Iterator[SearchResult]:
"""turn the result json from a search into a list"""
@abstractmethod
def parse_isbn_search_data(self, data):
def parse_isbn_search_data(self, data: Any) -> Iterator[SearchResult]:
"""turn the result json from a search into a list"""
class AbstractConnector(AbstractMinimalConnector):
"""generic book data connector"""
def __init__(self, identifier):
generated_remote_link_field = ""
def __init__(self, identifier: str):
super().__init__(identifier)
# fields we want to look for in book data to copy over
# title we handle separately.
self.book_mappings = []
self.book_mappings: list[Mapping] = []
self.author_mappings: list[Mapping] = []
def get_or_create_book(self, remote_id):
def get_or_create_book(self, remote_id: str) -> Optional[models.Book]:
"""translate arbitrary json into an Activitypub dataclass"""
# first, check if we have the origin_id saved
existing = models.Edition.find_existing_by_remote_id(
remote_id
) or models.Work.find_existing_by_remote_id(remote_id)
if existing:
if hasattr(existing, "default_edition"):
if hasattr(existing, "default_edition") and isinstance(
existing.default_edition, models.Edition
):
return existing.default_edition
return existing
@ -154,6 +179,9 @@ class AbstractConnector(AbstractMinimalConnector):
)
# this will dedupe automatically
work = work_activity.to_model(model=models.Work, overwrite=False)
if not work:
return None
for author in self.get_authors_from_data(work_data):
work.authors.add(author)
@ -161,12 +189,21 @@ class AbstractConnector(AbstractMinimalConnector):
load_more_data.delay(self.connector.id, work.id)
return edition
def get_book_data(self, remote_id): # pylint: disable=no-self-use
def get_book_data(self, remote_id: str) -> JsonDict: # pylint: disable=no-self-use
"""this allows connectors to override the default behavior"""
return get_data(remote_id)
def create_edition_from_data(self, work, edition_data, instance=None):
def create_edition_from_data(
self,
work: models.Work,
edition_data: Union[str, JsonDict],
instance: Optional[models.Edition] = None,
) -> Optional[models.Edition]:
"""if we already have the work, we're ready"""
if isinstance(edition_data, str):
# We don't expect a string here
return None
mapped_data = dict_from_mappings(edition_data, self.book_mappings)
mapped_data["work"] = work.remote_id
edition_activity = activitypub.Edition(**mapped_data)
@ -174,6 +211,9 @@ class AbstractConnector(AbstractMinimalConnector):
model=models.Edition, overwrite=False, instance=instance
)
if not edition:
return None
# if we're updating an existing instance, we don't need to load authors
if instance:
return edition
@ -190,7 +230,9 @@ class AbstractConnector(AbstractMinimalConnector):
return edition
def get_or_create_author(self, remote_id, instance=None):
def get_or_create_author(
self, remote_id: str, instance: Optional[models.Author] = None
) -> Optional[models.Author]:
"""load that author"""
if not instance:
existing = models.Author.find_existing_by_remote_id(remote_id)
@ -210,46 +252,51 @@ class AbstractConnector(AbstractMinimalConnector):
model=models.Author, overwrite=False, instance=instance
)
def get_remote_id_from_model(self, obj):
def get_remote_id_from_model(self, obj: models.BookDataModel) -> Optional[str]:
"""given the data stored, how can we look this up"""
return getattr(obj, getattr(self, "generated_remote_link_field"))
remote_id: Optional[str] = getattr(obj, self.generated_remote_link_field)
return remote_id
def update_author_from_remote(self, obj):
def update_author_from_remote(self, obj: models.Author) -> Optional[models.Author]:
"""load the remote data from this connector and add it to an existing author"""
remote_id = self.get_remote_id_from_model(obj)
if not remote_id:
return None
return self.get_or_create_author(remote_id, instance=obj)
def update_book_from_remote(self, obj):
def update_book_from_remote(self, obj: models.Edition) -> Optional[models.Edition]:
"""load the remote data from this connector and add it to an existing book"""
remote_id = self.get_remote_id_from_model(obj)
if not remote_id:
return None
data = self.get_book_data(remote_id)
return self.create_edition_from_data(obj.parent_work, data, instance=obj)
@abstractmethod
def is_work_data(self, data):
def is_work_data(self, data: JsonDict) -> bool:
"""differentiate works and editions"""
@abstractmethod
def get_edition_from_work_data(self, data):
def get_edition_from_work_data(self, data: JsonDict) -> JsonDict:
"""every work needs at least one edition"""
@abstractmethod
def get_work_from_edition_data(self, data):
def get_work_from_edition_data(self, data: JsonDict) -> JsonDict:
"""every edition needs a work"""
@abstractmethod
def get_authors_from_data(self, data):
def get_authors_from_data(self, data: JsonDict) -> Iterator[models.Author]:
"""load author data"""
@abstractmethod
def expand_book_data(self, book):
def expand_book_data(self, book: models.Book) -> None:
"""get more info on a book"""
def dict_from_mappings(data, mappings):
def dict_from_mappings(data: JsonDict, mappings: list[Mapping]) -> JsonDict:
"""create a dict in Activitypub format, using mappings supplies by
the subclass"""
result = {}
result: JsonDict = {}
for mapping in mappings:
# sometimes there are multiple mappings for one field, don't
# overwrite earlier writes in that case
@ -259,7 +306,11 @@ def dict_from_mappings(data, mappings):
return result
def get_data(url, params=None, timeout=settings.QUERY_TIMEOUT):
def get_data(
url: str,
params: Optional[dict[str, str]] = None,
timeout: int = settings.QUERY_TIMEOUT,
) -> JsonDict:
"""wrapper for request.get"""
# check if the url is blocked
raise_not_valid_url(url)
@ -292,10 +343,15 @@ def get_data(url, params=None, timeout=settings.QUERY_TIMEOUT):
logger.info(err)
raise ConnectorException(err)
if not isinstance(data, dict):
raise ConnectorException("Unexpected data format")
return data
def get_image(url, timeout=10):
def get_image(
url: str, timeout: int = 10
) -> Union[tuple[ContentFile[bytes], str], tuple[None, None]]:
"""wrapper for requesting an image"""
raise_not_valid_url(url)
try:
@ -325,14 +381,19 @@ def get_image(url, timeout=10):
class Mapping:
"""associate a local database field with a field in an external dataset"""
def __init__(self, local_field, remote_field=None, formatter=None):
def __init__(
self,
local_field: str,
remote_field: Optional[str] = None,
formatter: Optional[Callable[[Any], Any]] = None,
):
noop = lambda x: x
self.local_field = local_field
self.remote_field = remote_field or local_field
self.formatter = formatter or noop
def get_value(self, data):
def get_value(self, data: JsonDict) -> Optional[Any]:
"""pull a field from incoming json and return the formatted version"""
value = data.get(self.remote_field)
if not value:
@ -343,7 +404,7 @@ class Mapping:
return None
def infer_physical_format(format_text):
def infer_physical_format(format_text: str) -> Optional[str]:
"""try to figure out what the standardized format is from the free value"""
format_text = format_text.lower()
if format_text in format_mappings:
@ -356,7 +417,7 @@ def infer_physical_format(format_text):
return matches[0]
def unique_physical_format(format_text):
def unique_physical_format(format_text: str) -> Optional[str]:
"""only store the format if it isn't directly in the format mappings"""
format_text = format_text.lower()
if format_text in format_mappings:
@ -365,7 +426,7 @@ def unique_physical_format(format_text):
return format_text
def maybe_isbn(query):
def maybe_isbn(query: str) -> bool:
"""check if a query looks like an isbn"""
isbn = re.sub(r"[\W_]", "", query) # removes filler characters
# ISBNs must be numeric except an ISBN10 checkdigit can be 'X'

View file

@ -1,4 +1,7 @@
""" using another bookwyrm instance as a source of book data """
from __future__ import annotations
from typing import Any, Iterator
from bookwyrm import activitypub, models
from bookwyrm.book_search import SearchResult
from .abstract_connector import AbstractMinimalConnector
@ -7,15 +10,19 @@ from .abstract_connector import AbstractMinimalConnector
class Connector(AbstractMinimalConnector):
"""this is basically just for search"""
def get_or_create_book(self, remote_id):
def get_or_create_book(self, remote_id: str) -> models.Edition:
return activitypub.resolve_remote_id(remote_id, model=models.Edition)
def parse_search_data(self, data, min_confidence):
def parse_search_data(
self, data: list[dict[str, Any]], min_confidence: float
) -> Iterator[SearchResult]:
for search_result in data:
search_result["connector"] = self
yield SearchResult(**search_result)
def parse_isbn_search_data(self, data):
def parse_isbn_search_data(
self, data: list[dict[str, Any]]
) -> Iterator[SearchResult]:
for search_result in data:
search_result["connector"] = self
yield SearchResult(**search_result)

View file

@ -1,8 +1,11 @@
""" interface with whatever connectors the app has """
from __future__ import annotations
import asyncio
import importlib
import ipaddress
import logging
from asyncio import Future
from typing import Iterator, Any, Optional, Union, overload, Literal
from urllib.parse import urlparse
import aiohttp
@ -12,8 +15,10 @@ from django.db.models import signals
from requests import HTTPError
from bookwyrm import book_search, models
from bookwyrm.book_search import SearchResult
from bookwyrm.connectors import abstract_connector
from bookwyrm.settings import SEARCH_TIMEOUT
from bookwyrm.tasks import app, LOW
from bookwyrm.tasks import app, CONNECTORS
logger = logging.getLogger(__name__)
@ -22,11 +27,15 @@ class ConnectorException(HTTPError):
"""when the connector can't do what was asked"""
async def async_connector_search(query, items, min_confidence):
async def async_connector_search(
query: str,
items: list[tuple[str, abstract_connector.AbstractConnector]],
min_confidence: float,
) -> list[Optional[abstract_connector.ConnectorResults]]:
"""Try a number of requests simultaneously"""
timeout = aiohttp.ClientTimeout(total=SEARCH_TIMEOUT)
async with aiohttp.ClientSession(timeout=timeout) as session:
tasks = []
tasks: list[Future[Optional[abstract_connector.ConnectorResults]]] = []
for url, connector in items:
tasks.append(
asyncio.ensure_future(
@ -35,14 +44,29 @@ async def async_connector_search(query, items, min_confidence):
)
results = await asyncio.gather(*tasks)
return results
return list(results)
def search(query, min_confidence=0.1, return_first=False):
@overload
def search(
query: str, *, min_confidence: float = 0.1, return_first: Literal[False]
) -> list[abstract_connector.ConnectorResults]:
...
@overload
def search(
query: str, *, min_confidence: float = 0.1, return_first: Literal[True]
) -> Optional[SearchResult]:
...
def search(
query: str, *, min_confidence: float = 0.1, return_first: bool = False
) -> Union[list[abstract_connector.ConnectorResults], Optional[SearchResult]]:
"""find books based on arbitrary keywords"""
if not query:
return []
results = []
return None if return_first else []
items = []
for connector in get_connectors():
@ -57,8 +81,12 @@ def search(query, min_confidence=0.1, return_first=False):
items.append((url, connector))
# load as many results as we can
results = asyncio.run(async_connector_search(query, items, min_confidence))
results = [r for r in results if r]
# failed requests will return None, so filter those out
results = [
r
for r in asyncio.run(async_connector_search(query, items, min_confidence))
if r
]
if return_first:
# find the best result from all the responses and return that
@ -66,11 +94,12 @@ def search(query, min_confidence=0.1, return_first=False):
all_results = sorted(all_results, key=lambda r: r.confidence, reverse=True)
return all_results[0] if all_results else None
# failed requests will return None, so filter those out
return results
def first_search_result(query, min_confidence=0.1):
def first_search_result(
query: str, min_confidence: float = 0.1
) -> Union[models.Edition, SearchResult, None]:
"""search until you find a result that fits"""
# try local search first
result = book_search.search(query, min_confidence=min_confidence, return_first=True)
@ -80,18 +109,20 @@ def first_search_result(query, min_confidence=0.1):
return search(query, min_confidence=min_confidence, return_first=True) or None
def get_connectors():
def get_connectors() -> Iterator[abstract_connector.AbstractConnector]:
"""load all connectors"""
for info in models.Connector.objects.filter(active=True).order_by("priority").all():
yield load_connector(info)
def get_or_create_connector(remote_id):
def get_or_create_connector(remote_id: str) -> abstract_connector.AbstractConnector:
"""get the connector related to the object's server"""
url = urlparse(remote_id)
identifier = url.netloc
identifier = url.hostname
if not identifier:
raise ValueError("Invalid remote id")
raise ValueError(f"Invalid remote id: {remote_id}")
base_url = f"{url.scheme}://{url.netloc}"
try:
connector_info = models.Connector.objects.get(identifier=identifier)
@ -99,58 +130,75 @@ def get_or_create_connector(remote_id):
connector_info = models.Connector.objects.create(
identifier=identifier,
connector_file="bookwyrm_connector",
base_url=f"https://{identifier}",
books_url=f"https://{identifier}/book",
covers_url=f"https://{identifier}/images/covers",
search_url=f"https://{identifier}/search?q=",
base_url=base_url,
books_url=f"{base_url}/book",
covers_url=f"{base_url}/images/covers",
search_url=f"{base_url}/search?q=",
priority=2,
)
return load_connector(connector_info)
@app.task(queue=LOW)
def load_more_data(connector_id, book_id):
@app.task(queue=CONNECTORS)
def load_more_data(connector_id: str, book_id: str) -> None:
"""background the work of getting all 10,000 editions of LoTR"""
connector_info = models.Connector.objects.get(id=connector_id)
connector = load_connector(connector_info)
book = models.Book.objects.select_subclasses().get(id=book_id)
book = models.Book.objects.select_subclasses().get( # type: ignore[no-untyped-call]
id=book_id
)
connector.expand_book_data(book)
@app.task(queue=LOW)
def create_edition_task(connector_id, work_id, data):
@app.task(queue=CONNECTORS)
def create_edition_task(
connector_id: int, work_id: int, data: Union[str, abstract_connector.JsonDict]
) -> None:
"""separate task for each of the 10,000 editions of LoTR"""
connector_info = models.Connector.objects.get(id=connector_id)
connector = load_connector(connector_info)
work = models.Work.objects.select_subclasses().get(id=work_id)
work = models.Work.objects.select_subclasses().get( # type: ignore[no-untyped-call]
id=work_id
)
connector.create_edition_from_data(work, data)
def load_connector(connector_info):
def load_connector(
connector_info: models.Connector,
) -> abstract_connector.AbstractConnector:
"""instantiate the connector class"""
connector = importlib.import_module(
f"bookwyrm.connectors.{connector_info.connector_file}"
)
return connector.Connector(connector_info.identifier)
return connector.Connector(connector_info.identifier) # type: ignore[no-any-return]
@receiver(signals.post_save, sender="bookwyrm.FederatedServer")
# pylint: disable=unused-argument
def create_connector(sender, instance, created, *args, **kwargs):
def create_connector(
sender: Any,
instance: models.FederatedServer,
created: Any,
*args: Any,
**kwargs: Any,
) -> None:
"""create a connector to an external bookwyrm server"""
if instance.application_type == "bookwyrm":
get_or_create_connector(f"https://{instance.server_name}")
def raise_not_valid_url(url):
def raise_not_valid_url(url: str) -> None:
"""do some basic reality checks on the url"""
parsed = urlparse(url)
if not parsed.scheme in ["http", "https"]:
raise ConnectorException("Invalid scheme: ", url)
if not parsed.hostname:
raise ConnectorException("Hostname missing: ", url)
try:
ipaddress.ip_address(parsed.netloc)
ipaddress.ip_address(parsed.hostname)
raise ConnectorException("Provided url is an IP address: ", url)
except ValueError:
# it's not an IP address, which is good

View file

@ -1,9 +1,10 @@
""" inventaire data connector """
import re
from typing import Any, Union, Optional, Iterator, Iterable
from bookwyrm import models
from bookwyrm.book_search import SearchResult
from .abstract_connector import AbstractConnector, Mapping
from .abstract_connector import AbstractConnector, Mapping, JsonDict
from .abstract_connector import get_data
from .connector_manager import ConnectorException, create_edition_task
@ -13,7 +14,7 @@ class Connector(AbstractConnector):
generated_remote_link_field = "inventaire_id"
def __init__(self, identifier):
def __init__(self, identifier: str):
super().__init__(identifier)
get_first = lambda a: a[0]
@ -60,13 +61,13 @@ class Connector(AbstractConnector):
Mapping("died", remote_field="wdt:P570", formatter=get_first),
] + shared_mappings
def get_remote_id(self, value):
def get_remote_id(self, value: str) -> str:
"""convert an id/uri into a url"""
return f"{self.books_url}?action=by-uris&uris={value}"
def get_book_data(self, remote_id):
def get_book_data(self, remote_id: str) -> JsonDict:
data = get_data(remote_id)
extracted = list(data.get("entities").values())
extracted = list(data.get("entities", {}).values())
try:
data = extracted[0]
except (KeyError, IndexError):
@ -74,10 +75,16 @@ class Connector(AbstractConnector):
# flatten the data so that images, uri, and claims are on the same level
return {
**data.get("claims", {}),
**{k: data.get(k) for k in ["uri", "image", "labels", "sitelinks", "type"]},
**{
k: data.get(k)
for k in ["uri", "image", "labels", "sitelinks", "type"]
if k in data
},
}
def parse_search_data(self, data, min_confidence):
def parse_search_data(
self, data: JsonDict, min_confidence: float
) -> Iterator[SearchResult]:
for search_result in data.get("results", []):
images = search_result.get("image")
cover = f"{self.covers_url}/img/entities/{images[0]}" if images else None
@ -96,7 +103,7 @@ class Connector(AbstractConnector):
connector=self,
)
def parse_isbn_search_data(self, data):
def parse_isbn_search_data(self, data: JsonDict) -> Iterator[SearchResult]:
"""got some data"""
results = data.get("entities")
if not results:
@ -114,35 +121,44 @@ class Connector(AbstractConnector):
connector=self,
)
def is_work_data(self, data):
def is_work_data(self, data: JsonDict) -> bool:
return data.get("type") == "work"
def load_edition_data(self, work_uri):
def load_edition_data(self, work_uri: str) -> JsonDict:
"""get a list of editions for a work"""
# pylint: disable=line-too-long
url = f"{self.books_url}?action=reverse-claims&property=wdt:P629&value={work_uri}&sort=true"
return get_data(url)
def get_edition_from_work_data(self, data):
data = self.load_edition_data(data.get("uri"))
def get_edition_from_work_data(self, data: JsonDict) -> JsonDict:
work_uri = data.get("uri")
if not work_uri:
raise ConnectorException("Invalid URI")
data = self.load_edition_data(work_uri)
try:
uri = data.get("uris", [])[0]
except IndexError:
raise ConnectorException("Invalid book data")
return self.get_book_data(self.get_remote_id(uri))
def get_work_from_edition_data(self, data):
uri = data.get("wdt:P629", [None])[0]
def get_work_from_edition_data(self, data: JsonDict) -> JsonDict:
try:
uri = data.get("wdt:P629", [])[0]
except IndexError:
raise ConnectorException("Invalid book data")
if not uri:
raise ConnectorException("Invalid book data")
return self.get_book_data(self.get_remote_id(uri))
def get_authors_from_data(self, data):
def get_authors_from_data(self, data: JsonDict) -> Iterator[models.Author]:
authors = data.get("wdt:P50", [])
for author in authors:
yield self.get_or_create_author(self.get_remote_id(author))
model = self.get_or_create_author(self.get_remote_id(author))
if model:
yield model
def expand_book_data(self, book):
def expand_book_data(self, book: models.Book) -> None:
work = book
# go from the edition to the work, if necessary
if isinstance(book, models.Edition):
@ -154,11 +170,16 @@ class Connector(AbstractConnector):
# who knows, man
return
for edition_uri in edition_options.get("uris"):
for edition_uri in edition_options.get("uris", []):
remote_id = self.get_remote_id(edition_uri)
create_edition_task.delay(self.connector.id, work.id, remote_id)
def create_edition_from_data(self, work, edition_data, instance=None):
def create_edition_from_data(
self,
work: models.Work,
edition_data: Union[str, JsonDict],
instance: Optional[models.Edition] = None,
) -> Optional[models.Edition]:
"""pass in the url as data and then call the version in abstract connector"""
if isinstance(edition_data, str):
try:
@ -168,22 +189,26 @@ class Connector(AbstractConnector):
return None
return super().create_edition_from_data(work, edition_data, instance=instance)
def get_cover_url(self, cover_blob, *_):
def get_cover_url(
self, cover_blob: Union[list[JsonDict], JsonDict], *_: Any
) -> Optional[str]:
"""format the relative cover url into an absolute one:
{"url": "/img/entities/e794783f01b9d4f897a1ea9820b96e00d346994f"}
"""
# covers may or may not be a list
if isinstance(cover_blob, list) and len(cover_blob) > 0:
if isinstance(cover_blob, list):
if len(cover_blob) == 0:
return None
cover_blob = cover_blob[0]
cover_id = cover_blob.get("url")
if not cover_id:
if not isinstance(cover_id, str):
return None
# cover may or may not be an absolute url already
if re.match(r"^http", cover_id):
return cover_id
return f"{self.covers_url}{cover_id}"
def resolve_keys(self, keys):
def resolve_keys(self, keys: Iterable[str]) -> list[str]:
"""cool, it's "wd:Q3156592" now what the heck does that mean"""
results = []
for uri in keys:
@ -191,10 +216,10 @@ class Connector(AbstractConnector):
data = self.get_book_data(self.get_remote_id(uri))
except ConnectorException:
continue
results.append(get_language_code(data.get("labels")))
results.append(get_language_code(data.get("labels", {})))
return results
def get_description(self, links):
def get_description(self, links: JsonDict) -> str:
"""grab an extracted excerpt from wikipedia"""
link = links.get("enwiki")
if not link:
@ -204,15 +229,15 @@ class Connector(AbstractConnector):
data = get_data(url)
except ConnectorException:
return ""
return data.get("extract")
return str(data.get("extract", ""))
def get_remote_id_from_model(self, obj):
def get_remote_id_from_model(self, obj: models.BookDataModel) -> str:
"""use get_remote_id to figure out the link from a model obj"""
remote_id_value = obj.inventaire_id
return self.get_remote_id(remote_id_value)
def get_language_code(options, code="en"):
def get_language_code(options: JsonDict, code: str = "en") -> Any:
"""when there are a bunch of translation but we need a single field"""
result = options.get(code)
if result:

View file

@ -1,9 +1,13 @@
""" openlibrary data connector """
import re
from typing import Any, Optional, Union, Iterator, Iterable
from markdown import markdown
from bookwyrm import models
from bookwyrm.book_search import SearchResult
from .abstract_connector import AbstractConnector, Mapping
from bookwyrm.utils.sanitizer import clean
from .abstract_connector import AbstractConnector, Mapping, JsonDict
from .abstract_connector import get_data, infer_physical_format, unique_physical_format
from .connector_manager import ConnectorException, create_edition_task
from .openlibrary_languages import languages
@ -14,7 +18,7 @@ class Connector(AbstractConnector):
generated_remote_link_field = "openlibrary_link"
def __init__(self, identifier):
def __init__(self, identifier: str):
super().__init__(identifier)
get_first = lambda a, *args: a[0]
@ -94,14 +98,14 @@ class Connector(AbstractConnector):
Mapping("inventaire_id", remote_field="links", formatter=get_inventaire_id),
]
def get_book_data(self, remote_id):
def get_book_data(self, remote_id: str) -> JsonDict:
data = get_data(remote_id)
if data.get("type", {}).get("key") == "/type/redirect":
remote_id = self.base_url + data.get("location")
remote_id = self.base_url + data.get("location", "")
return get_data(remote_id)
return data
def get_remote_id_from_data(self, data):
def get_remote_id_from_data(self, data: JsonDict) -> str:
"""format a url from an openlibrary id field"""
try:
key = data["key"]
@ -109,10 +113,10 @@ class Connector(AbstractConnector):
raise ConnectorException("Invalid book data")
return f"{self.books_url}{key}"
def is_work_data(self, data):
def is_work_data(self, data: JsonDict) -> bool:
return bool(re.match(r"^[\/\w]+OL\d+W$", data["key"]))
def get_edition_from_work_data(self, data):
def get_edition_from_work_data(self, data: JsonDict) -> JsonDict:
try:
key = data["key"]
except KeyError:
@ -124,7 +128,7 @@ class Connector(AbstractConnector):
raise ConnectorException("No editions for work")
return edition
def get_work_from_edition_data(self, data):
def get_work_from_edition_data(self, data: JsonDict) -> JsonDict:
try:
key = data["works"][0]["key"]
except (IndexError, KeyError):
@ -132,7 +136,7 @@ class Connector(AbstractConnector):
url = f"{self.books_url}{key}"
return self.get_book_data(url)
def get_authors_from_data(self, data):
def get_authors_from_data(self, data: JsonDict) -> Iterator[models.Author]:
"""parse author json and load or create authors"""
for author_blob in data.get("authors", []):
author_blob = author_blob.get("author", author_blob)
@ -144,7 +148,7 @@ class Connector(AbstractConnector):
continue
yield author
def get_cover_url(self, cover_blob, size="L"):
def get_cover_url(self, cover_blob: list[str], size: str = "L") -> Optional[str]:
"""ask openlibrary for the cover"""
if not cover_blob:
return None
@ -152,8 +156,10 @@ class Connector(AbstractConnector):
image_name = f"{cover_id}-{size}.jpg"
return f"{self.covers_url}/b/id/{image_name}"
def parse_search_data(self, data, min_confidence):
for idx, search_result in enumerate(data.get("docs")):
def parse_search_data(
self, data: JsonDict, min_confidence: float
) -> Iterator[SearchResult]:
for idx, search_result in enumerate(data.get("docs", [])):
# build the remote id from the openlibrary key
key = self.books_url + search_result["key"]
author = search_result.get("author_name") or ["Unknown"]
@ -174,7 +180,7 @@ class Connector(AbstractConnector):
confidence=confidence,
)
def parse_isbn_search_data(self, data):
def parse_isbn_search_data(self, data: JsonDict) -> Iterator[SearchResult]:
for search_result in list(data.values()):
# build the remote id from the openlibrary key
key = self.books_url + search_result["key"]
@ -188,12 +194,12 @@ class Connector(AbstractConnector):
year=search_result.get("publish_date"),
)
def load_edition_data(self, olkey):
def load_edition_data(self, olkey: str) -> JsonDict:
"""query openlibrary for editions of a work"""
url = f"{self.books_url}/works/{olkey}/editions"
return self.get_book_data(url)
def expand_book_data(self, book):
def expand_book_data(self, book: models.Book) -> None:
work = book
# go from the edition to the work, if necessary
if isinstance(book, models.Edition):
@ -206,14 +212,14 @@ class Connector(AbstractConnector):
# who knows, man
return
for edition_data in edition_options.get("entries"):
for edition_data in edition_options.get("entries", []):
# does this edition have ANY interesting data?
if ignore_edition(edition_data):
continue
create_edition_task.delay(self.connector.id, work.id, edition_data)
def ignore_edition(edition_data):
def ignore_edition(edition_data: JsonDict) -> bool:
"""don't load a million editions that have no metadata"""
# an isbn, we love to see it
if edition_data.get("isbn_13") or edition_data.get("isbn_10"):
@ -232,19 +238,30 @@ def ignore_edition(edition_data):
return True
def get_description(description_blob):
def get_description(description_blob: Union[JsonDict, str]) -> str:
"""descriptions can be a string or a dict"""
if isinstance(description_blob, dict):
return description_blob.get("value")
return description_blob
description = markdown(description_blob.get("value", ""))
else:
description = markdown(description_blob)
if (
description.startswith("<p>")
and description.endswith("</p>")
and description.count("<p>") == 1
):
# If there is just one <p> tag and it is around the text remove it
return description[len("<p>") : -len("</p>")].strip()
return clean(description)
def get_openlibrary_key(key):
def get_openlibrary_key(key: str) -> str:
"""convert /books/OL27320736M into OL27320736M"""
return key.split("/")[-1]
def get_languages(language_blob):
def get_languages(language_blob: Iterable[JsonDict]) -> list[Optional[str]]:
"""/language/eng -> English"""
langs = []
for lang in language_blob:
@ -252,14 +269,14 @@ def get_languages(language_blob):
return langs
def get_dict_field(blob, field_name):
def get_dict_field(blob: Optional[JsonDict], field_name: str) -> Optional[Any]:
"""extract the isni from the remote id data for the author"""
if not blob or not isinstance(blob, dict):
return None
return blob.get(field_name)
def get_wikipedia_link(links):
def get_wikipedia_link(links: list[Any]) -> Optional[str]:
"""extract wikipedia links"""
if not isinstance(links, list):
return None
@ -272,7 +289,7 @@ def get_wikipedia_link(links):
return None
def get_inventaire_id(links):
def get_inventaire_id(links: list[Any]) -> Optional[str]:
"""extract and format inventaire ids"""
if not isinstance(links, list):
return None
@ -282,11 +299,13 @@ def get_inventaire_id(links):
continue
if link.get("title") == "inventaire.io":
iv_link = link.get("url")
if not isinstance(iv_link, str):
return None
return iv_link.split("/")[-1]
return None
def pick_default_edition(options):
def pick_default_edition(options: list[JsonDict]) -> Optional[JsonDict]:
"""favor physical copies with covers in english"""
if not options:
return None

View file

@ -2,7 +2,7 @@
from bookwyrm import models, settings
def site_settings(request): # pylint: disable=unused-argument
def site_settings(request):
"""include the custom info about the site"""
request_protocol = "https://"
if not request.is_secure():

View file

@ -3,8 +3,8 @@ from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template
from bookwyrm import models, settings
from bookwyrm.tasks import app, HIGH
from bookwyrm.settings import DOMAIN
from bookwyrm.tasks import app, EMAIL
from bookwyrm.settings import DOMAIN, BASE_URL
def email_data():
@ -14,6 +14,7 @@ def email_data():
"site_name": site.name,
"logo": site.logo_small_url,
"domain": DOMAIN,
"base_url": BASE_URL,
"user": None,
}
@ -75,7 +76,7 @@ def format_email(email_name, data):
return (subject, html_content, text_content)
@app.task(queue=HIGH)
@app.task(queue=EMAIL)
def send_email(recipient, subject, html_content, text_content):
"""use a task to send the email"""
email = EmailMultiAlternatives(

View file

@ -15,6 +15,7 @@ class AuthorForm(CustomForm):
"aliases",
"bio",
"wikipedia_link",
"wikidata",
"website",
"born",
"died",
@ -32,6 +33,7 @@ class AuthorForm(CustomForm):
"wikipedia_link": forms.TextInput(
attrs={"aria-describedby": "desc_wikipedia_link"}
),
"wikidata": forms.TextInput(attrs={"aria-describedby": "desc_wikidata"}),
"website": forms.TextInput(attrs={"aria-describedby": "desc_website"}),
"born": forms.SelectDateWidget(attrs={"aria-describedby": "desc_born"}),
"died": forms.SelectDateWidget(attrs={"aria-describedby": "desc_died"}),

View file

@ -1,8 +1,9 @@
""" using django model forms """
from django import forms
from file_resubmit.widgets import ResubmitImageWidget
from bookwyrm import models
from bookwyrm.models.fields import ClearableFileInputWithWarning
from .custom_form import CustomForm
from .widgets import ArrayWidget, SelectDateWidget, Select
@ -20,6 +21,7 @@ class EditionForm(CustomForm):
model = models.Edition
fields = [
"title",
"sort_title",
"subtitle",
"description",
"series",
@ -45,6 +47,9 @@ class EditionForm(CustomForm):
]
widgets = {
"title": forms.TextInput(attrs={"aria-describedby": "desc_title"}),
"sort_title": forms.TextInput(
attrs={"aria-describedby": "desc_sort_title"}
),
"subtitle": forms.TextInput(attrs={"aria-describedby": "desc_subtitle"}),
"description": forms.Textarea(
attrs={"aria-describedby": "desc_description"}
@ -66,9 +71,7 @@ class EditionForm(CustomForm):
"published_date": SelectDateWidget(
attrs={"aria-describedby": "desc_published_date"}
),
"cover": ClearableFileInputWithWarning(
attrs={"aria-describedby": "desc_cover"}
),
"cover": ResubmitImageWidget(attrs={"aria-describedby": "desc_cover"}),
"physical_format": Select(
attrs={"aria-describedby": "desc_physical_format"}
),
@ -107,6 +110,7 @@ class EditionFromWorkForm(CustomForm):
model = models.Work
fields = [
"title",
"sort_title",
"subtitle",
"authors",
"description",

View file

@ -15,9 +15,9 @@ class StyledForm(ModelForm):
css_classes["number"] = "input"
css_classes["checkbox"] = "checkbox"
css_classes["textarea"] = "textarea"
# pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
input_type = ""
if hasattr(visible.field.widget, "input_type"):
input_type = visible.field.widget.input_type
if isinstance(visible.field.widget, Textarea):

View file

@ -18,6 +18,7 @@ class EditUserForm(CustomForm):
"email",
"summary",
"show_goal",
"show_ratings",
"show_suggested_users",
"manually_approves_followers",
"default_post_privacy",
@ -70,6 +71,22 @@ class DeleteUserForm(CustomForm):
fields = ["password"]
class MoveUserForm(CustomForm):
target = forms.CharField(widget=forms.TextInput)
class Meta:
model = models.User
fields = ["password"]
class AliasUserForm(CustomForm):
username = forms.CharField(widget=forms.TextInput)
class Meta:
model = models.User
fields = ["password"]
class ChangePasswordForm(CustomForm):
current_password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)

View file

@ -25,6 +25,10 @@ class ImportForm(forms.Form):
csv_file = forms.FileField()
class ImportUserForm(forms.Form):
archive_file = forms.FileField()
class ShelfForm(CustomForm):
class Meta:
model = models.Shelf

View file

@ -34,7 +34,6 @@ class LoginForm(CustomForm):
def add_invalid_password_error(self):
"""We don't want to be too specific about this"""
# pylint: disable=attribute-defined-outside-init
self.non_field_errors = _("Username or password are incorrect")

View file

@ -1,4 +1,5 @@
""" using django model forms """
from urllib.parse import urlparse
from django.utils.translation import gettext_lazy as _
@ -25,7 +26,7 @@ class FileLinkForm(CustomForm):
url = cleaned_data.get("url")
filetype = cleaned_data.get("filetype")
book = cleaned_data.get("book")
domain = urlparse(url).netloc
domain = urlparse(url).hostname
if models.LinkDomain.objects.filter(domain=domain).exists():
status = models.LinkDomain.objects.get(domain=domain).status
if status == "blocked":
@ -37,10 +38,9 @@ class FileLinkForm(CustomForm):
),
)
if (
not self.instance
and models.FileLink.objects.filter(
url=url, book=book, filetype=filetype
).exists()
models.FileLink.objects.filter(url=url, book=book, filetype=filetype)
.exclude(pk=self.instance)
.exists()
):
# pylint: disable=line-too-long
self.add_error(

View file

@ -24,7 +24,7 @@ class SortListForm(forms.Form):
sort_by = ChoiceField(
choices=(
("order", _("List Order")),
("title", _("Book Title")),
("sort_title", _("Book Title")),
("rating", _("Rating")),
),
label=_("Sort By"),

View file

@ -5,8 +5,6 @@ from django import forms
class ArrayWidget(forms.widgets.TextInput):
"""Inputs for postgres array fields"""
# pylint: disable=unused-argument
# pylint: disable=no-self-use
def value_from_datadict(self, data, files, name):
"""get all values for this name"""
return [i for i in data.getlist(name) if i]

View file

@ -1,6 +1,7 @@
""" import classes """
from .importer import Importer
from .bookwyrm_import import BookwyrmImporter, BookwyrmBooksImporter
from .calibre_import import CalibreImporter
from .goodreads_import import GoodreadsImporter
from .librarything_import import LibrarythingImporter

View file

@ -0,0 +1,39 @@
"""Import data from Bookwyrm export files"""
from django.http import QueryDict
from bookwyrm.models import User
from bookwyrm.models.bookwyrm_import_job import BookwyrmImportJob
from . import Importer
class BookwyrmImporter:
"""Import a Bookwyrm User export file.
This is kind of a combination of an importer and a connector.
"""
# pylint: disable=no-self-use
def process_import(
self, user: User, archive_file: bytes, settings: QueryDict
) -> BookwyrmImportJob:
"""import user data from a Bookwyrm export file"""
required = [k for k in settings if settings.get(k) == "on"]
job = BookwyrmImportJob.objects.create(
user=user, archive_file=archive_file, required=required
)
return job
class BookwyrmBooksImporter(Importer):
"""
Handle reading a csv from BookWyrm.
Goodreads is the default importer, we basically just use the same structure
But BookWyrm has additional attributes in the csv
"""
service = "BookWyrm"
row_mappings_guesses = Importer.row_mappings_guesses + [
("shelf_name", ["shelf_name"]),
("review_published", ["review_published"]),
]

View file

@ -1,4 +1,6 @@
""" handle reading a csv from calibre """
from typing import Any, Optional
from bookwyrm.models import Shelf
from . import Importer
@ -9,20 +11,15 @@ class CalibreImporter(Importer):
service = "Calibre"
def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any):
# Add timestamp to row_mappings_guesses for date_added to avoid
# integrity error
row_mappings_guesses = []
for field, mapping in self.row_mappings_guesses:
if field in ("date_added",):
row_mappings_guesses.append((field, mapping + ["timestamp"]))
else:
row_mappings_guesses.append((field, mapping))
self.row_mappings_guesses = row_mappings_guesses
self.row_mappings_guesses = [
(field, mapping + (["timestamp"] if field == "date_added" else []))
for field, mapping in self.row_mappings_guesses
]
super().__init__(*args, **kwargs)
def get_shelf(self, normalized_row):
def get_shelf(self, normalized_row: dict[str, Optional[str]]) -> Optional[str]:
# Calibre export does not indicate which shelf to use. Use a default one for now
return Shelf.TO_READ

View file

@ -1,8 +1,10 @@
""" handle reading a csv from an external service, defaults are from Goodreads """
import csv
from datetime import timedelta
from typing import Iterable, Optional
from django.utils import timezone
from bookwyrm.models import ImportJob, ImportItem, SiteSettings
from bookwyrm.models import ImportJob, ImportItem, SiteSettings, User
class Importer:
@ -16,17 +18,26 @@ class Importer:
row_mappings_guesses = [
("id", ["id", "book id"]),
("title", ["title"]),
("authors", ["author", "authors", "primary author"]),
("isbn_10", ["isbn10", "isbn", "isbn/uid"]),
("isbn_13", ["isbn13", "isbn", "isbns", "isbn/uid"]),
("authors", ["author_text", "author", "authors", "primary author"]),
("isbn_10", ["isbn_10", "isbn10", "isbn", "isbn/uid"]),
("isbn_13", ["isbn_13", "isbn13", "isbn", "isbns", "isbn/uid"]),
("shelf", ["shelf", "exclusive shelf", "read status", "bookshelf"]),
("review_name", ["review name"]),
("review_body", ["my review", "review"]),
("review_name", ["review_name", "review name"]),
("review_body", ["review_content", "my review", "review"]),
("rating", ["my rating", "rating", "star rating"]),
("date_added", ["date added", "entry date", "added"]),
("date_started", ["date started", "started"]),
("date_finished", ["date finished", "last date read", "date read", "finished"]),
(
"date_added",
["shelf_date", "date_added", "date added", "entry date", "added"],
),
("date_started", ["start_date", "date started", "started"]),
(
"date_finished",
["finish_date", "date finished", "last date read", "date read", "finished"],
),
]
# TODO: stopped
date_fields = ["date_added", "date_started", "date_finished"]
shelf_mapping_guesses = {
"to-read": ["to-read", "want to read"],
@ -34,20 +45,33 @@ class Importer:
"reading": ["currently-reading", "reading", "currently reading"],
}
# pylint: disable=too-many-locals
def create_job(self, user, csv_file, include_reviews, privacy):
# pylint: disable=too-many-arguments
def create_job(
self,
user: User,
csv_file: Iterable[str],
include_reviews: bool,
privacy: str,
create_shelves: bool = True,
) -> ImportJob:
"""check over a csv and creates a database entry for the job"""
csv_reader = csv.DictReader(csv_file, delimiter=self.delimiter)
rows = list(csv_reader)
if len(rows) < 1:
raise ValueError("CSV file is empty")
rows = enumerate(rows)
mappings = (
self.create_row_mappings(list(fieldnames))
if (fieldnames := csv_reader.fieldnames)
else {}
)
job = ImportJob.objects.create(
user=user,
include_reviews=include_reviews,
create_shelves=create_shelves,
privacy=privacy,
mappings=self.create_row_mappings(csv_reader.fieldnames),
mappings=mappings,
source=self.service,
)
@ -55,16 +79,20 @@ class Importer:
if enforce_limit and allowed_imports <= 0:
job.complete_job()
return job
for index, entry in rows:
for index, entry in enumerate(rows):
if enforce_limit and index >= allowed_imports:
break
self.create_item(job, index, entry)
return job
def update_legacy_job(self, job):
def update_legacy_job(self, job: ImportJob) -> None:
"""patch up a job that was in the old format"""
items = job.items
headers = list(items.first().data.keys())
first_item = items.first()
if first_item is None:
return
headers = list(first_item.data.keys())
job.mappings = self.create_row_mappings(headers)
job.updated_date = timezone.now()
job.save()
@ -75,24 +103,24 @@ class Importer:
item.normalized_data = normalized
item.save()
def create_row_mappings(self, headers):
def create_row_mappings(self, headers: list[str]) -> dict[str, Optional[str]]:
"""guess what the headers mean"""
mappings = {}
for (key, guesses) in self.row_mappings_guesses:
value = [h for h in headers if h.lower() in guesses]
value = value[0] if len(value) else None
values = [h for h in headers if h.lower() in guesses]
value = values[0] if len(values) else None
if value:
headers.remove(value)
mappings[key] = value
return mappings
def create_item(self, job, index, data):
def create_item(self, job: ImportJob, index: int, data: dict[str, str]) -> None:
"""creates and saves an import item"""
normalized = self.normalize_row(data, job.mappings)
normalized["shelf"] = self.get_shelf(normalized)
ImportItem(job=job, index=index, data=data, normalized_data=normalized).save()
def get_shelf(self, normalized_row):
def get_shelf(self, normalized_row: dict[str, Optional[str]]) -> Optional[str]:
"""determine which shelf to use"""
shelf_name = normalized_row.get("shelf")
if not shelf_name:
@ -101,13 +129,17 @@ class Importer:
shelf = [
s for (s, gs) in self.shelf_mapping_guesses.items() if shelf_name in gs
]
return shelf[0] if shelf else None
return shelf[0] if shelf else normalized_row.get("shelf") or None
def normalize_row(self, entry, mappings): # pylint: disable=no-self-use
# pylint: disable=no-self-use
def normalize_row(
self, entry: dict[str, str], mappings: dict[str, Optional[str]]
) -> dict[str, Optional[str]]:
"""use the dataclass to create the formatted row of data"""
return {k: entry.get(v) for k, v in mappings.items()}
return {k: entry.get(v) if v else None for k, v in mappings.items()}
def get_import_limit(self, user): # pylint: disable=no-self-use
# pylint: disable=no-self-use
def get_import_limit(self, user: User) -> tuple[int, int]:
"""check if import limit is set and return how many imports are left"""
site_settings = SiteSettings.objects.get()
import_size_limit = site_settings.import_size_limit
@ -125,11 +157,14 @@ class Importer:
allowed_imports = import_size_limit - imported_books
return enforce_limit, allowed_imports
def create_retry_job(self, user, original_job, items):
def create_retry_job(
self, user: User, original_job: ImportJob, items: list[ImportItem]
) -> ImportJob:
"""retry items that didn't import"""
job = ImportJob.objects.create(
user=user,
include_reviews=original_job.include_reviews,
create_shelves=original_job.create_shelves,
privacy=original_job.privacy,
source=original_job.source,
# TODO: allow users to adjust mappings

View file

@ -1,11 +1,16 @@
""" handle reading a tsv from librarything """
import re
from typing import Optional
from bookwyrm.models import Shelf
from . import Importer
def _remove_brackets(value: Optional[str]) -> Optional[str]:
return re.sub(r"\[|\]", "", value) if value else None
class LibrarythingImporter(Importer):
"""csv downloads from librarything"""
@ -13,16 +18,19 @@ class LibrarythingImporter(Importer):
delimiter = "\t"
encoding = "ISO-8859-1"
def normalize_row(self, entry, mappings): # pylint: disable=no-self-use
def normalize_row(
self, entry: dict[str, str], mappings: dict[str, Optional[str]]
) -> dict[str, Optional[str]]:
"""use the dataclass to create the formatted row of data"""
remove_brackets = lambda v: re.sub(r"\[|\]", "", v) if v else None
normalized = {k: remove_brackets(entry.get(v)) for k, v in mappings.items()}
isbn_13 = normalized.get("isbn_13")
isbn_13 = isbn_13.split(", ") if isbn_13 else []
normalized = {
k: _remove_brackets(entry.get(v) if v else None)
for k, v in mappings.items()
}
isbn_13 = value.split(", ") if (value := normalized.get("isbn_13")) else []
normalized["isbn_13"] = isbn_13[1] if len(isbn_13) > 1 else None
return normalized
def get_shelf(self, normalized_row):
def get_shelf(self, normalized_row: dict[str, Optional[str]]) -> Optional[str]:
if normalized_row["date_finished"]:
return Shelf.READ_FINISHED
if normalized_row["date_started"]:

View file

@ -1,4 +1,6 @@
""" handle reading a csv from openlibrary"""
from typing import Any
from . import Importer
@ -7,7 +9,7 @@ class OpenLibraryImporter(Importer):
service = "OpenLibrary"
def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any):
self.row_mappings_guesses.append(("openlibrary_key", ["edition id"]))
self.row_mappings_guesses.append(("openlibrary_work_key", ["work id"]))
super().__init__(*args, **kwargs)

File diff suppressed because it is too large Load diff

View file

128
bookwyrm/isbn/isbn.py Normal file
View file

@ -0,0 +1,128 @@
""" Use the range message from isbn-international to hyphenate ISBNs """
import os
from typing import Optional
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
import requests
from bookwyrm import settings
def _get_rules(element: Element) -> list[Element]:
if (rules_el := element.find("Rules")) is not None:
return rules_el.findall("Rule")
return []
class IsbnHyphenator:
"""Class to manage the range message xml file and use it to hyphenate ISBNs"""
__range_message_url = "https://www.isbn-international.org/export_rangemessage.xml"
__range_file_path = os.path.join(
settings.BASE_DIR, "bookwyrm", "isbn", "RangeMessage.xml"
)
__element_tree = None
def update_range_message(self) -> None:
"""Download the range message xml file and save it locally"""
response = requests.get(self.__range_message_url, timeout=15)
with open(self.__range_file_path, "w", encoding="utf-8") as file:
file.write(response.text)
self.__element_tree = None
def hyphenate(self, isbn_13: Optional[str]) -> Optional[str]:
"""hyphenate the given ISBN-13 number using the range message"""
if isbn_13 is None:
return None
if self.__element_tree is None:
self.__element_tree = ElementTree.parse(self.__range_file_path)
gs1_prefix = isbn_13[:3]
try:
reg_group = self.__find_reg_group(isbn_13, gs1_prefix)
except ValueError:
# if the reg groups are invalid, just return the original isbn
return isbn_13
if reg_group is None:
return isbn_13 # failed to hyphenate
registrant = self.__find_registrant(isbn_13, gs1_prefix, reg_group)
if registrant is None:
return isbn_13 # failed to hyphenate
publication = isbn_13[len(gs1_prefix) + len(reg_group) + len(registrant) : -1]
check_digit = isbn_13[-1:]
return "-".join((gs1_prefix, reg_group, registrant, publication, check_digit))
def __find_reg_group(self, isbn_13: str, gs1_prefix: str) -> Optional[str]:
if self.__element_tree is None:
self.__element_tree = ElementTree.parse(self.__range_file_path)
ucc_prefixes_el = self.__element_tree.find("EAN.UCCPrefixes")
if ucc_prefixes_el is None:
return None
for ean_ucc_el in ucc_prefixes_el.findall("EAN.UCC"):
if (
prefix_el := ean_ucc_el.find("Prefix")
) is not None and prefix_el.text == gs1_prefix:
for rule_el in _get_rules(ean_ucc_el):
length_el = rule_el.find("Length")
if length_el is None:
continue
length = int(text) if (text := length_el.text) else 0
if length == 0:
continue
range_el = rule_el.find("Range")
if range_el is None or range_el.text is None:
continue
reg_grp_range = [int(x[:length]) for x in range_el.text.split("-")]
reg_group = isbn_13[len(gs1_prefix) : len(gs1_prefix) + length]
if reg_grp_range[0] <= int(reg_group) <= reg_grp_range[1]:
return reg_group
return None
return None
def __find_registrant(
self, isbn_13: str, gs1_prefix: str, reg_group: str
) -> Optional[str]:
from_ind = len(gs1_prefix) + len(reg_group)
if self.__element_tree is None:
self.__element_tree = ElementTree.parse(self.__range_file_path)
reg_groups_el = self.__element_tree.find("RegistrationGroups")
if reg_groups_el is None:
return None
for group_el in reg_groups_el.findall("Group"):
if (
prefix_el := group_el.find("Prefix")
) is not None and prefix_el.text == "-".join((gs1_prefix, reg_group)):
for rule_el in _get_rules(group_el):
length_el = rule_el.find("Length")
if length_el is None:
continue
length = int(text) if (text := length_el.text) else 0
if length == 0:
continue
range_el = rule_el.find("Range")
if range_el is None or range_el.text is None:
continue
registrant_range = [
int(x[:length]) for x in range_el.text.split("-")
]
registrant = isbn_13[from_ind : from_ind + length]
if registrant_range[0] <= int(registrant) <= registrant_range[1]:
return registrant
return None
return None
hyphenator_singleton = IsbnHyphenator()

View file

@ -5,7 +5,7 @@ from django.db.models import signals, Count, Q
from bookwyrm import models
from bookwyrm.redis_store import RedisStore
from bookwyrm.tasks import app, MEDIUM, HIGH
from bookwyrm.tasks import app, LISTS
class ListsStream(RedisStore):
@ -18,7 +18,7 @@ class ListsStream(RedisStore):
return f"{user}-lists"
return f"{user.id}-lists"
def get_rank(self, obj): # pylint: disable=no-self-use
def get_rank(self, obj):
"""lists are sorted by updated date"""
return obj.updated_date.timestamp()
@ -217,14 +217,14 @@ def add_list_on_account_create_command(user_id):
# ---- TASKS
@app.task(queue=MEDIUM)
@app.task(queue=LISTS)
def populate_lists_task(user_id):
"""background task for populating an empty list stream"""
user = models.User.objects.get(id=user_id)
ListsStream().populate_lists(user)
@app.task(queue=MEDIUM)
@app.task(queue=LISTS)
def remove_list_task(list_id, re_add=False):
"""remove a list from any stream it might be in"""
stores = models.User.objects.filter(local=True, is_active=True).values_list(
@ -239,14 +239,14 @@ def remove_list_task(list_id, re_add=False):
add_list_task.delay(list_id)
@app.task(queue=HIGH)
@app.task(queue=LISTS)
def add_list_task(list_id):
"""add a list to any stream it should be in"""
book_list = models.List.objects.get(id=list_id)
ListsStream().add_list(book_list)
@app.task(queue=MEDIUM)
@app.task(queue=LISTS)
def remove_user_lists_task(viewer_id, user_id, exclude_privacy=None):
"""remove all lists by a user from a viewer's stream"""
viewer = models.User.objects.get(id=viewer_id)
@ -254,7 +254,7 @@ def remove_user_lists_task(viewer_id, user_id, exclude_privacy=None):
ListsStream().remove_user_lists(viewer, user, exclude_privacy=exclude_privacy)
@app.task(queue=MEDIUM)
@app.task(queue=LISTS)
def add_user_lists_task(viewer_id, user_id):
"""add all lists by a user to a viewer's stream"""
viewer = models.User.objects.get(id=viewer_id)

View file

@ -1,13 +1,14 @@
""" PROCEED WITH CAUTION: uses deduplication fields to permanently
merge book data objects """
from django.core.management.base import BaseCommand
from django.db.models import Count
from bookwyrm import models
from bookwyrm.management.merge import merge_objects
def dedupe_model(model):
def dedupe_model(model, dry_run=False):
"""combine duplicate editions and update related models"""
print(f"deduplicating {model.__name__}:")
fields = model._meta.get_fields()
dedupe_fields = [
f for f in fields if hasattr(f, "deduplication_field") and f.deduplication_field
@ -16,30 +17,42 @@ def dedupe_model(model):
dupes = (
model.objects.values(field.name)
.annotate(Count(field.name))
.filter(**{"%s__count__gt" % field.name: 1})
.filter(**{f"{field.name}__count__gt": 1})
.exclude(**{field.name: ""})
.exclude(**{f"{field.name}__isnull": True})
)
for dupe in dupes:
value = dupe[field.name]
if not value or value == "":
continue
print("----------")
print(dupe)
objs = model.objects.filter(**{field.name: value}).order_by("id")
canonical = objs.first()
print("keeping", canonical.remote_id)
action = "would merge" if dry_run else "merging"
print(
f"{action} into {model.__name__} {canonical.remote_id} based on {field.name} {value}:"
)
for obj in objs[1:]:
print(obj.remote_id)
merge_objects(canonical, obj)
print(f"- {obj.remote_id}")
absorbed_fields = obj.merge_into(canonical, dry_run=dry_run)
print(f" absorbed fields: {absorbed_fields}")
class Command(BaseCommand):
"""deduplicate allllll the book data models"""
help = "merges duplicate book data"
def add_arguments(self, parser):
"""add the arguments for this command"""
parser.add_argument(
"--dry_run",
action="store_true",
help="don't actually merge, only print what would happen",
)
# pylint: disable=no-self-use,unused-argument
def handle(self, *args, **options):
"""run deduplications"""
dedupe_model(models.Edition)
dedupe_model(models.Work)
dedupe_model(models.Author)
dedupe_model(models.Edition, dry_run=options["dry_run"])
dedupe_model(models.Work, dry_run=options["dry_run"])
dedupe_model(models.Author, dry_run=options["dry_run"])

View file

@ -0,0 +1,43 @@
""" Erase any data stored about deleted users """
import sys
from django.core.management.base import BaseCommand, CommandError
from bookwyrm import models
from bookwyrm.models.user import erase_user_data
# pylint: disable=missing-function-docstring
class Command(BaseCommand):
"""command-line options"""
help = "Remove Two Factor Authorisation from user"
def add_arguments(self, parser): # pylint: disable=no-self-use
parser.add_argument(
"--dryrun",
action="store_true",
help="Preview users to be cleared without altering the database",
)
def handle(self, *args, **options): # pylint: disable=unused-argument
# Check for anything fishy
bad_state = models.User.objects.filter(is_deleted=True, is_active=True)
if bad_state.exists():
raise CommandError(
f"{bad_state.count()} user(s) marked as both active and deleted"
)
deleted_users = models.User.objects.filter(is_deleted=True)
self.stdout.write(f"Found {deleted_users.count()} deleted users")
if options["dryrun"]:
self.stdout.write("\n".join(u.username for u in deleted_users[:5]))
if deleted_users.count() > 5:
self.stdout.write("... and more")
sys.exit()
self.stdout.write("Erasing user data:")
for user_id in deleted_users.values_list("id", flat=True):
erase_user_data.delay(user_id)
self.stdout.write(".", ending="")
self.stdout.write("")
self.stdout.write("Tasks created successfully")

View file

@ -1,54 +0,0 @@
""" Get your admin code to allow install """
from django.core.management.base import BaseCommand
from bookwyrm import models
from bookwyrm.settings import VERSION
# pylint: disable=no-self-use
class Command(BaseCommand):
"""command-line options"""
help = "What version is this?"
def add_arguments(self, parser):
"""specify which function to run"""
parser.add_argument(
"--current",
action="store_true",
help="Version stored in database",
)
parser.add_argument(
"--target",
action="store_true",
help="Version stored in settings",
)
parser.add_argument(
"--update",
action="store_true",
help="Update database version",
)
# pylint: disable=unused-argument
def handle(self, *args, **options):
"""execute init"""
site = models.SiteSettings.objects.get()
current = site.version or "0.0.1"
target = VERSION
if options.get("current"):
print(current)
return
if options.get("target"):
print(target)
return
if options.get("update"):
site.version = target
site.save()
return
if current != target:
print(f"{current}/{target}")
else:
print(current)

View file

@ -0,0 +1,21 @@
""" Repair editions with missing works """
from django.core.management.base import BaseCommand
from bookwyrm import models
class Command(BaseCommand):
"""command-line options"""
help = "Repairs an edition that is in a broken state"
# pylint: disable=unused-argument
def handle(self, *args, **options):
"""Find and repair broken editions"""
# Find broken editions
editions = models.Edition.objects.filter(parent_work__isnull=True)
self.stdout.write(f"Repairing {editions.count()} edition(s):")
# Do repair
for edition in editions:
edition.repair()
self.stdout.write(".", ending="")

View file

@ -1,50 +0,0 @@
from django.db.models import ManyToManyField
def update_related(canonical, obj):
"""update all the models with fk to the object being removed"""
# move related models to canonical
related_models = [
(r.remote_field.name, r.related_model) for r in canonical._meta.related_objects
]
for (related_field, related_model) in related_models:
# Skip the ManyToMany fields that arent auto-created. These
# should have a corresponding OneToMany field in the model for
# the linking table anyway. If we update it through that model
# instead then we wont lose the extra fields in the linking
# table.
related_field_obj = related_model._meta.get_field(related_field)
if isinstance(related_field_obj, ManyToManyField):
through = related_field_obj.remote_field.through
if not through._meta.auto_created:
continue
related_objs = related_model.objects.filter(**{related_field: obj})
for related_obj in related_objs:
print("replacing in", related_model.__name__, related_field, related_obj.id)
try:
setattr(related_obj, related_field, canonical)
related_obj.save()
except TypeError:
getattr(related_obj, related_field).add(canonical)
getattr(related_obj, related_field).remove(obj)
def copy_data(canonical, obj):
"""try to get the most data possible"""
for data_field in obj._meta.get_fields():
if not hasattr(data_field, "activitypub_field"):
continue
data_value = getattr(obj, data_field.name)
if not data_value:
continue
if not getattr(canonical, data_field.name):
print("setting data field", data_field.name, data_value)
setattr(canonical, data_field.name, data_value)
canonical.save()
def merge_objects(canonical, obj):
copy_data(canonical, obj)
update_related(canonical, obj)
# remove the outdated entry
obj.delete()

View file

@ -1,4 +1,3 @@
from bookwyrm.management.merge import merge_objects
from django.core.management.base import BaseCommand
@ -9,6 +8,11 @@ class MergeCommand(BaseCommand):
"""add the arguments for this command"""
parser.add_argument("--canonical", type=int, required=True)
parser.add_argument("--other", type=int, required=True)
parser.add_argument(
"--dry_run",
action="store_true",
help="don't actually merge, only print what would happen",
)
# pylint: disable=no-self-use,unused-argument
def handle(self, *args, **options):
@ -26,4 +30,8 @@ class MergeCommand(BaseCommand):
print("other book doesnt exist!")
return
merge_objects(canonical, other)
absorbed_fields = other.merge_into(canonical, dry_run=options["dry_run"])
action = "would be" if options["dry_run"] else "has been"
print(f"{other.remote_id} {action} merged into {canonical.remote_id}")
print(f"absorbed fields: {absorbed_fields}")

View file

@ -1,3 +1,4 @@
""" look at all this nice middleware! """
from .timezone_middleware import TimezoneMiddleware
from .ip_middleware import IPBlocklistMiddleware
from .file_too_big import FileTooBig

View file

@ -0,0 +1,30 @@
"""Middleware to display a custom 413 error page"""
from django.http import HttpResponse
from django.shortcuts import render
from django.core.exceptions import RequestDataTooBig
class FileTooBig:
"""Middleware to display a custom page when a
RequestDataTooBig exception is thrown"""
def __init__(self, get_response):
"""boilerplate __init__ from Django docs"""
self.get_response = get_response
def __call__(self, request):
"""If RequestDataTooBig is thrown, render the 413 error page"""
try:
body = request.body # pylint: disable=unused-variable
except RequestDataTooBig:
rendered = render(request, "413.html")
response = HttpResponse(rendered)
return response
response = self.get_response(request)
return response

View file

@ -1,5 +1,5 @@
""" Makes the app aware of the users timezone """
import pytz
import zoneinfo
from django.utils import timezone
@ -12,9 +12,7 @@ class TimezoneMiddleware:
def __call__(self, request):
if request.user.is_authenticated:
timezone.activate(pytz.timezone(request.user.preferred_timezone))
timezone.activate(zoneinfo.ZoneInfo(request.user.preferred_timezone))
else:
timezone.activate(pytz.utc)
response = self.get_response(request)
timezone.deactivate()
return response
return self.get_response(request)

View file

@ -10,6 +10,7 @@ class Migration(migrations.Migration):
]
operations = [
# The new timezones are "Factory" and "localtime"
migrations.AlterField(
model_name="user",
name="preferred_timezone",

View file

@ -0,0 +1,51 @@
import re
from itertools import chain
from django.db import migrations, transaction
from django.db.models import Q
from bookwyrm.settings import LANGUAGE_ARTICLES
def set_sort_title(edition):
articles = chain(
*(LANGUAGE_ARTICLES.get(language, ()) for language in tuple(edition.languages))
)
edition.sort_title = re.sub(
f'^{" |^".join(articles)} ', "", str(edition.title).lower()
)
return edition
@transaction.atomic
def populate_sort_title(apps, schema_editor):
Edition = apps.get_model("bookwyrm", "Edition")
db_alias = schema_editor.connection.alias
editions_wo_sort_title = Edition.objects.using(db_alias).filter(
Q(sort_title__isnull=True) | Q(sort_title__exact="")
)
batch_size = 1000
start = 0
end = batch_size
while True:
batch = editions_wo_sort_title[start:end]
if not batch.exists():
break
Edition.objects.bulk_update(
(set_sort_title(edition) for edition in batch), ["sort_title"]
)
start = end
end += batch_size
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0178_auto_20230328_2132"),
]
operations = [
migrations.RunPython(
populate_sort_title, reverse_code=migrations.RunPython.noop
),
]

View file

@ -0,0 +1,36 @@
# Generated by Django 3.2.18 on 2023-05-16 16:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0178_auto_20230328_2132"),
]
operations = [
migrations.AddField(
model_name="reportcomment",
name="action_type",
field=models.CharField(
choices=[
("comment", "Comment"),
("resolve", "Resolved report"),
("reopen", "Re-opened report"),
("message_reporter", "Messaged reporter"),
("message_offender", "Messaged reported user"),
("user_suspension", "Suspended user"),
("user_unsuspension", "Un-suspended user"),
("user_perms", "Changed user permission level"),
("user_deletion", "Deleted user account"),
("block_domain", "Blocked domain"),
("approve_domain", "Approved domain"),
("delete_item", "Deleted item"),
],
default="comment",
max_length=20,
),
),
migrations.RenameModel("ReportComment", "ReportAction"),
]

View file

@ -0,0 +1,17 @@
# Generated by Django 3.2.18 on 2023-06-21 22:01
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0179_reportcomment_comment_type"),
]
operations = [
migrations.AlterModelOptions(
name="reportaction",
options={"ordering": ("created_date",)},
),
]

View file

@ -0,0 +1,44 @@
# Generated by Django 3.2.19 on 2023-07-23 19:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0179_populate_sort_title"),
]
operations = [
migrations.AlterField(
model_name="user",
name="preferred_language",
field=models.CharField(
blank=True,
choices=[
("en-us", "English"),
("ca-es", "Català (Catalan)"),
("de-de", "Deutsch (German)"),
("eo-uy", "Esperanto (Esperanto)"),
("es-es", "Español (Spanish)"),
("eu-es", "Euskara (Basque)"),
("gl-es", "Galego (Galician)"),
("it-it", "Italiano (Italian)"),
("fi-fi", "Suomi (Finnish)"),
("fr-fr", "Français (French)"),
("lt-lt", "Lietuvių (Lithuanian)"),
("nl-nl", "Nederlands (Dutch)"),
("no-no", "Norsk (Norwegian)"),
("pl-pl", "Polski (Polish)"),
("pt-br", "Português do Brasil (Brazilian Portuguese)"),
("pt-pt", "Português Europeu (European Portuguese)"),
("ro-ro", "Română (Romanian)"),
("sv-se", "Svenska (Swedish)"),
("zh-hans", "简体中文 (Simplified Chinese)"),
("zh-hant", "繁體中文 (Traditional Chinese)"),
],
max_length=255,
null=True,
),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.20 on 2023-08-06 23:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0180_alter_reportaction_options"),
("bookwyrm", "0180_alter_user_preferred_language"),
]
operations = []

View file

@ -0,0 +1,130 @@
# Generated by Django 3.2.20 on 2023-10-27 11:22
import bookwyrm.models.activitypub_mixin
import bookwyrm.models.fields
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0181_merge_20230806_2302"),
]
operations = [
migrations.AddField(
model_name="user",
name="also_known_as",
field=bookwyrm.models.fields.ManyToManyField(to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name="user",
name="moved_to",
field=bookwyrm.models.fields.RemoteIdField(
max_length=255,
null=True,
validators=[bookwyrm.models.fields.validate_remote_id],
),
),
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("BOOST", "Boost"),
("IMPORT", "Import"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
migrations.CreateModel(
name="Move",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_date", models.DateTimeField(auto_now_add=True)),
("updated_date", models.DateTimeField(auto_now=True)),
(
"remote_id",
bookwyrm.models.fields.RemoteIdField(
max_length=255,
null=True,
validators=[bookwyrm.models.fields.validate_remote_id],
),
),
("object", bookwyrm.models.fields.CharField(max_length=255)),
(
"origin",
bookwyrm.models.fields.CharField(
blank=True, default="", max_length=255, null=True
),
),
(
"user",
bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"abstract": False,
},
bases=(bookwyrm.models.activitypub_mixin.ActivityMixin, models.Model),
),
migrations.CreateModel(
name="MoveUser",
fields=[
(
"move_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="bookwyrm.move",
),
),
(
"target",
bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="move_target",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"abstract": False,
},
bases=("bookwyrm.move",),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2023-11-05 16:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0182_auto_20231027_1122"),
]
operations = [
migrations.AddField(
model_name="user",
name="is_deleted",
field=models.BooleanField(default=False),
),
]

View file

@ -0,0 +1,35 @@
# Generated by Django 3.2.20 on 2023-11-06 04:21
from django.db import migrations
from bookwyrm.models import User
def update_deleted_users(apps, schema_editor):
"""Find all the users who are deleted, not just inactive, and set deleted"""
users = apps.get_model("bookwyrm", "User")
db_alias = schema_editor.connection.alias
users.objects.using(db_alias).filter(
is_active=False,
deactivation_reason__in=[
"self_deletion",
"moderator_deletion",
],
).update(is_deleted=True)
# differente rules for remote users
users.objects.using(db_alias).filter(is_active=False, local=False,).exclude(
deactivation_reason="moderator_deactivation",
).update(is_deleted=True)
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0183_auto_20231105_1607"),
]
operations = [
migrations.RunPython(
update_deleted_users, reverse_code=migrations.RunPython.noop
),
]

View file

@ -0,0 +1,42 @@
# Generated by Django 3.2.20 on 2023-11-13 22:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0184_auto_20231106_0421"),
]
operations = [
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("BOOST", "Boost"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("IMPORT", "Import"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
]

View file

@ -0,0 +1,212 @@
# Generated by Django 3.2.20 on 2023-11-16 00:48
from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0185_alter_notification_notification_type"),
]
operations = [
migrations.CreateModel(
name="ParentJob",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("task_id", models.UUIDField(blank=True, null=True, unique=True)),
(
"created_date",
models.DateTimeField(default=django.utils.timezone.now),
),
(
"updated_date",
models.DateTimeField(default=django.utils.timezone.now),
),
("complete", models.BooleanField(default=False)),
(
"status",
models.CharField(
choices=[
("pending", "Pending"),
("active", "Active"),
("complete", "Complete"),
("stopped", "Stopped"),
("failed", "Failed"),
],
default="pending",
max_length=50,
null=True,
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"abstract": False,
},
),
migrations.AddField(
model_name="sitesettings",
name="user_import_time_limit",
field=models.IntegerField(default=48),
),
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("BOOST", "Boost"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("IMPORT", "Import"),
("USER_IMPORT", "User Import"),
("USER_EXPORT", "User Export"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
migrations.CreateModel(
name="BookwyrmExportJob",
fields=[
(
"parentjob_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="bookwyrm.parentjob",
),
),
("export_data", models.FileField(null=True, upload_to="")),
],
options={
"abstract": False,
},
bases=("bookwyrm.parentjob",),
),
migrations.CreateModel(
name="BookwyrmImportJob",
fields=[
(
"parentjob_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="bookwyrm.parentjob",
),
),
("archive_file", models.FileField(blank=True, null=True, upload_to="")),
("import_data", models.JSONField(null=True)),
(
"required",
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(blank=True, max_length=50),
blank=True,
size=None,
),
),
],
options={
"abstract": False,
},
bases=("bookwyrm.parentjob",),
),
migrations.CreateModel(
name="ChildJob",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("task_id", models.UUIDField(blank=True, null=True, unique=True)),
(
"created_date",
models.DateTimeField(default=django.utils.timezone.now),
),
(
"updated_date",
models.DateTimeField(default=django.utils.timezone.now),
),
("complete", models.BooleanField(default=False)),
(
"status",
models.CharField(
choices=[
("pending", "Pending"),
("active", "Active"),
("complete", "Complete"),
("stopped", "Stopped"),
("failed", "Failed"),
],
default="pending",
max_length=50,
null=True,
),
),
(
"parent_job",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="child_jobs",
to="bookwyrm.parentjob",
),
),
],
options={
"abstract": False,
},
),
migrations.AddField(
model_name="notification",
name="related_user_export",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="bookwyrm.bookwyrmexportjob",
),
),
]

View file

@ -0,0 +1,48 @@
# Generated by Django 3.2.20 on 2023-11-14 10:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0185_alter_notification_notification_type"),
]
operations = [
migrations.AddField(
model_name="notification",
name="related_invite_requests",
field=models.ManyToManyField(to="bookwyrm.InviteRequest"),
),
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("BOOST", "Boost"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("IMPORT", "Import"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE_REQUEST", "Invite Request"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
]

View file

@ -0,0 +1,54 @@
# Generated by Django 3.2.20 on 2023-11-09 16:57
import bookwyrm.models.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0186_invite_request_notification"),
]
operations = [
migrations.AddField(
model_name="book",
name="first_published_date_precision",
field=models.CharField(
blank=True,
choices=[
("DAY", "Day prec."),
("MONTH", "Month prec."),
("YEAR", "Year prec."),
],
editable=False,
max_length=10,
null=True,
),
),
migrations.AddField(
model_name="book",
name="published_date_precision",
field=models.CharField(
blank=True,
choices=[
("DAY", "Day prec."),
("MONTH", "Month prec."),
("YEAR", "Year prec."),
],
editable=False,
max_length=10,
null=True,
),
),
migrations.AlterField(
model_name="book",
name="first_published_date",
field=bookwyrm.models.fields.PartialDateField(blank=True, null=True),
),
migrations.AlterField(
model_name="book",
name="published_date",
field=bookwyrm.models.fields.PartialDateField(blank=True, null=True),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2023-11-20 18:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0187_partial_publication_dates"),
]
operations = [
migrations.AddField(
model_name="theme",
name="loads",
field=models.BooleanField(blank=True, null=True),
),
]

View file

@ -0,0 +1,45 @@
# Generated by Django 3.2.23 on 2023-12-12 23:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0188_theme_loads"),
]
operations = [
migrations.AlterField(
model_name="user",
name="preferred_language",
field=models.CharField(
blank=True,
choices=[
("en-us", "English"),
("ca-es", "Català (Catalan)"),
("de-de", "Deutsch (German)"),
("eo-uy", "Esperanto (Esperanto)"),
("es-es", "Español (Spanish)"),
("eu-es", "Euskara (Basque)"),
("gl-es", "Galego (Galician)"),
("it-it", "Italiano (Italian)"),
("fi-fi", "Suomi (Finnish)"),
("fr-fr", "Français (French)"),
("lt-lt", "Lietuvių (Lithuanian)"),
("nl-nl", "Nederlands (Dutch)"),
("no-no", "Norsk (Norwegian)"),
("pl-pl", "Polski (Polish)"),
("pt-br", "Português do Brasil (Brazilian Portuguese)"),
("pt-pt", "Português Europeu (European Portuguese)"),
("ro-ro", "Română (Romanian)"),
("sv-se", "Svenska (Swedish)"),
("uk-ua", "Українська (Ukrainian)"),
("zh-hans", "简体中文 (Simplified Chinese)"),
("zh-hant", "繁體中文 (Traditional Chinese)"),
],
max_length=255,
null=True,
),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2023-11-25 05:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0188_theme_loads"),
]
operations = [
migrations.AddField(
model_name="importjob",
name="create_shelves",
field=models.BooleanField(default=True),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2023-11-22 10:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0186_auto_20231116_0048"),
("bookwyrm", "0188_theme_loads"),
]
operations = []

View file

@ -0,0 +1,45 @@
# Generated by Django 3.2.23 on 2023-11-23 19:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0189_merge_0186_auto_20231116_0048_0188_theme_loads"),
]
operations = [
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("BOOST", "Boost"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("IMPORT", "Import"),
("USER_IMPORT", "User Import"),
("USER_EXPORT", "User Export"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE_REQUEST", "Invite Request"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
]

View file

@ -0,0 +1,16 @@
# Generated by Django 3.2.20 on 2023-11-24 17:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0188_theme_loads"),
]
operations = [
migrations.RemoveIndex(
model_name="author",
name="bookwyrm_au_search__b050a8_gin",
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2024-01-02 03:26
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0189_alter_user_preferred_language"),
("bookwyrm", "0190_alter_notification_notification_type"),
]
operations = []

View file

@ -0,0 +1,76 @@
# Generated by Django 3.2.20 on 2023-11-25 00:47
from importlib import import_module
import re
from django.db import migrations
import pgtrigger.compiler
import pgtrigger.migrations
trigger_migration = import_module("bookwyrm.migrations.0077_auto_20210623_2155")
# it's _very_ convenient for development that this migration be reversible
search_vector_trigger = trigger_migration.Migration.operations[4]
author_search_vector_trigger = trigger_migration.Migration.operations[5]
assert re.search(r"\bCREATE TRIGGER search_vector_trigger\b", search_vector_trigger.sql)
assert re.search(
r"\bCREATE TRIGGER author_search_vector_trigger\b",
author_search_vector_trigger.sql,
)
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0190_book_search_updates"),
]
operations = [
pgtrigger.migrations.AddTrigger(
model_name="book",
trigger=pgtrigger.compiler.Trigger(
name="update_search_vector_on_book_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="new.search_vector := setweight(coalesce(nullif(to_tsvector('english', new.title), ''), to_tsvector('simple', new.title)), 'A') || setweight(to_tsvector('english', coalesce(new.subtitle, '')), 'B') || (SELECT setweight(to_tsvector('simple', coalesce(array_to_string(array_agg(bookwyrm_author.name), ' '), '')), 'C') FROM bookwyrm_author LEFT JOIN bookwyrm_book_authors ON bookwyrm_author.id = bookwyrm_book_authors.author_id WHERE bookwyrm_book_authors.book_id = new.id ) || setweight(to_tsvector('english', coalesce(new.series, '')), 'D');RETURN NEW;",
hash="77d6399497c0a89b0bf09d296e33c396da63705c",
operation='INSERT OR UPDATE OF "title", "subtitle", "series", "search_vector"',
pgid="pgtrigger_update_search_vector_on_book_edit_bec58",
table="bookwyrm_book",
when="BEFORE",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="author",
trigger=pgtrigger.compiler.Trigger(
name="reset_search_vector_on_author_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="WITH updated_books AS (SELECT book_id FROM bookwyrm_book_authors WHERE author_id = new.id ) UPDATE bookwyrm_book SET search_vector = '' FROM updated_books WHERE id = updated_books.book_id;RETURN NEW;",
hash="e7bbf08711ff3724c58f4d92fb7a082ffb3d7826",
operation='UPDATE OF "name"',
pgid="pgtrigger_reset_search_vector_on_author_edit_a447c",
table="bookwyrm_author",
when="AFTER",
),
),
),
migrations.RunSQL(
sql="""DROP TRIGGER IF EXISTS search_vector_trigger ON bookwyrm_book;
DROP FUNCTION IF EXISTS book_trigger;
""",
reverse_sql=search_vector_trigger.sql,
),
migrations.RunSQL(
sql="""DROP TRIGGER IF EXISTS author_search_vector_trigger ON bookwyrm_author;
DROP FUNCTION IF EXISTS author_trigger;
""",
reverse_sql=author_search_vector_trigger.sql,
),
migrations.RunSQL(
# Recalculate book search vector for any missed author name changes
# due to bug in JOIN in the old trigger.
sql="UPDATE bookwyrm_book SET search_vector = NULL;",
reverse_sql=migrations.RunSQL.noop,
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.23 on 2024-01-04 23:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0191_merge_20240102_0326"),
]
operations = [
migrations.AlterField(
model_name="quotation",
name="endposition",
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name="quotation",
name="position",
field=models.TextField(blank=True, null=True),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2024-01-02 19:36
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0191_merge_20240102_0326"),
]
operations = [
migrations.RenameField(
model_name="sitesettings",
old_name="version",
new_name="available_version",
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2024-01-16 10:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0191_merge_20240102_0326"),
]
operations = [
migrations.AddField(
model_name="sitesettings",
name="user_exports_enabled",
field=models.BooleanField(default=False),
),
]

View file

@ -0,0 +1,92 @@
# Generated by Django 3.2.23 on 2024-01-28 02:49
import django.core.serializers.json
from django.db import migrations, models
import django.db.models.deletion
from django.core.files.storage import storages
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0192_sitesettings_user_exports_enabled"),
]
operations = [
migrations.AddField(
model_name="bookwyrmexportjob",
name="export_json",
field=models.JSONField(
encoder=django.core.serializers.json.DjangoJSONEncoder, null=True
),
),
migrations.AddField(
model_name="bookwyrmexportjob",
name="json_completed",
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name="bookwyrmexportjob",
name="export_data",
field=models.FileField(
null=True,
storage=storages["exports"],
upload_to="",
),
),
migrations.CreateModel(
name="AddFileToTar",
fields=[
(
"childjob_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="bookwyrm.childjob",
),
),
(
"parent_export_job",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="child_edition_export_jobs",
to="bookwyrm.bookwyrmexportjob",
),
),
],
options={
"abstract": False,
},
bases=("bookwyrm.childjob",),
),
migrations.CreateModel(
name="AddBookToUserExportJob",
fields=[
(
"childjob_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="bookwyrm.childjob",
),
),
(
"edition",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="bookwyrm.edition",
),
),
],
options={
"abstract": False,
},
bases=("bookwyrm.childjob",),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2024-02-03 15:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0192_make_page_positions_text"),
("bookwyrm", "0192_sitesettings_user_exports_enabled"),
]
operations = []

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2024-02-03 16:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0192_rename_version_sitesettings_available_version"),
("bookwyrm", "0193_merge_20240203_1539"),
]
operations = []

View file

@ -0,0 +1,46 @@
# Generated by Django 3.2.23 on 2024-02-21 00:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0194_merge_20240203_1619"),
]
operations = [
migrations.AlterField(
model_name="user",
name="preferred_language",
field=models.CharField(
blank=True,
choices=[
("en-us", "English"),
("ca-es", "Català (Catalan)"),
("de-de", "Deutsch (German)"),
("eo-uy", "Esperanto (Esperanto)"),
("es-es", "Español (Spanish)"),
("eu-es", "Euskara (Basque)"),
("gl-es", "Galego (Galician)"),
("it-it", "Italiano (Italian)"),
("ko-kr", "한국어 (Korean)"),
("fi-fi", "Suomi (Finnish)"),
("fr-fr", "Français (French)"),
("lt-lt", "Lietuvių (Lithuanian)"),
("nl-nl", "Nederlands (Dutch)"),
("no-no", "Norsk (Norwegian)"),
("pl-pl", "Polski (Polish)"),
("pt-br", "Português do Brasil (Brazilian Portuguese)"),
("pt-pt", "Português Europeu (European Portuguese)"),
("ro-ro", "Română (Romanian)"),
("sv-se", "Svenska (Swedish)"),
("uk-ua", "Українська (Ukrainian)"),
("zh-hans", "简体中文 (Simplified Chinese)"),
("zh-hant", "繁體中文 (Traditional Chinese)"),
],
max_length=255,
null=True,
),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2024-03-18 17:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0193_auto_20240128_0249"),
("bookwyrm", "0195_alter_user_preferred_language"),
]
operations = []

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2024-03-18 00:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0191_migrate_search_vec_triggers_to_pgtriggers"),
("bookwyrm", "0195_alter_user_preferred_language"),
]
operations = []

View file

@ -0,0 +1,41 @@
# Generated by Django 3.2.25 on 2024-03-20 15:15
import django.contrib.postgres.indexes
from django.db import migrations
import pgtrigger.compiler
import pgtrigger.migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0196_merge_pr3134_into_main"),
]
operations = [
migrations.AddIndex(
model_name="author",
index=django.contrib.postgres.indexes.GinIndex(
fields=["search_vector"], name="bookwyrm_au_search__b050a8_gin"
),
),
pgtrigger.migrations.AddTrigger(
model_name="author",
trigger=pgtrigger.compiler.Trigger(
name="update_search_vector_on_author_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="new.search_vector := setweight(to_tsvector('simple', new.name), 'A') || setweight(to_tsvector('simple', coalesce(array_to_string(new.aliases, ' '), '')), 'B');RETURN NEW;",
hash="b97919016236d74d0ade51a0769a173ea269da64",
operation='INSERT OR UPDATE OF "name", "aliases", "search_vector"',
pgid="pgtrigger_update_search_vector_on_author_edit_c61cb",
table="bookwyrm_author",
when="BEFORE",
),
),
),
migrations.RunSQL(
# Calculate search vector for all Authors.
sql="UPDATE bookwyrm_author SET search_vector = NULL;",
reverse_sql="UPDATE bookwyrm_author SET search_vector = NULL;",
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.25 on 2024-03-24 02:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0196_merge_20240318_1737"),
("bookwyrm", "0196_merge_pr3134_into_main"),
]
operations = []

View file

@ -0,0 +1,48 @@
# Generated by Django 3.2.24 on 2024-02-28 21:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0196_merge_pr3134_into_main"),
]
operations = [
migrations.CreateModel(
name="MergedBook",
fields=[
("deleted_id", models.IntegerField(primary_key=True, serialize=False)),
(
"merged_into",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="absorbed",
to="bookwyrm.book",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="MergedAuthor",
fields=[
("deleted_id", models.IntegerField(primary_key=True, serialize=False)),
(
"merged_into",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="absorbed",
to="bookwyrm.author",
),
),
],
options={
"abstract": False,
},
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.25 on 2024-03-26 11:37
import bookwyrm.models.bookwyrm_export_job
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0197_merge_20240324_0235"),
]
operations = [
migrations.AlterField(
model_name="bookwyrmexportjob",
name="export_data",
field=models.FileField(
null=True,
storage=bookwyrm.models.bookwyrm_export_job.select_exports_storage,
upload_to="",
),
),
]

View file

@ -0,0 +1,57 @@
# Generated by Django 3.2.25 on 2024-03-20 15:52
from django.db import migrations
import pgtrigger.compiler
import pgtrigger.migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0197_author_search_vector"),
]
operations = [
pgtrigger.migrations.RemoveTrigger(
model_name="author",
name="reset_search_vector_on_author_edit",
),
pgtrigger.migrations.RemoveTrigger(
model_name="book",
name="update_search_vector_on_book_edit",
),
pgtrigger.migrations.AddTrigger(
model_name="author",
trigger=pgtrigger.compiler.Trigger(
name="reset_book_search_vector_on_author_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="WITH updated_books AS (SELECT book_id FROM bookwyrm_book_authors WHERE author_id = new.id ) UPDATE bookwyrm_book SET search_vector = '' FROM updated_books WHERE id = updated_books.book_id;RETURN NEW;",
hash="68422c0f29879c5802b82159dde45297eff53e73",
operation='UPDATE OF "name", "aliases"',
pgid="pgtrigger_reset_book_search_vector_on_author_edit_a50c7",
table="bookwyrm_author",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="book",
trigger=pgtrigger.compiler.Trigger(
name="update_search_vector_on_book_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="WITH author_names AS (SELECT array_to_string(bookwyrm_author.name || bookwyrm_author.aliases, ' ') AS name_and_aliases FROM bookwyrm_author LEFT JOIN bookwyrm_book_authors ON bookwyrm_author.id = bookwyrm_book_authors.author_id WHERE bookwyrm_book_authors.book_id = new.id ) SELECT setweight(coalesce(nullif(to_tsvector('english', new.title), ''), to_tsvector('simple', new.title)), 'A') || setweight(to_tsvector('english', coalesce(new.subtitle, '')), 'B') || (SELECT setweight(to_tsvector('simple', coalesce(array_to_string(array_agg(name_and_aliases), ' '), '')), 'C') FROM author_names) || setweight(to_tsvector('english', coalesce(new.series, '')), 'D') INTO new.search_vector;RETURN NEW;",
hash="9324f5ca76a6f5e63931881d62d11da11f595b2c",
operation='INSERT OR UPDATE OF "title", "subtitle", "series", "search_vector"',
pgid="pgtrigger_update_search_vector_on_book_edit_bec58",
table="bookwyrm_book",
when="BEFORE",
),
),
),
migrations.RunSQL(
# Recalculate search vector for all Books because it now includes
# Author aliases.
sql="UPDATE bookwyrm_book SET search_vector = NULL;",
reverse_sql="UPDATE bookwyrm_book SET search_vector = NULL;",
),
]

View file

@ -0,0 +1,70 @@
# Generated by Django 4.2.11 on 2024-03-29 19:25
import bookwyrm.models.fields
from django.conf import settings
from django.db import migrations
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0198_book_search_vector_author_aliases"),
]
operations = [
migrations.AlterField(
model_name="userblocks",
name="user_object",
field=bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="%(class)s_user_object",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="userblocks",
name="user_subject",
field=bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="%(class)s_user_subject",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="userfollowrequest",
name="user_object",
field=bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="%(class)s_user_object",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="userfollowrequest",
name="user_subject",
field=bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="%(class)s_user_subject",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="userfollows",
name="user_object",
field=bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="%(class)s_user_object",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="userfollows",
name="user_subject",
field=bookwyrm.models.fields.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="%(class)s_user_subject",
to=settings.AUTH_USER_MODEL,
),
),
]

View file

@ -0,0 +1,13 @@
# Generated by Django 3.2.25 on 2024-03-26 12:17
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0198_alter_bookwyrmexportjob_export_data"),
("bookwyrm", "0198_book_search_vector_author_aliases"),
]
operations = []

View file

@ -0,0 +1,19 @@
# Generated by Django 3.2.25 on 2024-04-02 19:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0198_book_search_vector_author_aliases"),
]
operations = [
migrations.AddIndex(
model_name="status",
index=models.Index(
fields=["remote_id"], name="bookwyrm_st_remote__06aeba_idx"
),
),
]

Some files were not shown because too many files have changed in this diff Show more