Skip to content
Open
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
0cad79c
feat: add GraphQL API for snapshot subscription management
HarshitVerma109 Jun 26, 2026
0f4f87b
feat: add ProjectSubscriptionPreference model and update GraphQL API
HarshitVerma109 Jul 3, 2026
9cb8cf3
update code
HarshitVerma109 Jul 4, 2026
f774a5f
update tests
HarshitVerma109 Jul 4, 2026
bfed2f9
fix issues
HarshitVerma109 Jul 4, 2026
7481c1d
Add support for multiple subscriptions
HarshitVerma109 Jul 21, 2026
9f55a62
update code
HarshitVerma109 Jul 22, 2026
5aab13d
resolve conflicts
HarshitVerma109 Jul 22, 2026
1367cfb
fix issues
HarshitVerma109 Jul 22, 2026
78be5c6
Merge remote-tracking branch 'upstream/feature/community-snapshots' i…
HarshitVerma109 Jul 22, 2026
9b7a018
fix sonar issue
HarshitVerma109 Jul 23, 2026
b3608f3
update code
HarshitVerma109 Jul 24, 2026
a23c2d6
fix comment
HarshitVerma109 Jul 24, 2026
65635ad
fix comments
HarshitVerma109 Jul 24, 2026
37db804
feat: add settings page for snapshot subscription preferences
HarshitVerma109 Jul 4, 2026
13597eb
update tests
HarshitVerma109 Jul 4, 2026
7de6d3c
fix sonar issues
HarshitVerma109 Jul 4, 2026
2f1eeb1
fix: issues
HarshitVerma109 Jul 4, 2026
3253186
fix: sonar issue
HarshitVerma109 Jul 4, 2026
af9d7d0
fix comments
HarshitVerma109 Jul 8, 2026
5d60e40
fix comment
HarshitVerma109 Jul 8, 2026
b7fd6f0
update code
HarshitVerma109 Jul 8, 2026
c52062f
fix buttons
HarshitVerma109 Jul 9, 2026
8d560e5
fix button
HarshitVerma109 Jul 9, 2026
8a2a8ed
update code
HarshitVerma109 Jul 25, 2026
4fad0f1
fix sonar issue
HarshitVerma109 Jul 25, 2026
9dd24a4
feat: add email service abstraction layer
HarshitVerma109 Jul 12, 2026
f9f3a3b
update code
HarshitVerma109 Aug 2, 2026
b53c355
fix code
HarshitVerma109 Aug 2, 2026
4dfc9b5
Merge branch 'feature/snapshot-subscription-graphql' into feat/snapsh…
HarshitVerma109 Aug 2, 2026
f525f0c
update code
HarshitVerma109 Aug 4, 2026
cc409ad
fix issues
HarshitVerma109 Aug 4, 2026
ad4aa04
update code
HarshitVerma109 Aug 5, 2026
dcf4412
update code
HarshitVerma109 Aug 6, 2026
1147724
Merge branch 'feature/snapshot-subscription-graphql' into feat/snapsh…
HarshitVerma109 Aug 6, 2026
35ece3f
update code
HarshitVerma109 Aug 6, 2026
d383fed
Merge branch 'feat/snapshot-subscription-frontend' into feat/email-se…
HarshitVerma109 Aug 6, 2026
43d9a81
update code
HarshitVerma109 Aug 10, 2026
8424625
fix code
HarshitVerma109 Aug 10, 2026
8dc91c3
update code
HarshitVerma109 Aug 10, 2026
82f392d
update code
HarshitVerma109 Aug 10, 2026
156df98
update code
HarshitVerma109 Aug 10, 2026
5f8549b
fix issues
HarshitVerma109 Aug 10, 2026
3ca5a52
Merge branch 'feature/snapshot-subscription-graphql' into feat/snapsh…
HarshitVerma109 Aug 10, 2026
f2cef1c
fix issues
HarshitVerma109 Aug 11, 2026
193b8d6
Merge branch 'feature/snapshot-subscription-graphql' into feat/snapsh…
HarshitVerma109 Aug 11, 2026
ca54835
update code
HarshitVerma109 Aug 11, 2026
4b41651
fix issues
HarshitVerma109 Aug 11, 2026
c2a4084
update code
HarshitVerma109 Aug 11, 2026
5d5c665
fix sonar issue
HarshitVerma109 Aug 11, 2026
1f91b6f
update code
HarshitVerma109 Aug 12, 2026
fb80c6e
update code
HarshitVerma109 Aug 12, 2026
2d012dd
fix issue
HarshitVerma109 Aug 12, 2026
c75fbae
Merge branch 'feature/snapshot-subscription-graphql' into feat/snapsh…
HarshitVerma109 Aug 12, 2026
ea25297
update code
HarshitVerma109 Aug 12, 2026
88b3721
fix issue
HarshitVerma109 Aug 12, 2026
d9703a1
Merge branch 'feat/snapshot-subscription-frontend' into feat/email-se…
HarshitVerma109 Aug 13, 2026
2bf8b4d
Snapshot subscription backend
HarshitVerma109 Aug 17, 2026
a867959
update code
HarshitVerma109 Aug 17, 2026
8e2855c
fix issues
HarshitVerma109 Aug 17, 2026
9ac7dc8
Snapshot subscription frontend
HarshitVerma109 Aug 18, 2026
84f3738
fix issue
HarshitVerma109 Aug 18, 2026
efbac85
update code
HarshitVerma109 Aug 18, 2026
1cfc3e3
Merge branch 'feat/snapshot-subscription-frontend' into feat/email-se…
HarshitVerma109 Aug 18, 2026
2827bd5
update code
HarshitVerma109 Aug 27, 2026
e1fdcef
fix issues
HarshitVerma109 Aug 27, 2026
08f308e
fix issue
HarshitVerma109 Aug 27, 2026
d9cb9f6
update code
HarshitVerma109 Aug 27, 2026
7df7921
feat: add EmailLog model
HarshitVerma109 Aug 30, 2026
7d971fd
update code
HarshitVerma109 Aug 30, 2026
b04940c
Merge branch 'feat/email-service-abstraction' into feat/email-log-model
HarshitVerma109 Aug 30, 2026
6e02bc2
update code
HarshitVerma109 Aug 30, 2026
4adf32f
update code
HarshitVerma109 Sep 1, 2026
3f8e3fb
Merge branch 'feat/email-service-abstraction' into feat/email-log-model
HarshitVerma109 Sep 1, 2026
173dd95
update code
HarshitVerma109 Sep 4, 2026
428007a
Merge branch 'feat/email-service-abstraction' into feat/email-log-model
HarshitVerma109 Sep 4, 2026
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
Binary file modified backend/data/nest.dump
Binary file not shown.
12 changes: 6 additions & 6 deletions backend/make/apps/owasp.mk
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ owasp-create-project-metadata-file:
@echo "Generating metadata"
@CMD="python manage.py owasp_create_project_metadata_file $(entity_key)" $(MAKE) backend-exec-command

