Sample tracking and experiment preparation

Entities

Shipment

Any participant of an investigation can create a shipment. A shipment is needed when samples are sent to the facility and it is linked to a scheduled investigation. It is specially important to track parcels and sample on-site.

A shipment is mainly composed by:

  • Sender and return addresses. These are default addresses that they can be overrided for each aprcel
  • A list of parcels: a parcel is the "thing" that will be sent by courier and has a status that allows determine in which stage (or place) the parcel is. A label containing a Qr code can be printed from the application and which identifies phisically a parcel.

Shipments are persisted in a MongoDB database. The full scheme description can be found here: schema

Parcel

A parcel is a "box" which is sent to the facility with the samples and tools needed to perform the experiment. A parcel has one status depending on which stage the parcel is and it is described here

Items

The content of a parcel is filled by items. An item can be:

  1. Tool
  2. Sample linked to a sample sheet
  3. Container
  4. Other

Tutorial

Creation of a shipment and a parcel

This notebook shows how to create addresses, shipment and a parcel from an existing investigation. It uses the ICAT+ API and needs to have a existing user with permissions to such investigation

import requests
import json

Configuration

As mentioned it needs a running instance of ICAT+, a valid user credentials and an existing investigation.

#icatplus_server = "http://dau-dm-03:8000"
icatplus_server = "http://lalex.esrf.fr:8000"
credential = {
  "plugin": "db",
  "username": "********",
  "password": "********"
}
investigationId = 124564607

Login

This logs into ICAT in order to get the token that is called sessionId. The sessionId expires depending of the ICAT configuration and it is returned in the variable lefeTimeMinutes

session = requests.post(icatplus_server + "/session", data = credential)
session = json.loads(session.text)
sessionId = session["sessionId"]
print(session)
{'name': 'admin', 'username': 'admin', 'fullName': 'adminFullName', 'lifeTimeMinutes': 719.9995333333334, 'isAdministrator': True, 'isInstrumentScientist': False, 'isMinter': False, 'sessionId': '8d4728d0-e84d-40fb-b6d2-543e296ba194', 'usersByPrefix': []}

Get Investigation

url = (f'{icatplus_server}/catalogue/{sessionId}/investigation?ids={investigationId}')
print(url)
http://lalex.esrf.fr:8000/catalogue/8d4728d0-e84d-40fb-b6d2-543e296ba194/investigation?ids=124564607
investigation = requests.get(f'{url}')
print(json.dumps(json.loads(investigation.text), indent=2))
[
  {
    "name": "ID002306",
    "startDate": "2023-06-19T13:28:44.492+02:00",
    "id": 124564607,
    "title": "ID002306",
    "visitId": "id00",
    "parameters": {
      "__datasetCount": "6",
      "__sampleCount": "2",
      "__fileCount": "0",
      "__volume": "0",
      "__elapsedTime": "0",
      "__acquisitionDatasetCount": "4",
      "__processedDatasetCount": "2",
      "__acquisitionFileCount": "0",
      "__processedFileCount": "0",
      "__acquisitionVolume": "0",
      "__processedVolume": "0"
    },
    "instrument": {
      "name": "ID00",
      "id": 92177647
    },
    "investigationUsers": [],
    "meta": {
      "page": {
        "totalWithoutFilters": 1,
        "total": 1,
        "totalPages": 1,
        "currentPage": 1
      }
    },
    "type": {
      "id": 325,
      "createId": "root",
      "createTime": "2014-06-18T15:00:43.500+02:00",
      "modId": "root",
      "modTime": "2014-06-18T15:00:43.500+02:00",
      "description": "Test investigation",
      "investigations": [],
      "name": "TEST"
    }
  }
]

Shipment and addresses

List Shipment

url_shipment = f'{icatplus_server}/tracking/{sessionId}/investigation/id/{investigationId}/shipment'
print(url_shipment)
http://lalex.esrf.fr:8000/tracking/8d4728d0-e84d-40fb-b6d2-543e296ba194/investigation/id/124564607/shipment
shipment = requests.get(f'{url_shipment}')
print(json.dumps(json.loads(shipment.text), indent=2))
[]

List addresses

url_address = f'{icatplus_server}/tracking/{sessionId}/investigation/id/{investigationId}/address'
print(url_address)
http://lalex.esrf.fr:8000/tracking/8d4728d0-e84d-40fb-b6d2-543e296ba194/investigation/id/124564607/address
addresses = requests.get(f'{url_address}')
print(addresses.text)
[]

Create address

address = {
  "name": "Name",
  "surname": "Surname",
  "companyName": "My Company",
  "address": "Rue Victor Hugo",
  "city": "Grenoble",
  "region": "Isere",
  "postalCode": "38000",
  "country": "France",
  "email": "myemail@email.com",
  "phoneNumber": "065222584"
}

