Skip to content

Coral Onboarding

OwenGalvia edited this page Nov 30, 2023 · 12 revisions

Resource Models (Graphs)

Resource Models are how we group our data into types.
In arches a model contains a fixed set of cards. Each card has one or more nodes which collect one specific type of information.

card

In the above example the Archive Material card has 4 visible nodes.
Archive Source accepts a 'concept' which is a predefined list of related terms.
Repository Owner accepts a reference to different resources. In this case Person or Organization resources. The other two nodes only allow text.

Screenshot from 2023-11-30 11-00-30

Since a Resource Model is a collection of these cards it means they control exactly what kinds of data are allowed in a resource of that type.
Resources can only have data from one resource model so I like to imagine them as being stored together in their own filing cabinets.

Screenshot from 2023-11-30 10-47-22

A resource is therefore a folder holding completed cards.

Workflows

HED has a lot of processes which are interconnected and may involve multiple resource models.
Also, different responsibilities for the same resource model can be divided amongst separate teams.
This means some cards on a resource model will never be used by some users but are essential for others.
HED have concerns that staff will get confused with witch fields they are meant to fill in and which are only supposed to be filled in by other teams.
Furthermore, some processes are carried out over a long period of time and different actions are needed at different stages of the process. This could leave staff unsure of which fields to update when carrying out different stages of the process.

In order to prevent these confusions Coral uses a lot of workflows.
Screenshot from 2023-11-30 11-13-12

User's can select the process they are completing and will be presented with the relevant cards to fill out. Screenshot from 2023-11-30 11-16-16

Workflows are split into 'steps' which logically organise the cards rendered on each page.
The designs for these steps are usually given to developers via penpot.


Screenshot from 2023-11-30 11-23-41

and we do our best to recreate these in arches.
Screenshot from 2023-11-30 11-26-28

The basics

To create a workflow, first we define the modules we will be using.
This tends to be knockout, arches, the workflow viewmodel, and the html for the workflow.

We make sure we register the component with a name that is not used by another component. A workflow will return the javascript logic for creating the workflow and the template provided.

// coral-arches/coral/coral/media/js/views/components/plugins/assign-consultation-workflow.js
define([
  'knockout',
  'arches',
  'viewmodels/workflow',
  'templates/views/components/plugins/default-workflow.htm', // this will work unless you need custom html for a workflow
], function (ko, arches, Workflow, workflowTemplate) {
  return ko.components.register('archive-catalog-workflow', {
    viewModel: function (params) {
      this.componentName = 'archive-catalog-workflow';
      this.stepConfig = [
        ... // step logic will go here
      ];

      Workflow.apply(this, [params]); // it's important to include the logic from the workflow viewmodel like so

      this.quitUrl = arches.urls.plugin('init-workflow');
    },
    template: workflowTemplate
  });
});

Now we can start configuring the steps. The first step in coral tends to be an initial step. Some of these can be quite complex. A simple one can be created like below.

...
this.stepConfig = [
        {
          title: 'Initialise Archive Source',
          name: 'init-step',  // init-step and app-id are consistent naming conventions currently. Please try to use them for your own workflows.
          required: true,
          informationboxdata: {
            heading: 'Important Information',
            text: 'Boiler plate text displayed in the steps header.'
          },
          layoutSections: [
            {
              componentConfigs: [
                {
                  componentName: 'default-card',
                  uniqueInstanceName: 'app-id', // init-step and app-id are consistent naming conventions currently.
                  tilesManaged: 'one',
                  parameters: {
                    graphid: 'b07cfa6f-894d-11ea-82aa-f875a44e0e11',
                    nodegroupid: '3bdc39f8-9a93-11ea-b807-f875a44e0e11',
                  }
                }
              ]
            }
          ]
        },
      ];
...

Each step needs a title which will be displayed and a name that identifies it during the workflow logic. The name must be unique within each workflow or else there can be issues with reloading pages and potentially how data gets saved.

The required paramater dictates whether a user is allowed to move on in the workflow without saving a change during this step.

