Skip to content

activityData

Stian Håklev edited this page Jun 14, 2017 · 3 revisions

Introduction

FROG scenarios are based on the flow of data, including socialStructures, configuration data, learning traces, etc. One important form of data is activityData, which is generated by students in activities (activity product), or retrieved from outside sources by operators (operator product), and can be transformed (aggregate, distribute, transform, etc.) by operators. We call operators able to produce activityData as their product, product operators.

activityData units

An activityData unit is a single unit of activityData, whose structure depends entirely on the needs of the activities producing and consuming it. A simple unit, the output of an editing tool, could be:

{ string: 'We think that leaves change color because the sun hits them, and they get burnt' }

However, it can also be a more complex structure, such as an object representing the result of a user filling out a form, or a list of objects. For example, a unit from a brainstorming activity could be:

{ xx:
   { id: 'xx',
     title: 'Recycling flowers',
     content: 'We can make jam out of old flowers',
     score: 0 },
  yy:
   { id: 'yy',
     title: 'Recycling bicycles',
     content: 'Recycling bicycles is a good idea',
     score: 2 } }

(Note that the above example could also have been an array, but the object, which also includes its id in the object, is a nice structure, which lets us map over the values of the object, but still easily remove or update individual list items).

If the unit is a primitive type (string/number) like in the first example, it should be packaged in an object like this:

{ string: 'This is what I edited' }

Specifying activityData unit shapes/types

We will introduce a way for operators and activities to specify the exact shape of the data that they can accept, and the data that they produce. The graph editor will verify that the dataflow is valid, ie. that all downstream activities are able to accept the data produced by upstream activities/operators. If you need to connect two nodes which produce incompatible data structures, you need to introduce an operator in the middle, which transforms the source data format into something legible by the target.

activityData mapped to social structures

An activityData structure is a set of activityData payloads mapped to a social structure. An activityData payload contains an optional activityData unit (data) and an optional configData object (config). There are three possibilities for the social mapping:

activityData mapped to an attributeKey

A activityData structure can be mapped to an attributeKey, for example, given the social structure described in social structure:

  structure: { groupingKey: 'role' },
  payload: {
    chef: {
      data: {
        xx: {
          id: 'xx',
          title: 'Recycling flowers',
          content: 'We can make jam out of old flowers',
          score: 0
        },
        yy: {
          id: 'yy',
          title: 'Recycling bicycles',
          content: 'Recycling bicycles is a good idea',
          score: 2
        }
      }
    },
    waiter: {
      data: {
        zz: {
          id: 'zz',
          title: 'Uber for rollerskates',
          content: 'Make it easier for people to get around',
          score: 4
        },
        yy: {
          id: 'rr',
          title: 'StackExchange for beekeeping',
          content: 'A sweet project to spread honey',
          score: 2
        }
      }
    }
  }
};

As another example, let us imagine that students have entered their reflections in small groups. An operator redistributes these reflections to other students, and another operator searches YouTube for relevant TED talks, based on semantic analysis of the reflections. The final activityData could look like the example below, and this could be fed into two different activities, one video player, which interprets the config data to load the correct video, and a text viewer that let's students read and comment upon the reflection of their peers:

{ structure: { groupingKey: 'group' },
  payload: {
    chef: {
      data: {
        string:
          'We believe that the 21st century learning skills will become increasingly important in the modern world.'
      },
      config: { 
        url: 'https://youtube.com/0df09f' 
      }
    },
    waiter: {
      data: {
        string:
          'Empathy and sympathy are both equally important in developing collaboration between people, and between nations'
      },
      config: { 
        url: 'https://youtube.com/349023' 
      }
    }
  }
};

activityData mapped to individual students

An activityData structure might specify a specific activityData unit per student:

{ structure: 'individual',
  payload: {
    aa: { data: { string: 'Your instructions are to paint a waterballon green' } },
    bb: { data: { string: 'Your instructions are to eat a watermelon quickly' } },
    cc: { data: { string: 'You need to find Waldo in the forest' } }
  }
};

A single activityData unit for all students

This is how to specify that a single activityData unit applies to the whole class

{ structure: 'class',
  payload: {
    data: {
      location: 'Lausanne',
      weather: '20C',
      altitude: 500,
      mayor: 'Grégoire Junod'
    }
  }
};

Mapping to planes (activities)

FROG has three planes, Plane 1 for individual activities, Plane 2 for team-based activities and Plane 3 for whole class activities. The incoming activityData to an activity must be at the same or lower level of granularity as the activity, for example:

  • all activities can receive a 'whole class' data structure
  • a groupingKey-mapped product
    • can be received by a team-based activity, in which case one of the groupingKeys must correspond to the groupingKey of the activity (this is enforced by the Graph Editor)
    • can be received by a Plane 1 activity, in which case all the students who match a certain groupingKey/Attribute pair will receive the corresponding activityData unit
  • only individual activities can receive activityData mapped to individual students

Config data

The examples above all have a payload consisting of data elements with mappings. Each activity also has a global config object, which is the result of the designer configuring the activity in the graph editor. This config object is merged with the config payload of products, before being sent to activities.

You could have a product which only modifies config, or one that contains both config and data payloads. For example, if we wanted two different groups to watch different videos, we would configure a video activity in the graph editor:

configData: { 
  url: 'https://youtube.com/vadsda',
  autoPlay: no,
  speed: '1x'
};

An operator could then produce the following output:

{ structure: { groupingKey: 'role' },
  payload: {
    chef: { config: { url: 'https://youtube.com/crazychef' },
            data:  [1, 2, 3] },
    waiter: { config: { url: 'https://youtube.com/servingnicely' },
              data: [2,3,4] }
  }
};

Merging this with the config above, would result in the activity instance for chefs receiving the following activityData as input:

{ config: 
  { url: 'https://youtube.com/crazychef' ,
    autoPlay: no,
    speed: '1x'},
  data: [ 1, 2, 3 ]
}
};

Data flow

Operators

Operators receive an object as input, which has the following structure:

{ activityData: activityData,
  socialStructure: socialStructure,
  globalStructure: globalStructure
};

The activityData structure is one or more fully mapped activityData units, as seen above.

Only a product operator can produce activityData as its product, which must be a single fully mapped activityData structure, as described above.

Activities

Each activity has access to an arbitrarily complex reactive data structure, which is scoped to its groupingKey, resulting in a number of separate instances of the same activity (a single instance for class-based activities, one instance per groupingAttribute for a team-based activity, and one instance per student for an individual activity).

When an activity is instantiated (when the teacher chooses 'next activity', and the activity is about to become activated), an optional activity.mergeFunction is called once for each instance, and takes two arguments, an instance-specific object, and a dataFn which can manipulate that instance's reactive data structure. The object received is

{ activityData: instanceSpecificActivityData,
  socialStructure: instanceSpecificSocialStructure,
  globalStructure: globalStructure
};

Each activity instance receives only the activityData unit relevant to its grouping and granularity, as well as the socialStructure of the members of the activity instance, with the primary grouping key removed.

The output product of each activity is taken from its reactive data structure. The activity can optionally provide an exportData function, which takes the reactive data object, and returns the activityData unit. All the activityData units from the activity instances are collected, and aggregated into a mapped activityData structure.

Helper functions

We should specify some helper functions that make it easier to work with activityData structures, for example something to map over a list that's implemented as a keyed object (as the first example), one to merge two activityData structures of the same type, etc.