Skip to content

Commit

Permalink
Trademark fetching and results display (OWASP-BLT#3257)
Browse files Browse the repository at this point in the history
* Trademark fetching and results display

* Store trademark data in models

* Command

* Merge migrations

* Migration fix

---------

Co-authored-by: DonnieBLT <[email protected]>
  • Loading branch information
SahilDhillon21 and DonnieBLT authored Jan 26, 2025
1 parent a48685c commit 6ba769a
Show file tree
Hide file tree
Showing 8 changed files with 494 additions and 2 deletions.
4 changes: 4 additions & 0 deletions website/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
SuggestionVotes,
Tag,
TimeLog,
Trademark,
TrademarkOwner,
Transaction,
UserProfile,
Wallet,
Expand Down Expand Up @@ -494,3 +496,5 @@ class PostAdmin(admin.ModelAdmin):
admin.site.register(Activity)
admin.site.register(PRAnalysisReport)
admin.site.register(Post, PostAdmin)
admin.site.register(Trademark)
admin.site.register(TrademarkOwner)
109 changes: 109 additions & 0 deletions website/management/commands/fetch_trademarks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import time

import requests
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone

from website.models import Organization, Trademark, TrademarkOwner


class Command(BaseCommand):
help = "Fetch trademark information for organizations and store it in the database"

def handle(self, *args, **kwargs):
organizations = Organization.objects.all()

for organization in organizations:
name = organization.name
retries = 3 # Number of retries
while retries > 0:
try:
# Logging start of data fetching
self.stdout.write(self.style.NOTICE(f"Starting data fetch for organization: {name}"))

# Fetch trademark data
url = "https://uspto-trademark.p.rapidapi.com/v1/batchTrademarkSearch/"
initial_payload = {
"keywords": f' ["{name}"]',
"start_index": "0",
}
headers = {
"x-rapidapi-key": f"{settings.USPTO_API}",
"x-rapidapi-host": "uspto-trademark.p.rapidapi.com",
"Content-Type": "application/x-www-form-urlencoded",
}
response = requests.post(url, data=initial_payload, headers=headers)
response.raise_for_status()
response_json = response.json()

# The initial call returns a scroll_id, which is then used to obtain pagination results
scroll_id = response_json.get("scroll_id")
pagination_payload = {
"keywords": f' ["{name}"]',
"start_index": "0",
"scroll_id": scroll_id,
}
response = requests.post(url, data=pagination_payload, headers=headers)
response.raise_for_status()
results = response.json().get("results")

# Store trademark data in the database
if results:
for item in results:
trademark, created = Trademark.objects.update_or_create(
keyword=item["keyword"],
registration_number=item.get("registration_number"),
serial_number=item.get("serial_number"),
status_label=item.get("status_label"),
status_code=item.get("status_code"),
status_date=item.get("status_date"),
status_definition=item.get("status_definition"),
filing_date=item.get("filing_date"),
registration_date=item.get("registration_date"),
abandonment_date=item.get("abandonment_date"),
expiration_date=item.get("expiration_date"),
description=item.get("description"),
organization=organization,
)

# Update or create owners
if item.get("owners"):
for owner_data in item["owners"]:
owner, owner_created = TrademarkOwner.objects.update_or_create(
name=owner_data.get("name"),
address1=owner_data.get("address1"),
address2=owner_data.get("address2"),
city=owner_data.get("city"),
state=owner_data.get("state"),
country=owner_data.get("country"),
postcode=owner_data.get("postcode"),
owner_type=owner_data.get("owner_type"),
owner_label=owner_data.get("owner_label"),
legal_entity_type=owner_data.get("legal_entity_type"),
legal_entity_type_label=owner_data.get("legal_entity_type_label"),
)
trademark.owners.add(owner)

organization.trademark_check_date = timezone.now()
organization.trademark_count = results and len(results) or 0
organization.save()

self.stdout.write(self.style.SUCCESS(f"Successfully stored data for organization: {name}"))

# Introduced delay between requests to avoid rate limiting
time.sleep(2)

break
except requests.exceptions.RequestException as e:
retries -= 1
if retries == 0:
self.stdout.write(self.style.ERROR(f"Failed to fetch data for {name}: {e}"))
else:
# Retry after a delay if rate limited
self.stdout.write(
self.style.WARNING(f"Retrying for {name} due to {e}. Retries left: {retries}")
)
time.sleep(5)

self.stdout.write(self.style.SUCCESS("Successfully fetched and stored trademark data for all organizations"))
99 changes: 99 additions & 0 deletions website/migrations/0181_trademarkowner_trademark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Generated by Django 5.1.4 on 2025-01-25 19:49

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


class Migration(migrations.Migration):
dependencies = [
("website", "0180_rename_project_visit_count_repo_repo_visit_count"),
]

operations = [
migrations.CreateModel(
name="TrademarkOwner",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
("address1", models.CharField(blank=True, max_length=255, null=True)),
("address2", models.CharField(blank=True, max_length=255, null=True)),
("city", models.CharField(blank=True, max_length=100, null=True)),
("state", models.CharField(blank=True, max_length=100, null=True)),
("country", models.CharField(blank=True, max_length=100, null=True)),
("postcode", models.CharField(blank=True, max_length=20, null=True)),
("owner_type", models.CharField(blank=True, max_length=20, null=True)),
(
"owner_label",
models.CharField(blank=True, max_length=100, null=True),
),
(
"legal_entity_type",
models.CharField(blank=True, max_length=20, null=True),
),
(
"legal_entity_type_label",
models.CharField(blank=True, max_length=100, null=True),
),
],
),
migrations.CreateModel(
name="Trademark",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("keyword", models.CharField(max_length=255)),
(
"registration_number",
models.CharField(blank=True, max_length=50, null=True),
),
(
"serial_number",
models.CharField(blank=True, max_length=50, null=True),
),
(
"status_label",
models.CharField(blank=True, max_length=50, null=True),
),
("status_code", models.CharField(blank=True, max_length=20, null=True)),
("status_date", models.DateField(blank=True, null=True)),
(
"status_definition",
models.CharField(blank=True, max_length=255, null=True),
),
("filing_date", models.DateField(blank=True, null=True)),
("registration_date", models.DateField(blank=True, null=True)),
("abandonment_date", models.DateField(blank=True, null=True)),
("expiration_date", models.DateField(blank=True, null=True)),
("description", models.TextField(blank=True, null=True)),
(
"organization",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="trademarks",
to="website.organization",
),
),
(
"owners",
models.ManyToManyField(related_name="trademarks", to="website.trademarkowner"),
),
],
),
]
12 changes: 12 additions & 0 deletions website/migrations/0184_merge_20250125_2005.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 5.1.4 on 2025-01-25 20:05

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("website", "0181_trademarkowner_trademark"),
("website", "0183_slackbotactivity"),
]

