Repository: CSCfi/beacon-python

Issue:
Differentiate wildcard results
#### Proposed solution
Currently in a wildcard search it is difficult to differentiate between wildcard results, as illustrated.
![image](https://user-images.githubusercontent.com/47524/47644992-5c8b6d80-db78-11e8-813f-e4454c681819.png)

The idea is to add values in the `info` key to differentiate between such results.
Results of search are:
```
CT > AT (allele count: 1)
CT > TC (allele count: 1)
CT > TT (allele count: 118)
```
`info` key could contain something like:
```
 "info":
    {
        "referenceBases": "CT",
        "alternateBases": "AT"
    }
```

#### DoD (Definition of Done)
Added values to `info` key to be used in UI.
Updated JSON schema.

#### Testing
Unit test updated.


Discussion:

Patch:
diff --git a/beacon_api/__init__.py b/beacon_api/__init__.py
index f20f8a5..42894f9 100644
--- a/beacon_api/__init__.py
+++ b/beacon_api/__init__.py
@@ -28,4 +28,4 @@ __org_address__ = 'empty'
 __org_welcomeUrl__ = 'https://www.csc.fi/'
 __org_contactUrl__ = 'empty'
 __org_logoUrl__ = 'https://www.csc.fi/documents/10180/161914/CSC_2012_LOGO_RGB_72dpi.jpg'
-__org_info__ = [{'author': __author__}]
+__org_info__ = {'author': __author__}
diff --git a/beacon_api/api/info.py b/beacon_api/api/info.py
index 341c89f..631cab1 100644
--- a/beacon_api/api/info.py
+++ b/beacon_api/api/info.py
@@ -71,7 +71,7 @@ async def beacon_info(host, pool):
         'updateDateTime': __updatetime__,
         'datasets': beacon_dataset,
         'sampleAlleleRequests': sample_allele_request,
-        'info': [{"key": "value"}]
+        'info': {}
     }
 
     return beacon_info
diff --git a/beacon_api/schemas/info.json b/beacon_api/schemas/info.json
index b973857..cef1abc 100644
--- a/beacon_api/schemas/info.json
+++ b/beacon_api/schemas/info.json
@@ -58,10 +58,7 @@
           "pattern": "^(.*)$"
         },
         "info": {
-          "type": "array",
-          "items": {
-            "type": "object"
-          }
+          "type": "object"
         }
       }
     },
@@ -146,10 +143,7 @@
             "pattern": "^(.*)$"
           },
           "info": {
-            "type": "array",
-            "items": {
-              "type": "object"
-            }
+            "type": "object"
           },
           "dataUseConditions": {
             "type": "object",
@@ -831,10 +825,7 @@
       }
     },
     "info": {
-      "type": "array",
-      "items": {
-        "type": "object"
-      }
+      "type": "object"
     }
   }
 }
diff --git a/beacon_api/schemas/response.json b/beacon_api/schemas/response.json
index 060d078..ee2ad67 100644
--- a/beacon_api/schemas/response.json
+++ b/beacon_api/schemas/response.json
@@ -110,6 +110,14 @@
           "exists": {
             "type": "boolean"
           },
+          "referenceBases": {
+            "type": "string",
+            "pattern": "^([ACGT]+)$"
+          },
+          "alternateBases": {
+            "type": "string",
+            "pattern": "^([ACGT]+)$"
+          },
           "error": {
             "type": "object",
             "required": [
@@ -152,11 +160,7 @@
             "pattern": "^(.*)$"
           },
           "info": {
-            "type": "array",
-            "default": [],
-            "items": {
-              "type": "object"
-            }
+            "type": "object"
           }
         }
       }
diff --git a/beacon_api/utils/data_query.py b/beacon_api/utils/data_query.py
index 35107ee..84312f8 100644
--- a/beacon_api/utils/data_query.py
+++ b/beacon_api/utils/data_query.py
@@ -19,8 +19,10 @@ def sql_tuple(array):
 def transform_record(record):
     """Format the record we got from the database to adhere to the response schema."""
     response = dict(record)
+    response["referenceBases"] = response.pop("referenceBases")
+    response["alternateBases"] = response.pop("alternateBases")
     response["frequency"] = round(response.pop("frequency"), 9)
-    response["info"] = [{"accessType": response.pop("accessType")}]
+    response["info"] = {"accessType": response.pop("accessType")}
     # Error is not required and should not be shown
     # otherwise schema validation will fail
     # response["error"] = None
@@ -31,11 +33,13 @@ def transform_record(record):
 def transform_misses(record):
     """Format the missed datasets record we got from the database to adhere to the response schema."""
     response = dict(record)
+    response["referenceBases"] = ''
+    response["alternateBases"] = ''
     response["frequency"] = 0
     response["variantCount"] = 0
     response["callCount"] = 0
     response["sampleCount"] = 0
-    response["info"] = [{"accessType": response.pop("accessType")}]
+    response["info"] = {"accessType": response.pop("accessType")}
     # Error is not required and should not be shown
     # otherwise schema validation will fail
     # response["error"] = None
@@ -46,7 +50,7 @@ def transform_misses(record):
 def transform_metadata(record):
     """Format the metadata record we got from the database to adhere to the response schema."""
     response = dict(record)
