Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
81571c3
Added column for TDAMM tags
dhanur-sharma Jan 2, 2025
3f1eb6b
Added get_tag_source method in delta_url model
dhanur-sharma Jan 31, 2025
ab90a0e
Added get_tag_source to delta and curated serializers
dhanur-sharma Jan 31, 2025
58db367
Added CSS background-color for manual tdamm tag
dhanur-sharma Jan 31, 2025
520fdaf
TDAMM tags is dynamically visible based on presence of the tag
dhanur-sharma Feb 3, 2025
1755178
Deletion logic in place and functional
dhanur-sharma Feb 4, 2025
04793d7
Passing tdamm choice fields to the front end through views
dhanur-sharma Feb 4, 2025
1b38642
Add TDAMM tag functionality functional in Delta URLs
dhanur-sharma Feb 5, 2025
fe795a0
Removed unused CSS
dhanur-sharma Feb 6, 2025
d63db8e
Updated source based background CSS
dhanur-sharma Feb 6, 2025
a92223c
Dynamic tag background based on source
dhanur-sharma Feb 6, 2025
4097b17
Added hover activated tooltips
dhanur-sharma Feb 6, 2025
cd9dbb2
Standardized the modal to confirm delete tags
dhanur-sharma Feb 6, 2025
6dd39ed
Moved the add_tag and remove_tag to delta_url
dhanur-sharma Feb 6, 2025
555c97c
Can add or remove tags from Curated URLs tab
dhanur-sharma Feb 7, 2025
c2de692
Added tests
dhanur-sharma Feb 7, 2025
d2b9685
Added HTML for tdamm tag filter
dhanur-sharma Feb 7, 2025
26f6308
Potential fix for code scanning alert no. 51: Information exposure th…
dhanur-sharma Feb 7, 2025
b8f1865
Potential fix for code scanning alert no. 52: Information exposure th…
dhanur-sharma Feb 7, 2025
8127fc7
Merge branch 'dev' of https://github.com/NASA-IMPACT/COSMOS into 1058…
dhanur-sharma Feb 7, 2025
fb7d964
Cleanup views
dhanur-sharma Feb 7, 2025
d3b1d17
Cleanup delta_url model
dhanur-sharma Feb 7, 2025
3094214
Cleanup JS
dhanur-sharma Feb 7, 2025
aec3bfd
Added additional tests
dhanur-sharma Feb 7, 2025
1b24c0d
Added TDAMM patterns and update views
dhanur-sharma Feb 15, 2025
d1e18b1
Updated patterns, views, serializers, urls
dhanur-sharma Feb 24, 2025
31a24a5
Merge branch 'dev' of https://github.com/NASA-IMPACT/COSMOS into 1058…
dhanur-sharma Feb 24, 2025
853c13b
Added migration file
dhanur-sharma Feb 25, 2025
4cb16b0
Patterns work with delta URL page
dhanur-sharma Feb 25, 2025
3fb2940
Tag addition and removal functional through patterns
dhanur-sharma Feb 27, 2025
1d2848b
TDAMM tag patterns functional
dhanur-sharma Mar 6, 2025
c6b982c
Updated pattern model for multiple tags
dhanur-sharma Mar 10, 2025
6274acb
Updating logic for tags list - wip
dhanur-sharma Mar 11, 2025
68c1e40
Merge branch 'dev' of https://github.com/NASA-IMPACT/COSMOS into 1058…
dhanur-sharma Mar 28, 2025
26a44e7
Merged migrations
dhanur-sharma Apr 2, 2025
8205c24
Updated add and remove tag in views
dhanur-sharma Apr 2, 2025
10eba72
Updated unapply and _apply_tag_operation
dhanur-sharma Apr 2, 2025
0d2df05
add_tag functional
dhanur-sharma Apr 3, 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
18 changes: 18 additions & 0 deletions sde_collections/models/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,24 @@ def add_to_public_query(self):
scraper_content = scraper_editor.update_config_xml()
gh.create_or_update_file(query_path, scraper_content)

def has_tdamm_tags(self):
"""Check if any URLs in this collection have TDAMM tags."""
# Check DeltaUrls
has_delta_tags = (
self.delta_urls.filter(models.Q(tdamm_tag_manual__isnull=False) | models.Q(tdamm_tag_ml__isnull=False))
.exclude(models.Q(tdamm_tag_manual=[]) & models.Q(tdamm_tag_ml=[]))
.exists()
)

# Check CuratedUrls
has_curated_tags = (
self.curated_urls.filter(models.Q(tdamm_tag_manual__isnull=False) | models.Q(tdamm_tag_ml__isnull=False))
.exclude(models.Q(tdamm_tag_manual=[]) & models.Q(tdamm_tag_ml=[]))
.exists()
)

return has_delta_tags or has_curated_tags

@property
def _scraper_config_path(self) -> str:
return f"sources/scrapers/{self.config_folder}/default.xml"
Expand Down
26 changes: 26 additions & 0 deletions sde_collections/models/delta_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,32 @@ def splits(self) -> list[tuple[str, str]]:
parts.append((part_string, part))
return parts