address = requests.post(f'{url_address}', data=address)
print(json.dumps(json.loads(address.text), indent=2))
{
  "status": "ACTIVE",
  "_id": "649168052e95f7e77d8c8cae",
  "name": "Name",
  "surname": "Surname",
  "companyName": "My Company",
  "address": "Rue Victor Hugo",
  "city": "Grenoble",
  "region": "Isere",
  "postalCode": "38000",
  "country": "France",
  "email": "myemail@email.com",
  "phoneNumber": "065222584",
  "investigationName": "ID002306",
  "createdBy": "admin",
  "investigationId": 124564607,
  "createdAt": "2023-06-20T08:49:09.978Z",
  "updatedAt": "2023-06-20T08:49:09.978Z",
  "__v": 0
}

Create shipment

shipment = {
    "name": "my new Shipment",
    "investigationId":investigationId,
    "defaultReturnAddress": json.loads(address.text)["_id"],
    "defaultShippingAddress": json.loads(address.text)["_id"]
}
shipment = requests.post(f'{url_shipment}', data=shipment)
print(json.dumps(json.loads(shipment.text), indent=2))
{
  "status": "OPENED",
  "parcels": [],
  "_id": "649168062e95f7e77d8c8caf",
  "name": "my new Shipment",
  "investigationId": 124564607,
  "defaultReturnAddress": {
    "status": "ACTIVE",
    "_id": "649168052e95f7e77d8c8cae",
    "name": "Name",
    "surname": "Surname",
    "companyName": "My Company",
    "address": "Rue Victor Hugo",
    "city": "Grenoble",
    "region": "Isere",
    "postalCode": "38000",
    "country": "France",
    "email": "myemail@email.com",
    "phoneNumber": "065222584",
    "investigationName": "ID002306",
    "createdBy": "admin",
    "investigationId": 124564607,
    "createdAt": "2023-06-20T08:49:09.978Z",
    "updatedAt": "2023-06-20T08:49:09.978Z",
    "__v": 0
  },
  "defaultShippingAddress": {
    "status": "ACTIVE",
    "_id": "649168052e95f7e77d8c8cae",
    "name": "Name",
    "surname": "Surname",
    "companyName": "My Company",
    "address": "Rue Victor Hugo",
    "city": "Grenoble",
    "region": "Isere",
    "postalCode": "38000",
    "country": "France",
    "email": "myemail@email.com",
    "phoneNumber": "065222584",
    "investigationName": "ID002306",
    "createdBy": "admin",
    "investigationId": 124564607,
    "createdAt": "2023-06-20T08:49:09.978Z",
    "updatedAt": "2023-06-20T08:49:09.978Z",
    "__v": 0
  },
  "investigationName": "ID002306",
  "createdAt": "2023-06-20T08:49:10.054Z",
  "updatedAt": "2023-06-20T08:49:10.054Z",
  "__v": 0
}
shipment = requests.get(f'{url_shipment}')
shipmentId = json.loads(shipment.text)[0]["_id"]
print(json.dumps(json.loads(shipment.text), indent=2))
[
  {
    "status": "OPENED",
    "parcels": [],
    "_id": "649168062e95f7e77d8c8caf",
    "name": "my new Shipment",
    "investigationId": 124564607,
    "defaultReturnAddress": {
      "status": "ACTIVE",
      "_id": "649168052e95f7e77d8c8cae",
      "name": "Name",
      "surname": "Surname",
      "companyName": "My Company",
      "address": "Rue Victor Hugo",
      "city": "Grenoble",
      "region": "Isere",
      "postalCode": "38000",
      "country": "France",
      "email": "myemail@email.com",
      "phoneNumber": "065222584",
      "investigationName": "ID002306",
      "createdBy": "admin",
      "investigationId": 124564607,
      "createdAt": "2023-06-20T08:49:09.978Z",
      "updatedAt": "2023-06-20T08:49:09.978Z",
      "__v": 0
    },
    "defaultShippingAddress": {
      "status": "ACTIVE",
      "_id": "649168052e95f7e77d8c8cae",
      "name": "Name",
      "surname": "Surname",
      "companyName": "My Company",
      "address": "Rue Victor Hugo",
      "city": "Grenoble",
      "region": "Isere",
      "postalCode": "38000",
      "country": "France",
      "email": "myemail@email.com",
      "phoneNumber": "065222584",
      "investigationName": "ID002306",
      "createdBy": "admin",
      "investigationId": 124564607,
      "createdAt": "2023-06-20T08:49:09.978Z",
      "updatedAt": "2023-06-20T08:49:09.978Z",
      "__v": 0
    },
    "investigationName": "ID002306",
    "createdAt": "2023-06-20T08:49:10.054Z",
    "updatedAt": "2023-06-20T08:49:10.054Z",
    "__v": 0
  }
]

Create parcels