-    response["info"] = [{"accessType": response.pop("accessType")}]
+    response["info"] = {"accessType": response.pop("accessType")}
     # TO DO test with null date
     if 'createDateTime' in response and isinstance(response["createDateTime"], datetime):
         response["createDateTime"] = response.pop("createDateTime").strftime('%Y-%m-%dT%H:%M:%SZ')
@@ -128,7 +132,7 @@ async def fetch_filtered_dataset(db_pool, position, chromosome, reference, alter
 
                 # UBER QUERY - TBD if it is what we need
                 query = f"""SELECT {"DISTINCT ON (a.datasetId)" if misses else ''} a.datasetId as "datasetId", b.accessType as "accessType",
-                            a.chromosome as "referenceName",
+                            a.chromosome as "referenceName", a.reference as "referenceBases", a.alternate as "alternateBases",
                             b.externalUrl as "externalUrl", b.description as "note",
                             a.variantCount as "variantCount",
                             a.callCount as "callCount", b.sampleCount as "sampleCount",


Test patch:
diff --git a/tests/test_data_query.py b/tests/test_data_query.py
index f87248c..a12ad10 100644
--- a/tests/test_data_query.py
+++ b/tests/test_data_query.py
@@ -13,14 +13,17 @@ class Record:
     Mimic asyncpg Record object.
     """
 
-    def __init__(self, accessType, frequency=None, createDateTime=None, updateDateTime=None):
+    def __init__(self, accessType, frequency=None, createDateTime=None, updateDateTime=None, referenceBases=None, alternateBases=None):
         """Initialise things."""
         self.data = {"accessType": accessType}
+        if referenceBases:
+            self.data.update({"referenceBases": referenceBases})
+        if alternateBases:
+            self.data.update({"alternateBases": alternateBases})
         if frequency:
             self.data.update({"frequency": frequency})
         if createDateTime:
             self.data.update({"createDateTime": createDateTime})
-
         if updateDateTime:
             self.data.update({"updateDateTime": updateDateTime})
 
@@ -77,15 +80,17 @@ class TestDataQueryFunctions(asynctest.TestCase):
 
     def test_transform_record(self):
         """Test transform DB record."""
-        response = {"frequency": 0.009112876, "info": [{"accessType": "PUBLIC"}]}
-        record = Record("PUBLIC", 0.009112875989879)
+        response = {"frequency": 0.009112876, "info": {"accessType": "PUBLIC"},
+                    "referenceBases": "CT", "alternateBases": "AT"}
+        record = Record("PUBLIC", 0.009112875989879, referenceBases="CT", alternateBases="AT")
         result = transform_record(record)
         self.assertEqual(result, response)
 
     def test_transform_misses(self):
         """Test transform misses record."""
-        response = {"frequency": 0, "callCount": 0, "sampleCount": 0, "variantCount": 0,
-                    "info": [{"accessType": "PUBLIC"}]}
+        response = {"referenceBases": '', "alternateBases": '',
+                    "frequency": 0, "callCount": 0, "sampleCount": 0, "variantCount": 0,
+                    "info": {"accessType": "PUBLIC"}}
         record = Record("PUBLIC")
         result = transform_misses(record)
         print(result)
@@ -94,9 +99,9 @@ class TestDataQueryFunctions(asynctest.TestCase):
     def test_transform_metadata(self):
         """Test transform medata record."""
         response = {"createDateTime": "2018-10-20T20:33:40Z", "updateDateTime": "2018-10-20T20:33:40Z",
-                    "info": [{"accessType": "PUBLIC"}]}
-        record = Record("PUBLIC", None, datetime.strptime("2018-10-20 20:33:40+00", '%Y-%m-%d %H:%M:%S+00'),
-                        datetime.strptime("2018-10-20 20:33:40+00", '%Y-%m-%d %H:%M:%S+00'))
+                    "info": {"accessType": "PUBLIC"}}
+        record = Record("PUBLIC", createDateTime=datetime.strptime("2018-10-20 20:33:40+00", '%Y-%m-%d %H:%M:%S+00'),
+                        updateDateTime=datetime.strptime("2018-10-20 20:33:40+00", '%Y-%m-%d %H:%M:%S+00'))
         result = transform_metadata(record)
         print(result)
         self.assertEqual(result, response)
diff --git a/tests/test_response.py b/tests/test_response.py
index d3efd52..1e18e38 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -20,7 +20,7 @@ mock_dataset_metadata = {"id": "id1",
                          "callCount": 0,
                          "sampleCount": 2534,
                          "version": "v0.4",
-                         "info": [{"accessType": "PUBLIC"}],
+                         "info": {"accessType": "PUBLIC"},
                          "createDateTime": "2013-05-02T12:00:00Z",
                          "updateDateTime": "2013-05-02T12:00:00Z"}
 
@@ -34,7 +34,7 @@ mock_data = [{"datasetId": "id1",
               "sampleCount": 2534,
               "exists": True,
               "frequency": 0.001183899,
-              "info": [{"accessType": "PUBLIC"}]},
+              "info": {"accessType": "PUBLIC"}},
              {"datasetId": "id1",
               "referenceName": "MT",
               "externalUrl": "url",
@@ -44,7 +44,7 @@ mock_data = [{"datasetId": "id1",
               "sampleCount": 0,
               "exists": False,
               "frequency": 0,
-              "info": [{"accessType": "REGISTERED"}]}]
+              "info": {"accessType": "REGISTERED"}}]
 
 
 class TestBasicFunctions(asynctest.TestCase):