From 26f404fcf5cd448af52a5b9425677f8e41ef1e39 Mon Sep 17 00:00:00 2001 From: romer8 Date: Tue, 1 Oct 2024 13:18:47 -0600 Subject: [PATCH 1/4] added community plugin --- backend/settings.py | 1 + backend/urls.py | 4 + .../__init__.py | 0 hydroshare_community_resources_app/admin.py | 0 hydroshare_community_resources_app/apps.py | 6 + .../cms_plugins.py | 20 +++ .../migrations/0001_initial.py | 32 ++++ .../migrations/__init__.py | 0 hydroshare_community_resources_app/models.py | 16 ++ .../static/css/hs_resources.css | 104 +++++++++++++ .../static/js/hs_resources.js | 140 +++++++++++++++++ .../hydroshare-community-resources-base.html | 16 ++ .../hydroshare-community-resources.html | 31 ++++ hydroshare_community_resources_app/urls.py | 13 ++ hydroshare_community_resources_app/utils.py | 144 ++++++++++++++++++ hydroshare_community_resources_app/views.py | 86 +++++++++++ setup.sh | 5 + 17 files changed, 618 insertions(+) create mode 100644 hydroshare_community_resources_app/__init__.py create mode 100644 hydroshare_community_resources_app/admin.py create mode 100644 hydroshare_community_resources_app/apps.py create mode 100644 hydroshare_community_resources_app/cms_plugins.py create mode 100644 hydroshare_community_resources_app/migrations/0001_initial.py create mode 100644 hydroshare_community_resources_app/migrations/__init__.py create mode 100644 hydroshare_community_resources_app/models.py create mode 100644 hydroshare_community_resources_app/static/css/hs_resources.css create mode 100644 hydroshare_community_resources_app/static/js/hs_resources.js create mode 100644 hydroshare_community_resources_app/templates/hydroshare-community-resources-base.html create mode 100644 hydroshare_community_resources_app/templates/hydroshare-community-resources.html create mode 100644 hydroshare_community_resources_app/urls.py create mode 100644 hydroshare_community_resources_app/utils.py create mode 100644 hydroshare_community_resources_app/views.py diff --git a/backend/settings.py b/backend/settings.py index d0d22815..4daff6ff 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -81,6 +81,7 @@ "zotero_publications_app", "hydrolearn_modules_app", "hydroshare_resources_app", + "hydroshare_community_resources_app", ] MIDDLEWARE = [ diff --git a/backend/urls.py b/backend/urls.py index 93294bec..255db7fa 100644 --- a/backend/urls.py +++ b/backend/urls.py @@ -9,6 +9,10 @@ path("zotero_publications_app/", include("zotero_publications_app.urls")), path("hydrolearn_modules_app/", include("hydrolearn_modules_app.urls")), path("hydroshare_resources_app/", include("hydroshare_resources_app.urls")), + path( + "hydroshare_community_resources_app/", + include("hydroshare_community_resources_app.urls"), + ), ] # if settings.DEBUG: diff --git a/hydroshare_community_resources_app/__init__.py b/hydroshare_community_resources_app/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hydroshare_community_resources_app/admin.py b/hydroshare_community_resources_app/admin.py new file mode 100644 index 00000000..e69de29b diff --git a/hydroshare_community_resources_app/apps.py b/hydroshare_community_resources_app/apps.py new file mode 100644 index 00000000..bff39bf8 --- /dev/null +++ b/hydroshare_community_resources_app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class HydroshareCommunitiesResourcesAppConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "hydroshare_community_resources_app" diff --git a/hydroshare_community_resources_app/cms_plugins.py b/hydroshare_community_resources_app/cms_plugins.py new file mode 100644 index 00000000..719467d5 --- /dev/null +++ b/hydroshare_community_resources_app/cms_plugins.py @@ -0,0 +1,20 @@ +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool +from django.utils.translation import gettext_lazy as _ +from .models import HydroShareCommunityResourcesList + +import logging + +logger = logging.getLogger(__name__) + + +@plugin_pool.register_plugin +class HydroShareCommunityResourcesList(CMSPluginBase): + model = HydroShareCommunityResourcesList + name = _("HydroShare Community Resources Plugin") + render_template = "hydroshare-community-resources.html" + cache = False + + def render(self, context, instance, placeholder): + context = super().render(context, instance, placeholder) + return context diff --git a/hydroshare_community_resources_app/migrations/0001_initial.py b/hydroshare_community_resources_app/migrations/0001_initial.py new file mode 100644 index 00000000..41e0a30f --- /dev/null +++ b/hydroshare_community_resources_app/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2 on 2024-10-01 18:31 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('cms', '0022_auto_20180620_1551'), + ] + + operations = [ + migrations.CreateModel( + name='HydroShareCommunityResourcesList', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='hydroshare_community_resources_app_hydrosharecommunityresourceslist', serialize=False, to='cms.cmsplugin')), + ('user', models.CharField(blank=True, default='', max_length=200)), + ('password', models.CharField(blank=True, default='', max_length=200)), + ('community_id', models.CharField(default='', max_length=200)), + ('placeholder_image', models.CharField(default='https://www.tethysplatform.org/images/tethys_data.png', max_length=200)), + ('updated_version', models.IntegerField(default=0, editable=False)), + ('resources', models.JSONField(default=dict, editable=False)), + ], + options={ + 'abstract': False, + }, + bases=('cms.cmsplugin',), + ), + ] diff --git a/hydroshare_community_resources_app/migrations/__init__.py b/hydroshare_community_resources_app/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hydroshare_community_resources_app/models.py b/hydroshare_community_resources_app/models.py new file mode 100644 index 00000000..b088ff97 --- /dev/null +++ b/hydroshare_community_resources_app/models.py @@ -0,0 +1,16 @@ +from cms.models.pluginmodel import CMSPlugin +from django.db import models +import logging + +logger = logging.getLogger(__name__) + + +class HydroShareCommunityResourcesList(CMSPlugin): + user = models.CharField(max_length=200, default="", blank=True) + password = models.CharField(max_length=200, default="", blank=True) + community_id = models.CharField(max_length=200, default="") + placeholder_image = models.CharField( + max_length=200, default="https://www.tethysplatform.org/images/tethys_data.png" + ) + updated_version = models.IntegerField(default=0, editable=False) + resources = models.JSONField(editable=False, default=dict) diff --git a/hydroshare_community_resources_app/static/css/hs_resources.css b/hydroshare_community_resources_app/static/css/hs_resources.css new file mode 100644 index 00000000..4858c86d --- /dev/null +++ b/hydroshare_community_resources_app/static/css/hs_resources.css @@ -0,0 +1,104 @@ +:root { + --image-width: 200px; + --image-height: 200px; + } + + .img-tile{ + object-fit: cover; + } + + .name{ + height: 150px; + cursor: pointer; + } + + /* Hide scrollbar for Chrome, Safari and Opera */ + .name::-webkit-scrollbar { + display: none; + } + + /* Hide scrollbar for IE, Edge and Firefox */ + .name { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } + + .hydroshare_resource{ + box-shadow: 0 1px 10px 0 rgba(0,0,0,0.125), inset 0 0 0 1px rgba(255,255,255,0.75); + border-color: white; + border-bottom: 3px solid #255f9c; + } + .hydroshare_resource:hover, + .hydroshare_resource:focus { + background: #f5f5f5; + border-color: #bebebe; + box-shadow: 0 1px 4px 0 rgba(0,117,180,0.4); + } + .cover-image { + position: relative; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + cursor: pointer; + } + + .cover-image img { + display: block; + width: 100%; + transition: filter 0.5s ease; /* Smooth transition for the filter */ + } + + .learn-more { + position: absolute; + color: white; + padding: 5px 10px; + visibility: hidden; + transition: visibility 0s, opacity 0.5s linear; /* Smooth transition for visibility and opacity */ + opacity: 0; + border-color: #255f9c; + border-radius: 3px; + background: #255f9c; + } + + .cover-image:hover .learn-more { + visibility: visible; + opacity: 1; /* Make it fully opaque on hover */ + } + + .cover-image:hover img { + filter: brightness(50%); /* Darken the image */ + } + .learn-more > a { + color: inherit; + text-decoration: none; + } + + .hidden { + display: none; + } + @keyframes placeHolderShimmer{ + 0%{ + background-position: -800px 0 + } + 100%{ + background-position: 800px 0 + } + } + + + + .animated-background { + animation-duration: 7s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-name: placeHolderShimmer; + animation-timing-function: linear; + } + .titles-loading-background{ + background: #cbcbcb; + background: linear-gradient(to right, #cbcbcb 8%, #bbbbbb 18%, #cbcbcb 33%); + } + .descriptions-background{ + background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%); + } \ No newline at end of file diff --git a/hydroshare_community_resources_app/static/js/hs_resources.js b/hydroshare_community_resources_app/static/js/hs_resources.js new file mode 100644 index 00000000..db486733 --- /dev/null +++ b/hydroshare_community_resources_app/static/js/hs_resources.js @@ -0,0 +1,140 @@ + + +const getCSRFToken = () => { + // Attempt to retrieve the CSRF token from the meta tag + const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); + return token; +} + +const fetchHydroShareResources = () => { + // Update the URL if your setup is different or if you are using a production server + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCSRFToken(), // Include the CSRF token in the request headers + }, + body: JSON.stringify(requestData), + }) + .then(response => { + document.getElementById('placeholder-hydroshare-resources').classList.add('hidden'); + document.getElementById('hydroshare-resources-list-plugin').classList.remove('hidden'); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); // Assuming the response is JSON + }) + .then(data => { + let resourcesHTML = `` + data['resources'].forEach(resource => { + resourcesHTML += ` +
+
+
` + if (resource.web_site_url){ + resourcesHTML += ` +
+
+ + ... + +
` + + } + else{ + resourcesHTML += ` + + ... +
` + } + resourcesHTML += ` +
+
+
+ +
${resource.title}
` + + resourcesHTML += `
` + resourcesHTML +=`