SNAPSHOT_FREQUENCY ?= weekly

owasp-create-snapshot:
@echo "Creating OWASP community snapshot"
@CMD="python manage.py owasp_create_snapshot --frequency $(SNAPSHOT_FREQUENCY)" $(MAKE) exec-backend-command

owasp-enrich-chapters:
@echo "Enriching OWASP chapters"
@CMD="python manage.py owasp_enrich_chapters" $(MAKE) backend-exec-command
Expand Down Expand Up @@ -58,12 +64,6 @@ owasp-generate-community-snapshot-video:
nest-snapshot-video \
python manage.py owasp_generate_community_snapshot_video $(snapshot_key) /home/owasp/generated_videos

SNAPSHOT_FREQUENCY ?= weekly

owasp-create-snapshot:
@echo "Creating OWASP community snapshot"
@CMD="python manage.py owasp_create_snapshot --frequency $(SNAPSHOT_FREQUENCY)" $(MAKE) exec-backend-command

owasp-process-snapshots:
@echo "Processing OWASP snapshots"
@CMD="python manage.py owasp_process_snapshots" $(MAKE) backend-exec-command
Expand Down
1 change: 1 addition & 0 deletions backend/src/apps/owasp/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .board_of_directors import BoardOfDirectorsAdmin
from .chapter import ChapterAdmin
from .committee import CommitteeAdmin
from .email_log import EmailLogAdmin
from .entity_channel import EntityChannelAdmin
from .entity_member import EntityMemberAdmin
from .event import EventAdmin
Expand Down
32 changes: 32 additions & 0 deletions backend/src/apps/owasp/admin/email_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Admin registration for EmailLog model."""

from django.contrib import admin

from apps.owasp.models.email_log import EmailLog


class EmailLogAdmin(admin.ModelAdmin):
"""Admin for EmailLog model."""

list_display = ("get_user", "snapshot", "status", "created_at")
list_filter = ("status", "created_at")
search_fields = ("snapshot_subscription__user__email",)
readonly_fields = (
"snapshot_subscription",
"snapshot",
"status",
"error_message",
"created_at",
)

def has_add_permission(self, request):
"""Prevent manual creation of email logs."""
return False

@admin.display(description="User")
def get_user(self, obj):
"""Return the user from the subscription."""
return obj.snapshot_subscription.user if obj.snapshot_subscription else "—"


admin.site.register(EmailLog, EmailLogAdmin)
103 changes: 96 additions & 7 deletions backend/src/apps/owasp/admin/snapshot_subscription.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,109 @@
"""Admin registration for SnapshotSubscription model."""

from django import forms
from django.contrib import admin
from django.core.exceptions import ValidationError

from apps.owasp.models.snapshot_subscription import SnapshotSubscription


class SnapshotSubscriptionAdminForm(forms.ModelForm):
"""Custom form for SnapshotSubscription admin to validate duplicates with M2M."""

class Meta:
"""Meta options."""

model = SnapshotSubscription
fields = (
"user",
"name",
"frequency",
"is_active",
"include_chapters",
"include_events",
"include_issues",
"include_posts",
"include_projects",
"include_pull_requests",
"include_releases",
"include_users",
"subscribed_projects",
"subscribed_chapters",
"subscribed_committees",
)

def clean(self):
"""Validate form data, including M2M fields, to check for duplicates."""
if self.instance:
self.instance._is_admin_form = True # noqa: SLF001
cleaned_data = super().clean()

user = cleaned_data.get("user")
if not user:
return cleaned_data

duplicate_found = SnapshotSubscription.check_duplicate_setup(
user=user,
frequency=cleaned_data.get("frequency"),
include_chapters=cleaned_data.get("include_chapters"),
include_events=cleaned_data.get("include_events"),
include_issues=cleaned_data.get("include_issues"),
include_posts=cleaned_data.get("include_posts"),
include_projects=cleaned_data.get("include_projects"),
include_pull_requests=cleaned_data.get("include_pull_requests"),
include_releases=cleaned_data.get("include_releases"),
include_users=cleaned_data.get("include_users"),
entity_ids={
"projects": [p.pk for p in (cleaned_data.get("subscribed_projects") or [])],
"chapters": [c.pk for c in (cleaned_data.get("subscribed_chapters") or [])],
"committees": [c.pk for c in (cleaned_data.get("subscribed_committees") or [])],
},
exclude_pk=self.instance.pk if self.instance else None,
)

toggles = [
cleaned_data.get("include_chapters"),
cleaned_data.get("include_events"),
cleaned_data.get("include_issues"),
cleaned_data.get("include_posts"),
cleaned_data.get("include_projects"),
cleaned_data.get("include_pull_requests"),
cleaned_data.get("include_releases"),
cleaned_data.get("include_users"),
]
has_entities = bool(
cleaned_data.get("subscribed_projects")
or cleaned_data.get("subscribed_chapters")
or cleaned_data.get("subscribed_committees")
)

if not any(toggles) and not has_entities:
msg = "Your subscription cannot be empty. Please choose something to follow."
raise ValidationError(msg)

if duplicate_found:
msg = "A subscription with the same configuration already exists."
raise ValidationError(msg)

return cleaned_data


class SnapshotSubscriptionAdmin(admin.ModelAdmin):
"""Admin for SnapshotSubscription model."""

list_display = ("user", "frequency", "is_active", "created_at", "updated_at")
form = SnapshotSubscriptionAdminForm

list_display = ("user", "name", "frequency", "is_active", "created_at", "updated_at")
list_filter = ("frequency", "is_active", "created_at")
search_fields = ("user__email", "user__username")
search_fields = ("user__email", "user__username", "name")
raw_id_fields = ("user",)
readonly_fields = ("unsubscribe_token", "created_at", "updated_at")
autocomplete_fields = ("subscribed_projects", "subscribed_chapters")
autocomplete_fields = ("subscribed_projects", "subscribed_chapters", "subscribed_committees")

fieldsets = (
(None, {"fields": ("user", "frequency", "is_active")}),
(None, {"fields": ("user", "name", "frequency", "is_active")}),
(
"Content Preferences",
"Content Toggles",
{
"fields": (
"include_chapters",
Expand All @@ -33,9 +118,13 @@ class SnapshotSubscriptionAdmin(admin.ModelAdmin):
},
),
(
"Subscription Filters",
"Subscribed Entities",
{
"fields": ("subscribed_projects", "subscribed_chapters"),
"fields": (
"subscribed_projects",
"subscribed_chapters",
"subscribed_committees",
),
},
),
(
Expand Down
10 changes: 10 additions & 0 deletions backend/src/apps/owasp/api/internal/mutations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""OWASP app mutations."""

import strawberry

from .snapshot_subscription import SnapshotSubscriptionMutations


@strawberry.type
class OwaspMutations(SnapshotSubscriptionMutations):
"""OWASP mutations."""
Loading
Loading