Repository: OCHA-DAP/hdx-ckan

Issue:
Add API action for updating resources
As per Alex, in prep for CPS metadata push.



Discussion:

Resolving patch:
diff --git a/ckanext-hdx_package/ckanext/hdx_package/helpers/update.py b/ckanext-hdx_package/ckanext/hdx_package/helpers/update.py
--- a/ckanext-hdx_package/ckanext/hdx_package/helpers/update.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/helpers/update.py
@@ -239,3 +239,20 @@ def hdx_package_update_metadata(context, data_dict):
         package['notes'] = ' '
     package = _get_action('package_update')(context, package)
     return package
+
+
+def hdx_resource_update_metadata(context, data_dict):
+    '''
+    With the default resource_update action from core ckan you need to supply the entire resource dict 
+    as a parameter and you can't supply just a modified field .
+    This function first loads the resource via resource_show() and then modifies the respective dict. 
+    '''
+    allowed_fields = ['last_data_update_date']
+
+    resource = _get_action('resource_show')(context, data_dict)
+    for key, value in data_dict.iteritems():
+        if key in allowed_fields:
+            resource[key] = value
+    resource = _get_action('resource_update')(context, resource)
+
+    return resource
diff --git a/ckanext-hdx_package/ckanext/hdx_package/plugin.py b/ckanext-hdx_package/ckanext/hdx_package/plugin.py
--- a/ckanext-hdx_package/ckanext/hdx_package/plugin.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/plugin.py
@@ -36,8 +36,10 @@ def _generate_license_list():
         license.License(license.LicenseCreativeCommonsAttribution()),
         license.License(license.LicenseCreativeCommonsAttributionShareAlike()),
         license.License(hdx_licenses.LicenseHdxOpenDatabaseLicense()),
-        license.License(hdx_licenses.LicenseHdxOpenDataCommonsAttributionLicense()),
-        license.License(hdx_licenses.LicenseHdxOpenDataCommonsPublicdomainDedicationAndLicense()),
+        license.License(
+            hdx_licenses.LicenseHdxOpenDataCommonsAttributionLicense()),
+        license.License(
+            hdx_licenses.LicenseHdxOpenDataCommonsPublicdomainDedicationAndLicense()),
         license.License(hdx_licenses.LicenseOtherPublicDomainNoRestrictions()),
         license.License(hdx_licenses.LicenseHdxMultiple()),
         license.License(hdx_licenses.LicenseHdxOther())
@@ -71,7 +73,7 @@ def before_map(self, map):
                     controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='resource_edit', ckan_icon='edit')
         map.connect('shorten_url', '/package/tools/shorten',
                     controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='shorten')
