From: Michael Tremer Date: Fri, 27 Feb 2026 17:28:16 +0000 (+0000) Subject: reports: Create a separate table for comments X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ddea86c14830b5a47ab94695268ed631974a30d4;p=dbl.git reports: Create a separate table for comments That way, we may have multiple comments on a report. Signed-off-by: Michael Tremer --- diff --git a/src/database.sql b/src/database.sql index 3bab542..6056116 100644 --- a/src/database.sql +++ b/src/database.sql @@ -2,7 +2,7 @@ -- PostgreSQL database dump -- -\restrict ZeEQIdrFtt8lZBacF2YikE2NldALCViC5aH05xXVTjf5DSP1ITtfKT3w9cYoumo +\restrict 97QQcysG1ebcqz7Y7sN4EJb44D05KafpruWuQjJfkHpdoO21URCTKpqX39K4fwP -- Dumped from database version 17.7 (Debian 17.7-0+deb13u1) -- Dumped by pg_dump version 17.7 (Debian 17.7-0+deb13u1) @@ -269,6 +269,22 @@ CREATE SEQUENCE public.nameservers_id_seq ALTER SEQUENCE public.nameservers_id_seq OWNED BY public.nameservers.id; +-- +-- Name: report_comments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.report_comments ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + report_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + created_by text, + deleted_at timestamp with time zone, + deleted_by text, + comment text DEFAULT ''::text NOT NULL, + points integer DEFAULT 0 NOT NULL +); + + -- -- Name: reports; Type: TABLE; Schema: public; Owner: - -- @@ -281,7 +297,6 @@ CREATE TABLE public.reports ( reported_by text NOT NULL, closed_at timestamp with time zone, closed_by text, - comment text DEFAULT ''::text NOT NULL, block boolean DEFAULT true, accepted boolean DEFAULT false ); @@ -453,6 +468,14 @@ ALTER TABLE ONLY public.nameservers ADD CONSTRAINT nameservers_pkey PRIMARY KEY (id); +-- +-- Name: report_comments report_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.report_comments + ADD CONSTRAINT report_comments_pkey PRIMARY KEY (id); + + -- -- Name: reports reports_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -554,6 +577,13 @@ CREATE UNIQUE INDEX list_stats_unique ON public.list_stats USING btree (list_id, CREATE UNIQUE INDEX lists_unique ON public.lists USING btree (slug) WHERE (deleted_at IS NULL); +-- +-- Name: report_comments_report_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX report_comments_report_id ON public.report_comments USING btree (report_id) WHERE (deleted_at IS NULL); + + -- -- Name: reports_open; Type: INDEX; Schema: public; Owner: - -- @@ -615,6 +645,14 @@ ALTER TABLE ONLY public.list_stats ADD CONSTRAINT list_stats_list_id FOREIGN KEY (list_id) REFERENCES public.lists(id); +-- +-- Name: report_comments report_comments_report_id; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.report_comments + ADD CONSTRAINT report_comments_report_id FOREIGN KEY (report_id) REFERENCES public.reports(id); + + -- -- Name: reports reports_list_id; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -643,5 +681,5 @@ ALTER TABLE ONLY public.sources -- PostgreSQL database dump complete -- -\unrestrict ZeEQIdrFtt8lZBacF2YikE2NldALCViC5aH05xXVTjf5DSP1ITtfKT3w9cYoumo +\unrestrict 97QQcysG1ebcqz7Y7sN4EJb44D05KafpruWuQjJfkHpdoO21URCTKpqX39K4fwP diff --git a/src/dbl/api/reports.py b/src/dbl/api/reports.py index cb13a11..8f0e340 100644 --- a/src/dbl/api/reports.py +++ b/src/dbl/api/reports.py @@ -76,5 +76,15 @@ async def close_report( # Send 204 return fastapi.Response(status_code=fastapi.status.HTTP_204_NO_CONTENT) +""" + Comments +""" + +@router.get("/{id}/comments") +async def get_comments( + report: reports.Report = fastapi.Depends(get_report_from_path) +) -> list[reports.ReportComment]: + return [comment async for comment in report.get_comments()] + # Include our endpoints app.include_router(router) diff --git a/src/dbl/reports.py b/src/dbl/reports.py index 534ecca..e4bf392 100644 --- a/src/dbl/reports.py +++ b/src/dbl/reports.py @@ -64,7 +64,7 @@ class Reports(object): return await self.backend.db.fetch_one(stmt) - async def create(self, **kwargs): + async def create(self, comment=None, **kwargs): """ Creates a new report """ @@ -75,6 +75,12 @@ class Reports(object): # Manifest the object in the database immediately to assign the ID await self.backend.db.flush_and_refresh(report) + # Post the comment + if comment: + await report.comment( + comment=comment, reporter=report.reported_by, notify=False, + ) + # Increment the counter of the list report.list.pending_reports += 1 @@ -223,9 +229,6 @@ class Report(sqlmodel.SQLModel, database.BackendMixin, table=True): # Closed By closed_by : str | None - # Comment - comment : str = "" - # Block? block : bool = True @@ -396,3 +399,100 @@ class Report(sqlmodel.SQLModel, database.BackendMixin, table=True): @property def url(self): return "https://www.ipfire.org/dbl/reports/%s" % self.id + + # Comments + + def get_comments(self): + """ + Returns all (non-deleted) comments to this report + """ + return self.backend.db.fetch( + sqlmodel + .select( + ReportComment, + ) + .where( + # Must match the report + ReportComment.report == self, + + # Must not be deleted + ReportComment.deleted_at == None, + ) + .order_by( + ReportComment.created_at, + ) + ) + + async def comment(self, comment, reporter, points=0, notify=True): + """ + Posts a new comment to this report + """ + # Write the comment to the database + comment = await self.backend.db.insert( + ReportComment, + report = self, + created_by = reporter, + comment = comment, + points = points, + ) + + # Send a notification about this comment + if notify: + pass # XXX TODO + + return comment + + +class ReportComment(sqlmodel.SQLModel, database.BackendMixin, table=True): + __tablename__ = "report_comments" + + def __str__(self): + return "%s" % self.id + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.id == other.id + + return NotImplemented + + def __lt__(self, other): + if isinstance(other, self.__class__): + return (self.report, self.created_at) < (other.report, other.created_at) + + return NotImplemented + + # ID + id: uuid.UUID = sqlmodel.Field( + primary_key=True, default=sqlmodel.func.gen_random_uuid(), + ) + + # Report ID + report_id: uuid.UUID = sqlmodel.Field(foreign_key="reports.id", exclude=True) + + # report + report: "Report" = sqlmodel.Relationship( + sa_relationship_kwargs = { + "lazy" : "joined", + "innerjoin" : True, + }, + ) + + # Created At + created_at: datetime.datetime = sqlmodel.Field( + sa_column_kwargs = {"server_default" : sqlmodel.text("CURRENT_TIMESTAMP")} + ) + + # Created By + created_by: str + + # Deleted At + deleted_at: datetime.datetime | None = sqlmodel.Field(exclude=True) + + # Deleted By + deleted_by: str | None = sqlmodel.Field(exclude=True) + + # Comment + comment: str = "" + + # Points + points: int = 0