Skip to content
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
231f5c2
added model, command , algolia , sponsors
abhayymishraa Jan 27, 2025
6fddf74
Enhanced command and removed extra indexed from data
abhayymishraa Jan 28, 2025
70a7dd2
Merge branch 'main' into feat/sponsors
abhayymishraa Jan 28, 2025
2262c9d
Added testcases
abhayymishraa Jan 28, 2025
1260440
verified commit
abhayymishraaa Jan 31, 2025
ad7584e
Merge branch 'main' into feat/sponsors
abhayymishraaa Jan 31, 2025
8d3b4f2
verified commit with chnages
abhayymishraa Feb 2, 2025
2df44d4
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 3, 2025
fa9459a
fixed importing
abhayymishraa Feb 3, 2025
f2dabbe
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 3, 2025
d6fe486
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 3, 2025
76e9010
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 4, 2025
5375a5b
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 4, 2025
dde2f69
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 13, 2025
9d0c001
resolved conflict
abhayymishraa Feb 13, 2025
16a758b
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 14, 2025
fc41e3f
fixed testcase
abhayymishraa Feb 14, 2025
2de5fe4
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 20, 2025
c004ae2
fixed makefile
abhayymishraa Feb 20, 2025
965a125
Merge branch 'main' into feat/sponsors
abhayymishraa Feb 22, 2025
874aedc
pre-commit after resolve conflict
abhayymishraa Feb 22, 2025
354149f
migrations
abhayymishraa Feb 22, 2025
8d29ff2
Merge branch 'main' into feat/sponsors
abhayymishraa Mar 3, 2025
3346deb
pre-commmit
abhayymishraa Mar 3, 2025
55103aa
Merge branch 'main' into feat/sponsors
abhayymishraa Mar 3, 2025
689d641
fixed sonaQube warning
abhayymishraa Mar 4, 2025
ad4b842
refractor code removed algolia
abhayymishraa Mar 4, 2025
2afa6e3
updated testcase
abhayymishraa Mar 4, 2025
ef34968
Merge branch 'main' into feat/sponsors
abhayymishraa Mar 4, 2025
818aa74
added moving Logo compoennt
abhayymishraa Mar 4, 2025
7db1f02
backned test fix
abhayymishraa Mar 4, 2025
cfc555d
frontend test case fix
abhayymishraa Mar 4, 2025
2c3eec3
pre-commit
abhayymishraa Mar 4, 2025
930c075
Merge branch 'main' into feat/sponsors
abhayymishraa Mar 4, 2025
5d06b86
fix e2e case
abhayymishraa Mar 4, 2025
b65d610
fix bug
abhayymishraa Mar 4, 2025
fdda190
improvements
abhayymishraa Mar 4, 2025
35fa0bf
Merge branch 'main' into pr/abhayymishraa/630
arkid15r Mar 5, 2025
370e32b
Update code
arkid15r Mar 5, 2025
d9a8317
changes
abhayymishraa Mar 5, 2025
b45db63
increase testcase timing
abhayymishraa Mar 5, 2025
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
7 changes: 6 additions & 1 deletion backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ owasp-update-events:
@echo "Getting OWASP events data"
@CMD="python manage.py owasp_update_events" $(MAKE) exec-backend-command

owasp-update-sponsors:
@echo "Getting OWASP sponsors data"
@CMD="python manage.py owasp_update_sponsors" $(MAKE) exec-backend-command

purge-data:
@CMD="python manage.py purge_data" $(MAKE) exec-backend-command

Expand Down Expand Up @@ -136,4 +140,5 @@ update-data: \
owasp-scrape-projects \
github-update-project-related-repositories \
owasp-aggregate-projects \
owasp-update-events
owasp-update-events \
owasp-update-sponsors
32 changes: 32 additions & 0 deletions backend/apps/owasp/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from apps.owasp.models.project_health_metrics import ProjectHealthMetrics
from apps.owasp.models.project_health_requirements import ProjectHealthRequirements
from apps.owasp.models.snapshot import Snapshot
from apps.owasp.models.sponsor import Sponsor


class GenericEntityAdminMixin:
Expand Down Expand Up @@ -141,10 +142,41 @@ class SnapshotAdmin(admin.ModelAdmin):
)


class SponsorAdmin(admin.ModelAdmin):
"""Admin configuration for Sponsor model."""

list_display = (
"name",
"sort_name",
"sponsor_type",
"is_member",
"member_type",
)

search_fields = (
"name",
"sort_name",
"description",
)

list_filter = (
"sponsor_type",
"is_member",
"member_type",
)

fieldsets = (
("Basic Information", {"fields": ("name", "sort_name", "description")}),
("URLs and Images", {"fields": ("url", "job_url", "image_url")}),
("Status", {"fields": ("is_member", "member_type", "sponsor_type")}),
)


