{"action":"created","comment":{"url":"https://api.github.com/repos/ioos/compliance-checker/pulls/comments/26345612","id":26345612,"diff_hunk":"@@ -0,0 +1,723 @@\n+#!/usr/bin/env python\n+'''\n+compliance_checker.glider_dac\n+\n+Compliance Test Suite for the IOOS National Glider Data Assembly Center\n+https://github.com/ioos/ioosngdac/wiki\n+'''\n+\n+from compliance_checker.base import BaseCheck, BaseNCCheck, Result\n+import numpy as np\n+\n+class GliderCheck(BaseNCCheck):\n+    @classmethod\n+    def beliefs(cls): \n+        '''\n+        Not applicable for gliders\n+        '''\n+        return {}\n+\n+    @classmethod\n+    def make_result(cls, level, score, out_of, name, messages):\n+        return Result(level, (score, out_of), name, messages)\n+\n+    def setup(self, ds):\n+        pass\n+\n+    def check_locations(self, ds):\n+        '''\n+        Validates that lat and lon are indeed variables\n+        '''\n+        level = BaseCheck.HIGH\n+        out_of = 1\n+        score = 0\n+        messages = []\n+        test = ('lat' in ds.dataset.variables and 'lon' in ds.dataset.variables)\n+        if test:\n+            score += 1\n+        else:\n+            messages.append(\"lat and lon don't exist\")\n+        return self.make_result(level, score, out_of, 'Verifies lat and lon are variables', messages)\n+\n+    def check_location_dimensions(self, ds):\n+        '''\n+        Validates that lat and lon are valid timeseries variables\n+        '''\n+        level = BaseCheck.MEDIUM\n+        out_of = 26\n+        score = 0\n+        messages = []\n+\n+        test = 'lat' in ds.dataset.variables\n+        score += int(test)\n+        if not test:\n+            messages.append('lat is a required variable')\n+            return self.make_result(level, score, out_of, 'Lat and Lon are Time Series', messages)\n+\n+        test = 'lon' in ds.dataset.variables\n+        score += int(test)\n+        if not test:\n+            messages.append('lon is a required variable')\n+            return self.make_result(level, score, out_of, 'Lat and Lon are Time Series', messages)\n+\n+        required_coordinate_attributes = [\n+            '_FillValue',\n+            'ancillary_variables',\n+            'comment',\n+            'coordinate_reference_frame',\n+            'long_name',\n+            'observation_type',\n+            'platform',\n+            'reference',\n+            'standard_name',\n+            'units',\n+            'valid_max',\n+            'valid_min'\n+        ]\n+        for attribute in required_coordinate_attributes:\n+            test = hasattr(ds.dataset.variables['lat'], attribute)\n+            score += int(test)\n+            if not test:\n+                messages.append('%s attribute is required for lat' % attribute)\n+            \n+            test = hasattr(ds.dataset.variables['lon'], attribute)\n+            score += int(test)\n+            if not test:\n+                messages.append('%s attribute is required for lat' % attribute)\n+\n+        return self.make_result(level, score, out_of, 'Lat and Lon are Time Series', messages)\n+\n+    def check_variables(self, ds):\n+        '''\n+        Verifies the dataset has the required variables\n+        '''\n+        required_variables = [\n+            'trajectory',\n+            'time',\n+            'lat',\n+            'lon',\n+            'pressure',\n+            'depth',\n+            'temperature',\n+            'conductivity',\n+            'density',\n+            'profile_id',\n+            'profile_time',\n+            'profile_lat',\n+            'profile_lon',\n+            'time_uv',\n+            'lat_uv',\n+            'lon_uv',\n+            'u',\n+            'v',\n+            'platform',\n+            'instrument_ctd'\n+        ]\n+        level = BaseCheck.HIGH\n+        out_of = len(required_variables)\n+        score = 0\n+        messages = []\n+        for variable in required_variables:\n+            test = variable in ds.dataset.variables\n+            score += int(test)\n+            if not test:\n+                messages.append(\"%s is a required variable\" % variable)\n+        return self.make_result(level, score, out_of, 'Required Variables', messages)\n+\n+    def check_qc_variables(self, ds):\n+        '''\n+        Verifies the dataset has all the required QC variables\n+        '''\n+        required_variables = [\n+            'time_qc',\n+            'lat_qc',\n+            'lon_qc',\n+            'pressure_qc',\n+            'depth_qc',\n+            'temperature_qc',\n+            'conductivity_qc',\n+            'density_qc',\n+            'profile_time_qc',\n+            'profile_lat_qc',\n+            'profile_lon_qc',\n+            'time_uv_qc',\n+            'lat_uv_qc',\n+            'lon_uv_qc',\n+            'u_qc',\n+            'v_qc'\n+        ]\n+\n+        required_attributes = [\n+            'flag_meanings',\n+            'flag_values',\n+            'long_name',\n+            'standard_name',\n+            'valid_max',\n+            'valid_min'\n+        ]\n+        level = BaseCheck.MEDIUM\n+        out_of = len(required_variables)\n+        score = 0\n+        messages = []\n+        for variable in required_variables:\n+            test = variable in ds.dataset.variables\n+            if not test:\n+                messages.append(\"%s is a required qc variable\" % variable)\n+                continue\n+            for field in required_attributes:\n+                if not hasattr(ds.dataset.variables[variable], field):\n+                    messages.append('%s is missing attribute %s' % (variable, field))\n+                    test = False\n+                    break\n+            score += int(test)\n+        return self.make_result(level, score, out_of, 'Required QC Variables', messages)\n+\n+    def check_global_attributes(self, ds):\n+        '''\n+        Verifies the base metadata in the global attributes\n+        '''\n+        attribute_fields = [\n+            'Conventions',\n+            'Metadata_Conventions',\n+            'comment',\n+            'contributor_name',\n+            'contributor_role',\n+            'creator_email',\n+            'creator_name',\n+            'creator_url',\n+            'date_created',\n+            'date_issued',\n+            'date_modified',\n+            'format_version',\n+            'history',\n+            'id',\n+            'institution',\n+            'keywords',\n+            'keywords_vocabulary',\n+            'license',\n+            'metadata_link',\n+            'naming_authority',\n+            'platform_type',\n+            'processing_level',\n+            'project',\n+            'publisher_email',\n+            'publisher_name',\n+            'publisher_url',\n+            'references',\n+            'sea_name',\n+            'source',\n+            'standard_name_vocabulary',\n+            'summary',\n+            'title',\n+            'wmo_id'\n+        ]\n+        level = BaseCheck.MEDIUM\n+        out_of = len(attribute_fields)\n+        score = 0\n+        messages = []\n+        for field in attribute_fields:\n+            test = hasattr(ds.dataset, field)\n+            score += int(test)\n+            if not test:\n+                messages.append('%s global attribute is missing' % field)\n+        \n+        return self.make_result(level, score, out_of, 'Required Global Attributes', messages)\n+\n+    def check_primary_variable_attributes(self, ds):\n+        '''\n+        Verifies that each primary variable has the necessary metadata\n+        '''\n+        level = BaseCheck.MEDIUM\n+        out_of = 0\n+        score = 0\n+        messages = []\n+        primary_variables = [\n+            'lat',\n+            'lon',\n+            'pressure',\n+            'depth',\n+            'temperature',\n+            'conductivity',\n+            'salinity',\n+            'density',\n+            'profile_time',\n+            'profile_lat',\n+            'profile_lon',\n+            'time_uv',\n+            'lat_uv',\n+            'lon_uv',\n+            'u',\n+            'v'\n+        ]\n+        required_attributes = [\n+            '_FillValue',\n+            'units',\n+            'standard_name',\n+            'observation_type'\n+        ]\n+        for var in primary_variables:\n+            for attribute in required_attributes:\n+                test = hasattr(ds.dataset.variables[var], attribute)\n+                out_of += 1\n+                score += int(test)\n+                if not test:\n+                    messages.append('%s variable is missing attribute %s' % (var, attribute))\n+\n+        return self.make_result(level, score, out_of, 'Required Variable Attributes', messages)\n+\n+    def check_dimensions(self, ds):\n+        '''\n+        NetCDF files submitted by the individual glider operators contain 2\n+        dimension variables:\n+         - time\n+         - traj\n+        '''\n+        level = BaseCheck.HIGH\n+        score = 0\n+        messages = []\n+\n+        required_dimensions = [\n+            'time',\n+            'traj_strlen'\n+        ]\n+        out_of = len(required_dimensions)\n+\n+        for dimension in ds.dataset.dimensions:\n+            test =  dimension in required_dimensions\n+            score += int(test)\n+            if not test:\n+                messages.append('%s is not a valid dimension' % dimension)\n+        return self.make_result(level, score, out_of, 'Required Dimensions', messages)\n+\n+    def check_trajectory_variables(self, ds):\n+        '''\n+        The trajectory variable stores a character array that identifies the\n+        deployment during which the data was gathered. This variable is used by\n+        the DAC to aggregate all individual NetCDF profiles containing the same\n+        trajectory value into a single trajectory profile data set. This value\n+        should be a character array that uniquely identifies the deployment and\n+        each individual NetCDF file from the deployment data set should have\n+        the same value.\n+        '''\n+        level = BaseCheck.MEDIUM\n+        out_of = 5\n+        score = 0\n+        messages = []\n+\n+        test = 'trajectory' in ds.dataset.variables\n+        score += int(test)\n+        if not test:\n+            messages.append('trajectory variable not found')\n+            return self.make_result(level, score, out_of, 'Trajectory Variable', messages)\n+        test = ds.dataset.variables['trajectory'].dimensions == ('traj_strlen',)\n+        score += int(test)\n+        if not test:\n+            messages.append('trajectory has an invalid dimension')\n+        test = hasattr(ds.dataset.variables['trajectory'], 'cf_role')\n+        score += int(test)\n+        if not test:\n+            messages.append('trajectory is missing cf_role')\n+        test = hasattr(ds.dataset.variables['trajectory'], 'comment')\n+        score += int(test)\n+        if not test:\n+            messages.append('trajectory is missing comment')\n+        test = hasattr(ds.dataset.variables['trajectory'], 'long_name')\n+        score += int(test)\n+        if not test:\n+            messages.append('trajectory is missing long_name')\n+        return self.make_result(level, score, out_of, 'Trajectory Variable', messages)\n+\n+    def check_time_series_variables(self, ds):\n+        '''\n+        Verifies that the time coordinate variable is correct\n+        '''\n+\n+        level = BaseCheck.HIGH\n+        out_of = 16\n+        score = 0\n+        messages = []\n+\n+        test = 'time' in ds.dataset.variables\n+        score += int(test)\n+        if not test:\n+            messages.append('Required coordinate variable time is missing')\n+            return self.make_result(level, score, out_of, 'Time Series Variable', messages)\n+\n+        test = ds.dataset.variables['time'].dtype.str == '<f8'\n+        score += int(test)\n+        if not test:\n+            messages.append('Invalid variable type for time, it should be float64')\n+\n+        test = ds.dataset.variables['time'].ancillary_variables == 'time_qc'\n+        score += int(test)\n+        if not test:\n+            messages.append('Invalid ancillary_variables attribute for time, should be \"time_qc\"')\n+\n+        test = ds.dataset.variables['time'].calendar == 'gregorian'\n+        score += int(test)\n+        if not test:\n+            messages.append('Invalid calendar for time, should be \"gregorian\"')\n+\n+        test = ds.dataset.variables['time'].long_name == 'Time'\n+        score += int(test)\n+        if not test:\n+            messages.append('Invalid long_name for time, should be \"Time\"')\n+\n+        test = ds.dataset.variables['time'].observation_type == 'measured'\n+        score += int(test)\n+        if not test:\n+            messages.append('Invalid observation_type for time, should be \"measured\"')\n+\n+        test = ds.dataset.variables['time'].standard_name == 'time'\n+        score += int(test)\n+        if not test:\n+            messages.append('Invalid standard name for time, should be \"time\"')\n+\n+        test = hasattr(ds.dataset.variables['time'], 'units')\n+        score += int(test)\n+        if not test:\n+            messages.append('No units defined for time')\n+\n+        test = 'time_qc' in ds.dataset.variables\n+        score += int(test)\n+        if not test:\n+            messages.append('time_qc is not defined')\n+            return self.make_result(level, score, out_of, 'Time Series Variable', messages)\n+\n+        required_time_qc_attributes = [\n+            '_FillValue',\n+            'flag_meanings',\n+            'flag_values',\n+            'long_name',\n+            'standard_name',\n+            'valid_max',\n+            'valid_min'\n+        ]\n+        for attribute in required_time_qc_attributes:\n+            test = hasattr(ds.dataset.variables['time_qc'], attribute)\n+            score += int(test)\n+            if not test:\n+                messages.append('%s attribute is required for time_qc' % attribute)\n+\n+        return self.make_result(level, score, out_of, 'Time Series Variable', messages)\n+\n+    def check_depth_coordinates(self, ds):\n+        '''\n+        Verifies that the pressure coordinate/data variable is correct\n+        '''\n+\n+        level = BaseCheck.HIGH\n+        out_of = 34\n+        score = 0\n+        messages = []\n+\n+        coordinates = ['pressure', 'depth']\n+\n+        for coordinate in coordinates:\n+            test = coordinate in ds.dataset.variables\n+            score += int(test)\n+\n+            if not test:\n+                messages.append('Required coordinate variable %s is missing' % coordinate)\n+                return self.make_result(level, score, out_of, 'Depth/Pressure Variables', messages)\n+\n+        required_values = {\n+        }\n+\n+        data_vars = { i: ds.dataset.variables[i] for i in coordinates}\n+        for key, value in required_values.iteritems():\n+            for data_var in data_vars:\n+                if not hasattr(data_vars[data_var], key):\n+                    messages.append('%s variable requires attribute %s with a value of %s' % (data_var, key, value))\n+                    continue\n+                if not test:\n+                    messages.append('%s.%s != %s' % (data_var, key, value))\n+                    continue\n+                score += 1\n+\n+        required_attributes = [\n+            '_FillValue',\n+            'ancillary_variables',\n+            'accuracy',\n+            'ancillary_variables',\n+            'comment',\n+            'instrument',\n+            'long_name',\n+            'platform',\n+            'positive',\n+            'precision',\n+            'reference_datum',\n+            'resolution',\n+            'standard_name',\n+            'units',\n+            'valid_max',\n+            'valid_min'\n+        ]\n+\n+        for field in required_attributes:\n+            for data_var in data_vars:\n+                if not hasattr(data_vars[data_var], field):\n+                    messages.append('%s variable requires attribute %s' % (data_var, field))\n+                    continue\n+                score += 1\n+\n+        return self.make_result(level, score, out_of, 'Depth/Pressure Variables', messages)\n+\n+    def check_ctd_variables(self, ds):\n+        '''\n+        Verifies that the CTD Variables are the correct data type and contain\n+        the correct metadata\n+        '''\n+\n+        level = BaseCheck.HIGH\n+        out_of = 56\n+        score = 0\n+        messages = []\n+\n+        variables = [\n+            'temperature',\n+            'conductivity',\n+            'salinity',\n+            'density'\n+        ]\n+\n+        required_fields = [\n+            'accuracy',\n+            'ancillary_variables',\n+            'instrument',\n+            'long_name',\n+            'observation_type',\n+            'platform',\n+            'precision',\n+            'resolution',\n+            'standard_name',\n+            'units',\n+            'valid_max',\n+            'valid_min'\n+        ]\n+\n+        for var in variables:\n+            if var not in ds.dataset.variables:\n+                messages.append('%s variable missing' % var)\n+                continue\n+            nc_var = ds.dataset.variables[var]\n+\n+            test = nc_var.dtype.str == '<f8'\n+            score += int(test)\n+            if not test:\n+                messages.append('%s variable is incorrect data type')\n+\n+            for field in required_fields:\n+                if not hasattr(nc_var, field):\n+                    messages.append('%s variable is missing required attribute %s' % (var, field))\n+                    continue\n+                score += 1\n+\n+            score += 1\n+        return self.make_result(level, score, out_of, 'CTD Variables', messages)\n+\n+\n+    def check_profile_vars(self, ds):\n+        '''\n+        Verifies that the profile variables are the of the correct data type\n+        and contain the correct metadata\n+        '''\n+\n+        level = BaseCheck.MEDIUM\n+        out_of = 74\n+        score = 0\n+        messages = []\n+\n+        data_struct = {\n+            'profile_id' : {\n+                'dtype' : '<i4',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            },\n+            'profile_time' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units'\n+                ]\n+            },\n+            'profile_lat' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            },\n+            'profile_lon' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            },\n+            'lat_uv' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            },\n+            'lon_uv' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            },\n+            'u' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            },\n+            'v' : {\n+                'dtype' : '<f8',\n+                'fields' : [\n+                    '_FillValue',\n+                    'comment',\n+                    'long_name',\n+                    'observation_type',\n+                    'platform',\n+                    'standard_name',\n+                    'units',\n+                    'valid_min',\n+                    'valid_max'\n+                ]\n+            }\n+        }\n+","path":"compliance_checker/glider_dac.py","position":639,"original_position":639,"commit_id":"634292786e4fc91a89aa21cd046a6ec2739d1337","original_commit_id":"634292786e4fc91a89aa21cd046a6ec2739d1337","user":{"login":"lukecampbell","id":1155163,"avatar_url":"https://avatars.githubusercontent.com/u/1155163?v=3","gravatar_id":"","url":"https://api.github.com/users/lukecampbell","html_url":"https://github.com/lukecampbell","followers_url":"https://api.github.com/users/lukecampbell/followers","following_url":"https://api.github.com/users/lukecampbell/following{/other_user}","gists_url":"https://api.github.com/users/lukecampbell/gists{/gist_id}","starred_url":"https://api.github.com/users/lukecampbell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lukecampbell/subscriptions","organizations_url":"https://api.github.com/users/lukecampbell/orgs","repos_url":"https://api.github.com/users/lukecampbell/repos","events_url":"https://api.github.com/users/lukecampbell/events{/privacy}","received_events_url":"https://api.github.com/users/lukecampbell/received_events","type":"User","site_admin":false},"body":"@daf I really like this pattern, think I'm gonna use it more often for validation stuff.","created_at":"2015-03-12T21:20:38Z","updated_at":"2015-03-12T21:20:38Z","html_url":"https://github.com/ioos/compliance-checker/pull/92#discussion_r26345612","pull_request_url":"https://api.github.com/repos/ioos/compliance-checker/pulls/92","_links":{"self":{"href":"https://api.github.com/repos/ioos/compliance-checker/pulls/comments/26345612"},"html":{"href":"https://github.com/ioos/compliance-checker/pull/92#discussion_r26345612"},"pull_request":{"href":"https://api.github.com/repos/ioos/compliance-checker/pulls/92"}}},"pull_request":{"url":"https://api.github.com/repos/ioos/compliance-checker/pulls/92","id":30945714,"html_url":"https://github.com/ioos/compliance-checker/pull/92","diff_url":"https://github.com/ioos/compliance-checker/pull/92.diff","patch_url":"https://github.com/ioos/compliance-checker/pull/92.patch","issue_url":"https://api.github.com/repos/ioos/compliance-checker/issues/92","number":92,"state":"closed","locked":false,"title":"GliderDAC Checks","user":{"login":"lukecampbell","id":1155163,"avatar_url":"https://avatars.githubusercontent.com/u/1155163?v=3","gravatar_id":"","url":"https://api.github.com/users/lukecampbell","html_url":"https://github.com/lukecampbell","followers_url":"https://api.github.com/users/lukecampbell/followers","following_url":"https://api.github.com/users/lukecampbell/following{/other_user}","gists_url":"https://api.github.com/users/lukecampbell/gists{/gist_id}","starred_url":"https://api.github.com/users/lukecampbell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lukecampbell/subscriptions","organizations_url":"https://api.github.com/users/lukecampbell/orgs","repos_url":"https://api.github.com/users/lukecampbell/repos","events_url":"https://api.github.com/users/lukecampbell/events{/privacy}","received_events_url":"https://api.github.com/users/lukecampbell/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-03-11T14:06:29Z","updated_at":"2015-03-12T21:20:38Z","closed_at":"2015-03-11T14:26:57Z","merged_at":"2015-03-11T14:26:57Z","merge_commit_sha":"d96421b6f8a9f1ee99450915bf0c4c2199de8b89","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ioos/compliance-checker/pulls/92/commits","review_comments_url":"https://api.github.com/repos/ioos/compliance-checker/pulls/92/comments","review_comment_url":"https://api.github.com/repos/ioos/compliance-checker/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ioos/compliance-checker/issues/92/comments","statuses_url":"https://api.github.com/repos/ioos/compliance-checker/statuses/634292786e4fc91a89aa21cd046a6ec2739d1337","head":{"label":"lukecampbell:gliderdac","ref":"gliderdac","sha":"634292786e4fc91a89aa21cd046a6ec2739d1337","user":{"login":"lukecampbell","id":1155163,"avatar_url":"https://avatars.githubusercontent.com/u/1155163?v=3","gravatar_id":"","url":"https://api.github.com/users/lukecampbell","html_url":"https://github.com/lukecampbell","followers_url":"https://api.github.com/users/lukecampbell/followers","following_url":"https://api.github.com/users/lukecampbell/following{/other_user}","gists_url":"https://api.github.com/users/lukecampbell/gists{/gist_id}","starred_url":"https://api.github.com/users/lukecampbell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lukecampbell/subscriptions","organizations_url":"https://api.github.com/users/lukecampbell/orgs","repos_url":"https://api.github.com/users/lukecampbell/repos","events_url":"https://api.github.com/users/lukecampbell/events{/privacy}","received_events_url":"https://api.github.com/users/lukecampbell/received_events","type":"User","site_admin":false},"repo":{"id":21203441,"name":"compliance-checker","full_name":"lukecampbell/compliance-checker","owner":{"login":"lukecampbell","id":1155163,"avatar_url":"https://avatars.githubusercontent.com/u/1155163?v=3","gravatar_id":"","url":"https://api.github.com/users/lukecampbell","html_url":"https://github.com/lukecampbell","followers_url":"https://api.github.com/users/lukecampbell/followers","following_url":"https://api.github.com/users/lukecampbell/following{/other_user}","gists_url":"https://api.github.com/users/lukecampbell/gists{/gist_id}","starred_url":"https://api.github.com/users/lukecampbell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lukecampbell/subscriptions","organizations_url":"https://api.github.com/users/lukecampbell/orgs","repos_url":"https://api.github.com/users/lukecampbell/repos","events_url":"https://api.github.com/users/lukecampbell/events{/privacy}","received_events_url":"https://api.github.com/users/lukecampbell/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/lukecampbell/compliance-checker","description":"Python tool to check your datasets vs compliance standards","fork":true,"url":"https://api.github.com/repos/lukecampbell/compliance-checker","forks_url":"https://api.github.com/repos/lukecampbell/compliance-checker/forks","keys_url":"https://api.github.com/repos/lukecampbell/compliance-checker/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lukecampbell/compliance-checker/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lukecampbell/compliance-checker/teams","hooks_url":"https://api.github.com/repos/lukecampbell/compliance-checker/hooks","issue_events_url":"https://api.github.com/repos/lukecampbell/compliance-checker/issues/events{/number}","events_url":"https://api.github.com/repos/lukecampbell/compliance-checker/events","assignees_url":"https://api.github.com/repos/lukecampbell/compliance-checker/assignees{/user}","branches_url":"https://api.github.com/repos/lukecampbell/compliance-checker/branches{/branch}","tags_url":"https://api.github.com/repos/lukecampbell/compliance-checker/tags","blobs_url":"https://api.github.com/repos/lukecampbell/compliance-checker/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lukecampbell/compliance-checker/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lukecampbell/compliance-checker/git/refs{/sha}","trees_url":"https://api.github.com/repos/lukecampbell/compliance-checker/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lukecampbell/compliance-checker/statuses/{sha}","languages_url":"https://api.github.com/repos/lukecampbell/compliance-checker/languages","stargazers_url":"https://api.github.com/repos/lukecampbell/compliance-checker/stargazers","contributors_url":"https://api.github.com/repos/lukecampbell/compliance-checker/contributors","subscribers_url":"https://api.github.com/repos/lukecampbell/compliance-checker/subscribers","subscription_url":"https://api.github.com/repos/lukecampbell/compliance-checker/subscription","commits_url":"https://api.github.com/repos/lukecampbell/compliance-checker/commits{/sha}","git_commits_url":"https://api.github.com/repos/lukecampbell/compliance-checker/git/commits{/sha}","comments_url":"https://api.github.com/repos/lukecampbell/compliance-checker/comments{/number}","issue_comment_url":"https://api.github.com/repos/lukecampbell/compliance-checker/issues/comments{/number}","contents_url":"https://api.github.com/repos/lukecampbell/compliance-checker/contents/{+path}","compare_url":"https://api.github.com/repos/lukecampbell/compliance-checker/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lukecampbell/compliance-checker/merges","archive_url":"https://api.github.com/repos/lukecampbell/compliance-checker/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lukecampbell/compliance-checker/downloads","issues_url":"https://api.github.com/repos/lukecampbell/compliance-checker/issues{/number}","pulls_url":"https://api.github.com/repos/lukecampbell/compliance-checker/pulls{/number}","milestones_url":"https://api.github.com/repos/lukecampbell/compliance-checker/milestones{/number}","notifications_url":"https://api.github.com/repos/lukecampbell/compliance-checker/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lukecampbell/compliance-checker/labels{/name}","releases_url":"https://api.github.com/repos/lukecampbell/compliance-checker/releases{/id}","created_at":"2014-06-25T13:06:46Z","updated_at":"2014-06-23T23:59:43Z","pushed_at":"2015-03-11T14:14:03Z","git_url":"git://github.com/lukecampbell/compliance-checker.git","ssh_url":"git@github.com:lukecampbell/compliance-checker.git","clone_url":"https://github.com/lukecampbell/compliance-checker.git","svn_url":"https://github.com/lukecampbell/compliance-checker","homepage":"","size":1243,"stargazers_count":0,"watchers_count":0,"language":"nesC","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ioos:master","ref":"master","sha":"df771d8322abc95ae3e3c4ac85845662f4281aaf","user":{"login":"ioos","id":5313832,"avatar_url":"https://avatars.githubusercontent.com/u/5313832?v=3","gravatar_id":"","url":"https://api.github.com/users/ioos","html_url":"https://github.com/ioos","followers_url":"https://api.github.com/users/ioos/followers","following_url":"https://api.github.com/users/ioos/following{/other_user}","gists_url":"https://api.github.com/users/ioos/gists{/gist_id}","starred_url":"https://api.github.com/users/ioos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ioos/subscriptions","organizations_url":"https://api.github.com/users/ioos/orgs","repos_url":"https://api.github.com/users/ioos/repos","events_url":"https://api.github.com/users/ioos/events{/privacy}","received_events_url":"https://api.github.com/users/ioos/received_events","type":"Organization","site_admin":false},"repo":{"id":13279768,"name":"compliance-checker","full_name":"ioos/compliance-checker","owner":{"login":"ioos","id":5313832,"avatar_url":"https://avatars.githubusercontent.com/u/5313832?v=3","gravatar_id":"","url":"https://api.github.com/users/ioos","html_url":"https://github.com/ioos","followers_url":"https://api.github.com/users/ioos/followers","following_url":"https://api.github.com/users/ioos/following{/other_user}","gists_url":"https://api.github.com/users/ioos/gists{/gist_id}","starred_url":"https://api.github.com/users/ioos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ioos/subscriptions","organizations_url":"https://api.github.com/users/ioos/orgs","repos_url":"https://api.github.com/users/ioos/repos","events_url":"https://api.github.com/users/ioos/events{/privacy}","received_events_url":"https://api.github.com/users/ioos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ioos/compliance-checker","description":"Python tool to check your datasets vs compliance standards","fork":false,"url":"https://api.github.com/repos/ioos/compliance-checker","forks_url":"https://api.github.com/repos/ioos/compliance-checker/forks","keys_url":"https://api.github.com/repos/ioos/compliance-checker/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ioos/compliance-checker/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ioos/compliance-checker/teams","hooks_url":"https://api.github.com/repos/ioos/compliance-checker/hooks","issue_events_url":"https://api.github.com/repos/ioos/compliance-checker/issues/events{/number}","events_url":"https://api.github.com/repos/ioos/compliance-checker/events","assignees_url":"https://api.github.com/repos/ioos/compliance-checker/assignees{/user}","branches_url":"https://api.github.com/repos/ioos/compliance-checker/branches{/branch}","tags_url":"https://api.github.com/repos/ioos/compliance-checker/tags","blobs_url":"https://api.github.com/repos/ioos/compliance-checker/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ioos/compliance-checker/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ioos/compliance-checker/git/refs{/sha}","trees_url":"https://api.github.com/repos/ioos/compliance-checker/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ioos/compliance-checker/statuses/{sha}","languages_url":"https://api.github.com/repos/ioos/compliance-checker/languages","stargazers_url":"https://api.github.com/repos/ioos/compliance-checker/stargazers","contributors_url":"https://api.github.com/repos/ioos/compliance-checker/contributors","subscribers_url":"https://api.github.com/repos/ioos/compliance-checker/subscribers","subscription_url":"https://api.github.com/repos/ioos/compliance-checker/subscription","commits_url":"https://api.github.com/repos/ioos/compliance-checker/commits{/sha}","git_commits_url":"https://api.github.com/repos/ioos/compliance-checker/git/commits{/sha}","comments_url":"https://api.github.com/repos/ioos/compliance-checker/comments{/number}","issue_comment_url":"https://api.github.com/repos/ioos/compliance-checker/issues/comments{/number}","contents_url":"https://api.github.com/repos/ioos/compliance-checker/contents/{+path}","compare_url":"https://api.github.com/repos/ioos/compliance-checker/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ioos/compliance-checker/merges","archive_url":"https://api.github.com/repos/ioos/compliance-checker/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ioos/compliance-checker/downloads","issues_url":"https://api.github.com/repos/ioos/compliance-checker/issues{/number}","pulls_url":"https://api.github.com/repos/ioos/compliance-checker/pulls{/number}","milestones_url":"https://api.github.com/repos/ioos/compliance-checker/milestones{/number}","notifications_url":"https://api.github.com/repos/ioos/compliance-checker/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ioos/compliance-checker/labels{/name}","releases_url":"https://api.github.com/repos/ioos/compliance-checker/releases{/id}","created_at":"2013-10-02T18:49:48Z","updated_at":"2015-03-11T14:26:57Z","pushed_at":"2015-03-11T14:26:57Z","git_url":"git://github.com/ioos/compliance-checker.git","ssh_url":"git@github.com:ioos/compliance-checker.git","clone_url":"https://github.com/ioos/compliance-checker.git","svn_url":"https://github.com/ioos/compliance-checker","homepage":"","size":1905,"stargazers_count":5,"watchers_count":5,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":24,"forks":9,"open_issues":24,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ioos/compliance-checker/pulls/92"},"html":{"href":"https://github.com/ioos/compliance-checker/pull/92"},"issue":{"href":"https://api.github.com/repos/ioos/compliance-checker/issues/92"},"comments":{"href":"https://api.github.com/repos/ioos/compliance-checker/issues/92/comments"},"review_comments":{"href":"https://api.github.com/repos/ioos/compliance-checker/pulls/92/comments"},"review_comment":{"href":"https://api.github.com/repos/ioos/compliance-checker/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ioos/compliance-checker/pulls/92/commits"},"statuses":{"href":"https://api.github.com/repos/ioos/compliance-checker/statuses/634292786e4fc91a89aa21cd046a6ec2739d1337"}}}}