` + if(resource.github_url){ + resourcesHTML +=` ` + } + if(resource.web_site_url){ + resourcesHTML +=` ` + } + if(resource.documentation_url){ + resourcesHTML +=` ` + } + // resourcesHTML +=` HydroShare` + resourcesHTML +=`

` + + + resourcesHTML +=`

View on HydroShare

` + + resourcesHTML +=`
+

+ ${resource.abstract} +

+
` + resourcesHTML += `
` + + }); + + document.getElementById('hydroshare-resources-list-plugin').innerHTML = resourcesHTML; + + // For example, you could iterate over the data and append it to an element in your HTML + }) + .catch(error => { + console.error('There was a problem with the fetch operation:', error); + }); + } + +const create_placeholders = () => { + let placeholdersHtmlElement = document.getElementById('placeholder-hydroshare-resources'); + let htmlPlaceholders = ''; + for(var i = 0; i < 10; i++){ + htmlPlaceholders+=` +
+
+
+ +
+
+ + +
+
+ +
+
+
+
+
+
+ +

+ +
+

+

+
+
+
+
+
+ + ` + } + placeholdersHtmlElement.innerHTML = htmlPlaceholders; +} + +create_placeholders(); +fetchHydroShareResources(); diff --git a/hydroshare_community_resources_app/templates/hydroshare-community-resources-base.html b/hydroshare_community_resources_app/templates/hydroshare-community-resources-base.html new file mode 100644 index 00000000..bae07810 --- /dev/null +++ b/hydroshare_community_resources_app/templates/hydroshare-community-resources-base.html @@ -0,0 +1,16 @@ +{% extends CMS_TEMPLATE %} + +{% load sekizai_tags static cms_tags %} + +{% addtoblock "js" %} + +{% endaddtoblock %} + +{% addtoblock "css" %} + + +{% endaddtoblock %} + +{% block content %} + +{% endblock %} diff --git a/hydroshare_community_resources_app/templates/hydroshare-community-resources.html b/hydroshare_community_resources_app/templates/hydroshare-community-resources.html new file mode 100644 index 00000000..14aae87e --- /dev/null +++ b/hydroshare_community_resources_app/templates/hydroshare-community-resources.html @@ -0,0 +1,31 @@ +{% load cms_tags sekizai_tags static %} + + {% csrf_token %} + + + +{% addtoblock "css" %} + + +{% endaddtoblock %} + +{% addtoblock "js" %} + + + +{% endaddtoblock %} + +{% block content %} + +
+ + +{% endblock %} + + \ No newline at end of file diff --git a/hydroshare_community_resources_app/urls.py b/hydroshare_community_resources_app/urls.py new file mode 100644 index 00000000..78798733 --- /dev/null +++ b/hydroshare_community_resources_app/urls.py @@ -0,0 +1,13 @@ +from django.urls import re_path + + +from .views import base_view, hydroshare_community_resources_view + +urlpatterns = [ + re_path(r"^$", base_view, name="base"), + re_path( + r"^update-hydroshare-community-resources/$", + hydroshare_community_resources_view, + name="update-hydroshare-community-resources", + ), +] diff --git a/hydroshare_community_resources_app/utils.py b/hydroshare_community_resources_app/utils.py new file mode 100644 index 00000000..14a5260c --- /dev/null +++ b/hydroshare_community_resources_app/utils.py @@ -0,0 +1,144 @@ +import requests +from bs4 import BeautifulSoup +import json +from itertools import chain +import datetime + + +def join_generators(generators): + generator_chain = chain(*generators) + return generator_chain + + +def get_group_ids(community_id): + url = f"https://www.hydroshare.org/community/{community_id}/" + response = requests.get(url) + soup = BeautifulSoup(response.content, "html.parser") + + group_ids = [] + + # Find the script tag with id 'community-app-data' + script_tag = soup.find("script", id="community-app-data", type="application/json") + if script_tag and script_tag.string: + data = json.loads(script_tag.string) + # The group data is in data['members'] + for group in data.get("members", []): + group_id = group.get("id") + if group_id: + href = f"{group_id}" + group_ids.append(href) + else: + print( + "No script tag with id 'community-app-data' found or it contains no data." + ) + + return group_ids + + +def get_dict_with_attribute(list_of_dicts, attribute, value): + # Loop through each dictionary in the list + for dictionary in list_of_dicts: + # logging.warning(dictionary) + # if attribute in dictionary: + # logging.warning(dictionary[attribute]) + # Check if the attribute exists in the dictionary and has the specified value + if attribute in dictionary and dictionary[attribute] == value: + return dictionary # Return the dictionary if found + + return None # Return None if not found in any dictionary + + +def get_most_recent_date(date_local_resource, date_api): + # logging.warning(f'{date_local_resource} , {date_api}') + + # Convert strings to datetime objects + date_time_local_resource = datetime.datetime.fromisoformat(date_local_resource[:-1]) + date_time_api = datetime.datetime.fromisoformat(date_api[:-1]) + # logging.warning(f'{date_time_local_resource} , {date_time_api}') + # Compare the datetime objects + if date_time_local_resource > date_time_api: + return False + elif date_time_local_resource < date_time_api: + return True + else: + return False + + +def update_resource(resource, hs, instance): + # logging.warning(science_metadata_json) + single_resource = {} + if resource["resource_type"] == "ToolResource": + science_metadata_json = hs.getScienceMetadata(resource["resource_id"]) + # logging.warning(f'{science_metadata_json}') + image_url = science_metadata_json.get( + "app_icon", instance.placeholder_image + ).get("value", instance.placeholder_image) + web_site_url = ( + "" + if not science_metadata_json.get("app_home_page_url", "") + else science_metadata_json.get("app_home_page_url").get("value", "") + ) + github_url = ( + "" + if not science_metadata_json.get("source_code_url", "") + else science_metadata_json.get("source_code_url").get("value", "") + ) + help_page_url = ( + "" + if not science_metadata_json.get("help_page_url", "") + else science_metadata_json.get("help_page_url").get("value", "") + ) + if resource["resource_type"] == "CompositeResource": + resource_scrapping = requests.get(resource["resource_url"]) + image_url = ( + instance.placeholder_image + if not extract_value_by_name(resource_scrapping.content, "app_icon") + else extract_value_by_name(resource_scrapping.content, "app_icon") + ) + web_site_url = ( + "" + if not extract_value_by_name(resource_scrapping.content, "home_page_url") + else extract_value_by_name(resource_scrapping.content, "home_page_url") + ) + github_url = ( + "" + if not extract_value_by_name(resource_scrapping.content, "source_code_url") + else extract_value_by_name(resource_scrapping.content, "source_code_url") + ) + help_page_url = ( + "" + if not extract_value_by_name(resource_scrapping.content, "help_page_url") + else extract_value_by_name(resource_scrapping.content, "help_page_url") + ) + + if image_url == "": + image_url = instance.placeholder_image + + single_resource = { + "title": resource["resource_title"], + "abstract": resource["abstract"], + "github_url": github_url, + "image": image_url, + "web_site_url": web_site_url, + "documentation_url": help_page_url, + "resource_id": resource["resource_id"], + "date_last_updated": resource["date_last_updated"], + "resource_type": resource["resource_type"], + "resource_url": resource["resource_url"], + } + # logging.warning(single_resource) + return single_resource + + +def extract_value_by_name(html, name): + soup = BeautifulSoup(html, "html.parser") + rows = soup.select("#extraMetaTable tbody tr") + + for row in rows: + name_cell = row.select_one("td:first-child") + value_cell = row.select_one("td:nth-child(2)") + + if name_cell and value_cell and name_cell.get_text(strip=True) == name: + return value_cell.get_text(strip=True) + + return None diff --git a/hydroshare_community_resources_app/views.py b/hydroshare_community_resources_app/views.py new file mode 100644 index 00000000..e68bd772 --- /dev/null +++ b/hydroshare_community_resources_app/views.py @@ -0,0 +1,86 @@ +from django.utils.translation import gettext_lazy as _ +from .utils import ( + join_generators, + get_group_ids, + get_dict_with_attribute, + get_most_recent_date, + update_resource, +) + +from .models import ( + HydroShareCommunityResourcesList, +) +from hs_restclient import HydroShare, HydroShareAuthBasic +import logging +import json +from django.http import JsonResponse +from django.shortcuts import render + +logger = logging.getLogger(__name__) + + +def base_view(request): + + context = {} + return render(request, "hydroshare-community-resources-base.html", context) + + +def hydroshare_community_resources_view(request): + + body_unicode = request.body.decode("utf-8") + body = json.loads(body_unicode) + instance = HydroShareCommunityResourcesList.objects.get(id=body["instance_id"]) + json_resources = {"resources": []} + if instance.user != "" and instance.password != "": + auth = HydroShareAuthBasic(username=instance.user, password=instance.password) + hs = HydroShare(auth=auth) + else: + hs = HydroShare(prompt_auth=False) + try: + generators_rs = [] + logger.warning(instance.community_id) + group_ids = get_group_ids(instance.community_id) + for group_id in group_ids: + generators_rs.append(hs.resources(group=group_id)) + + resources_api = join_generators(generators_rs) + logger.warning(resources_api) + resources_model = instance.resources.get("resources", []) + + for resource_api in resources_api: + matching_resource_model = get_dict_with_attribute( + resources_model, "resource_id", resource_api["resource_id"] + ) + + # If resource found locally, then check last update date + if matching_resource_model: + is_recent_date = get_most_recent_date( + matching_resource_model["date_last_updated"], + resource_api["date_last_updated"], + ) + if ( + is_recent_date + ): # If the resource retrieved from api is more recent, then update resource + # logging.warning("resource has a more recent version") + single_resource = update_resource(resource_api, hs, instance) + # logging.warning(single_resource) + json_resources["resources"].append(single_resource) + instance.resources = json_resources + + else: # resource is the same, then retrieve the resource saved locally + single_resource = matching_resource_model + json_resources["resources"].append(single_resource) + instance.resources = json_resources + # If the resource is not here then create one + else: + single_resource = update_resource(resource_api, hs, instance) + json_resources["resources"].append(single_resource) + + instance.resources = json_resources + + instance.save(update_fields=["resources"]) + return JsonResponse(json_resources) + + except Exception as e: + logging.warning(e) + instance.resources = {"Error": [f"The following error: {e}"]} diff --git a/setup.sh b/setup.sh index 5d95fef7..b49bc9de 100755 --- a/setup.sh +++ b/setup.sh @@ -66,6 +66,11 @@ python manage.py migrate hydrolearn_modules_app python manage.py makemigrations hydroshare_resources_app python manage.py migrate hydroshare_resources_app +python manage.py makemigrations hydroshare_community_resources_app +python manage.py migrate hydroshare_community_resources_app + + + if [ -z "${SKIP_CREATION_USER}" ]; then python manage.py createsuperuser --noinput --username $DJANGO_SUPERUSER_USERNAME --email $DJANGO_SUPERUSER_EMAIL fi From ef8b24d4687703d8833712e4cd5605f9fcbfdc8a Mon Sep 17 00:00:00 2001 From: romer8 Date: Tue, 1 Oct 2024 20:39:17 -0600 Subject: [PATCH 2/4] added curated with radio buttons --- ...sources.css => hs_community_resources.css} | 74 ++++++++++++++++++ .../static/images/files.png | Bin 0 -> 68705 bytes ...resources.js => hs_community_resources.js} | 42 +++++++++- .../hydroshare-community-resources.html | 28 +++++-- hydroshare_community_resources_app/utils.py | 28 +++++++ hydroshare_community_resources_app/views.py | 13 ++- 6 files changed, 174 insertions(+), 11 deletions(-) rename hydroshare_community_resources_app/static/css/{hs_resources.css => hs_community_resources.css} (56%) create mode 100644 hydroshare_community_resources_app/static/images/files.png rename hydroshare_community_resources_app/static/js/{hs_resources.js => hs_community_resources.js} (81%) diff --git a/hydroshare_community_resources_app/static/css/hs_resources.css b/hydroshare_community_resources_app/static/css/hs_community_resources.css similarity index 56% rename from hydroshare_community_resources_app/static/css/hs_resources.css rename to hydroshare_community_resources_app/static/css/hs_community_resources.css index 4858c86d..9eed2e77 100644 --- a/hydroshare_community_resources_app/static/css/hs_resources.css +++ b/hydroshare_community_resources_app/static/css/hs_community_resources.css @@ -101,4 +101,78 @@ } .descriptions-background{ background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%); + } + + /* The following are css for the toggle */ + @import url("https://fonts.googleapis.com/css?family=Inter:400'"); + + /* Include FontAwesome for icons */ + @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"); + + .middle { + width: 100%; + text-align: center; + } + + .middle h1 { + font-family: "Inter", sans-serif; + color: #fff; + } + + .middle input[type="radio"] { + display: none; + } + + .middle input[type="radio"]:checked + .box { + background-color: #19a7ce; + } + + .middle input[type="radio"]:checked + .box span { + color: white; + transform: translateY(35px); /* Adjusted from 70px */ + } + + .middle input[type="radio"]:checked + .box span:before { + transform: translateY(0px); + opacity: 1; + } + + .middle .box { + width: 100px; /* Adjusted from 200px */ + height: 100px; /* Adjusted from 200px */ + background-color: #fff; + transition: all 250ms ease; + will-change: transform; + display: inline-block; + text-align: center; + cursor: pointer; + position: relative; + font-family: "Inter", sans-serif; + font-weight: 900; + } + + .middle .box:active { + transform: translateY(5px); /* Adjusted from 10px */ + } + + .middle .box span { + position: absolute; + transform: translate(0, 30px); /* Adjusted from 60px */ + left: 0; + right: 0; + transition: all 300ms ease; + font-size: 1em; /* Adjusted from 1.5em */ + user-select: none; + color: #19a7ce; + } + + .middle .box span:before { + font-size: 0.8em; /* Adjusted from 1.2em */ + font-family: FontAwesome; + display: block; + transform: translateY(-40px); /* Adjusted from -80px */ + opacity: 0; + transition: all 300ms ease-in-out; + font-weight: normal; + color: white; } \ No newline at end of file diff --git a/hydroshare_community_resources_app/static/images/files.png b/hydroshare_community_resources_app/static/images/files.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd534853a735b12d58f54b952267fb97833a5af GIT binary patch literal 68705 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 z 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 {
- ... + ... ` } resourcesHTML += ` @@ -98,6 +98,7 @@ const fetchHydroShareResources = () => { } const create_placeholders = () => { + console.log('Creating placeholders'); let placeholdersHtmlElement = document.getElementById('placeholder-hydroshare-resources'); let htmlPlaceholders = ''; for(var i = 0; i < 10; i++){ @@ -136,5 +137,38 @@ const create_placeholders = () => { placeholdersHtmlElement.innerHTML = htmlPlaceholders; } -create_placeholders(); -fetchHydroShareResources(); + +document.addEventListener('DOMContentLoaded', function() { + // Select all radio buttons with name 'radio' + const radioButtons = document.querySelectorAll('input[name="radio"]'); + + // Function to handle selection changes + function handleSelection(value) { + document.getElementById('placeholder-hydroshare-resources').classList.remove('hidden'); + document.getElementById('hydroshare-resources-list-plugin').classList.add('hidden'); + console.log(`${value.charAt(0).toUpperCase() + value.slice(1)} selected`); + requestData.curated = (value === 'curated'); + create_placeholders(); + fetchHydroShareResources(); + } + + // Initial function call based on default selection + const selectedRadio = document.querySelector('input[name="radio"]:checked'); + if (selectedRadio && selectedRadio.value) { + handleSelection(selectedRadio.value); + } else { + console.error('No radio button is selected by default.'); + } + + // Add event listeners to each radio button + radioButtons.forEach(function(radio) { + radio.addEventListener('change', function() { + if (this.checked) { + handleSelection(this.value); + } + }); + }); +}); + +// Inspiration for radio buttons +// https://codepen.io/gabrielferreira/pen/oYxNVy \ No newline at end of file diff --git a/hydroshare_community_resources_app/templates/hydroshare-community-resources.html b/hydroshare_community_resources_app/templates/hydroshare-community-resources.html index 14aae87e..f65e6607 100644 --- a/hydroshare_community_resources_app/templates/hydroshare-community-resources.html +++ b/hydroshare_community_resources_app/templates/hydroshare-community-resources.html @@ -5,23 +5,41 @@ {% addtoblock "css" %} - - + + {% endaddtoblock %} {% addtoblock "js" %} + - - + {% endaddtoblock %} {% block content %} +
+

CSS Radio Button

+ + +
diff --git a/hydroshare_community_resources_app/utils.py b/hydroshare_community_resources_app/utils.py index 14a5260c..5af9ac9c 100644 --- a/hydroshare_community_resources_app/utils.py +++ b/hydroshare_community_resources_app/utils.py @@ -3,6 +3,34 @@ import json from itertools import chain import datetime +import re + + +def get_curated_resources( + composite_resource_id="302dcbef13614ac486fb260eaa1ca87c", hs=None +): + science = hs.getScienceMetadata(composite_resource_id) + relations = science.get("relations", []) + resource_ids = [] + for item in relations: + if item.get("type") == "hasPart": + value = item.get("value", "") + # Regular expression to find the resource ID in the URL + match = re.search( + r"http://www\.hydroshare\.org/resource/([a-f0-9]{32})", value + ) + if match: + resource_id = match.group(1) + resource_ids.append(resource_id) + return resource_ids + + +def filter_resources_list_by_resources_id(resources, ids): + filtered_resources = [] + for resource in resources: + if resource["resource_id"] in ids: + filtered_resources.append(resource) + return filtered_resources def join_generators(generators): diff --git a/hydroshare_community_resources_app/views.py b/hydroshare_community_resources_app/views.py index e68bd772..cbe9219e 100644 --- a/hydroshare_community_resources_app/views.py +++ b/hydroshare_community_resources_app/views.py @@ -5,6 +5,8 @@ get_dict_with_attribute, get_most_recent_date, update_resource, + filter_resources_list_by_resources_id, + get_curated_resources, ) from .models import ( @@ -30,6 +32,8 @@ def hydroshare_community_resources_view(request): body_unicode = request.body.decode("utf-8") body = json.loads(body_unicode) instance = HydroShareCommunityResourcesList.objects.get(id=body["instance_id"]) + is_curated = body["curated"] + json_resources = {"resources": []} if instance.user != "" and instance.password != "": auth = HydroShareAuthBasic(username=instance.user, password=instance.password) @@ -38,16 +42,21 @@ def hydroshare_community_resources_view(request): hs = HydroShare(prompt_auth=False) try: generators_rs = [] - logger.warning(instance.community_id) group_ids = get_group_ids(instance.community_id) for group_id in group_ids: generators_rs.append(hs.resources(group=group_id)) resources_api = join_generators(generators_rs) - logger.warning(resources_api) + if is_curated: + logger.warning("curated is true") + curated_ids = get_curated_resources(hs=hs) + resources_api = filter_resources_list_by_resources_id( + resources_api, curated_ids + ) resources_model = instance.resources.get("resources", []) for resource_api in resources_api: + matching_resource_model = get_dict_with_attribute( resources_model, "resource_id", resource_api["resource_id"] ) From 85fff00761b70cccf94b66a6cdf7f9e4452d3edf Mon Sep 17 00:00:00 2001 From: romer8 Date: Tue, 1 Oct 2024 20:57:35 -0600 Subject: [PATCH 3/4] fix problem of duplicate resources --- hydroshare_community_resources_app/utils.py | 9 +++++++-- hydroshare_community_resources_app/views.py | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hydroshare_community_resources_app/utils.py b/hydroshare_community_resources_app/utils.py index 5af9ac9c..a7add6ad 100644 --- a/hydroshare_community_resources_app/utils.py +++ b/hydroshare_community_resources_app/utils.py @@ -34,8 +34,13 @@ def filter_resources_list_by_resources_id(resources, ids): def join_generators(generators): - generator_chain = chain(*generators) - return generator_chain + seen_resource_ids = set() + for gen in generators: + for item in gen: + resource_id = item.get("resource_id") + if resource_id not in seen_resource_ids: + seen_resource_ids.add(resource_id) + yield item def get_group_ids(community_id): diff --git a/hydroshare_community_resources_app/views.py b/hydroshare_community_resources_app/views.py index cbe9219e..ed1215ef 100644 --- a/hydroshare_community_resources_app/views.py +++ b/hydroshare_community_resources_app/views.py @@ -43,6 +43,7 @@ def hydroshare_community_resources_view(request): try: generators_rs = [] group_ids = get_group_ids(instance.community_id) + logger.warning(group_ids) for group_id in group_ids: generators_rs.append(hs.resources(group=group_id)) @@ -56,7 +57,7 @@ def hydroshare_community_resources_view(request): resources_model = instance.resources.get("resources", []) for resource_api in resources_api: - + logger.warning(resource_api["resource_id"]) matching_resource_model = get_dict_with_attribute( resources_model, "resource_id", resource_api["resource_id"] ) From 40296bdec4bdec51d814bce68fcd7e2c4ce2ac5d Mon Sep 17 00:00:00 2001 From: romer8 Date: Tue, 1 Oct 2024 21:19:22 -0600 Subject: [PATCH 4/4] cleaning more code --- .../static/css/hs_community_resources.css | 1 + .../static/js/hs_community_resources.js | 48 ++++++---------- hydroshare_community_resources_app/utils.py | 55 +------------------ hydroshare_community_resources_app/views.py | 14 +++-- 4 files changed, 28 insertions(+), 90 deletions(-) diff --git a/hydroshare_community_resources_app/static/css/hs_community_resources.css b/hydroshare_community_resources_app/static/css/hs_community_resources.css index 9eed2e77..a89193ac 100644 --- a/hydroshare_community_resources_app/static/css/hs_community_resources.css +++ b/hydroshare_community_resources_app/static/css/hs_community_resources.css @@ -111,6 +111,7 @@ .middle { width: 100%; + padding-bottom: 50px; text-align: center; } diff --git a/hydroshare_community_resources_app/static/js/hs_community_resources.js b/hydroshare_community_resources_app/static/js/hs_community_resources.js index 5a5ecebf..cc92c457 100644 --- a/hydroshare_community_resources_app/static/js/hs_community_resources.js +++ b/hydroshare_community_resources_app/static/js/hs_community_resources.js @@ -32,29 +32,24 @@ const fetchHydroShareResources = () => {
` - if (resource.web_site_url){ - resourcesHTML += ` -
-
- - ... - -
` + + resourcesHTML += ` +
+
+ + ... + +
` - } - else{ - resourcesHTML += ` - - ... -
` - } + + resourcesHTML += `
@@ -64,15 +59,6 @@ const fetchHydroShareResources = () => { resourcesHTML += `
` resourcesHTML +=`