admin.site.register(Chapter, ChapterAdmin)
admin.site.register(Committee, CommitteeAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(Project, ProjectAdmin)
admin.site.register(ProjectHealthMetrics)
admin.site.register(ProjectHealthRequirements)
admin.site.register(Snapshot, SnapshotAdmin)
admin.site.register(Sponsor, SponsorAdmin)
15 changes: 15 additions & 0 deletions backend/apps/owasp/graphql/nodes/sponsor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""OWASP sponsors GraphQL node."""

from apps.common.graphql.nodes import BaseNode
from apps.owasp.models.sponsor import Sponsor


class SponsorNode(BaseNode):
"""Sponsor node."""

class Meta:
model = Sponsor
fields = (
"image_url",
"name",
)
3 changes: 2 additions & 1 deletion backend/apps/owasp/graphql/queries/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from .event import EventQuery
from .project import ProjectQuery
from .snapshot import SnapshotQuery
from .sponsor import SponsorQuery
from .stats import StatsQuery


class OwaspQuery(
ChapterQuery, CommitteeQuery, EventQuery, ProjectQuery, SnapshotQuery, StatsQuery
ChapterQuery, CommitteeQuery, EventQuery, ProjectQuery, SnapshotQuery, SponsorQuery, StatsQuery
):
"""OWASP queries."""
17 changes: 17 additions & 0 deletions backend/apps/owasp/graphql/queries/sponsor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""OWASP sponsors GraphQL queries."""

import graphene

from apps.common.graphql.queries import BaseQuery
from apps.owasp.graphql.nodes.sponsor import SponsorNode
from apps.owasp.models.sponsor import Sponsor


class SponsorQuery(BaseQuery):
"""Sponsor queries."""

sponsors = graphene.List(SponsorNode)

def resolve_sponsors(root, info):
"""Resolve sponsors."""
return Sponsor.objects.all()
Comment on lines +10 to +17
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement pagination and filtering for better performance.

The current implementation fetches all sponsors without pagination or filtering. This could lead to performance issues if there are many sponsors.

Consider implementing pagination and filtering:

class SponsorQuery(BaseQuery):
    """Sponsor queries."""

-   sponsors = graphene.List(SponsorNode)
+   sponsors = graphene.List(
+       SponsorNode,
+       sponsor_type=graphene.String(required=False),
+       is_member=graphene.Boolean(required=False),
+       limit=graphene.Int(required=False, default_value=10),
+       offset=graphene.Int(required=False, default_value=0),
+   )

-   def resolve_sponsors(root, info):
+   def resolve_sponsors(root, info, sponsor_type=None, is_member=None, limit=10, offset=0):
        """Resolve sponsors."""
-       return Sponsor.objects.all()
+       query = Sponsor.objects.all()
+       
+       if sponsor_type:
+           query = query.filter(sponsor_type=sponsor_type)
+           
+       if is_member is not None:
+           query = query.filter(is_member=is_member)
+           
+       return query[offset:offset+limit]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class SponsorQuery(BaseQuery):
"""Sponsor queries."""
sponsors = graphene.List(SponsorNode)
def resolve_sponsors(root, info):
"""Resolve sponsors."""
return Sponsor.objects.all()
class SponsorQuery(BaseQuery):
"""Sponsor queries."""
sponsors = graphene.List(
SponsorNode,
sponsor_type=graphene.String(required=False),
is_member=graphene.Boolean(required=False),
limit=graphene.Int(required=False, default_value=10),
offset=graphene.Int(required=False, default_value=0),
)
def resolve_sponsors(root, info, sponsor_type=None, is_member=None, limit=10, offset=0):
"""Resolve sponsors."""
query = Sponsor.objects.all()
if sponsor_type:
query = query.filter(sponsor_type=sponsor_type)
if is_member is not None:
query = query.filter(is_member=is_member)
return query[offset:offset+limit]

20 changes: 20 additions & 0 deletions backend/apps/owasp/management/commands/owasp_update_sponsors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""A command to add OWASP sponsors data."""

import yaml
from django.core.management.base import BaseCommand

from apps.github.utils import get_repository_file_content
from apps.owasp.models.sponsor import Sponsor


class Command(BaseCommand):
help = "Import sponsors from the provided YAML file"

def handle(self, *args, **kwargs):
sponsors = yaml.safe_load(
get_repository_file_content(
"https://raw.githubusercontent.com/OWASP/owasp.github.io/main/_data/corp_members.yml"
).expandtabs()
)

Sponsor.bulk_save([Sponsor.update_data(sponsor) for sponsor in sponsors])
85 changes: 85 additions & 0 deletions backend/apps/owasp/migrations/0022_sponsor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Generated by Django 5.1.5 on 2025-03-04 15:28

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("owasp", "0021_alter_snapshot_key"),
]

operations = [
migrations.CreateModel(
name="Sponsor",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("nest_created_at", models.DateTimeField(auto_now_add=True)),
("nest_updated_at", models.DateTimeField(auto_now=True)),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"key",
models.CharField(max_length=100, unique=True, verbose_name="Key"),
),
("name", models.CharField(max_length=255, verbose_name="Name")),
(
"sort_name",
models.CharField(max_length=255, verbose_name="Sort Name"),
),
("url", models.URLField(blank=True, verbose_name="Website URL")),
("job_url", models.URLField(blank=True, verbose_name="Job URL")),
(
"image_url",
models.CharField(blank=True, max_length=255, verbose_name="Image Path"),
),
(
"is_member",
models.BooleanField(default=False, verbose_name="Is Corporate Sponsor"),
),
(
"member_type",
models.CharField(
blank=True,
choices=[
("Platinum", "Platinum"),
("Gold", "Gold"),
("Silver", "Silver"),
],
default="Silver",
max_length=20,
verbose_name="Member Type",
),
),
(
"sponsor_type",
models.CharField(
choices=[
("Diamond", "Diamond"),
("Platinum", "Platinum"),
("Gold", "Gold"),
("Silver", "Silver"),
("Supporter", "Supporter"),
("Not a Sponsor", "Not Sponsor"),
],
default="Not a Sponsor",
max_length=20,
verbose_name="Sponsor Type",
),
),
],
options={
"verbose_name_plural": "Sponsors",
"db_table": "owasp_sponsors",
},
),
]
131 changes: 131 additions & 0 deletions backend/apps/owasp/models/sponsor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""OWASP app sponsor models."""

from django.db import models

from apps.common.models import BulkSaveModel, TimestampedModel
from apps.common.utils import slugify
from apps.github.utils import normalize_url


class Sponsor(BulkSaveModel, TimestampedModel):
"""Sponsor model."""

objects = models.Manager()

class Meta:
db_table = "owasp_sponsors"
verbose_name_plural = "Sponsors"

class SponsorType(models.TextChoices):
DIAMOND = "Diamond"
PLATINUM = "Platinum"
GOLD = "Gold"
SILVER = "Silver"
SUPPORTER = "Supporter"
NOT_SPONSOR = "Not a Sponsor"

class MemberType(models.TextChoices):
PLATINUM = "Platinum"
GOLD = "Gold"
SILVER = "Silver"

# Basic information
description = models.TextField(verbose_name="Description", blank=True)
key = models.CharField(verbose_name="Key", max_length=100, unique=True)
name = models.CharField(verbose_name="Name", max_length=255)
sort_name = models.CharField(verbose_name="Sort Name", max_length=255)

# URLs and images
url = models.URLField(verbose_name="Website URL", blank=True)
job_url = models.URLField(verbose_name="Job URL", blank=True)
image_url = models.CharField(verbose_name="Image Path", max_length=255, blank=True)

# Status fields
is_member = models.BooleanField(verbose_name="Is Corporate Sponsor", default=False)
member_type = models.CharField(
verbose_name="Member Type",
max_length=20,
choices=MemberType.choices,
default=MemberType.SILVER,
blank=True,
)
sponsor_type = models.CharField(
verbose_name="Sponsor Type",
max_length=20,
choices=SponsorType.choices,
default=SponsorType.NOT_SPONSOR,
)

def __str__(self):
"""Sponsor human readable representation."""
return f"{self.name}"

@property
def readable_member_type(self):
"""Get human-readable member type."""
return self.MemberType(self.member_type).label

@property
def readable_sponsor_type(self):
"""Get human-readable sponsor type."""
return self.SponsorType(self.sponsor_type).label

@staticmethod
def bulk_save(sponsors, fields=None):
"""Bulk save sponsors."""
BulkSaveModel.bulk_save(Sponsor, sponsors, fields=fields)

@staticmethod
def update_data(data, save=True):
"""Update sponsor data."""
key = slugify(data["name"])
try:
sponsor = Sponsor.objects.get(key=key)
except Sponsor.DoesNotExist:
sponsor = Sponsor(key=key)

sponsor.from_dict(data)

if save:
sponsor.save()

return sponsor

def from_dict(self, data):
"""Update instance based on the dict data."""
image_path = data.get("image", "").lstrip("/")
image_url = f"https://raw.githubusercontent.com/OWASP/owasp.github.io/main/{image_path}"

member_type_mapping = {
2: self.MemberType.PLATINUM,
3: self.MemberType.GOLD,
4: self.MemberType.SILVER,
}
sponsor_type_mapping = {
-1: self.SponsorType.NOT_SPONSOR,
1: self.SponsorType.DIAMOND,
2: self.SponsorType.PLATINUM,
3: self.SponsorType.GOLD,
4: self.SponsorType.SILVER,
5: self.SponsorType.SUPPORTER,
}

member_key = data.get("membertype", 4)
sponsor_key = data.get("sponsor", -1)
member_type_label = member_type_mapping.get(member_key, self.MemberType.SILVER)
sponsor_type_label = sponsor_type_mapping.get(sponsor_key, self.SponsorType.NOT_SPONSOR)

fields = {
"description": data.get("description", ""),
"image_url": image_url,
"is_member": data.get("member", False),
"job_url": normalize_url(data.get("job_url", "")) or "",
"member_type": member_type_label,
"name": data["name"],
"sort_name": data.get("sortname", ""),
"sponsor_type": sponsor_type_label,
"url": normalize_url(data.get("url", "")) or "",
}

for key, value in fields.items():
setattr(self, key, value)
Loading
Loading