diff --git a/openbis-ipad/core-plugin/readme.md b/openbis-ipad/core-plugin/readme.md index 54eea133ecc035c3efbd7fbf5b88e6319c4ec5fc..321639e1065eddd1caf74861d5921f521d514512 100644 --- a/openbis-ipad/core-plugin/readme.md +++ b/openbis-ipad/core-plugin/readme.md @@ -90,7 +90,7 @@ The iPad data model tracks information for displaying and navigating between ent </tr> <tr> <td>CATEGORY</td> - <td>A category identifier for showing the entity. If empty or None, then the the entity in this row is not shown in top level navigation views. Such entities may appear as children of other entities.</td> + <td>A category identifier for grouping entities.</td> <td>Used to group entities together</td> <td>openBIS entity type</td> </tr> @@ -112,6 +112,12 @@ The iPad data model tracks information for displaying and navigating between ent <td>Shown in detail views.</td> <td>openBIS properties</td> </tr> + <tr> + <td>ROOT_LEVEL</td> + <td>True if the entity should be shown on the root level.</td> + <td>Used to construct the initial navigation view.</td> + <td>Empty means do not show. Non-empty means show.</td> + </tr> </tbody> </table> @@ -144,7 +150,7 @@ The communication model between the iPad and the service has been designed to tr <td>None</td> <td>Return root entities.</td> <td>Used to initialize the root navigation view.</td> - <td>PERM_ID, REFCON, CATEGORY, SUMMARY_HEADER, SUMMARY, CHILDREN</td> + <td>PERM_ID, REFCON, CATEGORY, SUMMARY_HEADER, SUMMARY, CHILDREN, ROOT_LEVEL</td> </tr> <tr> <td>DRILL</td> diff --git a/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py index 7e109f6deb1ef2655d612cf233bc4086f7f355f1..c3fbe51da63500e1b14ad9bc542116cc0edeefd5 100644 --- a/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py +++ b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py @@ -1,6 +1,7 @@ from ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.v1 import MaterialIdentifierCollection from ch.systemsx.cisd.openbis.generic.shared.basic.dto import MaterialIdentifier from com.fasterxml.jackson.databind import ObjectMapper +from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto import SearchCriteria # # BEGIN Infrastructure @@ -60,9 +61,7 @@ class RequestHandler: REFCON : Data that is passed unchanged back to the server when a row is modified. This can be used by the server to encode whatever it needs in order to modify the row. (required) - CATEGORY : A category identifier for showing the entity. If empty or None, then the - the entity in this row is not shown in top level navigation views. Such entities - may appear as children of other entities. + CATEGORY : A category identifier for grouping entities. SUMMARY_HEADER : A short summary of the entity. SUMMARY : A potentially longer summary of the entity. CHILDREN : The permIds of the children of this entity. Transmitted as JSON. @@ -70,6 +69,7 @@ class RequestHandler: IMAGE_URL : A url for an image associated with this entity. If None or empty, no image is shown. PROPERTIES : Properties (metadata) that should be displayed for this entity. Transmitted as JSON. + ROOT_LEVEL : True if the entity should be shown on the root level. The relevant headers are determined by the request. """ @@ -80,16 +80,13 @@ class RequestHandler: """Append a row of data to the table""" row = self.builder.addRow() for header in self.headers: - row.setCell(header, entry.get(header)) + row.setCell(header, str(entry.get(header))) def add_rows(self, entities): """Take a collection of dictionaries and add a row for each one""" for entry in entities: self.add_row(entry) - def json_encoded_value(self, coll): - return ObjectMapper().writeValueAsString(coll) - def process_request(self): """Execute the steps necessary to process the request.""" self.add_headers() @@ -102,6 +99,24 @@ class AllDataRequestHandler(RequestHandler): def optional_headers(self): return ["CATEGORY", "SUMMARY_HEADER", "SUMMARY", "CHILDREN", "IDENTIFIER", "IMAGE_URL", "PROPERTIES"] +class RootRequestHandler(RequestHandler): + """Abstract Handler for the ROOT request.""" + + def optional_headers(self): + return ["CATEGORY", "SUMMARY_HEADER", "SUMMARY", "CHILDREN", "ROOT_LEVEL"] + +class DrillRequestHandler(RequestHandler): + """Abstract Handler for the DRILL request.""" + + def optional_headers(self): + return ["CATEGORY", "SUMMARY_HEADER", "SUMMARY", "CHILDREN"] + +class DetailRequestHandler(RequestHandler): + """Abstract Handler for the DETAIL request.""" + + def optional_headers(self): + return ["CATEGORY", "SUMMARY_HEADER", "SUMMARY", "IDENTIFIER", "IMAGE_URL", "PROPERTIES"] + # # END Infrastructure # @@ -120,6 +135,7 @@ def material_to_dict(material): material_dict['IDENTIFIER'] = material.getMaterialIdentifier() material_dict['PERM_ID'] = material.getMaterialIdentifier() refcon = {} + refcon['code'] = material.getCode() refcon['entityKind'] = 'MATERIAL' refcon['entityType'] = material.getMaterialType() material_dict['REFCON'] = json_encoded_value(refcon) @@ -130,6 +146,7 @@ def material_to_dict(material): else: material_dict['SUMMARY'] = material.getPropertyValue("DESC") material_dict['IMAGE_URL'] = "" + material_dict['ROOT_LEVEL'] = True material_dict['CHILDREN'] = json_encoded_value([]) @@ -145,6 +162,7 @@ def sample_to_dict(five_ht_sample, material_by_perm_id): sample_dict['IDENTIFIER'] = five_ht_sample.getSampleIdentifier() sample_dict['PERM_ID'] = five_ht_sample.getPermId() refcon = {} + refcon['code'] = five_ht_sample.getCode() refcon['entityKind'] = 'SAMPLE' refcon['entityType'] = five_ht_sample.getSampleType() sample_dict['REFCON'] = json_encoded_value(refcon) @@ -158,6 +176,7 @@ def sample_to_dict(five_ht_sample, material_by_perm_id): prop_names = ["DESC"] properties = dict((name, five_ht_sample.getPropertyValue(name)) for name in prop_names if five_ht_sample.getPropertyValue(name) is not None) sample_dict['PROPERTIES'] = json_encoded_value(properties) + sample_dict['ROOT_LEVEL'] = True # Need to handle the material links as entity links: "TARGET", "COMPOUND" return sample_dict @@ -181,6 +200,13 @@ def samples_to_dict(samples, material_by_perm_id): result = [sample_to_dict(sample, material_by_perm_id) for sample in samples] return result +def retrieve_samples(sample_perm_id_and_ref_cons): + sc = SearchCriteria() + for sample in sample_perm_id_and_ref_con: + code = sample['REFCON']['code'] + sc.addMatchClause(sc.MatchClause.createAttributeMatch(sc.MatchClauseAttribute.CODE, code)) + return searchService.searchForSamples(sc) + class ExampleAllDataRequestHandler(AllDataRequestHandler): """Handler for the ALLDATA request.""" @@ -196,6 +222,71 @@ class ExampleAllDataRequestHandler(AllDataRequestHandler): self.add_rows(self.material_dict_array) self.add_rows(samples_to_dict(self.samples, self.material_by_perm_id)) +class ExampleRootRequestHandler(RootRequestHandler): + """Handler for the ROOT request.""" + + def retrieve_data(self): + # Get the data and add a row for each data item + self.samples = self.searchService.searchForSamples("DESC", "*", "5HT_PROBE") + material_identifiers = gather_materials(self.samples) + materials = self.searchService.listMaterials(material_identifiers) + self.material_dict_array = materials_to_dict(materials) + self.material_by_perm_id = dict([(material.getMaterialIdentifier(), material) for material in materials]) + + def add_data_rows(self): + self.add_rows(self.material_dict_array) + self.add_rows(samples_to_dict(self.samples, self.material_by_perm_id)) + +class ExampleDrillRequestHandler(DrillRequestHandler): + """Handler for the DRILL request.""" + + def retrieve_data(self): + # Drill only happens on samples + drill_samples = self.parameters['entities'] + + self.samples = retrieve_samples(drill_samples) + material_identifiers = gather_materials(self.samples) + materials = self.searchService.listMaterials(material_identifiers) + self.material_dict_array = materials_to_dict(materials) + self.material_by_perm_id = dict([(material.getMaterialIdentifier(), material) for material in materials]) + + def add_data_rows(self): + self.add_rows(self.material_dict_array) + self.add_rows(samples_to_dict(self.samples, self.material_by_perm_id)) + +class ExampleDetailRequestHandler(DetailRequestHandler): + """Handler for the DETAIL request.""" + + def retrieve_data(self): + # Get the data and add a row for each data item + entities = self.parameters['entities'] + detail_samples = [entity for entity in entities and 'SAMPLE' == entity['REFCON']['entityKind']] + self.samples = retrieve_samples(detail_samples) + + detail_materials = [entity for entity in entities and 'MATERIAL' == entity['REFCON']['entityKind']] + material_identifiers = MaterialIdentifierCollection() + for detail_material in detail_materials: + add_material_to_collection(detail_material, material_identifiers) + + materials = self.searchService.listMaterials(material_identifiers) + self.material_dict_array = materials_to_dict(materials) + self.material_by_perm_id = dict([(material.getMaterialIdentifier(), material) for material in materials]) + + def add_data_rows(self): + self.add_rows(self.material_dict_array) + self.add_rows(samples_to_dict(self.samples, self.material_by_perm_id)) + def aggregate(parameters, builder): - handler = ExampleAllDataRequestHandler(parameters, builder) - handler.process_request() + request_key = parameters.get('requestKey') + if 'ROOT' == request_key: + handler = ExampleRootRequestHandler(parameters, builder) + handler.process_request() + elif 'DRILL' == request_key: + handler = ExampleDrillRequestHandler(parameters, builder) + handler.process_request() + elif 'DETAIL' == request_key: + handler = ExampleDetailRequestHandler(parameters, builder) + handler.process_request() + else: + handler = ExampleAllDataRequestHandler(parameters, builder) + handler.process_request()