url_parcel = f'{icatplus_server}/tracking/{sessionId}/parcel?investigationId={investigationId}&shipmentId={shipmentId}'
print(url_parcel)
http://lalex.esrf.fr:8000/tracking/8d4728d0-e84d-40fb-b6d2-543e296ba194/parcel?investigationId=124564607&shipmentId=649168062e95f7e77d8c8caf
content =  [
          {
            "name": "My Container",
            "type": "CONTAINER",
            "containerType": {
              "containerName": "SPINEPUCK",
              "capacity": 10,
            },
            "content": [
              {
                "type": "TOOL",
                "name": "A tool within a container",
              },
            ],
          },
        ]
parcel = {
        "name": "Parcel created by investigation user",
        "description": "This parcel has been automatically generated by the unit tests",
        "shipmentId" : shipmentId,
        "comment": "This is the comment",
        "storageConditions": "Fridge",
        "content": [
          {
            "name": "My Container",
            "type": "CONTAINER",
            "containerType": {
              "containerName": "SPINEPUCK",
              "capacity": 10,
            },
            "content": [
              {
                "type": "TOOL",
                "name": "A tool within a container",
              },
            ],
          },
        ]
      }
parcelResponse = requests.put(f'{url_parcel}', json=(parcel))
print(parcelResponse)
<Response [200]>
print(requests.get(f'{url_parcel}').text)
[{"type":"DEFAULT","localContactNames":[],"items":[],"_id":"649168062e95f7e77d8c8cb4","name":"Parcel created by investigation user","description":"This parcel has been automatically generated by the unit tests","shipmentId":"649168062e95f7e77d8c8caf","storageConditions":"Fridge","content":[{"content":[{"type":"TOOL","name":"A tool within a container"}],"experimentPlan":[],"processingPlan":[],"_id":"649168062e95f7e77d8c8cb5","name":"My Container","type":"CONTAINER","containerType":{"_id":"649168062e95f7e77d8c8cb6","containerName":"SPINEPUCK","capacity":10,"id":"649168062e95f7e77d8c8cb6"},"createdAt":"2023-06-20T08:49:10.235Z","updatedAt":"2023-06-20T08:49:10.235Z","id":"649168062e95f7e77d8c8cb5"}],"investigationId":124564607,"statuses":[{"status":"CREATED","_id":"649168062e95f7e77d8c8cb7","createdBy":"admin","createdAt":"2023-06-20T08:49:10.235Z","updatedAt":"2023-06-20T08:49:10.235Z","createdByFullName":"adminFullName","id":"649168062e95f7e77d8c8cb7"},{"status":"SCHEDULED","_id":"649168062e95f7e77d8c8cb8","createdBy":"admin","createdAt":"2023-06-20T08:49:10.235Z","updatedAt":"2023-06-20T08:49:10.235Z","createdByFullName":"adminFullName","id":"649168062e95f7e77d8c8cb8"}],"currentStatus":"SCHEDULED","investigationName":"ID002306","createdAt":"2023-06-20T08:49:10.235Z","updatedAt":"2023-06-20T08:49:10.235Z","__v":0,"status":"SCHEDULED","investigation":{"name":"ID002306","startDate":"2023-06-19T13:28:44.492+02:00","id":124564607,"title":"ID002306","visitId":"id00","parameters":{"__datasetCount":"6","__sampleCount":"2","__fileCount":"0","__volume":"0","__elapsedTime":"0","__acquisitionDatasetCount":"4","__processedDatasetCount":"2","__acquisitionFileCount":"0","__processedFileCount":"0","__acquisitionVolume":"0","__processedVolume":"0"},"instrument":{"name":"ID00","id":92177647},"investigationUsers":[],"meta":{"page":{"totalWithoutFilters":1650,"total":1650,"totalPages":1,"currentPage":1}},"type":{"id":325,"createId":"root","createTime":"2014-06-18T15:00:43.500+02:00","modId":"root","modTime":"2014-06-18T15:00:43.500+02:00","description":"Test investigation","investigations":[],"name":"TEST"}},"localContactFullnames":[],"id":"649168062e95f7e77d8c8cb4","meta":{"page":{"total":1,"totalPages":1,"currentPage":1}}}]
print(json.dumps(json.loads(parcelResponse.text)[0]["content"]))
[{"content": [{"type": "TOOL", "name": "A tool within a container"}], "experimentPlan": [], "processingPlan": [], "_id": "649168062e95f7e77d8c8cb5", "name": "My Container", "type": "CONTAINER", "containerType": {"_id": "649168062e95f7e77d8c8cb6", "containerName": "SPINEPUCK", "capacity": 10, "id": "649168062e95f7e77d8c8cb6"}, "createdAt": "2023-06-20T08:49:10.235Z", "updatedAt": "2023-06-20T08:49:10.235Z", "id": "649168062e95f7e77d8c8cb5"}]