{"action":"created","comment":{"url":"https://api.github.com/repos/hydroshare/hydroshare/pulls/comments/42397175","id":42397175,"diff_hunk":"@@ -0,0 +1,522 @@\n+__author__ = 'drew'\n+\n+\n+from django.dispatch import receiver\n+from hs_core.signals import *\n+from hs_geographic_feature_resource.models import GeographicFeatureResource, OriginalCoverage, GeographicFeatureMetaData\n+from hs_core import hydroshare\n+import tempfile\n+import shutil\n+import os\n+import pprint\n+from hs_geographic_feature_resource.parse_lib import *\n+from hs_geographic_feature_resource.forms import *\n+import zipfile, json\n+from hs_core.hydroshare.resource import ResourceFile\n+\n+def is_shapefiles(files):\n+#check if uploaded files are valid shapefiles (shp, shx, dbf)\n+    fn_list=[]\n+    for file in files:\n+        fn_list.append(file.name)\n+    return check_fn_for_shp(fn_list)\n+\n+\n+def check_fn_for_shp(files):\n+#used by is_shapefiles\n+#check a list of filenames contains valid shapefiles (shp, shx, dbf)\n+    shp, shx, dbf = False, False, False\n+    all_have_same_filename = False\n+    shp_filename, shx_filename, dbf_filename =None, None, None\n+    dir_count=0 # can have 0 or 1 folder\n+    if len(files) >= 3: # at least have 3 files: shp, shx, dbf\n+        for file in files:\n+            if file[len(file)-1]=='/':\n+                dir_count+=1\n+            else:\n+                fileName, fileExtension = os.path.splitext(file)\n+                if \".shp\" == fileExtension:\n+                    shp_filename = fileName\n+                    shp=True\n+                elif \".shx\" == fileExtension:\n+                    shx_filename = fileName\n+                    shx=True\n+                elif \".dbf\" == fileExtension:\n+                    dbf_filename = fileName\n+                    dbf=True\n+\n+        if shp_filename == shx_filename and shp_filename == dbf_filename:\n+            all_have_same_filename = True\n+\n+    if shp & shx & dbf & all_have_same_filename & (dir_count<=1):\n+        return True\n+    else:\n+        return False\n+\n+\n+def is_zipped_shapefiles(files):\n+#check if the uploaded zip files contains valid shapefiles (shp, shx, dbf)\n+    if(len(files) == 1) and '.zip' in files[0].name:\n+        zipfile_path=files[0].file.name\n+        if zipfile.is_zipfile(zipfile_path):\n+            zf = zipfile.ZipFile(zipfile_path, 'r')\n+            content_fn_list = zf.namelist()\n+            return check_fn_for_shp(content_fn_list)\n+    return False\n+\n+\n+#\n+# def is_spatialite(files):\n+# # check for sqlite\n+#     for file in files:\n+#         if \".sqlite\" in file.name:\n+#             return True\n+#     return False\n+\n+\n+def check_uploaded_files_type(files):\n+        rslt={}\n+        if is_shapefiles(files):\n+            uploaded_file_type = \"shp\"\n+            rslt[\"uploaded_file_type\"]=uploaded_file_type\n+            #create a temp foler and copy shapefiles\n+            tmp_dir=tempfile.mkdtemp()\n+            rslt[\"tmp_dir\"]=tmp_dir\n+            baseFilename=None\n+            files_list_for_extract_metadata = []\n+            for file in files:\n+                source = file.file.name\n+                fileName, fileExtension = os.path.splitext(file.name.lower())\n+                if fileExtension == \".shp\":\n+                    baseFilename=fileName\n+                #target = tmp_dir + \"/\" + res_titile_fn + fileExtension\n+                target = tmp_dir + \"/\" + fileName + fileExtension\n+                shutil.copy(source, target)\n+\n+            #shp_full_path = tmp_dir + \"/\" + res_titile_fn + \".shp\"\n+            shp_full_path = tmp_dir + \"/\" + fileName + \".shp\"\n+            rslt['shp_full_path']=shp_full_path\n+\n+            rslt[\"baseFilename\"]=baseFilename\n+            uploadedFileCount=len(files)\n+            rslt[\"uploadedFileCount\"]=uploadedFileCount\n+            uploadedFilenameString_dict={}\n+            for i in range(len(files)):\n+                uploadedFilenameString_dict[files[i].name]=str(i)\n+            uploadedFilenameString = json.dumps(uploadedFilenameString_dict)\n+            rslt[\"uploadedFilenameString\"]=uploadedFilenameString\n+            rslt[\"are_files_valid\"] = True\n+            rslt['message'] = 'All files are validated.'\n+\n+        elif is_zipped_shapefiles(files):\n+            uploaded_file_type = \"zipped_shp\"\n+            rslt[\"uploaded_file_type\"]=uploaded_file_type\n+            tmp_dir=tempfile.mkdtemp()\n+            rslt[\"tmp_dir\"]=tmp_dir\n+            zipfile_path=files[0].file.name\n+            zf = zipfile.ZipFile(zipfile_path, 'r')\n+            fn_list = zf.namelist()\n+            zf.extractall(path=tmp_dir) #extract all zip contents (files and folders) to tmp_dir (/tmp/tmpXXXXXX/)\n+            zf.close()\n+            baseFilename=None\n+            shp_full_path=None\n+            dir_count=0\n+            uploadedFileCount=0\n+            from django.core.files.uploadedfile import UploadedFile\n+            del files[:]\n+            uploadedFilenameString_dict={}\n+            for old_fn in fn_list:\n+                source = tmp_dir + '/' +old_fn\n+                target = tmp_dir\n+                if os.path.isfile(source): #only add files, filter out folders (should be 0 or 1 folder)\n+                    fileName, fileExtension = os.path.splitext(old_fn)\n+\n+                    if '/' in fileName: #if the file is inside a folder, move it to the root of tmp_dir\n+                        path_and_filename=fileName.split('/')\n+                        fileName= path_and_filename[len(path_and_filename)-1]\n+                        target=tmp_dir + '/' + fileName + fileExtension\n+                        shutil.move(source,target)\n+                    else:\n+                        target = source\n+\n+                    if fileExtension==\".shp\": #save the real path to .shp file\n+                        baseFilename=fileName #save the name of .shp as the baseFileName\n+                        shp_full_path=target\n+                    files.append(UploadedFile(file=open(target, 'r'), name=fileName + fileExtension))\n+                    uploadedFileCount+=1\n+                    uploadedFilenameString_dict[fileName + fileExtension]=str(1)\n+                else:\n+                    dir_count+=1 #folder count +1 (should be 0 or 1 folder)\n+\n+            rslt['shp_full_path']=shp_full_path\n+\n+            rslt[\"baseFilename\"]=baseFilename\n+            rslt[\"uploadedFileCount\"]=uploadedFileCount\n+            uploadedFilenameString = json.dumps(uploadedFilenameString_dict)\n+            rslt[\"uploadedFilenameString\"]=uploadedFilenameString\n+            rslt[\"are_files_valid\"] = True\n+            rslt['message'] = 'All files are validated.'\n+        else:\n+            rslt[\"are_files_valid\"] = False\n+            rslt['message'] = 'Please upload valid file(s).'\n+\n+        return rslt\n+\n+def parse_shp_zshp(uploaded_file_type, baseFilename, uploadedFileCount, uploadedFilenameString, shp_full_path):\n+    try:\n+        metadata_array=[]\n+        metadata_dict={}\n+\n+        #fileTypeInfo_dict\n+        originalFileInfo_dict={}\n+        originalFileInfo_dict[\"fileType\"]=\"SHP\" if uploaded_file_type == \"shp\" else \"ZSHP\"\n+        originalFileInfo_dict[\"baseFilename\"] = baseFilename\n+        originalFileInfo_dict[\"fileCount\"]= uploadedFileCount\n+        originalFileInfo_dict[\"filenameString\"]=uploadedFilenameString\n+        metadata_array.append({\"OriginalFileInfo\": originalFileInfo_dict})\n+        metadata_dict[\"originalfileinfo\"]=originalFileInfo_dict\n+\n+        # wgs84 extent\n+        parsed_md_dict = parse_shp(shp_full_path)\n+        if parsed_md_dict[\"wgs84_extent_dict\"][\"westlimit\"]!=UNKNOWN_STR:\n+            coverage_dict={\"Coverage\": {\"type\": \"box\", \"value\": parsed_md_dict[\"wgs84_extent_dict\"]}}\n+            metadata_array.append(coverage_dict)\n+            metadata_dict[\"coverage\"]=coverage_dict\n+\n+        #original extent\n+        original_coverage_dict ={}\n+        original_coverage_dict[\"originalcoverage\"]={\"northlimit\":parsed_md_dict[\"origin_extent_dict\"][\"northlimit\"],\n+                                                    \"southlimit\":parsed_md_dict[\"origin_extent_dict\"][\"southlimit\"],\n+                                                    \"westlimit\":parsed_md_dict[\"origin_extent_dict\"][\"westlimit\"],\n+                                                    \"eastlimit\":parsed_md_dict[\"origin_extent_dict\"][\"eastlimit\"],\n+                                                    \"projection_string\":parsed_md_dict[\"origin_projection_string\"],\n+                                                    \"projection_name\":parsed_md_dict[\"origin_projection_name\"],\n+                                                    \"datum\":parsed_md_dict[\"origin_datum\"],\n+                                                    \"unit\":parsed_md_dict[\"origin_unit\"]\n+                                                    }\n+        metadata_array.append(original_coverage_dict)\n+        metadata_dict[\"originalcoverage\"]=original_coverage_dict\n+\n+        #field\n+\n+        field_info_array=[]\n+        field_name_list=parsed_md_dict[\"field_meta_dict\"]['field_list']\n+        for field_name in field_name_list:\n+            field_info_dict_item={}\n+            field_info_dict_item['fieldinformation']=parsed_md_dict[\"field_meta_dict\"][\"field_attr_dict\"][field_name]\n+            field_info_array.append(field_info_dict_item)\n+        metadata_array.extend(field_info_array)\n+        metadata_dict['field_info_array']=field_info_array\n+\n+        #geometry\n+        geometryinformation={\"featureCount\":parsed_md_dict[\"feature_count\"],\"geometryType\":parsed_md_dict[\"geometry_type\"]}\n+        metadata_array.append({\"geometryinformation\": geometryinformation})\n+        metadata_dict[\"geometryinformation\"]=geometryinformation\n+        print \"\\n\"\n+        pp = pprint.PrettyPrinter(indent=4)\n+        pp.pprint (metadata_array)\n+\n+        return (metadata_array, metadata_dict)\n+    except:\n+        raise Exception(\"Parse Shapefiles Failed!\")\n+\n+\n+\n+# receiver used to extract metadata after user click on \"create resource\"\n+@receiver(pre_create_resource, sender=GeographicFeatureResource)\n+def geofeature_pre_create_resource(sender, **kwargs):\n+    if sender is GeographicFeatureResource:\n+        tmp_dir = None\n+        try:\n+            files = kwargs['files']\n+            metadata = kwargs['metadata']\n+            validate_files_dict = kwargs['validate_files']\n+\n+            if files:\n+                rslt = check_uploaded_files_type(files)\n+                validate_files_dict['are_files_valid'] = rslt['are_files_valid']\n+                validate_files_dict['message'] = rslt['message']\n+\n+                if validate_files_dict['are_files_valid']:\n+                    tmp_dir=rslt['tmp_dir']\n+                    baseFilename=rslt['baseFilename']\n+                    uploaded_file_type=rslt['uploaded_file_type']\n+                    uploadedFileCount=rslt['uploadedFileCount']\n+                    uploadedFilenameString=rslt['uploadedFilenameString']\n+                    shp_full_path=rslt['shp_full_path']\n+                    if uploaded_file_type == \"shp\" or uploaded_file_type == \"zipped_shp\":\n+                        meta_array, meta_dict=parse_shp_zshp(uploaded_file_type, baseFilename, uploadedFileCount, uploadedFilenameString, shp_full_path)\n+                        metadata.extend(meta_array)\n+            else:\n+                validate_files_dict['are_files_valid'] = False\n+                validate_files_dict['message'] = 'Please upload valid file(s).'\n+        except:\n+            validate_files_dict['are_files_valid'] = False\n+            validate_files_dict['message'] = 'Uploaded files are invalid or corrupt.'\n+        finally:\n+            if tmp_dir is not None:\n+                shutil.rmtree(tmp_dir)\n+                tmp_dir = None\n+\n+\n+# This handler is executed only when a metadata element is added as part of editing a resource\n+@receiver(pre_metadata_element_create, sender=GeographicFeatureResource)\n+def metadata_element_pre_create_handler(sender, **kwargs):\n+    element_name = kwargs['element_name'].lower()\n+    request = kwargs['request']\n+\n+    if element_name == 'originalcoverage':\n+        element_form = OriginalCoverageValidationForm(data=request.POST)\n+    elif element_name == 'fieldinformation':\n+        element_form = FieldInformationValidationForm(data=request.POST)\n+    elif element_name == 'geometryinformation':\n+        element_form = GeometryInformationValidationForm(data=request.POST)\n+\n+    if element_form.is_valid():\n+        return {'is_valid': True, 'element_data_dict': element_form.cleaned_data}\n+    else:\n+        return {'is_valid': False, 'element_data_dict': None}\n+\n+\n+# This handler is executed only when a metadata element is added as part of editing a resource\n+@receiver(pre_metadata_element_update, sender=GeographicFeatureResource)\n+def metadata_element_pre_update_handler(sender, **kwargs):\n+    element_name = kwargs['element_name'].lower()\n+    request = kwargs['request']\n+\n+    if element_name == 'originalcoverage':\n+        element_form = OriginalCoverageValidationForm(data=request.POST)\n+    elif element_name == 'fieldinformation':\n+        element_form = FieldInformationValidationForm(data=request.POST)\n+    elif element_name == 'geometryinformation':\n+        element_form = GeometryInformationValidationForm(data=request.POST)\n+\n+    if element_form.is_valid():\n+        return {'is_valid': True, 'element_data_dict': element_form.cleaned_data}\n+    else:\n+        return {'is_valid': False, 'element_data_dict': None}\n+\n+\n+@receiver(pre_delete_file_from_resource, sender=GeographicFeatureResource)\n+def geofeature_pre_delete_file_from_resource(sender, **kwargs):\n+    if sender is GeographicFeatureResource:\n+        res_obj = kwargs['resource']\n+        del_file = kwargs['file']\n+        #validate_files_dict = kwargs['validate_files']\n+        one_file_removed=True\n+        all_file_removed=False\n+        ori_file_info=res_obj.metadata.originalfileinfo.all().first()\n+        if ori_file_info.fileType == \"SHP\" or ori_file_info.fileType==\"ZSHP\":\n+            del_f_fullname=del_file.resource_file.name.lower()\n+            del_f_fullname= del_f_fullname[del_f_fullname.rfind('/')+1:]\n+            del_f_name, del_f_ext = os.path.splitext(del_f_fullname)\n+            if del_f_ext in [\".shp\", \".shx\", \".dbf\"]:\n+                # validate_files_dict['are_files_valid'] = False\n+                # validate_files_dict['message'] = \" The shp, shx or dbf files can not be removed.\"\n+                all_file_removed=True\n+                one_file_removed=False\n+\n+                for f in ResourceFile.objects.filter(object_id=res_obj.id):\n+                    res_f_fullname = f.resource_file.name.lower()\n+                    res_f_fullname= res_f_fullname[res_f_fullname.rfind('/')+1:]\n+                    if res_f_fullname != del_f_fullname:\n+                        f.resource_file.delete()\n+                        f.delete()\n+\n+            elif del_f_ext == \".prj\":\n+                originalcoverage_obj=res_obj.metadata.originalcoverage.all().first()\n+                res_obj.metadata.update_element('OriginalCoverage', element_id=originalcoverage_obj.id, projection_string=UNKNOWN_STR, projection_name =UNKNOWN_STR, datum =UNKNOWN_STR, unit =UNKNOWN_STR)\n+                res_obj.metadata.coverages.all().delete()\n+\n+            if one_file_removed:\n+                ori_fn_dict=json.loads(ori_file_info.filenameString)\n+                if del_f_fullname in ori_fn_dict:\n+                    del ori_fn_dict[del_f_fullname]\n+                    res_obj.metadata.update_element('OriginalFileInfo', element_id=ori_file_info.id,filenameString=json.dumps(ori_fn_dict))\n+            elif all_file_removed:\n+                res_obj.metadata.originalfileinfo.all().delete()\n+                res_obj.metadata.geometryinformation.all().delete()\n+                res_obj.metadata.fieldinformation.all().delete()\n+                res_obj.metadata.originalcoverage.all().delete()\n+                res_obj.metadata.coverages.all().delete()\n+\n+    pass\n+\n+@receiver(pre_add_files_to_resource, sender=GeographicFeatureResource)\n+def geofeature_pre_add_files_to_resource(sender, **kwargs):\n+    if sender is GeographicFeatureResource:\n+        res_obj = kwargs['resource']\n+        files = kwargs['files']\n+        validate_files_dict = kwargs['validate_files']\n+        ori_file_info=res_obj.metadata.originalfileinfo.all().first()\n+        some_new_files_added=True\n+        # res_file_list=ResourceFile.objects.filter(object_id=res_obj.id)\n+        # for res_f in res_file_list:\n+        #     n=res_f.resource_file.name\n+\n+        try:\n+            #for crt_f","path":"hs_geographic_feature_resource/receivers.py","position":358,"original_position":358,"commit_id":"0e0d5cddfaf572b54906c46e0c596a3ec74aed45","original_commit_id":"0e0d5cddfaf572b54906c46e0c596a3ec74aed45","user":{"login":"ironfroggy","id":34982,"avatar_url":"https://avatars.githubusercontent.com/u/34982?v=3","gravatar_id":"","url":"https://api.github.com/users/ironfroggy","html_url":"https://github.com/ironfroggy","followers_url":"https://api.github.com/users/ironfroggy/followers","following_url":"https://api.github.com/users/ironfroggy/following{/other_user}","gists_url":"https://api.github.com/users/ironfroggy/gists{/gist_id}","starred_url":"https://api.github.com/users/ironfroggy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ironfroggy/subscriptions","organizations_url":"https://api.github.com/users/ironfroggy/orgs","repos_url":"https://api.github.com/users/ironfroggy/repos","events_url":"https://api.github.com/users/ironfroggy/events{/privacy}","received_events_url":"https://api.github.com/users/ironfroggy/received_events","type":"User","site_admin":false},"body":"delete","created_at":"2015-10-19T17:01:59Z","updated_at":"2015-10-19T17:01:59Z","html_url":"https://github.com/hydroshare/hydroshare/pull/678#discussion_r42397175","pull_request_url":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678","_links":{"self":{"href":"https://api.github.com/repos/hydroshare/hydroshare/pulls/comments/42397175"},"html":{"href":"https://github.com/hydroshare/hydroshare/pull/678#discussion_r42397175"},"pull_request":{"href":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678"}}},"pull_request":{"url":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678","id":48087366,"html_url":"https://github.com/hydroshare/hydroshare/pull/678","diff_url":"https://github.com/hydroshare/hydroshare/pull/678.diff","patch_url":"https://github.com/hydroshare/hydroshare/pull/678.patch","issue_url":"https://api.github.com/repos/hydroshare/hydroshare/issues/678","number":678,"state":"open","locked":false,"title":"1.6.1 -- big-merge-503-geographic-feature-res-type-phase-1","user":{"login":"zhiyuli","id":10070599,"avatar_url":"https://avatars.githubusercontent.com/u/10070599?v=3","gravatar_id":"","url":"https://api.github.com/users/zhiyuli","html_url":"https://github.com/zhiyuli","followers_url":"https://api.github.com/users/zhiyuli/followers","following_url":"https://api.github.com/users/zhiyuli/following{/other_user}","gists_url":"https://api.github.com/users/zhiyuli/gists{/gist_id}","starred_url":"https://api.github.com/users/zhiyuli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zhiyuli/subscriptions","organizations_url":"https://api.github.com/users/zhiyuli/orgs","repos_url":"https://api.github.com/users/zhiyuli/repos","events_url":"https://api.github.com/users/zhiyuli/events{/privacy}","received_events_url":"https://api.github.com/users/zhiyuli/received_events","type":"User","site_admin":false},"body":"Testing coverage report\r\n\r\n```\r\nName                                       Stmts   Miss  Cover\r\n\r\n__init__                                       0      0   100%\r\nhs_access_control/__init__                     1      0   100%\r\nhs_access_control/models                     658    188    71%\r\nhs_app_netCDF/__init__                         0      0   100%\r\nhs_app_netCDF/admin                            4      0   100%\r\nhs_app_netCDF/forms                          121     70    42%\r\nhs_app_netCDF/models                         188    135    28%\r\nhs_app_netCDF/nc_functions/__init__            0      0   100%\r\nhs_app_netCDF/receivers                      195    184     6%\r\nhs_app_netCDF/serialization                  330    166    50%\r\nhs_app_netCDF/tasks                            0      0   100%\r\nhs_app_timeseries/__init__                     0      0   100%\r\nhs_app_timeseries/admin                        4      0   100%\r\nhs_app_timeseries/forms                      109     27    75%\r\nhs_app_timeseries/models                     269    169    37%\r\nhs_app_timeseries/receivers                  310    291     6%\r\nhs_app_timeseries/tasks                        0      0   100%\r\nhs_core/__init__                               0      0   100%\r\nhs_core/admin                                  8      0   100%\r\nhs_core/autocomplete_light_registry           21     14    33%\r\nhs_core/forms                                715    484    32%\r\nhs_core/hydroshare/__init__                    5      0   100%\r\nhs_core/hydroshare/date_util                  53     34    36%\r\nhs_core/hydroshare/discovery                   8      4    50%\r\nhs_core/hydroshare/hs_bagit                  132     44    67%\r\nhs_core/hydroshare/resource                  228     88    61%\r\nhs_core/hydroshare/social                     77     61    21%\r\nhs_core/hydroshare/users                     215    159    26%\r\nhs_core/hydroshare/utils                     237     76    68%\r\nhs_core/languages_iso                          1      0   100%\r\nhs_core/management/__init__                    1      0   100%\r\nhs_core/management/commands/__init__           1      0   100%\r\nhs_core/models                              1286    308    76%\r\nhs_core/receivers                             78     71     9%\r\nhs_core/serialization                        912    680    25%\r\nhs_core/signals                                9      0   100%\r\nhs_core/tasks                                  0      0   100%\r\nhs_core/templatetags/__init__                  1      0   100%\r\nhs_core/testing                               11      0   100%\r\nhs_core/views/__init__                       442    370    16%\r\nhs_core/views/pagination                       3      0   100%\r\nhs_core/views/resource_rest_api              169    113    33%\r\nhs_core/views/serializers                     75     15    80%\r\nhs_core/views/user_rest_api                    8      0   100%\r\nhs_core/views/utils                          105     78    26%\r\nhs_geo_raster_resource/__init__                0      0   100%\r\nhs_geo_raster_resource/admin                   4      0   100%\r\nhs_geo_raster_resource/forms                 154     93    40%\r\nhs_geo_raster_resource/models                177    113    36%\r\nhs_geo_raster_resource/receivers              98     88    10%\r\nhs_geo_raster_resource/serialization         341    210    38%\r\nhs_geo_raster_resource/tasks                   0      0   100%\r\nhs_geographic_feature_resource/__init__        0      0   100%\r\nhs_geographic_feature_resource/admin           4      0   100%\r\nhs_geographic_feature_resource/forms         157    104    34%\r\nhs_geographic_feature_resource/models        252     47    81%\r\nhs_geographic_feature_resource/parse_lib      84     10    88%\r\nhs_geographic_feature_resource/receivers     370     72    81%\r\nhs_metrics/__init__                            0      0   100%\r\nhs_metrics/models                              0      0   100%\r\nhs_model_program/__init__                      0      0   100%\r\nhs_model_program/admin                         4      0   100%\r\nhs_model_program/forms                        39     12    69%\r\nhs_model_program/models                       78     35    55%\r\nhs_model_program/receivers                    27     19    30%\r\nhs_modelinstance/__init__                      0      0   100%\r\nhs_modelinstance/admin                         4      0   100%\r\nhs_modelinstance/forms                        51     19    63%\r\nhs_modelinstance/models                      115     70    39%\r\nhs_modelinstance/receivers                    22     14    36%\r\nhs_modelinstance/tasks                         0      0   100%\r\nhs_swat_modelinstance/__init__                 0      0   100%\r\nhs_swat_modelinstance/admin                    5      0   100%\r\nhs_swat_modelinstance/forms                  164     61    63%\r\nhs_swat_modelinstance/models                 402    279    31%\r\nhs_swat_modelinstance/receivers               33     24    27%\r\nhs_swat_modelinstance/tasks                    0      0   100%\r\nhs_tools_resource/__init__                     0      0   100%\r\nhs_tools_resource/admin                        4      0   100%\r\nhs_tools_resource/forms                       85     30    65%\r\nhs_tools_resource/models                     169    118    30%\r\nhs_tools_resource/receivers                   39     29    26%\r\nhydroshare/__init__                            2      0   100%\r\nhydroshare/celery                             11      1    91%\r\nhydroshare/local_settings                     62      0   100%\r\nhydroshare/settings                           55      2    96%\r\nirods_browser_app/__init__                     0      0   100%\r\nirods_browser_app/models                       0      0   100%\r\nmanage                                         6      0   100%\r\nref_ts/__init__                                0      0   100%\r\nref_ts/admin                                   3      0   100%\r\nref_ts/models                                260    191    27%\r\nref_ts/receivers                              10      5    50%\r\ntheme/__init__                                 0      0   100%\r\ntheme/admin                                   12      0   100%\r\ntheme/blog_mods/__init__                       0      0   100%\r\ntheme/blog_mods/admin                          7      3    57%\r\ntheme/blog_mods/models                         0      0   100%\r\ntheme/models                                  85     12    86%\r\ntheme/templatetags/__init__                    0      0   100%\r\n\r\nTOTAL                                      10385   5490    47%\r\n```","created_at":"2015-10-19T15:33:02Z","updated_at":"2015-10-19T17:01:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3f03606021d47a805a47031f49242077c6d6f20b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678/commits","review_comments_url":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678/comments","review_comment_url":"https://api.github.com/repos/hydroshare/hydroshare/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hydroshare/hydroshare/issues/678/comments","statuses_url":"https://api.github.com/repos/hydroshare/hydroshare/statuses/0e0d5cddfaf572b54906c46e0c596a3ec74aed45","head":{"label":"hydroshare:big-merge-503-geographic-feature-res-type-phase-1","ref":"big-merge-503-geographic-feature-res-type-phase-1","sha":"0e0d5cddfaf572b54906c46e0c596a3ec74aed45","user":{"login":"hydroshare","id":3444493,"avatar_url":"https://avatars.githubusercontent.com/u/3444493?v=3","gravatar_id":"","url":"https://api.github.com/users/hydroshare","html_url":"https://github.com/hydroshare","followers_url":"https://api.github.com/users/hydroshare/followers","following_url":"https://api.github.com/users/hydroshare/following{/other_user}","gists_url":"https://api.github.com/users/hydroshare/gists{/gist_id}","starred_url":"https://api.github.com/users/hydroshare/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hydroshare/subscriptions","organizations_url":"https://api.github.com/users/hydroshare/orgs","repos_url":"https://api.github.com/users/hydroshare/repos","events_url":"https://api.github.com/users/hydroshare/events{/privacy}","received_events_url":"https://api.github.com/users/hydroshare/received_events","type":"Organization","site_admin":false},"repo":{"id":24703136,"name":"hydroshare","full_name":"hydroshare/hydroshare","owner":{"login":"hydroshare","id":3444493,"avatar_url":"https://avatars.githubusercontent.com/u/3444493?v=3","gravatar_id":"","url":"https://api.github.com/users/hydroshare","html_url":"https://github.com/hydroshare","followers_url":"https://api.github.com/users/hydroshare/followers","following_url":"https://api.github.com/users/hydroshare/following{/other_user}","gists_url":"https://api.github.com/users/hydroshare/gists{/gist_id}","starred_url":"https://api.github.com/users/hydroshare/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hydroshare/subscriptions","organizations_url":"https://api.github.com/users/hydroshare/orgs","repos_url":"https://api.github.com/users/hydroshare/repos","events_url":"https://api.github.com/users/hydroshare/events{/privacy}","received_events_url":"https://api.github.com/users/hydroshare/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hydroshare/hydroshare","description":"HydroShare is a collaborative website being developed for better access to data and models in the hydrologic sciences. ","fork":false,"url":"https://api.github.com/repos/hydroshare/hydroshare","forks_url":"https://api.github.com/repos/hydroshare/hydroshare/forks","keys_url":"https://api.github.com/repos/hydroshare/hydroshare/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hydroshare/hydroshare/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hydroshare/hydroshare/teams","hooks_url":"https://api.github.com/repos/hydroshare/hydroshare/hooks","issue_events_url":"https://api.github.com/repos/hydroshare/hydroshare/issues/events{/number}","events_url":"https://api.github.com/repos/hydroshare/hydroshare/events","assignees_url":"https://api.github.com/repos/hydroshare/hydroshare/assignees{/user}","branches_url":"https://api.github.com/repos/hydroshare/hydroshare/branches{/branch}","tags_url":"https://api.github.com/repos/hydroshare/hydroshare/tags","blobs_url":"https://api.github.com/repos/hydroshare/hydroshare/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hydroshare/hydroshare/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hydroshare/hydroshare/git/refs{/sha}","trees_url":"https://api.github.com/repos/hydroshare/hydroshare/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hydroshare/hydroshare/statuses/{sha}","languages_url":"https://api.github.com/repos/hydroshare/hydroshare/languages","stargazers_url":"https://api.github.com/repos/hydroshare/hydroshare/stargazers","contributors_url":"https://api.github.com/repos/hydroshare/hydroshare/contributors","subscribers_url":"https://api.github.com/repos/hydroshare/hydroshare/subscribers","subscription_url":"https://api.github.com/repos/hydroshare/hydroshare/subscription","commits_url":"https://api.github.com/repos/hydroshare/hydroshare/commits{/sha}","git_commits_url":"https://api.github.com/repos/hydroshare/hydroshare/git/commits{/sha}","comments_url":"https://api.github.com/repos/hydroshare/hydroshare/comments{/number}","issue_comment_url":"https://api.github.com/repos/hydroshare/hydroshare/issues/comments{/number}","contents_url":"https://api.github.com/repos/hydroshare/hydroshare/contents/{+path}","compare_url":"https://api.github.com/repos/hydroshare/hydroshare/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hydroshare/hydroshare/merges","archive_url":"https://api.github.com/repos/hydroshare/hydroshare/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hydroshare/hydroshare/downloads","issues_url":"https://api.github.com/repos/hydroshare/hydroshare/issues{/number}","pulls_url":"https://api.github.com/repos/hydroshare/hydroshare/pulls{/number}","milestones_url":"https://api.github.com/repos/hydroshare/hydroshare/milestones{/number}","notifications_url":"https://api.github.com/repos/hydroshare/hydroshare/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hydroshare/hydroshare/labels{/name}","releases_url":"https://api.github.com/repos/hydroshare/hydroshare/releases{/id}","created_at":"2014-10-02T02:19:41Z","updated_at":"2015-10-16T12:02:24Z","pushed_at":"2015-10-19T15:39:50Z","git_url":"git://github.com/hydroshare/hydroshare.git","ssh_url":"git@github.com:hydroshare/hydroshare.git","clone_url":"https://github.com/hydroshare/hydroshare.git","svn_url":"https://github.com/hydroshare/hydroshare","homepage":"https://www.hydroshare.org","size":69289,"stargazers_count":12,"watchers_count":12,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":166,"forks":2,"open_issues":166,"watchers":12,"default_branch":"develop"}},"base":{"label":"hydroshare:develop","ref":"develop","sha":"80a170fa93060242e50d4f239b644ac59140a14b","user":{"login":"hydroshare","id":3444493,"avatar_url":"https://avatars.githubusercontent.com/u/3444493?v=3","gravatar_id":"","url":"https://api.github.com/users/hydroshare","html_url":"https://github.com/hydroshare","followers_url":"https://api.github.com/users/hydroshare/followers","following_url":"https://api.github.com/users/hydroshare/following{/other_user}","gists_url":"https://api.github.com/users/hydroshare/gists{/gist_id}","starred_url":"https://api.github.com/users/hydroshare/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hydroshare/subscriptions","organizations_url":"https://api.github.com/users/hydroshare/orgs","repos_url":"https://api.github.com/users/hydroshare/repos","events_url":"https://api.github.com/users/hydroshare/events{/privacy}","received_events_url":"https://api.github.com/users/hydroshare/received_events","type":"Organization","site_admin":false},"repo":{"id":24703136,"name":"hydroshare","full_name":"hydroshare/hydroshare","owner":{"login":"hydroshare","id":3444493,"avatar_url":"https://avatars.githubusercontent.com/u/3444493?v=3","gravatar_id":"","url":"https://api.github.com/users/hydroshare","html_url":"https://github.com/hydroshare","followers_url":"https://api.github.com/users/hydroshare/followers","following_url":"https://api.github.com/users/hydroshare/following{/other_user}","gists_url":"https://api.github.com/users/hydroshare/gists{/gist_id}","starred_url":"https://api.github.com/users/hydroshare/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hydroshare/subscriptions","organizations_url":"https://api.github.com/users/hydroshare/orgs","repos_url":"https://api.github.com/users/hydroshare/repos","events_url":"https://api.github.com/users/hydroshare/events{/privacy}","received_events_url":"https://api.github.com/users/hydroshare/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hydroshare/hydroshare","description":"HydroShare is a collaborative website being developed for better access to data and models in the hydrologic sciences. ","fork":false,"url":"https://api.github.com/repos/hydroshare/hydroshare","forks_url":"https://api.github.com/repos/hydroshare/hydroshare/forks","keys_url":"https://api.github.com/repos/hydroshare/hydroshare/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hydroshare/hydroshare/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hydroshare/hydroshare/teams","hooks_url":"https://api.github.com/repos/hydroshare/hydroshare/hooks","issue_events_url":"https://api.github.com/repos/hydroshare/hydroshare/issues/events{/number}","events_url":"https://api.github.com/repos/hydroshare/hydroshare/events","assignees_url":"https://api.github.com/repos/hydroshare/hydroshare/assignees{/user}","branches_url":"https://api.github.com/repos/hydroshare/hydroshare/branches{/branch}","tags_url":"https://api.github.com/repos/hydroshare/hydroshare/tags","blobs_url":"https://api.github.com/repos/hydroshare/hydroshare/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hydroshare/hydroshare/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hydroshare/hydroshare/git/refs{/sha}","trees_url":"https://api.github.com/repos/hydroshare/hydroshare/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hydroshare/hydroshare/statuses/{sha}","languages_url":"https://api.github.com/repos/hydroshare/hydroshare/languages","stargazers_url":"https://api.github.com/repos/hydroshare/hydroshare/stargazers","contributors_url":"https://api.github.com/repos/hydroshare/hydroshare/contributors","subscribers_url":"https://api.github.com/repos/hydroshare/hydroshare/subscribers","subscription_url":"https://api.github.com/repos/hydroshare/hydroshare/subscription","commits_url":"https://api.github.com/repos/hydroshare/hydroshare/commits{/sha}","git_commits_url":"https://api.github.com/repos/hydroshare/hydroshare/git/commits{/sha}","comments_url":"https://api.github.com/repos/hydroshare/hydroshare/comments{/number}","issue_comment_url":"https://api.github.com/repos/hydroshare/hydroshare/issues/comments{/number}","contents_url":"https://api.github.com/repos/hydroshare/hydroshare/contents/{+path}","compare_url":"https://api.github.com/repos/hydroshare/hydroshare/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hydroshare/hydroshare/merges","archive_url":"https://api.github.com/repos/hydroshare/hydroshare/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hydroshare/hydroshare/downloads","issues_url":"https://api.github.com/repos/hydroshare/hydroshare/issues{/number}","pulls_url":"https://api.github.com/repos/hydroshare/hydroshare/pulls{/number}","milestones_url":"https://api.github.com/repos/hydroshare/hydroshare/milestones{/number}","notifications_url":"https://api.github.com/repos/hydroshare/hydroshare/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hydroshare/hydroshare/labels{/name}","releases_url":"https://api.github.com/repos/hydroshare/hydroshare/releases{/id}","created_at":"2014-10-02T02:19:41Z","updated_at":"2015-10-16T12:02:24Z","pushed_at":"2015-10-19T15:39:50Z","git_url":"git://github.com/hydroshare/hydroshare.git","ssh_url":"git@github.com:hydroshare/hydroshare.git","clone_url":"https://github.com/hydroshare/hydroshare.git","svn_url":"https://github.com/hydroshare/hydroshare","homepage":"https://www.hydroshare.org","size":69289,"stargazers_count":12,"watchers_count":12,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":166,"forks":2,"open_issues":166,"watchers":12,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678"},"html":{"href":"https://github.com/hydroshare/hydroshare/pull/678"},"issue":{"href":"https://api.github.com/repos/hydroshare/hydroshare/issues/678"},"comments":{"href":"https://api.github.com/repos/hydroshare/hydroshare/issues/678/comments"},"review_comments":{"href":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678/comments"},"review_comment":{"href":"https://api.github.com/repos/hydroshare/hydroshare/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hydroshare/hydroshare/pulls/678/commits"},"statuses":{"href":"https://api.github.com/repos/hydroshare/hydroshare/statuses/0e0d5cddfaf572b54906c46e0c596a3ec74aed45"}}}}