def get_tag_source(self):
"""Returns the source of the TDAMM tags: 'manual', 'ml', or 'Not Set'"""
# print("Checking tag sources for URL:", self.url)
# print("Manual tags:", self.tdamm_tag_manual)
# print("ML tags:", self.tdamm_tag_ml)

# if self.tdamm_tag_manual and self.tdamm_tag_manual != []:
# print("Using manual tags")
# return "manual"
# elif self.tdamm_tag_ml and self.tdamm_tag_ml != []:
# print("Using ML tags")
# return "ml"

# print("No tags found, using 'Not Set'")
# return "Not Set"
# Convert None to empty list for comparison
manual_tags = self.tdamm_tag_manual or []
ml_tags = self.tdamm_tag_ml or []

if manual_tags and manual_tags != []:
return "manual"
elif ml_tags and ml_tags != []:
return "ml"

return "Not Set"

@property
def path(self) -> str:
parsed = urlparse(self.url)
Expand Down
15 changes: 15 additions & 0 deletions sde_collections/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,20 @@ class DeltaURLSerializer(serializers.ModelSerializer):
match_pattern_type = serializers.SerializerMethodField(read_only=True)
delta_urls_count = serializers.SerializerMethodField(read_only=True)
tdamm_tag = serializers.SerializerMethodField()
tag_source = serializers.SerializerMethodField()

def get_tdamm_tag(self, obj):
tags = obj.tdamm_tag
# print(f"TDAMM tags for {obj.url}:")
# print(f"- Raw tags: {tags}")
# print(f"- Manual tags: {obj.tdamm_tag_manual}")
# print(f"- ML tags: {obj.tdamm_tag_ml}")
return tags if tags is not None else []

def get_tag_source(self, obj):
# print(f"get_tag_source called for {obj.url}, returning: {obj.get_tag_source()}")
return obj.get_tag_source()

def get_delta_urls_count(self, obj):
titlepattern = obj.deltatitlepatterns.last()
return titlepattern.delta_urls.count() if titlepattern else 0
Expand Down Expand Up @@ -108,6 +117,7 @@ class Meta:
"division_display",
"visited",
"tdamm_tag",
"tag_source",
)


Expand All @@ -120,11 +130,15 @@ class CuratedURLSerializer(serializers.ModelSerializer):
match_pattern_type = serializers.SerializerMethodField(read_only=True)
curated_urls_count = serializers.SerializerMethodField(read_only=True)
tdamm_tag = serializers.SerializerMethodField()
tag_source = serializers.SerializerMethodField()

def get_tdamm_tag(self, obj):
tags = obj.tdamm_tag
return tags if tags is not None else []

def get_tag_source(self, obj):
return obj.get_tag_source()

def get_curated_urls_count(self, obj):
titlepattern = obj.deltatitlepatterns.last()
return titlepattern.curated_urls.count() if titlepattern else 0
Expand Down Expand Up @@ -154,6 +168,7 @@ class Meta:
"division_display",
"visited",
"tdamm_tag",
"tag_source",
)


Expand Down
82 changes: 82 additions & 0 deletions sde_collections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.views.generic.edit import DeleteView
from django.views.generic.list import ListView
from rest_framework import generics, status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
Expand All @@ -25,6 +26,7 @@
Divisions,
DocumentTypes,
ReindexingStatusChoices,
TDAMMTags,
WorkflowStatusChoices,
)
from .models.delta_patterns import (
Expand Down Expand Up @@ -230,6 +232,16 @@
context["workflow_status_choices"] = WorkflowStatusChoices
context["reindexing_status_choices"] = ReindexingStatusChoices
context["is_multi_division"] = self.collection.is_multi_division
context["has_tdamm_tags"] = self.collection.has_tdamm_tags()

tdamm_choices = [
{"code": choice[0], "label": choice[1], "display": f"{choice[0]}: {choice[1]}"}
for choice in TDAMMTags.choices
# if choice[0] != 'Not TDAMM'
]
context["tdamm_choices"] = tdamm_choices

# print("TDAMM choices:", context['tdamm_choices'])

return context

Expand Down Expand Up @@ -291,6 +303,76 @@
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST, data={"error": "Division is required."})

@action(detail=True, methods=["post"], url_path="remove_tag")
def remove_tag(self, request, pk=None):
delta_url = self.get_object()
tag_to_remove = request.data.get("tag")
source = request.data.get("source")

if not tag_to_remove:
return Response({"error": "Tag to remove not specified"}, status=status.HTTP_400_BAD_REQUEST)

try:
# print(f"Current state - ML tags: {delta_url.tdamm_tag_ml}, Manual tags: {delta_url.tdamm_tag_manual}")
# print(f"Source: {source}, Tag to remove: {tag_to_remove}")

