Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions front_end/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@
"predicted": "Predicted",
"notPredicted": "Not Predicted",
"author": "Author",
"authorWithCount": "{count, plural, =1 {Author} other {Authors} }",
"authored": "Authored",
"upvoted": "Upvoted",
"moderating": "Moderating",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ const SidebarQuestionInfo: FC<Props> = ({ postData }) => {
<div className="flex flex-col justify-between gap-3 self-stretch @lg:grid @lg:grid-cols-4 @lg:gap-1">
<div className="flex justify-between gap-4 @lg:flex-col @lg:justify-start @lg:gap-1">
<span className="text-xs font-medium uppercase text-gray-700 dark:text-gray-700-dark">
{t("author")}:
{t("authorWithCount", { count: postData.coauthors.length > 0 })}:
</span>
<Link
className="flex min-w-0 shrink flex-col items-end gap-1 text-sm font-medium leading-4 text-blue-700 @lg:items-start dark:text-blue-700-dark"
href={`/accounts/profile/${postData.author_id}`}
>
{postData.author_username}
</Link>
<div className="text-metac-blue-700 dark:text-metac-blue-700-dark flex min-w-0 shrink flex-col items-end gap-1 font-medium @lg:items-start">
<Link
className="flex min-w-0 shrink flex-col items-end gap-1 text-sm font-medium leading-4 text-blue-700 @lg:items-start dark:text-blue-700-dark"
href={`/accounts/profile/${postData.author_id}`}
>
{postData.author_username}
</Link>

{postData.coauthors.map((coauthor) => (
<Link
className="flex min-w-0 shrink flex-col items-end gap-1 text-sm font-medium leading-4 text-blue-700 @lg:items-start dark:text-blue-700-dark"
href={`/accounts/profile/${coauthor.id}`}
>
{coauthor.username}
</Link>
))}
</div>
</div>

<div className="flex justify-between gap-4 @lg:flex-col @lg:justify-start @lg:gap-1">
Expand Down
1 change: 1 addition & 0 deletions front_end/src/types/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export type Post<QT = Question> = {
vote: PostVote;
nr_forecasters: number;
author_username: string;
coauthors: { id: number; username: string }[];
author_id: number;
question?: QT;
conditional?: PostConditional<QT>;
Expand Down
31 changes: 31 additions & 0 deletions migrator/services/migrate_questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,33 @@ def migrate_questions(site_ids: list[int] = None):
migrate_questions__composite(site_ids=site_ids)


def add_coauthors_for_post(post: Post):
for obj in paginated_query(
"""
WITH shared_projects AS (
SELECT project_id
FROM metac_project_questionprojectpermissions
WHERE question_id = %s
AND project_id IN (
SELECT id
FROM metac_project_project
WHERE type = 'PP'
)
)
SELECT upp.user_id AS id, u.username
FROM metac_project_userprojectpermissions upp
JOIN metac_account_user u ON upp.user_id = u.id
WHERE upp.project_id IN (SELECT project_id FROM shared_projects)
AND upp.user_id != (SELECT author_id
FROM metac_question_question
WHERE id = %s);
""",
[post.id, post.id],
):
user_id = obj["id"]
post.coauthors.add(user_id)


def migrate_questions__simple(site_ids: list[int] = None):
questions = []
posts = []
Expand Down Expand Up @@ -208,6 +235,10 @@ def migrate_questions__simple(site_ids: list[int] = None):
)
Question.objects.bulk_create(questions)
Post.objects.bulk_create(posts)

for post in posts:
add_coauthors_for_post(post)

print(
f"\033[Kmigrating questions/posts: {i}. "
f"dur:{str(timezone.now() - start).split('.')[0]} "
Expand Down
25 changes: 25 additions & 0 deletions posts/migrations/0023_post_coauthors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.0.8 on 2024-09-05 12:33

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
(
"posts",
"0022_remove_postsubscription_postsubscription_unique_type_user_post_and_more",
),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.AddField(
model_name="post",
name="coauthors",
field=models.ManyToManyField(
blank=True, related_name="coauthored_posts", to=settings.AUTH_USER_MODEL
),
),
]
13 changes: 13 additions & 0 deletions posts/migrations/0026_merge_20240906_1234.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 5.0.8 on 2024-09-06 12:34

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("posts", "0023_post_coauthors"),
("posts", "0025_remove_post_preview_image"),
]

operations = []
3 changes: 3 additions & 0 deletions posts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ class CurationStatus(models.TextChoices):
title = models.CharField(max_length=2000)
url_title = models.CharField(max_length=2000, default="")
author = models.ForeignKey(User, models.CASCADE, related_name="posts")
coauthors = models.ManyToManyField(
User, related_name="coauthored_posts", blank=True
)

curated_last_by = models.ForeignKey(
User,
Expand Down
5 changes: 5 additions & 0 deletions posts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class PostSerializer(serializers.ModelSerializer):
author_username = serializers.SerializerMethodField()
status = serializers.SerializerMethodField()
open_time = serializers.SerializerMethodField()
coauthors = serializers.SerializerMethodField()

class Meta:
model = Post
Expand All @@ -46,6 +47,7 @@ class Meta:
"url_title",
"author_id",
"author_username",
"coauthors",
"projects",
"created_at",
"published_at",
Expand All @@ -66,6 +68,9 @@ def get_projects(self, obj: Post):
def get_author_username(self, obj: Post):
return obj.author.username

def get_coauthors(self, obj: Post):
return [{"id": u.id, "username": u.username} for u in obj.coauthors.all()]

def get_status(self, obj: Post):
if obj.resolved:
return "resolved"
Expand Down