` - if(resource.github_url){ - resourcesHTML +=` ` - } - if(resource.web_site_url){ - resourcesHTML +=` ` - } - if(resource.documentation_url){ - resourcesHTML +=` ` - } // resourcesHTML +=` HydroShare` resourcesHTML +=`

` diff --git a/hydroshare_community_resources_app/utils.py b/hydroshare_community_resources_app/utils.py index a7add6ad..d7649f0a 100644 --- a/hydroshare_community_resources_app/utils.py +++ b/hydroshare_community_resources_app/utils.py @@ -97,69 +97,16 @@ def get_most_recent_date(date_local_resource, date_api): return False -def update_resource(resource, hs, instance): - # logging.warning(science_metadata_json) +def update_resource(resource): single_resource = {} - if resource["resource_type"] == "ToolResource": - science_metadata_json = hs.getScienceMetadata(resource["resource_id"]) - # logging.warning(f'{science_metadata_json}') - image_url = science_metadata_json.get( - "app_icon", instance.placeholder_image - ).get("value", instance.placeholder_image) - web_site_url = ( - "" - if not science_metadata_json.get("app_home_page_url", "") - else science_metadata_json.get("app_home_page_url").get("value", "") - ) - github_url = ( - "" - if not science_metadata_json.get("source_code_url", "") - else science_metadata_json.get("source_code_url").get("value", "") - ) - help_page_url = ( - "" - if not science_metadata_json.get("help_page_url", "") - else science_metadata_json.get("help_page_url").get("value", "") - ) - if resource["resource_type"] == "CompositeResource": - resource_scrapping = requests.get(resource["resource_url"]) - image_url = ( - instance.placeholder_image - if not extract_value_by_name(resource_scrapping.content, "app_icon") - else extract_value_by_name(resource_scrapping.content, "app_icon") - ) - web_site_url = ( - "" - if not extract_value_by_name(resource_scrapping.content, "home_page_url") - else extract_value_by_name(resource_scrapping.content, "home_page_url") - ) - github_url = ( - "" - if not extract_value_by_name(resource_scrapping.content, "source_code_url") - else extract_value_by_name(resource_scrapping.content, "source_code_url") - ) - help_page_url = ( - "" - if not extract_value_by_name(resource_scrapping.content, "help_page_url") - else extract_value_by_name(resource_scrapping.content, "help_page_url") - ) - - if image_url == "": - image_url = instance.placeholder_image - single_resource = { "title": resource["resource_title"], "abstract": resource["abstract"], - "github_url": github_url, - "image": image_url, - "web_site_url": web_site_url, - "documentation_url": help_page_url, "resource_id": resource["resource_id"], "date_last_updated": resource["date_last_updated"], "resource_type": resource["resource_type"], "resource_url": resource["resource_url"], } - # logging.warning(single_resource) return single_resource diff --git a/hydroshare_community_resources_app/views.py b/hydroshare_community_resources_app/views.py index ed1215ef..a94c1e03 100644 --- a/hydroshare_community_resources_app/views.py +++ b/hydroshare_community_resources_app/views.py @@ -42,14 +42,19 @@ def hydroshare_community_resources_view(request): hs = HydroShare(prompt_auth=False) try: generators_rs = [] + + # GET THE IDS OF THE GROUPS FROM THE COMMUNITY group_ids = get_group_ids(instance.community_id) - logger.warning(group_ids) + + # QUERY THE RESOURCES OF EACH ONE OF THE GROUPS for group_id in group_ids: generators_rs.append(hs.resources(group=group_id)) + # JOIN THE RESOURCES OF EACH ONE OF THE GROUPS AND ALSO MAKE SURE NO RESOURCE IS REPEATED resources_api = join_generators(generators_rs) + + # FILTER THE RESOURCES BY THE CURATED IDS IF NEEDED if is_curated: - logger.warning("curated is true") curated_ids = get_curated_resources(hs=hs) resources_api = filter_resources_list_by_resources_id( resources_api, curated_ids @@ -57,7 +62,6 @@ def hydroshare_community_resources_view(request): resources_model = instance.resources.get("resources", []) for resource_api in resources_api: - logger.warning(resource_api["resource_id"]) matching_resource_model = get_dict_with_attribute( resources_model, "resource_id", resource_api["resource_id"] ) @@ -72,7 +76,7 @@ def hydroshare_community_resources_view(request): is_recent_date ): # If the resource retrieved from api is more recent, then update resource # logging.warning("resource has a more recent version") - single_resource = update_resource(resource_api, hs, instance) + single_resource = update_resource(resource_api) # logging.warning(single_resource) json_resources["resources"].append(single_resource) instance.resources = json_resources @@ -83,7 +87,7 @@ def hydroshare_community_resources_view(request): instance.resources = json_resources # If the resource is not here then create one else: - single_resource = update_resource(resource_api, hs, instance) + single_resource = update_resource(resource_api) json_resources["resources"].append(single_resource) instance.resources = json_resources