From 2579befead1982b2913325f95a9bc311c5f9ee9d Mon Sep 17 00:00:00 2001
From: vermeul <swen@ethz.ch>
Date: Thu, 12 Sep 2019 11:48:13 +0200
Subject: [PATCH] lots of bugfixes in search, added _subcriteria_for method

---
 pybis/src/python/pybis/pybis.py | 206 +++++++++++++++++++++++---------
 1 file changed, 148 insertions(+), 58 deletions(-)

diff --git a/pybis/src/python/pybis/pybis.py b/pybis/src/python/pybis/pybis.py
index 398cf56cda8..e65ecd7eca2 100644
--- a/pybis/src/python/pybis/pybis.py
+++ b/pybis/src/python/pybis/pybis.py
@@ -381,9 +381,9 @@ def _subcriteria_for_tags(tags):
     if not isinstance(tags, list):
         tags = [tags]
 
-    criterias = []
+    criteria = []
     for tag in tags:
-        criterias.append({
+        criteria.append({
             "fieldName": "code",
             "fieldType": "ATTRIBUTE",
             "fieldValue": {
@@ -396,7 +396,7 @@ def _subcriteria_for_tags(tags):
     return {
         "@type": "as.dto.tag.search.TagSearchCriteria",
         "operator": "AND",
-        "criteria": criterias
+        "criteria": criteria
     }
 
 
@@ -423,14 +423,99 @@ def _subcriteria_for_properties(prop, val):
         }
     }
 
+def _subcriteria_for(thing, entity, parents_or_children='', operator='AND'):
+    """Returns the sub-search criteria for «thing», which can be either:
+    - a python object (sample, dataSet, experiment)
+    - a permId
+    - an identifier
+    - a code
+    """
+
+    if isinstance(thing, str):
+        if is_permid(thing):
+            return _subcriteria_for_permid(
+                thing, 
+                entity=entity,
+                parents_or_children=parents_or_children,
+                operator=operator
+            )
+        elif is_identifier(thing):
+            return _subcriteria_for_identifier(
+                thing, 
+                entity=entity,
+                parents_or_children=parents_or_children,
+                operator=operator
+            )
+        else:
+            # look for code
+            return _subcriteria_for_code_new(
+                thing,
+                entity=entity,
+                parents_or_children=parents_or_children,
+                operator=operator
+            )
+
+    elif isinstance(thing, list):
+        criteria = []
+        for element in thing:
+            crit = _subcriteria_for(element, entity, parents_or_children, operator)
+            criteria += crit["criteria"]
+
+        return {
+            "criteria": criteria,
+            "@type": crit["@type"],
+            "operator": "OR"
+        }
+    elif thing is None:
+        # we just need the type
+        search_type = get_type_for_entity(entity, 'search', parents_or_children)
+        return {
+            "criteria": [],
+            **search_type,
+            "operator": operator
+        }
+    else:
+        # we passed an object
+        return _subcriteria_for_permid(
+            thing.permId, 
+            entity=entity,
+            parents_or_children=parents_or_children,
+            operator=operator
+        )
+        
+
+def _subcriteria_for_identifier(ids, entity, parents_or_children='', operator='AND'):
+    if not isinstance(ids, list):
+        ids = [ids]
+
+    criteria = []
+    for id in ids:
+        criteria.append({
+            "@type": "as.dto.common.search.IdentifierSearchCriteria",
+            "fieldValue": {
+                "value": id,
+                "@type": "as.dto.common.search.StringEqualToValue"
+            },
+            "fieldType": "ATTRIBUTE",
+            "fieldName": "identifier"
+        })
+
+    search_type = get_type_for_entity(entity, 'search', parents_or_children)
+    return {
+        "criteria": criteria,
+        **search_type,
+        "operator": operator
+    }
+    return criteria
+
 
 def _subcriteria_for_permid(permids, entity, parents_or_children='', operator='AND'):
     if not isinstance(permids, list):
         permids = [permids]
 
-    criterias = []
+    criteria = []
     for permid in permids:
-        criterias.append({
+        criteria.append({
             "@type": "as.dto.common.search.PermIdSearchCriteria",
             "fieldValue": {
                 "value": permid,
@@ -440,14 +525,36 @@ def _subcriteria_for_permid(permids, entity, parents_or_children='', operator='A
             "fieldName": "code"
         })
 
-    criteria = {
-        "criteria": criterias,
-        "@type": "as.dto.{}.search.{}{}SearchCriteria".format(
-            entity.lower(), entity, parents_or_children
-        ),
+    search_type = get_type_for_entity(entity, 'search', parents_or_children)
+    return {
+        "criteria": criteria,
+        **search_type,
+        "operator": operator
+    }
+
+
+def _subcriteria_for_code_new(codes, entity, parents_or_children='', operator='AND'):
+    if not isinstance(codes, list):
+        codes = [codes]
+
+    criteria = []
+    for code in codes:
+        criteria.append({
+            "@type": "as.dto.common.search.CodeSearchCriteria",
+            "fieldValue": {
+                "value": code,
+                "@type": "as.dto.common.search.StringEqualToValue"
+            },
+            "fieldType": "ATTRIBUTE",
+            "fieldName": "code"
+        })
+
+    search_type = get_type_for_entity(entity, 'search', parents_or_children)
+    return {
+        "criteria": criteria,
+        **search_type,
         "operator": operator
     }
-    return criteria
 
 
 def _subcriteria_for_code(code, entity):
@@ -1403,37 +1510,34 @@ class Openbis:
 
     def get_samples(
         self, identifier=None, code=None, permId=None,
-        space=None, project=None, experiment=None, type=None,
+        space=None, project=None, experiment=None, collection=None, type=None,
         start_with=None, count=None,
         withParents=None, withChildren=None, tags=None, props=None, **properties
     ):
         """ Get a list of all samples for a given space/project/experiment (or any combination)
         """
 
+        if collection is not None:
+            experiment = collection
+
         sub_criteria = []
 
-        # v3 API does not offer a search for identifiers. We need to do a combined search instead:
-        # space && code or
-        # space && project && code
         if identifier:
-            identifier = identifier.lstrip('/')
-            elements = identifier.split('/')
-            if len(elements) == 2:
-                space = elements[0]
-                code  = elements[1]
-            elif len(elements) == 3:
-                space = elements[0]
-                code  = elements[2]
-            else:
-                raise ValueError("{} is not a valid sample identifier.".format(identifier))
+            crit = _subcriteria_for(identifier, 'sample')
+            sub_criteria += crit['criteria']
 
         if space:
-            sub_criteria.append(_subcriteria_for_code(space, 'space'))
+            sub_criteria.append(_subcriteria_for(space, 'space'))
         if project:
-            proj_crit = _subcriteria_for_code(project, 'project')
-            sub_criteria.append(proj_crit)
+            sub_criteria.append(_subcriteria_for(project, 'project'))
         if experiment:
-            sub_criteria.append(_subcriteria_for_code(experiment, 'experiment'))
+            sub_criteria.append(_subcriteria_for(experiment, 'experiment'))
+
+        if withParents:
+            sub_criteria.append(_subcriteria_for(withParents, 'sample', 'Parents'))
+        if withChildren:
+            sub_criteria.append(_subcriteria_for(withChildren, 'sample', 'Children'))
+
         if properties is not None:
             for prop in properties:
                 sub_criteria.append(_subcriteria_for_properties(prop, properties[prop]))
@@ -1445,26 +1549,6 @@ class Openbis:
             sub_criteria.append(_criteria_for_code(code))
         if permId:
             sub_criteria.append(_common_search("as.dto.common.search.PermIdSearchCriteria", permId))
-        if withParents:
-            if not isinstance(withParents, list):
-                withParents = [withParents]
-            for parent in withParents:
-                sub_criteria.append(
-                    _gen_search_criteria({
-                        "sample": "SampleParents",
-                        "identifier": parent
-                    })
-                )
-        if withChildren:
-            if not isinstance(withChildren, list):
-                withChildren = [withChildren]
-            for child in withChildren:
-                sub_criteria.append(
-                    _gen_search_criteria({
-                        "sample": "SampleChildren",
-                        "identifier": child
-                    })
-                )
 
         criteria = {
             "criteria": sub_criteria,
@@ -1596,29 +1680,35 @@ class Openbis:
     def get_datasets(
         self, code=None, type=None, withParents=None, withChildren=None,
         start_with=None, count=None, kind=None,
-        status=None, sample=None, experiment=None, project=None,
+        status=None, sample=None, experiment=None, collection=None, project=None,
         tags=None, props=None, **properties
     ):
 
+        if 'object' in properties:
+            sample = properties['object']
+        if collection is not None:
+            experiment = collection
+
         sub_criteria = []
 
         if code:
             sub_criteria.append(_criteria_for_code(code))
         if type:
-            sub_criteria.append(_subcriteria_for_type(type, 'DataSet'))
+            sub_criteria.append(_subcriteria_for_type(type, 'dataSet'))
+
         if withParents:
-            sub_criteria.append(_subcriteria_for_permid(withParents, 'DataSet', 'Parents'))
+            sub_criteria.append(_subcriteria_for(withParents, 'dataSet', 'Parents'))
         if withChildren:
-            sub_criteria.append(_subcriteria_for_permid(withChildren, 'DataSet', 'Children'))
+            sub_criteria.append(_subcriteria_for(withChildren, 'dataSet', 'Children'))
 
         if sample:
-            sub_criteria.append(_subcriteria_for_code(sample, 'Sample'))
+            sub_criteria.append(_subcriteria_for(sample, 'sample'))
         if experiment:
-            sub_criteria.append(_subcriteria_for_code(experiment, 'Experiment'))
+            sub_criteria.append(_subcriteria_for(experiment, 'experiment'))
+
         if project:
-            exp_crit = _subcriteria_for_code(experiment, 'Experiment')
-            proj_crit = _subcriteria_for_code(project, 'Project')
-            exp_crit['criteria'] = []
+            exp_crit = _subcriteria_for(experiment, 'experiment')
+            proj_crit = _subcriteria_for(project, 'project')
             exp_crit['criteria'].append(proj_crit)
             sub_criteria.append(exp_crit)
         if tags:
-- 
GitLab