Skip to content

Commit

Permalink
Merge branch 'main' into user-export
Browse files Browse the repository at this point in the history
  • Loading branch information
hughrun authored Apr 13, 2024
2 parents 501fb45 + 7d58175 commit d48d312
Show file tree
Hide file tree
Showing 52 changed files with 701 additions and 303 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:

# 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.
Expand All @@ -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
Expand All @@ -65,4 +65,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
3 changes: 2 additions & 1 deletion .github/workflows/lint-frontend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jobs:
- 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
Expand Down
14 changes: 5 additions & 9 deletions bookwyrm/activitystreams.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ def _get_audience(self, status): # pylint: disable=no-self-use
| (
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):
Expand All @@ -156,7 +156,7 @@ def get_audience(self, status):
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"""
Expand All @@ -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(
Expand Down Expand Up @@ -239,9 +237,7 @@ def _get_audience(self, status):
)

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,
Expand Down
37 changes: 25 additions & 12 deletions bookwyrm/management/commands/deduplicate_book_data.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"])
50 changes: 0 additions & 50 deletions bookwyrm/management/merge.py

This file was deleted.

12 changes: 10 additions & 2 deletions bookwyrm/management/merge_command.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from bookwyrm.management.merge import merge_objects
from django.core.management.base import BaseCommand


Expand All @@ -9,6 +8,11 @@ def add_arguments(self, parser):
"""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):
Expand All @@ -26,4 +30,8 @@ def handle(self, *args, **options):
print("other book doesn’t 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}")
48 changes: 48 additions & 0 deletions bookwyrm/migrations/0197_mergedauthor_mergedbook.py
Original file line number Diff line number Diff line change
@@ -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,
},
),
]
19 changes: 19 additions & 0 deletions bookwyrm/migrations/0199_status_bookwyrm_st_remote__06aeba_idx.py
Original file line number Diff line number Diff line change
@@ -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"
),
),
]
19 changes: 19 additions & 0 deletions bookwyrm/migrations/0200_status_bookwyrm_st_thread__cf064f_idx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.25 on 2024-04-03 19:05

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("bookwyrm", "0199_status_bookwyrm_st_remote__06aeba_idx"),
]

operations = [
migrations.AddIndex(
model_name="status",
index=models.Index(
fields=["thread_id"], name="bookwyrm_st_thread__cf064f_idx"
),
),
]
19 changes: 19 additions & 0 deletions bookwyrm/migrations/0201_keypair_bookwyrm_ke_remote__472927_idx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.25 on 2024-04-03 19:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("bookwyrm", "0200_status_bookwyrm_st_thread__cf064f_idx"),
]

operations = [
migrations.AddIndex(
model_name="keypair",
index=models.Index(
fields=["remote_id"], name="bookwyrm_ke_remote__472927_idx"
),
),
]
19 changes: 19 additions & 0 deletions bookwyrm/migrations/0202_user_bookwyrm_us_usernam_b2546d_idx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.25 on 2024-04-03 19:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("bookwyrm", "0201_keypair_bookwyrm_ke_remote__472927_idx"),
]

operations = [
migrations.AddIndex(
model_name="user",
index=models.Index(
fields=["username"], name="bookwyrm_us_usernam_b2546d_idx"
),
),
]
19 changes: 19 additions & 0 deletions bookwyrm/migrations/0203_user_bookwyrm_us_is_acti_972dc4_idx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.25 on 2024-04-03 19:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("bookwyrm", "0202_user_bookwyrm_us_usernam_b2546d_idx"),
]

operations = [
migrations.AddIndex(
model_name="user",
index=models.Index(
fields=["is_active", "local"], name="bookwyrm_us_is_acti_972dc4_idx"
),
),
]
13 changes: 13 additions & 0 deletions bookwyrm/migrations/0204_merge_20240409_1042.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 3.2.25 on 2024-04-09 10:42

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("bookwyrm", "0197_mergedauthor_mergedbook"),
("bookwyrm", "0203_user_bookwyrm_us_is_acti_972dc4_idx"),
]

operations = []
Loading

0 comments on commit d48d312

Please sign in to comment.