Repository: hydroshare/hydroshare

Issue:
Add web service endpoints to content-level metadata for multidimensional content type
For some content types, standards-based web services are automatically created when a content aggregation of those types is created within a resource. Under this issue, we will add links to the automatically generated OpenDAP service within the content-level metadata for multidimensional content type aggregations. Details of our discussion and the design are in the following Google Doc:

https://docs.google.com/document/d/1ZFFQMltaM01xlfBusw-TolT5sf5fUulVRU_XLPT3XEQ/edit#

The following are needed for this issue:

- [ ] Construct and add a link to the automatically created OpenDAP service according to the rules in the Google Doc
- [ ] Create a button for copying the service URL
- [ ] Display link within the content-level metadata pane for the aggregation

The OpenDAP service does not have an automatic preview functionality, so there will be no "Preview" button.


Discussion:
While there is no "Preview" the service URL should be clickable and link to the OpenDAP server data access form below that effectively serves as a preview for multidimensional data
![image](https://user-images.githubusercontent.com/3456833/80280637-8288c400-86c2-11ea-9619-aea4b2fd5d93.png)


Resolving patch:
diff --git a/hs_composite_resource/models.py b/hs_composite_resource/models.py
--- a/hs_composite_resource/models.py
+++ b/hs_composite_resource/models.py
@@ -571,14 +571,15 @@ def get_missing_file_type_metadata_info(self):
     def get_data_services_urls(self):
         """
         Generates data services URLs for the resource.
-
         If the resource contains any GeoFeature or GeoRaster content, and if it's public,
         generate data service endpoints.
+        If the resource contains any multidimensional content and it's public,
+        generate THREDDS catalog service endpoint as well.
         """
-
         wfs_url = None
         wms_url = None
         wcs_url = None
+        thredds_url = None
         if self.raccess.public:
             try:
                 resource_data_types = [lf.data_type for lf in self.logical_files]
@@ -592,14 +593,19 @@ def get_data_services_urls(self):
                 if 'GeographicRaster' in resource_data_types:
                     wcs_url = service_url.format('wcs')
                     wms_url = service_url.format('wms')
-
             except Exception as e:
                 logger.exception("get_data_services_urls: " + str(e))
 
+            if 'Multidimensional' in resource_data_types:
+                thredds_url = (
+                    f'{settings.THREDDS_SERVER_URL}catalog/hydroshare/resources/{self.short_id}/data/contents/'
+                    f'catalog.html'
+                )
         data_services_urls = {
             'wms_url': wms_url,
             'wfs_url': wfs_url,
-            'wcs_url': wcs_url
+            'wcs_url': wcs_url,
+            'thredds_url': thredds_url
         }
 
         return data_services_urls
diff --git a/hs_file_types/models/netcdf.py b/hs_file_types/models/netcdf.py
--- a/hs_file_types/models/netcdf.py
+++ b/hs_file_types/models/netcdf.py
@@ -7,10 +7,11 @@
 import netCDF4
 import numpy as np
 from django.core.exceptions import ValidationError
+from django.conf import settings
 from django.db import models, transaction
 from django.forms.models import formset_factory, BaseFormSet
 from django.template import Template, Context
-from dominate.tags import div, legend, form, button, p, textarea, _input
+from dominate.tags import div, legend, form, button, p, em, a, textarea, _input
 
 import hs_file_types.nc_functions.nc_dump as nc_dump
 import hs_file_types.nc_functions.nc_meta as nc_meta
@@ -47,10 +48,30 @@ def original_coverage(self):
         # with this metadata object
         return self.ori_coverage.all().first()
 
+    def _get_opendap_html(self):
+        opendap_div = div(cls="content-block")
+        res_id = self.logical_file.resource.short_id
+        file_name = self.logical_file.aggregation_name
+        opendap_url = f'{settings.THREDDS_SERVER_URL}dodsC/hydroshare/resources/{res_id}/data/contents/{file_name}.html'
+        with opendap_div:
+            legend('OPeNDAP using DAP2')
+            em('The netCDF data in this multidimensional content aggregation may be accessed at the link below '
+               'using the OPeNDAP DAP2 protocol enabled on the HydroShare deployment of Unidata’s THREDDS data server. '
+               'This enables direct and programmable access to this data through ')
+            a(" OPeNDAP client software",
+              href="https://www.opendap.org/support/OPeNDAP-clients",
+              target="_blank")
+            with div(style="margin-top:10px;"):
+                a(opendap_url, href=opendap_url, target='_blank')
+
+        return opendap_div.render()
+
     def get_html(self):
         """overrides the base class function"""
 
         html_string = super(NetCDFFileMetaData, self).get_html()
+        if self.logical_file.resource.raccess.public:
+            html_string += self._get_opendap_html()
         if self.spatial_coverage:
             html_string += self.spatial_coverage.get_html()
         if self.originalCoverage:
diff --git a/hydroshare/settings.py b/hydroshare/settings.py
--- a/hydroshare/settings.py
+++ b/hydroshare/settings.py
@@ -701,6 +701,11 @@
 
 HSWS_ACTIVATED = False
 
+# HydroShare THREDDS Data Server URL
+THREDDS_SERVER_URL = 'https://thredds.hydroshare.org/thredds/'
+# HydroShare Geoserver URL
+HSWS_GEOSERVER_URL = 'https://geoserver.hydroshare.org/geoserver'
+
 # celery task names to be recorded in task notification model
 TASK_NAME_LIST = [
     'hs_core.tasks.create_bag_by_irods',


Test patch: