Commit Graph

111 Commits

Author SHA1 Message Date
Klaas van Schelven
afd31d2263 API: datetime objects always in UTC
i.e. avoid the pain of time-conversions when 'talking with computers'.
2025-09-26 15:01:55 +02:00
Klaas van Schelven
0ca3e33e1f API: remove 'is_deleted' as a field
it's implicit(ly not so): soft-deleted items should
not be returned, and everything that's returned isn't
deleted
2025-09-26 13:53:51 +02:00
Klaas van Schelven
e60fbb2cd5 Fix invalid focus:ring-3-* classnames from migration
Tailwind 3 -> 4 migration renamed `ring` -> `ring-3`, but colors like
`ring-cyan-200` were also changed to `ring-3-cyan-200` which doesn't
actually exist.

broken in ac8e2e8cd6

See #225 (the present commit is related, but not a full fix)
2025-09-22 09:50:35 +02:00
Klaas van Schelven
4d3756f621 API: leave notes on get_object's future 2025-09-18 20:12:28 +02:00
Klaas van Schelven
018e934bdb Reference sourcemaps instruction in JS SDK page 2025-09-17 20:49:22 +02:00
Klaas van Schelven
3156f05756 Refactoring into the factory style
done while trying to fix an unrelated error; keeping this as a
stylistic change regardless
2025-09-12 11:49:32 +02:00
Klaas van Schelven
a4e84fa0a3 Add Swagger using drf-spectacular
See #146

DRF 3.16 and Django 5.2 are not in drf-spectacular's published
list of supported but here's some sources that give reason to believe
they are supported _in practice_:

* https://github.com/tfranzel/drf-spectacular/issues/1417
* https://github.com/tfranzel/drf-spectacular/issues/1414
2025-09-12 11:46:44 +02:00
Klaas van Schelven
9ad66d7b50 API: adhere to Bugsink's DB-transactional model
as per https://www.bugsink.com/blog/database-transactions/
2025-09-11 18:01:05 +02:00
Klaas van Schelven
1c0745f24f API: support expand=...
(implemented only for project.team for now, but in a generic way
2025-09-11 17:38:42 +02:00
Klaas van Schelven
07b792775a API pagination 2025-09-11 16:52:14 +02:00
Klaas van Schelven
30ae7881aa Teams & Projects API
See #146
2025-09-11 09:55:32 +02:00
Klaas van Schelven
4c2c26743e Canonical API 'skeleton': urls & views
this gives me something to look at and work with, despite
being wildly incomplete

See #146
2025-09-09 10:07:10 +02:00
Klaas van Schelven
a4ecd386b6 Support hosting at subpath
"In principle" setting `SCRIPT_NAME` is enough. The way we do this is [1] using
`FORCE_SCRIPT_NAME` (which does not depend on messing with reverse proxy
settings and [2] by deducing the correct value from `BASE_URL` (which must be
set anyway) automatically.

By works I mean: `reverse` and `{% url` pick it up from there.

However, there are subtleties / extra work:

* `STATIC_URL` is needed too b/c https://code.djangoproject.com/ticket/34028

* in many pre-existing code I just created a path manually in the html. Such
  hrefs are obviously not magically fixed for script_name. Rather than doing
  the "full rewrite" (into `{% url`) this commit just prepends the
  `script_name` in those cases. That's the way forward that will least likely
  break and it gives us something to grep for if we ever want to 'do it
  right'.

* `LOGIN_REDIRECT_URL` and `LOGIN_URL` needed to use a view-name for this to
  work (using a view-name gets revolved using the thing that introduces
  `script_name`)

Checked, no work needed:

* views (`redirect` and `HttpResponseRedirect`)
* html uses of action="..."

Fix #93
2025-09-05 22:47:22 +02:00
Klaas van Schelven
ad8a2a5e4f Tailwind 3 => 4: bg-opacity "folded into bg"
https://tailwindcss.com/docs/upgrade-guide#renamed-utilities
2025-09-04 13:20:22 +02:00
Klaas van Schelven
610e7b1c17 Tailwind 3 => 4: rounded => rounded-sm
https://tailwindcss.com/docs/upgrade-guide#renamed-utilities
2025-09-04 13:20:22 +02:00
Klaas van Schelven
ac8e2e8cd6 Tailwind 3 => 4: ring => ring-3
https://tailwindcss.com/docs/upgrade-guide#renamed-utilities
2025-09-04 13:20:22 +02:00
Klaas van Schelven
a0dc91c8c5 Push verbose_name to the model-level
at least for those fields where it is currently used.
this necessitates a patch to the migration machinery, which this commit adds

See #161
2025-08-28 16:03:27 +02:00
Klaas van Schelven
f38112f3df Pull markup out of translation files
it's bad enough that we do markup in views.py

See #161
2025-08-28 15:31:43 +02:00
Klaas van Schelven
a3cdeb9c8a Flake8 (including one breakage)
See #161
2025-08-28 15:01:05 +02:00
Klaas van Schelven
31fdf46a10 yesnomaybe translation fix
see https://code.djangoproject.com/ticket/36579

* remove workarounds; instead just provide a correctly formatted one in the .po file
* regen of .po file more generally

See #161
2025-08-28 14:17:31 +02:00
某亚瑟
2b5fb1bf67 Basically completed i18n support, and Chinese translation
Implement most Chinese text translations, adding default recognition browser language and user settings language
2025-08-02 10:25:19 +08:00
Klaas van Schelven
354af7ea0a Fix issues as reported by bandit or mark as nosec
Nothing worrying, but good to have checked this regardless
and important to have a green pipeline.

Fix #175
2025-07-30 12:16:40 +02:00
Klaas van Schelven
9b8409d8b2 Global trailing whitespace cleanup 2025-07-29 12:53:10 +02:00
Klaas van Schelven
91b99af08d project-alert edits: fix misalignments (by asking ChatGPT) 2025-07-28 22:18:02 +02:00
copilot-swe-agent[bot]
21ee428938 Add UI components to display alert backend failure status
Co-authored-by: vanschelven <223833+vanschelven@users.noreply.github.com>
2025-07-28 22:13:22 +02:00
Klaas van Schelven
2b46bfe9a1 Project-edit: redirect to list on-save 2025-07-14 16:08:30 +02:00
Klaas van Schelven
3baedcaab9 Project-delete: show success-message after delete 2025-07-07 12:48:35 +02:00
Klaas van Schelven
f21d9f989b Fix recently added UI elements to have dark mode too
the elements were added after the work on #125 was done but in a
branch w/o dark-mode, so they needed to still be fixed
2025-07-07 11:39:27 +02:00
Klaas van Schelven
d99f946665 Merge branch 'main' into feature/add_dark_theme 2025-07-07 11:08:00 +02:00
Klaas van Schelven
7b340fd8ff Hide in-progress deletions of Project & Issue from the UI
I've done a full grep on Issue.objects, Project.objects and get_object_or_404
equivelents, and applying some common sense. The goal: avoid having
confusing/half-broken pages in the UI.

On index-usage: I've decided not to update the indexes. The assumption is:
`is_deleted` items will be a tiny minority of items in general, making the
cost/benefit analysis not turn out favorably (just scanning them out as a final
step is more efficient).  Also: sqlite is able to use the correct index without
adding a special one, proof:

```
EXPLAIN QUERY PLAN SELECT [..] WHERE ("issues_issue"."project_id" = 1 AND "issues_issue"."is_muted" = (0) AND "issues_issue"."is_resolved" = (0)) ORDER BY "issues_issue"."last_seen" DESC LIMIT 250;
QUERY PLAN
`--SEARCH issues_issue USING INDEX issue_list_open (project_id=? AND is_resolved=? AND is_muted=?)

EXPLAIN QUERY PLAN SELECT [..] WHERE ("issues_issue"."project_id" = 1 AND "issues_issue"."is_muted" = (0) AND "issues_issue"."is_resolved" = (0) AND "issues_issue"."is_deleted" = 0) ORDER BY "issues_issue"."last_seen" DESC LIMIT 250;
QUERY PLAN
`--SEARCH issues_issue USING INDEX issue_list_open (project_id=? AND is_resolved=? AND is_muted=?)
```

See #139 for the 0/1 notation in the above.

(Project-indexes: not an issue, the scale is "below relevance for indexes")
2025-07-07 10:27:36 +02:00
Animesh Agrawal
72479fe982 add button for project-delete in UI
Implement delete functionality with confirmation modals for projects.

cherry picked (by Klaas) from commit 6764fbf343fb; but:

* projects only
* `delete_deferred`
* flake8

See #84 for the original PR
2025-07-04 17:33:02 +02:00
Klaas van Schelven
4900f0447e Project-deletion: slight optimization
Removes the following 2 redundant queries from the deletion process:

```
SELECT "tags_tagkey"."id" FROM "tags_tagkey" WHERE "tags_tagkey"."project_id" IN (1) ORDER BY "tags_tagkey"."project_id" ASC, "tags_tagkey"."id" ASC LIMIT 498
UPDATE "projects_project" SET "stored_event_count" = ("projects_project"."stored_event_count" - 1) WHERE "projects_project"."id" = 1
```
2025-07-03 22:04:51 +02:00
Klaas van Schelven
28b2ce0eaf Various models: .project SET_NULL => DO_NOTHING
Like e45c61d6f0, but for .project.

I originally thought `SET_NULL` would be a good way to "do stuff later", but
that's only so the degree that [1] updates are cheaper than deletes and [2]
2nd-order effects (further deletes in the dep-tree) are avoided.

Now that we have explicit Project-deletion (deps-first, delayed, properly batched)
the SET_NULL behavior is always a no-op (but with cost in queries).

As a result, in the test for project deletion (which has deletes for many
of the altered models), the following 12 queries are no longer done:

```
SELECT "projects_project"."id", [..many fields..] FROM "projects_project" WHERE "projects_project"."id" = 1
DELETE FROM "projects_projectmembership" WHERE "projects_projectmembership"."project_id" IN (1)
DELETE FROM "alerts_messagingserviceconfig" WHERE "alerts_messagingserviceconfig"."project_id" IN (1)
UPDATE "releases_release" SET "project_id" = NULL WHERE "releases_release"."project_id" IN (1)
UPDATE "issues_issue" SET "project_id" = NULL WHERE "issues_issue"."project_id" IN (1)
UPDATE "issues_grouping" SET "project_id" = NULL WHERE "issues_grouping"."project_id" IN (1)
UPDATE "events_event" SET "project_id" = NULL WHERE "events_event"."project_id" IN (1)
UPDATE "tags_tagkey" SET "project_id" = NULL WHERE "tags_tagkey"."project_id" IN (1)
UPDATE "tags_tagvalue" SET "project_id" = NULL WHERE "tags_tagvalue"."project_id" IN (1)
UPDATE "tags_eventtag" SET "project_id" = NULL WHERE "tags_eventtag"."project_id" IN (1)
UPDATE "tags_issuetag" SET "project_id" = NULL WHERE "tags_issuetag"."project_id" IN (1)
```
2025-07-03 21:49:49 +02:00
Klaas van Schelven
6b9e4d8011 Project.delete_deferred(): first version (WIP)
Implemented using a batch-wise dependency-scanner in delayed
(snappea) style.

* no real point-of-entry in the (regular, non-admin) UI yet.
* no hiding of Projects which are delete-in-progress from the UI

* lack of DRY
* some unnessary work (needed in the Issue-context, but not here)
  is still being done.

See #50
2025-07-03 21:01:28 +02:00
Fabien LEFEBVRE (d1ceward)
9cec248ad8 Add dark theme 2025-06-16 15:37:37 +02:00
Klaas van Schelven
fac5b19966 Slack Alerts
Fix #3
2025-06-10 22:00:37 +02:00
Klaas van Schelven
17fb9cc850 Remove open_issue_count from homepage; it's too expensive 2025-05-06 10:27:16 +02:00
Klaas van Schelven
6e0f1f0f54 Document team/project visibility/access design 2025-04-04 10:01:38 +02:00
Klaas van Schelven
8fc6f752cf Allow users to join their own team's projects
Fix #56

Looked into this for a while, but I think it was simply an oversight in
the logic-as-programmed; if you're part of a team, you should be able
to just click 'join' on any of that team's projects and be a project-member
2025-04-03 16:28:35 +02:00
Klaas van Schelven
427a2a341e Change tab header into "Team Projects"
It said "Other Team Projects", which has 2 ways to be read:

* "Other (Team Projects)" (the correct one)
* "(Other Team) Projects" (the incorrect one)
2025-04-03 16:23:06 +02:00
Klaas van Schelven
9e683d8c9d retention_max_event_count: in project settings form
somewhere, there's a case that such settings need a different level of
authorization; (just because you're the admin of the project doesn't mean you
should be able to do things that affect the system as a whole) but we don't
have such a level ATM.
2025-03-31 16:00:20 +02:00
Klaas van Schelven
12d7ce5629 Flake8: for migrations _just_ ignore the whitespace errors
this helps catching some _real_ errors while saving us from having to format
automatically generated code
2025-02-06 16:41:43 +01:00
Klaas van Schelven
615d2da4c8 Chache stored_event_count (on Issue and Projet)
"possibly expensive" turned out to be "actually expensive". On 'emu', with 1.5M
events, the counts take 85 and 154 ms for Project and Issue respectively;
bottlenecking our digestion to ~3 events/s.

Note: this is single-issue, single-project (presumably, the cost would be lower
for more spread-out cases)

Note on indexes: Event already has indexes for both Project & Issue (though as
the first item in a multi-column index). Without checking further: that appears
to not "magically solve counting".

This commit also optimizes the .count() on the issue-detail event list (via
Paginator).

This commit also slightly changes the value passed as `stored_event_count` to
be used for `get_random_irrelevance` to be the post-evication value. That won't
matter much in practice, but is slightly more correct IMHO.
2025-02-06 16:24:25 +01:00
Klaas van Schelven
86e8c4318b Add indexes on fields on which we order and vice versa
Triggered by issue_event_list being more than 5s on "emu" (my 1,500,000 event
test-machine). Reason: sorting those events on non-indexed field. Switching
to a field-with-index solved it.

I then analysed (grepped) for "ordering" and "order_by" and set indexes
accordingly and more or less indiscriminately (i.e. even on tables that are
assumed to have relatively few rows, such as Project & Team).
2025-02-04 21:19:24 +01:00
Klaas van Schelven
0b42d3ff1e Semi-manual squash-migrations
## Goal

Reduce the number of migrations for _fresh installs_ of Bugsink. This implies: squash as
broadly as possible.

## How?

"throw-away-and-rerun". In particular, for a given app:

* throw away the migrations from some starting point up until and including the last one.
* run "makemigrations" for that app. Django will see what's missing and just redo it
* rename to 000n_b_squashed or similar.
* manually set a `replaces` list on the migration to the just-removed migrations
* manually check dependencies; check that they are:
    * as low as possible, e.g. an FK should only depend on existence. this reduces the
      risk of circular dependencies.
    * pointing to "original migrations", i.e. not to a just-created squashed migration.
      because the squashed migrations "contain a lot" they increase the risk of circular
      dependencies.
* restore (git checkout) the thrown-away migration

## Further tips:

* "Some starting point" is often not 0000, but some higher number (see e.g. the outcome
  in the present commit). Leaving the migrations for creation of base models (Event,
  Issue, Project) in place saves you from a lot of circular dependency problems.
* Move db.sqlite3 out of the way to avoid superfluous warnings.

## RunPython worries

I grepped for RunPython in the replaced migrations, with the following results:

* phonehome's create_installation_id was copied-over to the squashed migration.
* all others where ignored, because:
    * they "do something with events", i.e. only when events are present will they have
      an effect. This means they are no-ops for _new installs_.
    * for existing installs, for any given app, they will only be missed (replaced) when
      the first replaced migration is not yet executed.

I used the following command (reading from the bottom) to establish that this means only
people that did a fresh install after 8ad6059722 (June 14, 2024), but before
c01d332e18 (July 16) _and then never did any upgrades_ would be affected. There are no
such people.

git log --name-only \
    events/migrations/0004_event_irrelevance_for_retention.py \
    issues/migrations/0004_rename_event_count_issue_digested_event_count.py \
    phonehome/migrations/0001_initial.py \
    projects/migrations/0002_initial.py \
    teams/migrations/0001_initial.py

Note that the above observation still be true for the next squashmigration (assuming
squashing starting at the same starting migrations).

## Cleanup of the replaced migrations

Django says:

> Once you’ve squashed your migration, you should then commit it alongside the
> migrations it replaces and distribute this change to all running instances of your
> application, making sure that they run migrate to store the change in their database.

Given that I'm not in control of all running instances of my application, this means the
cleanup must not happen "too soon", and only after announcing a migration path ("update
to version X before updating to version Y").

## Roads not taken

Q: Why not just do squashmigrations? A: It didn't work reliably (for me), presumably b/c
of the high number of strongly interdependant apps in combination with some RunPython.

Seen after I was mostly done, not explored seriously (yet):

* https://github.com/3YOURMIND/django-replace-migrations
* https://pypi.org/project/django-squash/
* https://django-extensions.readthedocs.io/en/latest/delete_squashed_migrations.html
2025-02-03 16:06:17 +01:00
Klaas van Schelven
0ec809cbb3 Simplify migration deps and document them 2025-02-03 14:04:44 +01:00
Klaas van Schelven
59372aba33 First version of multi-tenant setup (EE) 2025-01-29 09:04:19 +01:00
Klaas van Schelven
c6adfd7511 Remove 'slug' field from team
it was unused; probably added analogously with project (where it _is_ used)
2025-01-24 10:21:39 +01:00
Klaas van Schelven
2739422968 Generic SDK: link to 'list of issues' 2024-12-16 21:50:19 +01:00
Klaas van Schelven
28314a2683 Language icons: add to SDK page 2024-12-03 09:14:29 +01:00