-        
+
         with SubMapper(map, controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController') as m:
             m.connect('add dataset', '/dataset/new', action='new')
             m.connect(
@@ -167,6 +169,7 @@ def get_actions(self):
             'package_update': update.package_update,
             'hdx_get_activity_list': hdx_actions.hdx_get_activity_list,
             'hdx_package_update_metadata': update.hdx_package_update_metadata,
+            'hdx_resource_update_metadata': update.hdx_resource_update_metadata,
             'tag_autocomplete': hdx_actions.hdx_tag_autocomplete_list,
             'package_create': hdx_actions.package_create
         }
diff --git a/ckanext-hdx_search/ckanext/hdx_search/controllers/search_controller.py b/ckanext-hdx_search/ckanext/hdx_search/controllers/search_controller.py
--- a/ckanext-hdx_search/ckanext/hdx_search/controllers/search_controller.py
+++ b/ckanext-hdx_search/ckanext/hdx_search/controllers/search_controller.py
@@ -1,5 +1,6 @@
 
 import logging
+import datetime
 import copy
 from urllib import urlencode
 
@@ -48,6 +49,7 @@
 from ckan.controllers.package import PackageController
 import ckanext.hdx_search.actions.actions as hdx_actions
 
+
 def _encode_params(params):
     return [(k, v.encode('utf-8') if isinstance(v, basestring) else str(v))
             for k, v in params]
@@ -58,7 +60,6 @@ def url_with_params(url, params):
     return url + u'?' + urlencode(params)
 
 
-
 def search_for_all(context, data_dict):
     '''This search is independent of the tab in which the user
        currently is.
@@ -70,13 +71,13 @@ def search_for_all(context, data_dict):
     if not sort:
         sort = 'score desc'
     search = {
-                'q': data_dict.get('q', None),
-                'fq': data_dict.get('fq', None),
-                'facet.field': facet_fields,
-                'facet.limit': 1000,
-                'rows': 10,
-                'sort': 'extras_indicator desc, ' + sort,
-            }
+        'q': data_dict.get('q', None),
+        'fq': data_dict.get('fq', None),
+        'facet.field': facet_fields,
+        'facet.limit': 1000,
+        'rows': 10,
+        'sort': 'extras_indicator desc, ' + sort,
+    }
 #     if tab == 'indicators':
 #         search['extras'] = {'ext_indicator': 1}
 #     elif tab == 'datasets':
@@ -101,14 +102,15 @@ def extract_one_indicator(result):
     ''' Extracts the first indicator from a search_for_all() result '''
 
     if len(result['results']) > 0 \
-        and result['results'][0] \
-        and 'indicator' in result['results'][0] \
-        and result['results'][0]['indicator'] == '1':
+            and result['results'][0] \
+            and 'indicator' in result['results'][0] \
+            and result['results'][0]['indicator'] == '1':
         indicator = [result['results'][0]]
     else:
         indicator = None
     return indicator
 
+
 def sort_features(features):
     return sorted(features, key=lambda x: x['count'], reverse=True)
 
@@ -136,7 +138,7 @@ def isolate_features(context, facets, q, tab, skip=0, limit=25):
     for p in facets['vocab_Topics']['items']:
         tags.append(p['name'])
         extract[p['name']] = {'name': p['name'], 'display_name': p['name'],
-                                  'url': h.url_for(controller='ckanext.hdx_search.controllers.search_controller:HDXSearchController', action='search', vocab_Topics=p['name']), 'description': '', 'last_update': '', 'feature_type': 'topic', 'count': p['count']}
+                              'url': h.url_for(controller='ckanext.hdx_search.controllers.search_controller:HDXSearchController', action='search', vocab_Topics=p['name']), 'description': '', 'last_update': '', 'feature_type': 'topic', 'count': p['count']}
 
     for g in facets['groups']['items']:
         tags.append(g['name'])
@@ -346,18 +348,26 @@ def _performing_search(self, q, fq, facets, limit, page, sort_by,
             'start': (page - 1) * limit,
             'sort': sort_by,
             'extras': search_extras
-            }
+        }
 
         self._decide_adding_dataset_criteria(data_dict)
 
         query = hdx_actions.package_search(context, data_dict)
 
+        if not query.get('results', None):
+            log.warn('No query results found for data_dict: {}. Query dict is: {}. Query time {}'.format(
+                str(data_dict), str(query), datetime.datetime.now()))
+
         all_result = search_for_all(context, data_dict)
         c.dataset_counts, c.indicator_counts = extract_counts(all_result)
 
         c.count = c.dataset_counts + c.indicator_counts
+        if not c.count:
+            log.warn('Dataset counts are zero for data_dict: {}. all_results dict is: {}. Query time {}'.format(
+                str(data_dict), str(query), datetime.datetime.now()))
+
         if c.tab == "all":
-            #c.features = isolate_features(
+            # c.features = isolate_features(
             #     context, query['search_facets'], q, c.tab)
             c.indicator = extract_one_indicator(all_result)
             c.facets = all_result['facets']
@@ -368,8 +378,7 @@ def _performing_search(self, q, fq, facets, limit, page, sort_by,
 
 #             if c.tab == 'features':
 #                 c.features, c.count = isolate_features(
-#                     context, query['search_facets'], q, c.tab, ((page - 1) * limit), limit)
-
+# context, query['search_facets'], q, c.tab, ((page - 1) * limit), limit)
 
 
 #             if c.tab == 'features':
@@ -380,8 +389,8 @@ def _performing_search(self, q, fq, facets, limit, page, sort_by,
 #                     item_count=c.count,
 #                     items_per_page=limit
 #                 )
-# #                 c.facets = query['facets']
-# #                 c.search_facets = query['search_facets']
+# c.facets = query['facets']
+# c.search_facets = query['search_facets']
 #                 c.page.items = c.features
 #             else:
 #                 c.page = h.Page(
@@ -391,8 +400,8 @@ def _performing_search(self, q, fq, facets, limit, page, sort_by,
 #                     item_count=query['count'],
 #                     items_per_page=limit
 #                 )
-# #                 c.facets = query['facets']
-# #                 c.search_facets = query['search_facets']
+# c.facets = query['facets']
+# c.search_facets = query['search_facets']
 #                 c.page.items = query['results']
 
         c.page = h.Page(
@@ -425,29 +434,29 @@ def _search_url(self, params, package_type=None):
         else:
             url = h.url_for('{0}_search'.format(package_type))
         return url_with_params(url, params)
-    
+
     def _set_filters_are_selected_flag(self):
         if len(c.fields_grouped) > 0 \
-            and ( '_show_filters' not in request.params or request.params['_show_filters'] != 'false') :
+                and ('_show_filters' not in request.params or request.params['_show_filters'] != 'false'):
             c.filters_are_selected = True
         else:
             c.filters_are_selected = False
-            
+
     def _set_remove_field_function(self):
         def remove_field(key, value=None, replace=None):
             return h.remove_url_param(key, value=value, replace=replace,
                                       controller='ckanext.hdx_search.controllers.search_controller:HDXSearchController', action='search')
 
         c.remove_field = remove_field
-    
+
     def _get_named_route(self):
         return 'search'
-    
+
     def _set_other_links(self):
         named_route = self._get_named_route()
-        params = { k:v for k, v in request.params.items() 
-                  if k in ['sort', 'q', 'organization', 'tags', 'license_id', 'groups', 'res_format', '_show_filters'] }
-        
+        params = {k: v for k, v in request.params.items()
+                  if k in ['sort', 'q', 'organization', 'tags', 'license_id', 'groups', 'res_format', '_show_filters']}
+
         c.other_links = {}
         c.other_links['all'] = h.url_for(named_route, **params)
         params_copy = params.copy()
@@ -461,8 +470,7 @@ def _set_other_links(self):
         c.other_links['features'] = h.url_for(named_route, **params_copy)
 
 #         c.other_links['params'] = params
-        c.other_links['params_noq'] = { k:v for k,v in params.items() 
-                                              if k not in ['q', '_show_filters'] }
-        c.other_links['params_nosort_noq'] = { k:v for k,v in params.items() 
-                                              if k not in ['sort', 'q'] }
-
+        c.other_links['params_noq'] = {k: v for k, v in params.items()
+                                       if k not in ['q', '_show_filters']}
+        c.other_links['params_nosort_noq'] = {k: v for k, v in params.items()
+                                              if k not in ['sort', 'q']}


Test patch:
diff --git a/ckanext-hdx_package/ckanext/hdx_package/tests/test_actions/test_resource_update.py b/ckanext-hdx_package/ckanext/hdx_package/tests/test_actions/test_resource_update.py
new file mode 100644
--- /dev/null
+++ b/ckanext-hdx_package/ckanext/hdx_package/tests/test_actions/test_resource_update.py
@@ -0,0 +1,44 @@
+'''
+Created on Dec 24, 2014
+
+@author: alexandru-m-g
+'''
+
+import logging as logging
+
+import ckan.model as model
+
+import ckanext.hdx_theme.tests.hdx_test_base as hdx_test_base
+import ckanext.hdx_theme.tests.hdx_test_with_inds_and_orgs as hdx_test_with_inds_and_orgs
+
+log = logging.getLogger(__name__)
+
+
+class TestHDXUpdateResource(hdx_test_with_inds_and_orgs.HDXWithIndsAndOrgsTest):
+
+    @classmethod
+    def _load_plugins(cls):
+        hdx_test_base.load_plugin('hdx_package hdx_theme')
+
+    def test_resource_update_metadata(self):
+
+        field = 'last_data_update_date'
+        new_field_value = '2014-12-10T23:04:22.596156'
+
+        context = {'ignore_auth': True,
+                   'model': model, 'session': model.Session, 'user': 'testsysadmin'}
+
+        resource = self._get_action('resource_show')(
+            context, {'id': 'hdx_test.csv'})
+
+        original = resource.get(field, None)
+
+        self._get_action('hdx_resource_update_metadata')(
+            context, {'id': resource['id'], field: new_field_value})
+
+        changed_resource = self._get_action('resource_show')(
+            context, {'id': 'hdx_test.csv'})
+        modified = changed_resource[field]
+
+        assert original != modified, '{} should have been changed by action'.format(
+            field)