Skip to content
Open
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
20 changes: 17 additions & 3 deletions front_end/src/app/(main)/components/bulletins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,28 @@ const Bulletins: FC = () => {
);
}, [pathname]);

const bulletinParams = useMemo(() => {
const questionMatch = pathname.match(/^\/questions\/(\d+)(?:\/|$)/);
if (questionMatch) {
return { post_id: Number(questionMatch[1]) };
}

const projectMatch = pathname.match(/^\/tournament\/([^/]+)(?:\/|$)/);
if (projectMatch) {
return { project_slug: projectMatch[1] };
}
Comment on lines +39 to +47
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a cleaner way to do this? @ncarazon
I need the post_id or project_slug
This just reads from the URL and uses regex. Maybe we have a helper or a better way to grab the relevant post or project?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not totally sure we already have a helper for this
Given that, I’d keep the regex logic but extract it into a small helper under utils/navigation.ts (e.g. getBulletinParamsFromPathname(pathname)), and then just call that from here.


return undefined;
}, [pathname]);

const fetchBulletins = useCallback(async () => {
try {
const bulletins = await ClientMiscApi.getBulletins();
setBulletins(bulletins);
const bulletins = await ClientMiscApi.getBulletins(bulletinParams);
setBulletins(bulletins ?? []);
} catch (error) {
logError(error);
}
}, []);
}, [bulletinParams]);

useEffect(() => {
if (!shouldHide) {
Expand Down
11 changes: 9 additions & 2 deletions front_end/src/services/api/misc/misc.shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ApiService } from "@/services/api/api_service";
import { encodeQueryParams } from "@/utils/navigation";

export type ContactForm = {
email: string;
Expand All @@ -21,14 +22,20 @@ export interface SiteStats {
years_of_predictions: number;
}

type BulletinParams = {
post_id?: number;
project_slug?: string;
};

class MiscApi extends ApiService {
async getBulletins() {
async getBulletins(params?: BulletinParams) {
const queryParams = encodeQueryParams(params ?? {});
const resp = await this.get<{
bulletins: {
text: string;
id: number;
}[];
}>("/get-bulletins/");
}>(`/get-bulletins/${queryParams}`);
return resp?.bulletins;
}

Expand Down
8 changes: 7 additions & 1 deletion misc/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from admin_auto_filters.filters import AutocompleteFilterFactory
from django import forms
from django.contrib import admin
from django.core.exceptions import ValidationError
Expand All @@ -8,7 +9,12 @@
@admin.register(Bulletin)
class BulletinAdmin(admin.ModelAdmin):
list_display = ["__str__", "bulletin_start", "bulletin_end"]
search_fields = ["bulletin_start", "bulletin_end", "text"]
search_fields = ["post", "project", "bulletin_start", "bulletin_end", "text"]
list_filter = [
AutocompleteFilterFactory("Post", "post"),
AutocompleteFilterFactory("Project", "project"),
]
autocomplete_fields = ["post", "project"]


class SidebarItemAdminForm(forms.ModelForm):
Expand Down
38 changes: 38 additions & 0 deletions misc/migrations/0007_bulletin_post_bulletin_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 5.1.13 on 2025-11-22 16:04

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("misc", "0006_whitelistuser_notes_and_more"),
("posts", "0025_post_actual_resolve_time"),
("projects", "0021_projectindex_project_index_projectindexpost"),
]

operations = [
migrations.AddField(
model_name="bulletin",
name="post",
field=models.ForeignKey(
blank=True,
help_text="Optional. If set, places this Bulletin only on this post's page.",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="posts.post",
),
),
migrations.AddField(
model_name="bulletin",
name="project",
field=models.ForeignKey(
blank=True,
help_text="Optional. If set, places this Bulletin only on this project's page.",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="projects.project",
),
),
]
24 changes: 23 additions & 1 deletion misc/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,30 @@ class Bulletin(TimeStampedModel):
bulletin_end = models.DateTimeField()
text = models.TextField()

post = models.ForeignKey(
Post,
null=True,
blank=True,
db_index=True,
on_delete=models.CASCADE,
help_text="""Optional. If set, places this Bulletin only on this post's page.""",
)
project = models.ForeignKey(
Project,
null=True,
blank=True,
db_index=True,
on_delete=models.CASCADE,
help_text="""Optional. If set, places this Bulletin only on this project's page.""",
)

def __str__(self):
return self.text[:150] + "..." if len(self.text) > 150 else self.text
text = self.text
if self.post:
text = (self.post.short_title or self.post.title)[:50] + "... " + text
elif self.project:
text = self.project.name[:50] + "... " + text
return text[:150] + "..." if len(text) > 150 else text


class BulletinViewedBy(TimeStampedModel):
Expand Down
22 changes: 19 additions & 3 deletions misc/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from datetime import datetime

import django
from django.db.models import Q
from django.conf import settings
from django.core.mail import EmailMessage
from django.http import JsonResponse
from django.views.decorators.cache import cache_page
from django.utils import timezone
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.exceptions import PermissionDenied
Expand Down Expand Up @@ -85,12 +86,27 @@ def remove_article_api_view(request, pk):
@permission_classes([AllowAny])
def get_bulletins(request):
user = request.user
data = request.query_params
post_id = data.get("post_id")
project_slug = data.get("project_slug") # maybe needs to be slug for simplicity

bulletins = Bulletin.objects.filter(
bulletin_start__lte=django.utils.timezone.now(),
bulletin_end__gte=django.utils.timezone.now(),
bulletin_start__lte=timezone.now(),
bulletin_end__gte=timezone.now(),
)

if post_id:
bulletins = bulletins.filter(Q(post_id__isnull=True) | Q(post_id=post_id))
else:
bulletins = bulletins.filter(post_id__isnull=True)

if project_slug:
bulletins = bulletins.filter(
Q(project_id__isnull=True) | Q(project__slug=project_slug)
)
else:
bulletins = bulletins.filter(project_id__isnull=True)

bulletins_viewed_by_user = []
if user and user.is_authenticated:
bulletins_viewed_by_user = [
Expand Down