operations = []
12 changes: 12 additions & 0 deletions website/migrations/0185_merge_20250126_1451.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 5.1.4 on 2025-01-26 14:51

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("website", "0184_merge_0183_merge_20250124_0618_0183_slackbotactivity"),
("website", "0184_merge_20250125_2005"),
]

operations = []
39 changes: 39 additions & 0 deletions website/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,45 @@ def get_or_set_x_url(self, name):
pass


class TrademarkOwner(models.Model):
name = models.CharField(max_length=255)
address1 = models.CharField(max_length=255, blank=True, null=True)
address2 = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
state = models.CharField(max_length=100, blank=True, null=True)
country = models.CharField(max_length=100, blank=True, null=True)
postcode = models.CharField(max_length=20, blank=True, null=True)
owner_type = models.CharField(max_length=20, blank=True, null=True)
owner_label = models.CharField(max_length=100, blank=True, null=True)
legal_entity_type = models.CharField(max_length=20, blank=True, null=True)
legal_entity_type_label = models.CharField(max_length=100, blank=True, null=True)

def __str__(self):
return self.name


class Trademark(models.Model):
keyword = models.CharField(max_length=255)
registration_number = models.CharField(max_length=50, blank=True, null=True)
serial_number = models.CharField(max_length=50, blank=True, null=True)
status_label = models.CharField(max_length=50, blank=True, null=True)
status_code = models.CharField(max_length=20, blank=True, null=True)
status_date = models.DateField(blank=True, null=True)
status_definition = models.CharField(max_length=255, blank=True, null=True)
filing_date = models.DateField(blank=True, null=True)
registration_date = models.DateField(blank=True, null=True)
abandonment_date = models.DateField(blank=True, null=True)
expiration_date = models.DateField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
owners = models.ManyToManyField(TrademarkOwner, related_name="trademarks")
organization = models.ForeignKey(
Organization, null=True, blank=True, on_delete=models.CASCADE, related_name="trademarks"
)

def __str__(self):
return self.keyword


def validate_image(fieldfile_obj):
try:
filesize = fieldfile_obj.file.size
Expand Down
Loading

0 comments on commit 6ba769a

Please sign in to comment.