Repository: ioos/compliance-checker

Issue:
CF checker doesn't allow additional conventions in Conventions attribute
The test `check_conventions_are_cf_16` in cf.py requires the **Conventions** global attribute to be exactly equal to the string "CF-1.6". This is indeed what the [CF conventions document](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.6/build/cf-conventions.html#identification-of-conventions) _recommends_. However, the [NetCDF User's Guide](http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.html#Attribute-Conventions) allows this attribute to be a blank- or comma-separated list of convention names. 

Could the check be modifed to look for "CF-1.6" as a sub-string of the **Conventions** attribute?

E.g. [IMOS](http://imos.org.au/) data will usually have Conventions = "CF-1.6,IMOS-1.3". We would like these files to pass the CF checker.



Discussion:
I think that was always the idea.  Another need for having this flexibility is that OceanSITES, ACDD, and perhaps Argo all specify their own conventions and versions.  We already support ACDD so we should be checking for it's presence in the Conventions attribute.  We should probably err on the side of warnings and not errors when we encounter misuse of these identifiers.  For example, we should encourage the use of ACDD-1.1 and not "Unidata Data Discovery Conventions 1.1" which you still see in practice.


Resolving patch:
diff --git a/compliance_checker/cf/cf.py b/compliance_checker/cf/cf.py
--- a/compliance_checker/cf/cf.py
+++ b/compliance_checker/cf/cf.py
@@ -408,7 +408,8 @@ def check_conventions_are_cf_16(self, ds):
         valid_conventions = ['CF-1.0', 'CF-1.1', 'CF-1.2', 'CF-1.3',
                              'CF-1.4', 'CF-1.5', 'CF-1.6']
         if hasattr(ds.dataset, 'Conventions'):
-            if getattr(ds.dataset, 'Conventions', '') in valid_conventions:
+            conventions = getattr(ds.dataset, 'Conventions', '').split(',')
+            if any((c.strip() in valid_conventions for c in conventions)):
                 valid = True
                 reasoning = ['Conventions field is "CF-1.x (x in 0-6)"']
             else:


Test patch:
diff --git a/compliance_checker/tests/data/conv_bad.nc b/compliance_checker/tests/data/conv_bad.nc
new file mode 100644
Binary files /dev/null and b/compliance_checker/tests/data/conv_bad.nc differ
diff --git a/compliance_checker/tests/data/conv_multi.nc b/compliance_checker/tests/data/conv_multi.nc
new file mode 100644
Binary files /dev/null and b/compliance_checker/tests/data/conv_multi.nc differ
diff --git a/compliance_checker/tests/test_cf.py b/compliance_checker/tests/test_cf.py
--- a/compliance_checker/tests/test_cf.py
+++ b/compliance_checker/tests/test_cf.py
@@ -15,6 +15,8 @@
 
 static_files = {
         'rutgers'          : resource_filename('compliance_checker', 'tests/data/ru07-20130824T170228_rt0.nc'),
+        'conv_multi'       : resource_filename('compliance_checker', 'tests/data/conv_multi.nc'),
+        'conv_bad'         : resource_filename('compliance_checker', 'tests/data/conv_bad.nc'),
         'example-grid'     : resource_filename('compliance_checker', 'tests/data/example-grid.nc'),
         'badname'          : resource_filename('compliance_checker', 'tests/data/non-comp/badname.netcdf'),
         'bad'              : resource_filename('compliance_checker', 'tests/data/non-comp/bad.nc'),
@@ -173,11 +175,20 @@ def test_check_conventions_are_cf_16(self):
         """
         2.6.1 the NUG defined global attribute Conventions to the string value "CF-1.6"
         """
+        # :Conventions = "CF-1.6"
         dataset = self.get_pair(static_files['rutgers'])
         result = self.cf.check_conventions_are_cf_16(dataset)
         self.assertTrue(result.value)
 
-        #TODO add fail case?
+        # :Conventions = "CF-1.6 ,ACDD" ;
+        dataset = self.get_pair(static_files['conv_multi'])
+        result = self.cf.check_conventions_are_cf_16(dataset)
+        self.assertTrue(result.value)
+
+        # :Conventions = "NoConvention"
+        dataset = self.get_pair(static_files['conv_bad'])
+        result = self.cf.check_conventions_are_cf_16(dataset)
+        self.assertFalse(result.value)
 
     def test_check_convention_globals(self):
         """