Then we place however many cards as we need for this step as componentConfigs in the layoutSections.
uniqueInstanceName is the card equivilent to the steps 'name' field. It is used to identify this card within the workflow logic. If you reusue this name you will duplicate one of the cards, even if the other information is different.
This paramater is very important because it's how we ensure data entered in later steps is sent to the correct resource and tile.

...
  this.stepConfig = [
        {
          title: 'Initialise Archive Source',
          name: 'init-step',
          required: true,
          informationboxdata: {
            heading: 'Important Information',
            text: 'Boiler plate text displayed in the steps header.'
          },
          layoutSections: [
            {
              componentConfigs: [
                {
                  componentName: 'default-card',
                  uniqueInstanceName: 'app-id',
                  tilesManaged: 'one',
                  parameters: {
                    graphid: 'b07cfa6f-894d-11ea-82aa-f875a44e0e11',
                    nodegroupid: '3bdc39f8-9a93-11ea-b807-f875a44e0e11',
                    // no resourceid or tileid are specified so arches will create new ones
                  }
                }
              ]
            },
            {
          title: 'Archive Source Details',
          name: 'app-details-step',
          required: true,
          workflowstepclass: 'workflow-form-component',
          layoutSections: [
            {
              componentConfigs: [
                {
                  componentName: 'widget-labeller',
                  uniqueInstanceName: 'source-title',
                  tilesManaged: 'one',
                  parameters: {
                    graphid: 'b07cfa6f-894d-11ea-82aa-f875a44e0e11',
                    nodegroupid: '145f9615-9ad2-11ea-b4d3-f875a44e0e11',
                    /**
                     * We want the resourceid for this card to be the same as the resourceid for the 'app-id' card in the 'init-step' step
                     * The full path is a little confusing because arches changes its case frequently and object structures aren't always obvious.
                     * */
                    resourceid: "['init-step']['app-id'][0]['resourceid']['resourceInstanceId']",
                    // there is still no tile specified so data filled into this card will create a new tile within the same resource instance as the initial step
                  }
                },
              ]
            }
          ]
        },
      ];
    }
  ]
...

componentName decides how this card should be rendered.
You can decide on any of the components which are registered to django. (usually either a card or a widget component)

owen@debian:~/codebook/coral/coral-arches$ djmang card_component list
Default Card
Grouping Card
Related Resources Map Card
File Viewer
Photo Gallery Card
IIIF Card
Map Card
owen@debian:~/codebook/coral/coral-arches$ djmang widget list
file-widget
concept-select-widget
concept-radio-widget
map-widget
concept-multiselect-widget
concept-checkbox-widget
number-widget
resource-instance-select-widget
resource-instance-multiselect-widget
datepicker-widget
rich-text-widget
text-widget
domain-select-widget
domain-radio-widget
domain-multiselect-widget
domain-checkbox-widget
radio-boolean-widget
iiif-widget
switch-widget
node-value-select
edtf-widget
urldatatype
cover-letter-widget
check-open-applications
map-with-latlon-widget
sample-widget
bngpoint
auto-generate-id-widget
photo-widget

or if you have a custom component that hasn't been registered you can define it before hand.

define([
  'knockout',
  'arches',
  'viewmodels/editable-workflow',
  'templates/views/components/plugins/default-workflow.htm',
  'views/components/workflows/archive-storage-card' // defined a custom component here
], function (ko, arches, EditableWorkflow, workflowTemplate) {
  return ko.components.register('archive-catalog-workflow', {
    viewModel: function (params) {
      this.componentName = 'archive-catalog-workflow';
      this.stepConfig = [
        ...
        {
          title: 'Repository Storage Location',
          name: 'app-location-step',
          required: false,
          layoutSections: [
            {
              componentConfigs: [
                {
                  componentName: 'archive-storage-card', // so we can use it here
                  uniqueInstanceName: 'source-location',
                  tilesManaged: 'one',
                  parameters: {
                    graphid: 'b07cfa6f-894d-11ea-82aa-f875a44e0e11',
                    nodegroupid: 'c69aa19d-fe7b-11ea-9e10-f875a44e0e11',
                    resourceid: "['init-step']['app-id'][0]['resourceid']['resourceInstanceId']",
                    parenttileid: "['app-details-step']['archive-holding'][0]['tileId']",
                  }
                }
              ]
            }
          ]
        },
        ...
      ]
      Workflow.apply(this, [params]);

      this.quitUrl = arches.urls.plugin('init-workflow');
    },
    template: workflowTemplate
  });
});