if source == "ml":
# Get current ML tags
ml_tags = delta_url.tdamm_tag_ml
if ml_tags:
# Create a new list from ML tags, excluding the one to remove
new_manual_tags = [tag for tag in ml_tags if tag != tag_to_remove]
# Set the new list to manual tags
delta_url.tdamm_tag_manual = new_manual_tags
# print(f"New manual tags after copy and remove: {delta_url.tdamm_tag_manual}")
else:
if delta_url.tdamm_tag_manual:
manual_tags = delta_url.tdamm_tag_manual
if tag_to_remove in manual_tags:
manual_tags.remove(tag_to_remove)
delta_url.tdamm_tag_manual = manual_tags
# print(f"New manual tags after remove: {delta_url.tdamm_tag_manual}")

delta_url.save()
# print(f"Final state - ML tags: {delta_url.tdamm_tag_ml}, Manual tags: {delta_url.tdamm_tag_manual}")

return Response({"status": "success"})

except Exception as e:
print(f"Error occurred: {str(e)}")
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

@action(detail=True, methods=["post"], url_path="add_tag")
def add_tag(self, request, pk=None):
delta_url = self.get_object()
tag_to_add = request.data.get("tag")
source = request.data.get("source")

if not tag_to_add:
return Response({"error": "Tag to add not specified"}, status=status.HTTP_400_BAD_REQUEST)

try:
# Get current tags
if source == "ml":
# If source is ML, copy ML tags to manual and add new tag
current_tags = delta_url.tdamm_tag_ml or []
new_tags = list(current_tags) # Create a copy
if tag_to_add not in new_tags:
new_tags.append(tag_to_add)
delta_url.tdamm_tag_manual = new_tags
else:
# For manual source, just add to existing manual tags
current_tags = delta_url.tdamm_tag_manual or []
if tag_to_add not in current_tags:
current_tags.append(tag_to_add)
delta_url.tdamm_tag_manual = current_tags

delta_url.save()
return Response({"status": "success"})

except Exception as e:
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class CuratedURLViewSet(CollectionFilterMixin, viewsets.ModelViewSet):
queryset = CuratedUrl.objects.all()
Expand Down
162 changes: 162 additions & 0 deletions sde_indexing_helper/static/css/delta_url_list.css
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,165 @@ div.dt-container div.dt-paging ul.pagination {
max-width: 100%;
min-width: 100%;
}

.table_filter_row_input {
width: 100%;
}

.tdamm-tags {
color: white;
background-color: #3F4A58;
padding: 2px 6px;
border-radius: 4px;
margin: 2px;
display: inline-block;
}

.tdamm-tag-manual {
background-color: #28a745; /* Green background */
}

.tdamm-tags-container {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: center;
}

.tdamm-tag {
display: inline-flex;
align-items: center;
background: #304050;
border-radius: 16px;
padding: 2px 8px;
margin: 2px;
color: white;
font-size: 0.9em;
}

.delete-tag {
background: none;
border: none;
color: #ff9999;
margin-left: 4px;
padding: 0 4px;
cursor: pointer;
font-size: 1.2em;
line-height: 1;
}

.delete-tag:hover {
color: #ff0000;
}

.tdamm-dropdown {
position: absolute;
min-width: 300px;
background: #15232E;
/* border: 1px solid #A7BACD; */
border: 2px solid red !important;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
z-index: 9999;
}

.tdamm-dropdown .dropdown-menu {
position: static !important;
display: block !important;
min-width: 100%;
max-height: 400px;
overflow-y: auto;
padding: 10px;
background: none;
border: none;
margin: 0;
}

.tdamm-tag-menu {
position: absolute;
width: 300px;
}

.tdamm-search {
margin: 10px;
padding: 5px 10px;
width: calc(100% - 20px);
background: #3F4A58;
border: 1px solid #A7BACD;
color: white;
border-radius: 4px;
}

.tdamm-options {
/* max-height: 300px; */
/* overflow-y: auto; */
min-height: 50px;
background: rgba(255,255,255,0.1);
}

/* .tdamm-option {
padding: 8px 12px;
cursor: pointer;
color: white;
border-radius: 4px;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background: rgba(255,255,255,0.1);
} */

.tdamm-option:hover {
background: #0066CA;
}

.add-tdamm-tag {
margin-left: 8px;
padding: 2px 8px;
font-size: 0.9em;
}

/* .tdamm-dropdown-container {
position: absolute;
background: #15232E;
border: 1px solid #A7BACD;
border-radius: 4px;
padding: 5px;
z-index: 9999;
} */

.tdamm-dropdown-container {
position: absolute;
background: #15232E;
border: 1px solid #A7BACD;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 300px;
}

.tdamm-options-list {
max-height: 400px;
overflow-y: auto;
}

.tdamm-option {
padding: 8px 12px;
cursor: pointer;
color: white;
transition: background-color 0.2s;
}

/* .tdamm-select {
width: 300px;
background: #3F4A58;
color: white;
border: 1px solid #A7BACD;
border-radius: 4px;
padding: 5px;
}

.tdamm-select option {
background: #15232E;
color: white;
padding: 5px;
} */
Loading