Widgets will only work for one specified datatype so if you try to use a widget on a node of a different datatype you will get rendering errors.
Card components usually have rules on how to render different datatypes but there can still be rendering errors if a card is selected that isn't sutable for the nodes.

default-card is the easiest to use since it essentially means for every widget inside the card render it in the default way.

This default is selected in the graph_designer.

widget

Here we can see the defaults for a node with the string datatype.
There are different rules for how the widget is rendered and how the user inputs data but the data stored in that tile will always be a string.

tilesManaged decides whether we want the user to be able to fill in 'one' or 'many' tiles. It is important to note that, in order to use 'many' you must make sure the card is for a nodegroup which has multiple cardinality.

Then within paramaters: graphid specifies which resource model this card is associated with.
nodegroupid specifies the actual card (collection of nodes) to be rendered.
There are also resourceid; tileid; parenttileid paramaters which informs arches of the structure the data are in.

default-card-util

In coral, we have created another commonly used card which inherits from default-card but adds more flexibility on how to render cards and handle their logic.

  {
    componentName: 'default-card-util',
    uniqueInstanceName: 'source-subtitle',
    tilesManaged: 'one',
    parameters: {
      graphid: 'b07cfa6f-894d-11ea-82aa-f875a44e0e11',
      nodegroupid: '145f9615-9ad2-11ea-b4d3-f875a44e0e11',
      /**
       * hiddenNodes allows us to select the widgets we do not want to render on this occaision
       */
      hiddenNodes: [
        '145f9619-9ad2-11ea-83ec-f875a44e0e11',
        '145f961a-9ad2-11ea-948a-f875a44e0e11',
        '145f9618-9ad2-11ea-925e-f875a44e0e11'
      ],
      resourceid: "['init-step']['app-id'][0]['resourceid']['resourceInstanceId']",
      /**
       * labels allows us to change the title of the card. This is useful for customising how the  * same node is displayed for different teams or use cases
       */
      labels: [['Archive Source Name', 'Subtitle']],
      /**
       * prefilledNodes allows us to set values on behalf of our users.
       * we commonly use this to automatically fill in 'type' nodes
       * in this case we have prefilled the node for 'Archive Source Name Type' with the concept id * for 'subtitle'
       */
      prefilledNodes: [
        [
          '145f9618-9ad2-11ea-925e-f875a44e0e11',
          'c109d688-2ff4-45c5-873c-1a2ebc93d4fa'
        ]
      ]
    },
    ...
    {
                  componentName: 'default-card-util',
                  uniqueInstanceName: 'archive-holding',
                  tilesManaged: 'one',
                  parameters: {
                    graphid: 'b07cfa6f-894d-11ea-82aa-f875a44e0e11',
                    nodegroupid: '66576cda-9b69-11ea-9143-f875a44e0e11',
                    resourceid: "['init-step']['app-id'][0]['resourceid']['resourceInstanceId']",
                    hiddenNodes: [
                      '66576cda-9b69-11ea-9143-f875a44e0e11',
                      'e31709dd-b469-11ea-ae1c-f875a44e0e11',
                      'c69aa19d-fe7b-11ea-9e10-f875a44e0e11',
                      '3001afdf-b46a-11ea-a528-f875a44e0e11'
                    ],
                    /**
                     * hiddenCard allows us to not render the card at all.
                     * Here this is used to have an archive holding nodegroup to be instantiated
                     * This will allow nodegroups in future steps to use
                     * parenttileid: "['app-details-step']['archive-holding'][0]['tileId']"
                     * This is also commonly used for location Data where cards such address and geometries cards both need to
                     * belong to location data nodegroup
                     */
                    hiddenCard: true
                  }
                }
  },

workstepclass

Advanced Initial Steps

custom components

related-document-upload

Useful Scripts

graph-component-export endpoint

create-workflow script

cli util

djmang coral reload

Clone this wiki locally